Repository navigation
Commit dae25f0
authored
feat!: migrate to OpenCode V2 (#132)
## Description
Migrates the plugin to **OpenCode V2 only**. The `2.x` line
default-exports a V2 definition (`id: "devtheops.otel"`, `setup`) and
consumes the V2 event stream. The OpenCode V1 implementation remains on
the [`v1`
branch](https://github.com/DEVtheOPS/opencode-plugin-otel/tree/v1),
branched from `v1.5.1`.
Closes #128. Supersedes #131's dual V1/V2 approach with a V2-only major
release.
### V2 instrumentation
- Explicit `session.execution.*`, `session.step.*`, and `session.tool.*`
boundaries replace V1 message-part polling for run, LLM, and tool spans.
- `session.inbox.enqueued` emits `user_prompt` only after durable
admission; synthetic/compaction/move inbox entries are ignored. Queued
prompts are retained for the next execution, while steering prompts
apply to the current execution. `session.text.ended` restores
`output.value` and `llm.output_messages` on LLM spans.
- Per-step token, cost, cache, message, and model metrics; `api_request`
and `api_error` logs retain `duration_ms` even with LLM traces disabled.
Failed LLM spans retain any reported token and cost attributes.
Cumulative session totals come from `session.usage.updated` with a
per-step fallback. The cumulative baseline persists across executions
even if the next turn fails before a usage update; totals are flushed on
execution end (also handling idle events).
- Durable `session.retry.scheduled` is the single retry-counter source.
`permission.asked`/`permission.replied` preserve `tool_decision`;
subagent `session.created` events preserve `subtask.count` and
`subtask_invoked`.
- `commit.count` and `commit` are emitted only for an executed shell
tool that reaches `session.tool.success` with `git commit` in its
command. Tool spans and duration start at `session.tool.called`,
excluding model-side argument streaming. This observes a successful tool
call; Git's resulting repository state is not independently verified.
- `model.request` injects W3C trace context only for a configured
provider and a matching **primary** agent/model request, not title,
compaction, or transient generation. It waits for previously queued
event processing and, if `step.started` has not arrived, creates a
provisional LLM span that the step later adopts.
- Optional `OPENCODE_CAPTURE_MODEL_CONTEXT` captures a bounded,
text-only primary context preview (two system parts, twelve recent
messages, 1,000 characters per entry). Media bytes and structured tool
payloads are excluded; it remains off by default and may expose system
instructions and earlier user/tool text when enabled.
- For explicitly configured trace-propagation providers, the
experimental `experimental.ws.handshake` hook injects the matching W3C
context. Changing per-step headers may reopen a reused WebSocket; the
registration degrades gracefully when the experimental hook is
unavailable.
- Child subagent run spans nest under an unambiguously identified
`subagent` tool span using `session.tool.progress` / terminal metadata
containing the child `sessionID`. Links are consumed per child execution
so resumed sessions cannot inherit a stale dispatch. Foreground and
background tool events were inspected in OpenCode `2.0.18`; invalid
`executed: false` calls without a child ID do not create tool spans.
Ambiguous or missed correlations fall back to the parent run.
### Multi-location correctness
OTel providers and correlation state are shared per process and flushed,
never shut down. Enabled plugin instances must have identical telemetry
configuration; conflicting collector/credentials/resource/signal options
fail setup rather than exporting a different location's data to the
first collector. Accepted resource attributes and metric temporality are
passed directly into SDK initialization. Resolved
`OPENCODE_OTLP_HEADERS` are passed to exporters without copying them to
the process's `OTEL_EXPORTER_OTLP_HEADERS`; exporter construction
suppresses inherited OTLP header variables so a concurrent rejected
setup cannot leak its credentials. `project.id` is resolved from the
observed session, not assumed from `ctx.location`. Event subscriptions
enqueue subsequent events without awaiting each dispatch; processing is
serialized across subscribers before de-duplication so an asynchronous
session lookup cannot let `step.ended` overtake `step.started` or leave
model-request propagation using an older step. Exporter flushing is
scheduled outside the dispatch queue, so slow collectors do not block
unrelated model requests; cleanup drains pending events and flushes,
including when a subscription iterator throws. Event IDs and
message/session correlation sets are bounded. Agent/subagent identity,
selected agent, parent session, creation time, and cumulative usage
baseline are retained across executions and hydrated with
`ctx.session.get` for sessions whose creation event was missed.
### V1 parity limits
`lines_of_code.count` and `lines_of_code.total` are not emitted: the V1
`session.diff` event is absent from V2's public event stream. The V2
client has a session diff endpoint, but it is not exposed through the V2
plugin's `ctx.session` API; `ctx.vcs.diff` is repository-scoped and not
an equivalent per-session total. The removed `command.executed` event is
replaced for commit telemetry by successful tool completion.
Per-message/part spans become per-step LLM spans, while completed text
output remains attached to those spans.
### Docs and packaging
The README and CONTRIBUTING guide use V2's `plugins` object form and a
tested `.opencode/plugins/otel/index.ts` development entrypoint.
OpenCode `2.0.1` rejects an absolute `.ts` file as a `plugins` package
target; directory auto-discovery worked. V1 SDK dependencies were
removed, and the official V2 `@opencode/plugin` types are used without a
runtime import. The unused `.coderabbit.yaml` was removed.
## Type of change
- [x] Breaking change (V2-only package; V1 maintenance stays on `v1`)
- [x] Documentation update
## Verification
- [x] `bun run lint`
- [x] `bun run check:jsdoc-coverage` (81.63%)
- [x] `bun run typecheck`
- [x] `bun test` (191 pass)
- [x] `bun run build` and bundled default-export import
- [x] Local OpenCode `2.0.1` smoke test using `.opencode/plugins/`: OTLP
HTTP/JSON exported logs, traces, and metrics. Confirmed `user_prompt`,
`api_request`, `session.idle`, `opencode.session` → `opencode.llm`
nesting, LLM `output.value`, and session/token/cost/model metrics
including final session histograms. A run with trace propagation enabled
also completed with one correctly nested LLM span; a `delta` temporality
run exported counter sums with OTLP aggregation temporality `1` (delta).
- [x] OpenCode `2.0.18` foreground subagent OTLP export: parent
`opencode.session` → `opencode.tool.subagent` → child
`opencode.session`. Captured real foreground and background event
sequences; unit tests cover background parenting after the dispatch tool
ends and ambiguous-candidate fallback.
- [x] OpenCode `2.0.18` opt-in context capture exported a text-only
`llm.input_messages` preview with a matching LLM output span, including
a foreground subagent trace. WebSocket header injection has unit
coverage; a real WebSocket provider route was not available for an
end-to-end handshake test.
- [x] Added regression tests for conflicting configurations, observed
project attribution, bounded sets, durable retries/prompts, cost-only
failures, LLM output, subtask logs, and non-executed/failed commits.
Two-subscriber delayed-dispatch, buffered subscription events with a
concurrent model request, iterator failure with pending dispatch,
repeated/resumed subagent, selected agent across executions, delayed
model-request, accepted resource-attribute/temporality,
queued-next-prompt, steering-prompt, pending-exporter-flush,
two-execution cumulative-baseline, actual exporter inherited-header,
delayed-tool-input, and API log duration without traces cover the
lifecycle review findings.
- [x] One-use child dispatch mapping and provisional-span context
adoption tests cover the latest review findings.
## Related issues
Closes #128
## Additional context
The breaking `feat!` commit is intended to make release-please cut
**2.0.0**; `package.json` remains `1.5.1` until its release PR updates
it. The [V2 migration
guide](https://opencode.ai/v2/docs/build/plugins/migrate-v1) and [plugin
API guide](https://opencode.ai/v2/docs/build/plugins) informed the hook
selection. WebSocket propagation remains experimental; verify connection
reuse and handshake headers with each intended provider route before
enabling it broadly.1 parent a051cda commit dae25f0
36 files changed
Lines changed: 3455 additions & 3720 deletions
File tree
- src
- handlers
- tests
- handlers
This file was deleted.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
13 | | - | |
| 13 | + | |
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
21 | 27 | | |
22 | 28 | | |
23 | 29 | | |
24 | 30 | | |
25 | | - | |
26 | | - | |
27 | | - | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
28 | 36 | | |
29 | 37 | | |
30 | | - | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
31 | 41 | | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
36 | 47 | | |
37 | 48 | | |
38 | 49 | | |
39 | 50 | | |
40 | 51 | | |
41 | 52 | | |
42 | 53 | | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
48 | | - | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
49 | 66 | | |
50 | | - | |
| 67 | + | |
51 | 68 | | |
52 | 69 | | |
53 | 70 | | |
| |||
59 | 76 | | |
60 | 77 | | |
61 | 78 | | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
| 79 | + | |
| 80 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
18 | | - | |
| 18 | + | |
| 19 | + | |
19 | 20 | | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
| 21 | + | |
| 22 | + | |
25 | 23 | | |
26 | 24 | | |
27 | | - | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
28 | 31 | | |
29 | 32 | | |
30 | 33 | | |
| |||
39 | 42 | | |
40 | 43 | | |
41 | 44 | | |
42 | | - | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
43 | 48 | | |
44 | 49 | | |
45 | 50 | | |
46 | 51 | | |
47 | 52 | | |
48 | 53 | | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
53 | 59 | | |
54 | 60 | | |
55 | 61 | | |
| |||
0 commit comments