Temporal Rules
Temporal rules evaluate behavioral patterns over sliding time windows of pipeline events. Where static policy checks evaluate each action in isolation, temporal rules detect patterns across many actions — a sudden spike in simulation failures, a spend velocity exceeding a threshold, or a flood of approval requests.
Setup
import { createAgent, createMemoryEventStore } from '@txfence/core'
const eventStore = createMemoryEventStore({
maxWindowMs: 86_400_000, // 24 hours (default)
maxEvents: 10_000, // (default)
})
const agent = createAgent(
config, adapters, rpcUrls, executor,
undefined, undefined, undefined, undefined, // 5–8: capLockProvider, metadataVerifier, approvalProvider, receiptStore
undefined, undefined, undefined, undefined, // 9–12: auditLog, telemetryProvider, capLockConfigs, policyNode
undefined, undefined, // 13–14: notificationProvider, provenanceChain
eventStore, // 15: eventStore
'agent-0x123', // 16: agentId — used for scoped predicates
)Policy with temporal rules
import type { Policy } from '@txfence/core'
const policy: Policy = {
// ...static fields...
temporalRules: [
{
predicate: {
kind: 'simulation_failure_rate',
windowMs: 3_600_000, // 1 hour
threshold: 3,
},
consequence: { kind: 'require_approval' },
label: 'sim-failure-guard',
},
{
predicate: {
kind: 'spend_velocity',
windowMs: 1_800_000, // 30 minutes
maxAmount: 50_000n,
token: 'USDC',
},
consequence: { kind: 'reject' },
label: 'velocity-limit',
},
],
}Temporal rules are evaluated after static policy checks and before simulation.
Six predicate kinds
simulation_failure_rate
Triggers when simulation failure count meets threshold in window. Agent-scoped when agentId is provided.
{ kind: 'simulation_failure_rate', windowMs: 3_600_000, threshold: 3 }contract_call_frequency
Triggers when a specific contract is called too many times in window.
{ kind: 'contract_call_frequency', windowMs: 300_000, contractAddress: '0x...', threshold: 10 }success_drought
Triggers when success count falls below threshold in window — indicates a stalled agent.
{ kind: 'success_drought', windowMs: 3_600_000, threshold: 1 }spend_velocity
Triggers when cumulative spend (historical + current action) exceeds limit in window.
{ kind: 'spend_velocity', windowMs: 1_800_000, maxAmount: 50_000n, token: 'USDC' }consecutive_failures
Triggers when the last N pipeline events are all non-success, regardless of time.
{ kind: 'consecutive_failures', count: 5 }approval_flood
Triggers when approval timeout count meets threshold in window.
{ kind: 'approval_flood', windowMs: 3_600_000, threshold: 3 }TemporalConsequence
Three consequence kinds:
type TemporalConsequence =
| { kind: 'require_approval' }
| { kind: 'reject' }
| { kind: 'flag_for_review' }EventStore interface
All four methods are synchronous — no Promises. The store is meant to be hit at every pipeline event with negligible overhead.
type EventStore = {
record: (event: PipelineEvent) => void
query: (filter?: EventFilter) => PipelineEvent[]
prune: (olderThan: number) => void // ms timestamp; events older than this are dropped
recent: (
n: number, // number of events, not a window
filter?: Omit<EventFilter, 'from' | 'to' | 'windowMs'>,
) => PipelineEvent[]
}EventFilter lets query() slice the window by agent, chain, outcome status, contract, or time range:
type EventFilter = {
agentId?: string
chain?: ChainId
status?: PipelineEventOutcome['status']
contractAddress?: string
from?: number // ms timestamp lower bound
to?: number // ms timestamp upper bound
windowMs?: number // relative window — "events in the last N ms"
}Each recorded event:
type PipelineEvent = {
id: string
timestamp: number
agentId: string
chain: ChainId
action: Action
outcome: PipelineEventOutcome
spendAmount?: bigint
}The pipeline records events automatically after each run — recording failures are swallowed so event-store errors never crash the pipeline.
The temporal_rule_triggered rejection reason appears in audit log entries and policy evaluations when a temporal rule fires.