fix(providers): send OpenCode session attribution header - #114
fix(providers): send OpenCode session attribution header#114sambitcreate wants to merge 2 commits into
Conversation
Every OpenCode Go/Zen generation failed before inference with 400 MissingSessionID: the OpenCode gateway requires a stable per-conversation x-opencode-session header on every inference request, and the pinned @earendil-works/pi-ai (no published version emits it) left Aiden unable to send one. Attach the header to the resolved runtime model via a new main/services/opencode-session-attribution.ts helper (opencode, opencode-go, opencode-zen, or any opencode.ai base URL), since every pi-ai transport merges model.headers into the outgoing request and subagent/isolated paths inherit the runtime model. Thread an optional conversationId through the runtime resolvers and pass it from chat turns, Bot preparation, compaction (both engines), chat titles, advisor dispatch, RPiV-BtW side questions, vision analysis, bot avatars, and dictation cleanup (fresh UUIDs for conversation-less one-shots). Tests cover the gate matrix, injection-safe bounded ids, pinned-Bot dispatch, and real wire-level header delivery through the completions transport; the suite is registered in test, test:google-provider, and test:coverage.
There was a problem hiding this comment.
ℹ️ No critical issues — one wire-coverage suggestion inline, plus a nitpick.
Reviewed changes
- New attribution module —
main/services/opencode-session-attribution.tsclones the resolved runtime model and mergesx-opencode-session, gated on provider id (opencode/opencode-go/opencode-zen) or an exactopencode.aibase-URL host, with a bounded id grammar and case-insensitive replacement of any existing variant. - Resolver seam —
resolveModelRuntimeWith,resolveModelRuntime,resolveBotModelRuntime, andresolveCompactionModelMetadataaccept an optionalconversationIdand apply attribution on both the Pi-native and custom-provider paths. - Threaded ids — chat turns (
chat.id), Bot preparation (chat.id), both compaction engines, chat titles, advisor dispatch, BTW side questions, vision analysis, bot avatars (requestId), and dictation cleanup (fresh UUID) all pass a stable conversation or per-request id. - Tests — 15 new cases in
main/services/opencode-session-attribution.test.tscovering the gate matrix, id validation, clone-only behavior, pinned-Bot dispatch, both resolver paths, and real wire delivery; registered intest,test:google-provider, andtest:coverage.
ℹ️ Nitpicks
.papercuts/troubleshooting.mdadds asubagent-artifacts/*_output.mdnote that is unrelated to this change; consider dropping it from this PR.
DeepSeek Flash | 𝕏
Address Pullfrog review feedback on PR #114: parameterize the wire-level test across openai-completions, openai-responses, and anthropic-messages so a pi-ai change that stops merging model.headers fails loudly per transport, and drop the unrelated troubleshooting note from this PR.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Pinned the session header on every OpenCode Go transport — the wire-level test now loops over
openai-completions,openai-responses, andanthropic-messages, asserting per transport that the attributed request carriesx-opencode-sessionwhile the unrelated provider does not. - Dropped the unrelated
.papercutsnote — thesubagent-artifacts/*_output.mdtroubleshooting entry is no longer part of this PR.
The new coverage is genuine: each lazy transport wraps its own streamSimple implementation, so a transport that stops merging model.headers fails its own assertion. Verified at c1c2c6f — 15/15 in the suite, tsc --noEmit and eslint clean.
DeepSeek Flash | 𝕏
|
All feedback from review #5185375806 was already addressed at the branch HEAD (
|

Summary
Every generation on the OpenCode Zen Go provider (
opencode-go) failed before inference with:OpenCode's gateway requires a stable per-conversation
x-opencode-sessionheader on every inference request (docs; the operators filed the same ask with jcode, Kilo Code, and VS Code — "any stable UUID per conversation works", "all outbound inference HTTP requests"). A Hermes control matrix proved the session header alone flips the request 400 → 200.Aiden's pinned
@earendil-works/pi-aicannot emit that header — no published version can (upstream fixed it only inside the pi coding-agent, pi#4847; thepi-aipackage fix pi#9326 is unreleased). So Aiden owns the attribution.Changes
main/services/opencode-session-attribution.ts— attachesx-opencode-session: <conversation id>to the resolved runtime model. Gate: provideropencode/opencode-go/opencode-zen(forward-compat) or an exactopencode.aibase URL. Case-insensitive overwrite, clone-only (never mutates the shared catalog model), and ids are validated as bounded tokens so renderer-adjacent inputs can never carry CR/LF into a header.resolveModelRuntimeWith/resolveModelRuntime/resolveBotModelRuntime/resolveCompactionModelMetadatagrew an optionalconversationId. Every pi-ai transport mergesmodel.headersinto the outgoing request, so one attachment covers chat turns, subagent children, isolated inference workers (headers survive the utility-process protocol), and the pinned-Bot dispatch path.chat.id), Bot preparation, compaction (both engines), chat titles, advisor dispatch, RPiV-BtW side questions, vision analysis, bot avatars (requestId), and dictation cleanup (fresh UUID for conversation-less one-shots).Testing
main/services/opencode-session-attribution.test.ts: gate matrix (all provider ids, host matches, foreign/malformed URLs), blank/invalid ids, case-variant replacement, header preservation, catalog non-mutation, pinned-Bot dispatch, real wire-level delivery (local HTTP server + actual pi-ai completions transport), and native/custom resolver paths.test,test:google-provider,test:coverage.npm run test:bots440 pass. The 3google-provider.test.tsfailures on this Mac are pre-existing and environment-specific (verified identical on a stashed clean tree; CI is unaffected).Notes
bots.contract.test.tswas fixed in this branch)..memory/PROJECT-HISTORY.md): preserving provider error status/type in production diagnostics, a live gateway probe (incl./messages+/responsesroutes), and removing this shim once a publishedpi-aiships pi#9326.