Observability
The runtime emits a typed event for every state transition. The event log is append-only, hash-chained, and signable. Every other observability concern — debugging, replay, audit, BI — is a projection.
The event bus
agent.events.on('*', (event) => {
console.log(event.type, event.runId, event.turnId, event.payload);
});
// Filtered:
agent.events.on('policy_decision', (e) => alert(e.payload.verdict));
agent.events.on('tool_executed', (e) => metrics.inc('tool.calls'));Event taxonomy (non-exhaustive):
run_started, run_completed, run_failed
turn_started, turn_completed
context_compiled
model_call_started, model_call_completed
tool_proposed, tool_executed
policy_decision, policy_violation
approval_requested, approval_resolved
memory_proposed, memory_written, memory_retrieved
checkpoint_saved
handoff, transport_request
security_event, errorEvery event has:
{ id, runId, turnId?, timestamp, type, payload, contentHash, parentHash? }parentHash = sha256(canonical(previousEvent)) creates a hash chain — tampering with any event breaks subsequent verification.
Streaming to a UI
import { useTrace } from '@veridex/agents-react';
function TraceView({ runId }: { runId: string }) {
const { events } = useTrace({ runId, types: ['tool_proposed', 'tool_executed', 'policy_decision'] });
return events.map(e => <Row key={e.id} event={e} />);
}The provider streams over SSE (default) or WebSocket. High-frequency events (token deltas) are batched.
Signed Evidence Bundles
For external disclosure, the EvidenceBundler (in @veridex/agents-treasury, generalised in core) collects the relevant events for a workflow and signs them:
import { EvidenceBundler, Ed25519EvidenceSigner } from '@veridex/agents-treasury';
const bundler = new EvidenceBundler({
signer: new Ed25519EvidenceSigner({ privateKey, publicKey, keyId: 'ops-2026' }),
portal: portalTelemetry,
});
bundler.recordTrace(traceEvent);
bundler.recordPolicyVerdict(verdict);
bundler.recordApproval(approval);
bundler.recordProposal(proposal);
bundler.recordChainTx(receipt);
const bundle = await bundler.finalize({ workflowId: 'transfer-...', submit: true });Verification:
import { verifyEvidenceBundle } from '@veridex/agents-treasury';
await verifyEvidenceBundle(bundle, { publicKey });
// throws on signature mismatch or content tamperSigners: NoopEvidenceSigner (dev), HmacEvidenceSigner (shared-secret), Ed25519EvidenceSigner (public-verifiable), or your own.
Filtering, retention, export
// Filter the log:
const audit = await agent.trace.query({
runId, types: ['policy_decision', 'approval_resolved', 'tool_executed'],
});
// Export:
const jsonl = await agent.exportRun(runId);
fs.writeFileSync('run.jsonl', jsonl);The control plane (ADR-0061) enforces per-tenant retention; events past retention are summarised, hash-anchored, and deleted.
Tamper detection
A single event's contentHash mismatch invalidates every later event's parentHash. Verifying the chain is a single linear pass:
import { verifyEventChain } from '@veridex/agents';
const { ok, brokenAt } = verifyEventChain(events);
if (!ok) throw new Error(`Tamper detected at event ${brokenAt}`);Optional transparency-log anchoring (periodic chain roots committed to an external log) defends against compromise of the log storage itself.
Cost & metrics
import { useBudget } from '@veridex/agents-react';
function Budget({ runId }: { runId: string }) {
const { tokensIn, tokensOut, dollars, toolCalls } = useBudget({ runId });
return <Card cost={dollars} tokens={tokensIn + tokensOut} calls={toolCalls} />;
}Per-tenant, per-policy-pack-version aggregates surface in the control plane.