anomalyco / anomalyco/opencode

[FEATURE]: Add stable agent lifecycle telemetry

Open
#47,701 1 comment 0 reactions 1 assignee View on GitHub

@jlongster is already working on this.

Since Sep 7, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

Feature hasn't been suggested before.
  • I have verified this feature I'm about to request hasn't been suggested before.
Describe the enhancement you want to request

We already collect Claude Code telemetry for enterprise audit. We use it to review prompts, tool inputs, permission decisions, MCP use, token consumption, and cost.

OpenCode exports internal Effect logs and spans, but those records do not provide stable agent lifecycle events. We cannot reconstruct the same activity without depending on internal span names or incomplete plugin events.

Basic OTLP transport already exists. This request is about the lifecycle data sent through that transport.

Requested decision

Would the core team accept a stable lifecycle event contract for prompts, model requests, tools, permissions, MCP calls, skills, and subagents?

One option is a typed core event contract that feeds OpenTelemetry logs, traces, and metrics. Exporters and compatibility mappings can remain separate changes.

Why this needs core support

A plugin can observe public events, but it cannot reliably observe every runtime boundary or preserve active trace context.

Core owns these facts:

  • Model request start, result, refusal, retry, and cancellation
  • Tool input, permission wait, execution, result, and cancellation
  • Provider-executed tools
  • MCP connection and tool activity
  • Skill and plugin loading
  • Foreground and background subagent relationships
  • Token usage and cost

The existing public event stream can remain unchanged.

Common event data

Each lifecycle event needs:

  • A schema version, event ID, timestamp, and session sequence number
  • Applicable session, turn, message, request, tool, permission, MCP, and subagent IDs
  • Trace and span IDs when tracing is active
  • OpenCode version, client, and process run ID
  • The applied content policy

OpenCode does not know an enterprise user or organization by default. Operators can add optional resource metadata:

OTEL_RESOURCE_ATTRIBUTES='user.email=user@example.com,organization.id=example'

OpenCode would treat these values as operator-supplied metadata. It would not infer identity from Git settings, paths, or prompts.

Proposed event coverage
Area Event Main data
Session opencode.session.created location, agent, model
Session opencode.session.resumed reason
Session opencode.session.forked parent session, reason
Session opencode.session.moved old and new locations
Session opencode.session.ended reason, duration, result
Prompt opencode.user_prompt.submitted delivery, agent, model, attachments
Prompt opencode.user_prompt.admitted queue duration, delivery
Response opencode.assistant_response.completed finish reason, duration
Model opencode.model_request.started provider, model, attempt
Model opencode.model_request.completed duration, usage, cost, finish reason
Model opencode.model_request.failed duration, category, retryable
Model opencode.model_request.refused duration, refusal category
Model opencode.model_retry.scheduled attempt, delay, category
Model opencode.model_retries.exhausted attempts, category
Tool opencode.tool.input.completed name, source
Permission opencode.tool.permission.requested action, resources, matched rule
Permission opencode.tool.permission.decided decision, source, wait duration
Tool opencode.tool.execution.started name, source, provider-executed flag
Tool opencode.tool.execution.completed duration, result summary, changed files
Tool opencode.tool.execution.failed duration, error category
MCP opencode.mcp.connection.started server, transport, scope
MCP opencode.mcp.connection.completed server, duration
MCP opencode.mcp.connection.failed server, duration, category
MCP opencode.mcp.disconnected server, reason
MCP opencode.mcp.tool.discovered server, tool
Skill opencode.skill.discovered name, source, version, digest
Skill opencode.skill.loaded name, source, version, result
Skill opencode.skill.activated name, activation source
Plugin opencode.plugin.loaded name, source, version, result
Plugin opencode.plugin.failed name, source, error category
Subagent opencode.subagent.started agent, model, parent tool, foreground flag
Subagent opencode.subagent.completed duration, usage, cost, result
Subagent opencode.subagent.failed duration, error category
Subagent opencode.subagent.cancelled duration, reason
Compaction opencode.compaction.started reason, input tokens
Compaction opencode.compaction.completed reason, duration, token counts
Permission opencode.permission_mode.changed old mode, new mode, trigger
Authentication opencode.auth.completed action, method, result
Internal opencode.internal_error component, category, recoverable

Tool events would identify core, plugin, mcp, or provider as their source. MCP tool events would also identify their server.

Privacy requirements

Prompts, responses, reasoning, tool data, model bodies, paths, and errors can contain sensitive information.

Each content class needs an independent none, metadata, or full policy. Every class would default to none.

The publisher would apply the policy before export. Raw content would never appear in metric attributes or span names.

Trace shape

Each admitted prompt would create one bounded interaction trace:

opencode.interaction
├── opencode.model_request
├── opencode.tool
│   ├── opencode.tool.blocked_on_user
│   └── opencode.tool.execution
├── opencode.mcp.request
└── opencode.subagent

Turns would use span links instead of one session-long trace. Background subagents would link to their parent tool span.

Model spans would use applicable OpenTelemetry gen_ai.* conventions. OpenCode-specific attributes would use opencode.* names.

Metrics

The first metrics would cover request count and duration, tokens, cost, tool results, permission decisions, MCP connections, and subagent results.

Metric attributes would exclude identifiers, paths, commands, prompts, and tool content.

Claude Code as a compatibility target

Claude Code provides the level of coverage we need. Exact claude_code.* compatibility can live in a Collector transform.

OpenCode can keep native names. The transform would omit events for Claude Code features that OpenCode does not have.

Delivery scope

Normal OpenTelemetry export is best-effort. Documentation must not describe it as complete or durable audit delivery.

A bounded local outbox can be future work. The external audit system would still own retention, access control, search, and legal holds.

Suggested first change

The first change could define events for prompts, model requests, tools, and permissions. It could include an in-memory sink and privacy tests.

Later changes could add OpenTelemetry projections, MCP coverage, skill events, and subagent correlation.

Minimum acceptance criteria
  • Every admitted prompt, model request, and accepted tool call has one terminal result.
  • Every permission request has one decision.
  • Every MCP call identifies its server and tool.
  • Every subagent identifies its parent.
  • Every usage and cost value identifies its model request.
  • The content policy applies before export.
  • An end-to-end test proves the event order.
Related work
  • #12142, #14246, and #14697 cover basic OTLP and earlier telemetry requests.
  • #32920 covers prompt details and trace boundaries.
  • #40384 covers attributable session metadata.
  • #46685 covers subagent event lineage.
  • #46856 covers MCP trace propagation.
  • #47351 covers administrator-enforced OTLP settings.
  • #34633, #35935, and #36179 contain unmerged metrics and trace work.

I would like agreement on core ownership before implementation starts. If the direction is acceptable, I can split it into smaller proposals.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.