Skip to content

Traces

The trace format the agent platform uses everywhere is the activity frame — a small structured record emitted on every cognitive step of an agent loop. Frames stream from AgentActor to its activityTo sinks (Director, Chat, observability) and are also persisted on the session for replay.

ActivityFrame shape

From IAgentSessionStore.ts:85-103:

ts
interface ActivityFrame {
  requestId: string;
  seq: number;            // monotonic per request
  ts: number;             // epoch-ms
  source: string;         // emitting mount (e.g. system.agents.<target>)
  traceId?: string;
  spanId?: string;
  parentSpanId?: string;
  sessionId?: string;
  resumeId?: string;
  principalId?: string;
  kind: string;           // see "Kinds" below
  phase: string;          // 'start' | 'progress' | 'complete' | 'error'
  summary?: string;       // short human label
  target?: string;        // for tool frames: the invoked mount
  op?: string;            // for tool frames: the invoked op
  data?: unknown;         // kind-specific payload
  durationMs?: number;
}

Frames are normalized: regardless of whether the underlying event came from the internal oracle, a Claude Code worker, or a Codex worker, the consumer sees the same envelope.

Kinds (canonical set)

From NormalizedActivityEvent.ts (NormalizedActivityKind):

  • prompt.in — the user/caller prompt arrived
  • inference.start / inference.token / inference.complete — model call lifecycle
  • thinking — extended-thinking output (Anthropic) / reasoning summary (Codex)
  • tool.call — model requested a tool
  • tool.result — tool returned
  • approval.required — model needs human approval (paired with approval.granted/approval.denied)
  • response.partial / response.final — streamed and final assistant text
  • error — terminal failure
  • session.bootstrap — session created or resumed

NormalizedActivityPhase is start | progress | complete | error.

Where frames come from

RuntimeNormalizerNotes
Internal oracle (InternalOracleRuntimeAdapter)emits frames directly via OracleSink callbacksthe executeActorOracle → sink → frame path
Claude Code workerClaudeEventNormalizer (activity/ClaudeEventNormalizer.ts)maps Anthropic SDK event types to frames
Codex workerCodexEventNormalizer (activity/CodexEventNormalizer.ts)maps Codex SDK event types to frames

Where frames go

Three sinks are wired today:

  1. Caller mount. Every frame is published to activityTo[] mounts on the bus. Director and Chat subscribe.
  2. Session store. IAgentSessionStore.appendActivityFrames(sessionId, requestId, frames) persists each request's frames. Two backends: FileAgentSessionStore (filesystem under <host-home>/sessions/) and RecordBackedSessionStore (observability record store).
  3. $traceQuery. AgentsRoot.$traceQuery is the read accept that returns activity frames for a given session/request — this is the same surface a future Director timeline UI would call.

Trace IDs

Frames carry OpenTelemetry-aligned IDs (traceId, spanId, parentSpanId). When system.agents is called from another agent (delegated agency), the parentSpanId chains the trace across the bus boundary so a single user prompt that fans out to multiple agents can be reconstructed end-to-end.

See also

Source: projects/matrix-3/packages/agents/src/IAgentSessionStore.ts:85-103, projects/matrix-3/packages/agents/src/activity/NormalizedActivityEvent.ts, normalizers under projects/matrix-3/packages/agents/src/activity/.