Action Trace Export

June 18, 2026 · View on GitHub

agent-kernel records an ActionTrace for every invocation. The trace export contract turns those records into a stable, JSON-serialisable shape that an external tool can consume — for example a LessonWeaver-style lesson-extraction layer that learns from past actions, policies, denials, corrections, and outcomes.

from weaver_kernel import export_action_traces

envelope = export_action_traces(kernel.list_traces())

Runnable companion: examples/trace_export_demo.py.

How this differs from OpenTelemetry export

agent-kernel also ships an OpenTelemetry integration (weaver_kernel.otel, pip install weaver-kernel[otel]). The two serve different consumers and do not compete:

OpenTelemetry (instrument_kernel)Trace export (export_action_traces)
ConsumerLive observability backends (traces/metrics)Offline analysis / learning tools
ShapeOTel spans + metrics, vendor-definedStable JSON envelope defined here
TimingEmitted during executionPulled after the fact from the TraceStore
StabilityTracks OTel semantic conventionsVersioned by TRACE_EXPORT_VERSION

Use OTel for dashboards and alerting; use the export contract when another program needs a durable, replayable record of what the agent did.

Privacy

The export is derived only from fields the ActionTrace already holds, all of which are redaction-safe by construction:

  • args has memory payloads stripped at record time (keys like payload, content, value, memory, text, body for memory.* capabilities become "[REDACTED]").
  • result_summary carries counts and flags taken from the post-firewall Frame — never raw driver data.

The contract adds no field the trace did not already carry, so exporting can never widen the I-01 firewall boundary or leak sensitive payloads. A denied request never produces an ActionTrace — policy gates before invocation (I-02) — so the export only ever describes authorised invocations; denials are surfaced separately via PolicyDenied / Kernel.explain_denial.

Envelope shape

export_action_traces(...) returns a versioned envelope:

{
  "schema": "weaver_kernel.action_trace_export",
  "version": "1",
  "traces": [ /* one object per ActionTrace */ ]
}

Each trace object:

FieldTypeNotes
action_idstringUnique id; matches Kernel.explain(action_id).
capability_idstringThe capability (tool) that was invoked.
principal_idstringWho invoked it.
token_idstringThe capability token used.
invoked_atstringISO 8601 timestamp.
response_modestringsummary / table / handle_only / raw.
driver_idstringDriver that served the call ("" on failure).
handle_idstring | nullHandle for the full dataset, if one was minted.
sensitivitystringNONE / PII / PCI / SECRETS / MEMORY.
statusstringsucceeded or failed (derived from error).
errorstring | nullFailure reason; null on success.
argsobjectRedacted invocation arguments.
result_summaryobject | nullPost-firewall counts/flags; null on failure.
correctionobject | nullOptional human-correction metadata (see below).

Human corrections

agent-kernel does not record human corrections itself. A downstream tool can attach them at export time by passing a mapping of action_id → metadata:

envelope = export_action_traces(
    traces,
    corrections={"act-123": {"corrected_by": "reviewer", "note": "wrong customer"}},
)

Example output

{
  "schema": "weaver_kernel.action_trace_export",
  "version": "1",
  "traces": [
    {
      "action_id": "0a1b...",
      "capability_id": "billing.list_invoices",
      "principal_id": "agent-007",
      "token_id": "f3c2...",
      "invoked_at": "2026-06-05T12:00:00+00:00",
      "response_mode": "summary",
      "driver_id": "billing",
      "handle_id": "9d7e...",
      "sensitivity": "PII",
      "status": "succeeded",
      "error": null,
      "args": {"operation": "list_invoices", "status": "paid"},
      "result_summary": {"fact_count": 4, "row_count": 0, "warning_count": 1, "has_handle": true},
      "correction": null
    },
    {
      "action_id": "5e6f...",
      "capability_id": "billing.flaky_report",
      "principal_id": "agent-007",
      "token_id": "11aa...",
      "invoked_at": "2026-06-05T12:00:01+00:00",
      "response_mode": "summary",
      "driver_id": "",
      "handle_id": null,
      "sensitivity": "NONE",
      "status": "failed",
      "error": "Handler for operation='flaky_report' raised: reporting backend is unavailable",
      "args": {"operation": "flaky_report"},
      "result_summary": null,
      "correction": {"corrected_by": "on-call", "note": "known outage; retried later"}
    }
  ]
}

The envelope also carries event_type (invoke/expand/deny) and, for denials, a stable reason_code (#175), so an exported trail distinguishes invocations, handle expansions, and policy denials.

Stability

TRACE_EXPORT_VERSION is bumped only on a breaking change to the field shape. New optional fields may be added without a bump, so consumers should ignore unknown keys. Assert on status, sensitivity, and the presence of error rather than on human-readable strings (the error text itself may evolve).

  • Querying the trail: Kernel.query_traces(TraceQuery(...)) filters by principal, capability, event type, outcome, reason code, and time window — see architecture.md.
  • SIEM export: traces_to_ocsf() renders the trail as OCSF/AOS events for a security pipeline — see integrations.md. The OTel observability export is distinct from both (live spans/metrics).