Skip to content

feat(opencode): every OpenCode request now carries x-opencode-session for backend affinity (#81584) - #101864

Merged
teknium1 merged 1 commit into
mainfrom
hermes/hermes-e2fa55cb
Sep 3, 2026
Merged

teknium1 merged 1 commit into
mainfrom
hermes/hermes-e2fa55cb

Conversation

@teknium1

@teknium1 teknium1 commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Every request Hermes sends to OpenCode (Zen / Go / Free, and custom providers pointing at opencode.ai) now carries an opaque, per-conversation x-opencode-session header, on every transport and on auxiliary calls too. OpenCode uses it to pin a conversation to one backend so its prompt cache stays warm; without it, cache ratios on Hermes traffic were poor (reported directly by the OpenCode team) and some Go backends 400'd outright (#81584, #81832).

Changes

  • agent/opencode_affinity.py (new, ~80 LOC): single owner of target detection (opencode_provider_family + opencode.ai host) and the key. Key resolution matches the OpenRouter sticky session_id / xAI x-grok-conv-id hints: declared affinity scope → conversation root → physical session_id, cron per-fire timestamp stripped.
  • agent/chat_completion_helpers.py: build_api_kwargs merges the header once after the per-mode builder → chat_completions, codex_responses, anthropic_messages all covered with one site.
  • agent/auxiliary_client.py: _build_call_kwargs adds the same key from the runtime-main session (compression / titles / vision / MoA stay on the conversation's backend); the aux Codex and Anthropic adapters now forward extra_headers (previously dropped on the floor).
  • website/docs/integrations/providers.md: one paragraph documenting the header.
  • 2 invariant tests (tests/agent/test_opencode_session_affinity.py).

Validation

Path Before After
main turn, go / chat_completions (glm-5) no header x-opencode-session: <session>
main turn, go / codex_responses (gpt-5.6-luna) no header same value
main turn, go / anthropic_messages (minimax-m2.7) no header same value
main turn, zen + free + custom-URL-only no header same value
aux _build_call_kwargs (compression etc.) no header same value as main turn
openrouter (negative) no header no header
Live wire (keyless free tier, httpx request hook) — POST https://opencode.ai/zen/v1/chat/completions carried x-opencode-session: sess-live-77, 200 OK

Targeted suites: 242 + 305 passed (opencode profile/free/headers, aux client, codex/anthropic transports, prompt caching, attribution headers). ruff clean.

Supersedes #81591 (@chelsealong) — that PR covered opencode-go only and re-derived the key in three places; this generalizes to all OpenCode providers + aux path with one owner. Credit to chelsealong for the original diagnosis.

Closes #81584
Closes #81832

Infographic

x-opencode-session: one conversation, one backend

… backend affinity

OpenCode pins requests sharing an x-opencode-session value to one upstream
backend, which is what keeps its prompt cache warm across a conversation.
Hermes never sent it, so cache ratios on OpenCode traffic were poor.

- agent/opencode_affinity.py: single owner of the header — target detection
  (built-in zen/go/free, custom opencode-* providers, any opencode.ai URL)
  and the key (affinity scope → conversation root → session id, cron
  timestamp stripped), same resolution as OpenRouter/xAI affinity hints.
- build_api_kwargs: merged once after the per-mode builder, so
  chat_completions, codex_responses and anthropic_messages all carry it.
- auxiliary _build_call_kwargs: same key from the runtime-main session so
  compression/title/vision calls stay on the conversation's backend; the
  aux Codex and Anthropic adapters now forward extra_headers.

Closes #81584, #81832 (deepseek-v4-flash 400 without the header).
@github-actions

github-actions Bot commented Sep 3, 2026 •

Copy link
Copy Markdown

૮ >ﻌ< ა ci review

ran on c9e2075 — feat(opencode): send x-opencode-session on every OpenCode re

⚠️ Warnings

OSV vulnerability scan · View job

28 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.


debug info

CI timings

CI timings · View report · View job

Wall time 4m28s vs 4m7s (+8.5%). 7 job(s) slower, 7 faster, 1 unchanged.

  • OS-specific tests / Windows-only tests: +22.0s
  • OS-specific tests / macOS-only tests: +11.0s
  • Python tests / e2e: +7.0s
  • Python tests / Run tests: -4.0s
  • Profile artifact check / Reject profile archives: -3.0s

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Sep 3, 2026
@alt-glitch

Copy link
Copy Markdown

This was generated by AI during triage.

Supersedes #81591's narrower OpenCode Go-only repair: this implementation covers all OpenCode target detection, every main transport, and auxiliary calls.

@teknium1
teknium1 merged commit 1393969 into main Sep 3, 2026
40 checks passed
@teknium1
teknium1 deleted the hermes/hermes-e2fa55cb branch September 3, 2026 04:46
@jake-101

jake-101 commented Sep 3, 2026 •

Copy link
Copy Markdown

can there be a point release for this? i've never updated to a random commit before. i got an email i have to send this header by September 6th or I'll experience errors.

@Du7chManiac

Copy link
Copy Markdown

can there be a point release for this? i've never updated to a random commit before. i got an email i have to send this header by September 6th or I'll experience errors.

Running hermes update will update to latest commit so it will include this. You can also ask hermes to update itself.

@jake-101

jake-101 commented Sep 4, 2026

Copy link
Copy Markdown

can there be a point release for this? i've never updated to a random commit before. i got an email i have to send this header by September 6th or I'll experience errors.

Running hermes update will update to latest commit so it will include this. You can also ask hermes to update itself.

yeah i went ahead and updated to this commit. it wasnt that hard but i'll go back to a versioned release when possible.

@H3xKatana

H3xKatana commented Sep 7, 2026 •

Copy link
Copy Markdown

this wont work with docker / container deployments a new image release is needed

can there be a point release for this? i've never updated to a random commit before. i got an email i have to send this header by September 6th or I'll experience errors.

Running hermes update will update to latest commit so it will include this. You can also ask hermes to update itself.

shuv1337 added a commit to shuv1337/hermes-agent that referenced this pull request Sep 7, 2026
… backend affinity

Port of upstream PR NousResearch#101864 (1393969). OpenCode Go now 400s requests
that omit x-opencode-session; the header also pins a conversation to one
backend so prompt cache stays warm.

- agent/opencode_affinity.py: single owner of target detection and the key
- build_api_kwargs: merge once after the per-mode builder so every transport
  carries it
- auxiliary _build_call_kwargs: same key from the runtime-main session;
  Codex/Anthropic aux adapters now forward extra_headers

A full upstream merge is 5514 commits behind; this ports the isolated fix.
Finn763 added a commit to Finn763/hermes-agent that referenced this pull request Sep 10, 2026
The Zen relay's free tier (https://opencode.ai/zen/v1, keyless) answers HTTP 429
FreeUsageLimitError to Hermes' canonical attribution headers (User-Agent:
HermesAgent/<v> + the hermes-agent referer/title) while the same model returns 200
to the OpenCode client fingerprint. NousResearch#101864's x-opencode-session fixed the
MissingSessionID 400 and stands; only the client default_headers identity was
never switched.

- hermes_cli/opencode_client_headers.py: stdlib-only leaf module owning the
  fingerprint (UA opencode/0.20.5, referer https://opencode.ai/, title opencode,
  opaque X-Session-ID), shared by the runtime header set and the provider profile
  so the two copies cannot drift.
- hermes_cli/models.py: opencode_zen_free_headers() returns the fingerprint plus
  the empty Authorization that keeps the keyless placeholder off the wire.
- plugins/model-providers/opencode-free: the profile's default_headers read that
  same module instead of duplicating the Hermes values.
- agent/agent_runtime_helpers.py: key the header set off the keyless placeholder,
  not the provider name, so a free slug picked under opencode-zen/go (healed onto
  the same keyless Zen route) gets it too.

Keyed Zen/Go keep the Hermes attribution and never send X-Session-ID;
x-opencode-session still rides on every OpenCode request, so both header policies
coexist.

Tests: wire-level mock guard
tests/run_agent/test_opencode_zen_free_wire_headers.py (red on main, green after
the fix, bites under sabotage) plus the updated free-tier header assertions.
vuralozsen added a commit to vuralozsen/hermes-agent that referenced this pull request Sep 11, 2026
…t of upstream PR NousResearch#101864)

opencode-go returns HTTP 400 MissingSessionID on real multi-message
conversations without the header. Ported:
- agent/opencode_affinity.py (new): header derivation per conversation
- chat_completion_helpers.build_api_kwargs: wrap mode builder, merge header
- auxiliary_client: forward extra_headers on Codex + Anthropic aux adapters

E2E: 4-msg probe without header -> 400; with header -> 200.
vuralozsen added a commit to vuralozsen/hermes-agent that referenced this pull request Oct 5, 2026
…ackport of upstream PR NousResearch#101864; upstream main already contains its own evolved copy)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

5 participants