🤖 feat: per-workspace OpenRouter session_id (sticky routing + session grouping) - #3866
🤖 feat: per-workspace OpenRouter session_id (sticky routing + session grouping)#3866asm wants to merge 2 commits into
Conversation
…and session grouping Workspace requests through the OpenRouter provider carry session_id "mux-<workspace id>" — OpenRouter's explicit sticky-routing key (same upstream provider per session for prompt-cache hits) and the grouping key for its Sessions log view. Explicit config session_id wins verbatim, null disables tagging, and a configured x-session-id header suppresses the stamp. Status and title generation forward workspaceId so their traffic joins the workspace session. Also fixes pre-existing config leakage in the same block: the baseUrl→baseURL rename ran before a destructure that stripped only the stale key, so baseURL, apiKeyFile (a local path), and enabled shipped as junk top-level body fields; config-level extraBody was demoted into a literal nested body field instead of being lifted to the root. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 26f6e30472
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…sion-free Explicit config overrides (either spelling) are clamped like the generated stamp instead of forwarded verbatim, and overlong values keep a readable head plus a sha256 digest tail so two legacy workspace ids sharing a long project prefix cannot merge into one session. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@codex review Both findings addressed in 450033f:
|
|
Codex Review: Didn't find any major issues. Keep it up! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Summary
Workspace requests sent through the OpenRouter provider now carry a per-workspace
session_id(mux-<workspace id>). OpenRouter usessession_idas its explicit sticky-routing key — all of a session's requests route to the same upstream provider to maximize prompt-cache hits even as the opening messages change between turns (exactly the shape of agentic traffic) — and groups the session's requests in the Sessions view of the Logs page, linking turns, retries, and compaction into one trace.A workspace is mux's conversation unit (its
chat.jsonlis the session transcript), so the workspace id is the natural session key: every turn, retry, compaction, status generation, and title generation for a workspace shares one session, while child task workspaces get their own.Implementation
ProviderModelFactory, openrouter branch): whenopts.workspaceIdis present and no explicit session key is configured,extraBody.session_id = "mux-<workspaceId>"(prefix derived fromMUX_APP_ATTRIBUTION_TITLEso the app's identity strings stay in lockstep; value sliced to OpenRouter's 256-char cap because legacy workspace ids embed project/branch basenames). Models are created per-request withworkspaceIdalready threaded for DevTools, so this adds one field to_createModelCore's opts — no new plumbing.session_idin the provider config wins verbatim;"session_id": null(or any non-string) disables tagging entirely and is stripped rather than forwarded; a configuredx-session-idheader — OpenRouter's documented header spelling of the same key, which the body field would beat server-side — also suppresses the stamp.session_idis declared onOpenRouterProviderConfigSchema(typed override + null opt-out) alongside its sibling body options.baseUrl → baseURLrename happens before the openrouter destructure, which stripped only the stale lowercase key — sobaseURL,apiKeyFile(a local filesystem path), andenabledleaked intoextraBodyand were transmitted as junk top-level body fields on every request. The destructure now strips the full mux-internal config surface (baseURL,apiKeyFile,enabled,providerType,displayName,modelParameters). A config-levelextraBodykey — the OpenRouter SDK's own settings option, promised verbatim parity by the factory's doc comment — was likewise demoted into a literal nested"extraBody"body field; its entries are now lifted to the body root (and asession_idinside it counts as the explicit override).workspaceStatusGeneratorandworkspaceTitleGeneratornow accept and forwardworkspaceIdfrom the callers that hold one, so recurring status/title traffic joins the workspace's session. Requests with no workspace context (pre-creation naming) are deliberately untagged, and the docs say so.docs/config/providers.mdxdescribing the tag, the static override, thenullopt-out, and the header interaction.Review
An exhaustive multi-angle review ran before this PR (10 finder angles, adversarial verification, gap sweep): 15 findings, 14 fixed here — including the pre-existing
baseURL/apiKeyFilebody leak, the missing opt-out, thex-session-idheader conflict, the config-extraBodydemotion, the 256-char cap for legacy ids, and the status/title coverage gaps. The one non-fix: empty-string workspace ids were verified unreachable (every producer asserts or guards non-empty).Validation
session_idtests inproviderModelFactory.test.tsbehind a shared registry-capture helper: per-workspace stamping, theresolveAndCreateModelseam production callers use, config override precedence,nullopt-out,x-session-idheader suppression, SDK-conventionalextraBodylift, omission without workspace context, the 256-char clamp, mux-internal key stripping, composition with nestedproviderrouting options, and a wire-level test provingsession_idlands at the top level of the actual HTTP body via a captured fetch.providerModelFactory.test.ts(123 tests), plus the touched-service suites (agentStatusService,workspaceStatusGenerator,workspaceTitleGenerator,providersConfigschema — 68 tests) all green; typecheck, eslint, prettier clean.Risks
Low and scoped to the openrouter factory branch. Users who configured nothing see one new body field on workspace requests (OpenRouter's own documented option, and creation paths without workspace context are unchanged). The strip-list change removes junk fields that were never intended to reach the wire; anyone who somehow relied on
baseURL/apiKeyFile/enabledappearing in request bodies would be relying on an accident. Gateway paths (mux-gateway,coder) and customopenai-compatibleproviders are untouched.🤖 Generated with Claude Code