Exported spans

Plugin attribution is span-only. It does not add a plugin dimension to shared OpenTelemetry metrics or change Prometheus metric labels.

When content capture is explicitly enabled, model and tool spans can also include bounded, redacted apus.content.* attributes for the specific content classes you opted into.

Diagnostic event catalog

The events below back the metrics and spans above. Public events are also available for direct plugin subscription; trusted core events such as model.usage are restricted to authorized internal consumers. run.progress and run.execution_phase are direct-only lifecycle signals; the diagnostics-otel plugin does not export them as standalone OTLP signals. Event kinds and run.execution_phase.phase values are additive. TypeScript consumers should keep default branches instead of assuming either union is permanently exhaustive.

agent.commentary records completed preambles, not text deltas. It carries the original agent-event sequence and timestamp and attaches to the active harness span. Recent duplicate completions are suppressed per attempt. Like other queued diagnostics, commentary may be dropped under queue pressure; the session transcript remains the durable conversation record. At debug level, the diagnostic logger also records completion metadata without commentary text.

Model usage

model.usage is a trusted, in-process diagnostic event, not a JSONL log record. A representative event has this shape:

{
  "type": "model.usage",
  "ts": 1735689600000,
  "seq": 42,
  "provider": "openai",
  "model": "gpt-5.4",
  "channel": "webchat",
  "agentId": "main",
  "sessionId": "session-123",
  "sessionKey": "agent:main:main",
  "usage": {
    "input": 120,
    "output": 40,
    "cacheRead": 30,
    "cacheWrite": 10,
    "promptTokens": 160,
    "total": 200
  },
  "lastCallUsage": {
    "input": 120,
    "output": 40,
    "cacheRead": 30,
    "cacheWrite": 10,
    "total": 200
  },
  "context": { "limit": 128000, "used": 160 },
  "costUsd": 0.0012,
  "durationMs": 850,
  "trace": {
    "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
    "spanId": "00f067aa0ba902b7",
    "traceFlags": "01"
  }
}

The Gateway's /tmp/apus/apus-YYYY-MM-DD.log JSONL file and diagnostics.otel.logsExporter: "stdout" contain ordinary log records, not raw model.usage events. Public diagnostic subscriptions and diagnostics.stability do not expose trusted core usage events. The diagnostics-otel plugin converts them to metrics such as apus.tokens and apus.cost.usd and to apus.model.usage spans; those usage metrics and spans intentionally omit session identifiers.

For an external integration that needs session-correlated usage, query the authenticated Gateway instead:

apus gateway call sessions.usage --params '{"range":"30d","agentScope":"all"}' --json
apus gateway usage-cost --days 30 --all-agents --json

Both commands require operator.read. sessions.usage can include per-session sessionId, provider/model details, and token/cost summaries; per-session usage can be temporarily null while its cache refreshes. usage-cost provides aggregate estimates. Omit agentScope or --all-agents to scope the report to the default agent. For continuously updated clients, subscribe to session changes instead of polling usage reports. See the Gateway RPC method reference for usage methods and request options.

Message flow

Gateway RPC

Queue and session

Harness lifecycle

Exec