Skip to content

fix: apply OAuth transforms to stateless generation - #288

Open
peeyush-ciridae wants to merge 1 commit into
ex-machina-co:v2/mainfrom
peeyush-ciridae:generate-oauth
Open

peeyush-ciridae wants to merge 1 commit into
ex-machina-co:v2/mainfrom
peeyush-ciridae:generate-oauth

Conversation

@peeyush-ciridae

Copy link
Copy Markdown

Problem

Haiku 5.5 succeeds in OpenCode sessions but fails through stateless ctx.generate.text because that path skips the session HTTP hooks. With the same OAuth token and prompt, the untransformed provider request returned 429 Error; the transformed request returned 200. Haiku 4.5 accepted both.

Change

Reuse the existing request and response transforms for generation HTTP hooks when the host exposes them. Retain compatibility with older V2 hosts and keep session recovery keys unchanged. No credentials or provider-specific guards are weakened.

Requires OpenCode's hook API in anomalyco/opencode#53990 for stateless support. This plugin change is safe to release before that API: older hosts keep their existing session behavior.

Verification

  • bun run check: build, types, lint, formatting, and all 440 tests passed.
  • New regression exercises both stateless hooks without session or agent fields.
  • Isolated patched OpenCode source server: /api/experimental/generate returned OK for Haiku 5.5 and Haiku 4.5 using OAuth. Installed configuration unchanged.

@changeset-bot

changeset-bot Bot commented Oct 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 042534f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@ex-machina/opencode-anthropic-auth Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Walkthrough

The plugin now reuses its session HTTP request and response transforms for stateless generation when ctx.generate.hook is available. Stateless response recovery keys use the session header or an empty string, the agent value "stateless", and the provider and model IDs. Tests cover request transformations, an unchanged successful event-stream response, and cleanup. The README and changeset describe the added support and compatibility with older V2 hosts.

Priority: ➖ Normal

Estimated code review effort:

Merge Risk: 🔵 Low · up to 04253

Other providers incur unnecessary request-processing overhead. The change is mergeable with owner awareness, though scoping both hooks before merge would avoid that overhead.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 04253

Credential and destination checks are preserved, but independent stateless requests can share recovery state. Host-side request processing and cleanup after registration failure remain unverified.

Retained concerns

  • Low · reliability · inferred: Stateless requests without x-opencode-session share one setup-lifetime recovery allowance per provider/model. After one qualifying version rejection receives the retry marker, another independent request using that identity cannot receive it. This couples failure recovery between operations, including concurrent requests already sent with a stale version. Updating the shared version helps later requests but does not eliminate this interference; the host's intended identity contract remains unknown.
Security review details

Security Blast Radius

  • inferred — The newly reachable sensitive outcome is attaching the context's active Anthropic OAuth credential to eligible stateless requests. Request destinations remain constrained by the existing origin checks. Host preprocessing may have a broader provider scope than these callbacks, but its effective exposure could not be established.

Trust Boundaries and Controls

  • observed — Provider identity and OAuth method selection gate credential use. Response ownership additionally uses direct request tracking or setup-local alias ownership, whose fingerprint binds method, URL, authorization identity, and body. A recovery key only limits retry marking; it does not authorize an unrelated response.

Resilience and Maintainability Implications

  • observed — Automatic version adoption requires a bounded structured 400 rejection matching the version actually sent, and only advances the shared version. Recovery tracking is capped and fails closed. These controls constrain retry authority but do not separate missing-identity stateless operations.

Hardening Proposals

  • proposed — Define a host-backed operation identity that stays stable across retries while separating independent stateless calls. Confirm provider-scoped registration and paired-registration rollback semantics before relying on callback guards or host cleanup for failure containment.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 2 files. (2 skipped: 2 … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly explains the stateless OAuth transformation problem, the implementation, compatibility behavior, and verification results.
Title check ✅ Passed The title clearly and concisely describes the main change: applying OAuth transforms to stateless generation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 2 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[Medium risk] Applies OAuth transforms to stateless code generation.

The PR appears safe to merge, with a non-blocking gap in the stateless response test.

Summary

Reuses the existing OAuth request and response transforms for stateless generation when the host provides ctx.generate.hook.

  • Keeps session recovery keys unchanged and skips generation-hook registration on older hosts.
  • Adds a regression test, but its response assertion should check an actual transformation.

Reviews (1) · Last reviewed commit: "fix: transform stateless generation OAut..." · Reviewed by Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository YAML (base), Organization UI (inherited)
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 677d0865-2c5f-4689-b57b-263dea363e8a
📥 Commits

Reviewing files that changed from the base of the PR and between 156cb66 and 042534f.

📒 Files selected for processing (4)
  • .changeset/stateless-oauth-hooks.md
  • README.md
  • src/index.ts
  • src/tests/index.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

Comment thread src/index.ts
await generateHook.call(
ctx.generate,
'http.request',
transformHttpRequest,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

Scope both generation hooks to Anthropic.

When another native provider generates text, these unscoped registrations make OpenCode enter its HTTP hook path even though transformHttpRequest and transformHttpResponse return for that provider. The host converts the request and buffers its body in that path. Pass { providerID: INTEGRATION_ID } to both generateHook.call registrations so other providers keep the no-hook path. (github.com)

Comment thread src/tests/index.test.ts
Comment on lines +838 to +844
response: new Response('data: {"type":"message_stop"}\n\n', {
headers: { 'content-type': 'text/event-stream' },
}),
}
await hooks.get('http.response')!(response)
expect(await response.response.text()).toBe(
'data: {"type":"message_stop"}\n\n',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Broken response changes go undetected

The new stateless test passes even if the response callback does nothing: its message_stop body stays unchanged either way. This leaves tool-name restoration untested on the new path. Add a tool to the request, return its rewritten name in a tool_use response, and assert that the callback restores the original name.

Knowledge Base Used: Request transformation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant