Skip to content

feat(provider): add Claude subscription OAuth#45

Merged
CreatorGhost merged 4 commits into
devfrom
anthropic-subscription
Jul 15, 2026
Merged

feat(provider): add Claude subscription OAuth#45
CreatorGhost merged 4 commits into
devfrom
anthropic-subscription

Conversation

@CreatorGhost

@CreatorGhost CreatorGhost commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Issue

No linked issue. Adds first-party Claude Pro/Max subscription authentication to DCode.

Type of change

  • New feature
  • Bug fix / hardening
  • Tests
  • Documentation only

What does this PR do?

  • Adds built-in Claude Pro/Max OAuth under a separate anthropic-subscription provider.
  • Routes legacy CLI/TUI and durable V2 sessions through Claude-compatible native transports.
  • Persists credentials across login, refresh, migration, reconnect, and logout with process-global single-flight, bounded refresh requests, atomic compare-and-set writes, and rollback.
  • Enforces trusted origins, provider/package identity, PKCE state, OAuth method identity, and reversible collision-safe tool names.
  • Exposes provider-auth loading state in the TUI and adds durable end-to-end session coverage.

How did you verify this change?

  • Repository-wide Turbo typecheck passed in the pre-push hook.
  • packages/llm: typecheck and full suite passed, 303 passed / 30 skipped.
  • packages/tui: typecheck and full suite passed, 209 passed / 1 skipped.
  • packages/core: typecheck and all focused tests passed; full suite passed 1,088 tests with one known unrelated recorded OpenAI fixture mismatch.
  • packages/opencode: typecheck and Anthropic OAuth, durable migration/rollback, provider, native runtime, Codex, and HTTP auth lifecycle tests passed.
  • Root lint completed with 0 errors; existing warnings remain.
  • Prettier and git diff --check passed.
  • Independent pre-push review found no remaining production issues.

Screenshots

Not applicable. This is provider/auth/runtime behavior covered through CLI, TUI state tests, HTTP tests, and durable session integration tests.

Checklist

  • I tested the affected behavior.
  • I added regression coverage for new behavior and review findings.
  • I ran typechecks, lint, formatting, and diff validation.
  • I reviewed credential handling for origin, refresh, logout, rollback, and concurrency safety.
  • Live Claude request verified after interactive /connect authorization.

Notes

  • Snyk Code was retried after the final changes, but the service returned SNYK-0003: The request cannot be processed.
  • A live Claude request requires interactive OAuth authorization through /connect.

Summary by CodeRabbit

  • New Features
    • Added Claude Pro/Max subscription support with OAuth sign-in, token refresh, and Claude model access.
    • Claude subscription models now appear as a separate provider with no usage costs shown.
    • Added provider listing, login, logout, credential migration, and session support for subscriptions.
  • Bug Fixes
    • Improved concurrent token refresh handling to prevent stale credentials from overwriting newer ones.
    • Blocked OpenAI OAuth credentials from being sent to unauthorized origins.
  • Security
    • Added request validation and protection for subscription authentication flows.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@CreatorGhost, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 46 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8f5b8e1e-1849-4009-8acc-6fbf1470b8df

📥 Commits

Reviewing files that changed from the base of the PR and between cc16e1e and 5ac7813.

📒 Files selected for processing (4)
  • packages/opencode/src/cli/cmd/providers.ts
  • packages/opencode/src/provider/anthropic-subscription-credential.ts
  • packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts
  • packages/opencode/test/provider/anthropic-subscription-credential.test.ts
📝 Walkthrough

Walkthrough

Adds Anthropic Claude Pro/Max subscription OAuth support across core integrations, LLM routing, OpenCode credential management, session execution, provider discovery, and TUI selection. It includes refresh deduplication, PKCE authorization, token persistence, request transformation, and streaming response handling.

Changes

Anthropic subscription support

Layer / File(s) Summary
Core OAuth provider and refresh flow
packages/core/src/integration.ts, packages/core/src/credential.ts, packages/core/src/plugin/provider*, packages/core/src/session/runner/*, packages/core/test/*anthropic-subscription*
Adds OAuth authorization and refresh flows, atomic credential updates, session-model validation, refresh deduplication, and coverage for refresh races and session execution.
Anthropic subscription LLM route
packages/llm/src/providers/*, packages/llm/package.json, packages/llm/test/provider/*
Adds Anthropic authentication, request and billing transformations, MCP tool-name mapping, SSE response restoration, and origin validation.
OpenCode credential and provider wiring
packages/opencode/src/provider/*, packages/opencode/src/cli/cmd/providers.ts, packages/opencode/src/server/routes/instance/httpapi/handlers/*, packages/opencode/test/provider/*, packages/opencode/test/server/*
Adds durable credential locking, migration, synthetic provider construction, OAuth persistence and rollback, CLI login/logout handling, HTTP credential management, and provider catalog integration.
OpenCode subscription plugin and runtime execution
packages/opencode/src/plugin/anthropic/*, packages/opencode/src/plugin/index.ts, packages/opencode/src/session/llm/*, packages/opencode/src/plugin/openai/codex.ts, packages/opencode/test/plugin/*, packages/opencode/test/session/*
Adds OAuth authorization, token refresh and retry behavior, transformed streaming requests, native runtime compatibility checks, and Codex origin protection.
TUI provider discovery and selection
packages/tui/src/component/dialog-provider.tsx, packages/tui/src/context/sync.tsx, packages/tui/test/cli/cmd/tui/provider-options.test.ts
Adds provider-auth loading status, Anthropic subscription provider metadata and ordering, guarded selection behavior, and updated option ordering coverage.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant TUI
  participant OpenCode
  participant CredentialService
  participant AnthropicAPI
  User->>TUI: select Claude Pro/Max provider
  TUI->>OpenCode: start OAuth authorization
  OpenCode-->>User: authorization URL and PKCE state
  User->>OpenCode: complete OAuth callback
  OpenCode->>CredentialService: store OAuth credential
  OpenCode->>AnthropicAPI: send transformed authenticated request
  AnthropicAPI-->>OpenCode: stream response
  OpenCode-->>User: display restored model response
Loading

Suggested reviewers: thdxr

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Concise and specific; it clearly summarizes the addition of Claude subscription OAuth.
Description check ✅ Passed Mostly complete and aligned with the template; it includes issue, type, changes, verification, screenshots, and checklist.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch anthropic-subscription

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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: 12

🧹 Nitpick comments (2)
packages/opencode/src/cli/cmd/providers.ts (1)

306-321: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated synthetic-subscription-entry injection block.

The "fetch latest subscription credential, inject a synthetic OAuth entry if not already present" block (lines 306-320) is duplicated almost verbatim in ProvidersLogoutCommand (566-579). Consider extracting a small helper (e.g. withSyntheticSubscriptionAuth(entries)) shared by both commands to avoid the two copies drifting.

♻️ Proposed helper extraction
+const withSyntheticSubscriptionAuth = Effect.fn("Cli.providers.withSyntheticSubscriptionAuth")(function* (
+  entries: Array<[string, Auth.Info]>,
+) {
+  const subscription = yield* Effect.gen(function* () {
+    return (yield* (yield* Credential.Service).list(AnthropicSubscriptionIntegrationID)).at(-1)
+  }).pipe(Effect.provide(credentialLayer))
+  if (subscription?.value.type !== "oauth" || entries.some(([id]) => id === AnthropicSubscriptionProviderID)) {
+    return entries
+  }
+  return [
+    ...entries,
+    [
+      AnthropicSubscriptionProviderID,
+      { type: "oauth", access: subscription.value.access, refresh: subscription.value.refresh, expires: subscription.value.expires },
+    ] as [string, Auth.Info],
+  ]
+})

Also applies to: 566-585

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/src/cli/cmd/providers.ts` around lines 306 - 321, Extract
the duplicated subscription-auth injection logic from the current command and
ProvidersLogoutCommand into a shared helper such as
withSyntheticSubscriptionAuth(entries). Have the helper fetch the latest
Anthropic subscription credential, add the synthetic OAuth entry only when
appropriate and absent, then update both commands to use it while preserving
their existing results flow.
packages/core/test/session-runner-anthropic-subscription.test.ts (1)

25-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace the star import with the exported namespace name.

Import the module’s named SessionRunnerLLM namespace directly, adding the prescribed self-reexport if it is not currently exposed.

As per coding guidelines, “Never use star imports, including type star imports. For namespace-style values, import the module's exported namespace by name and reference it through that namespace.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/test/session-runner-anthropic-subscription.test.ts` at line 25,
Replace the star import of the LLM runner with the module’s exported
SessionRunnerLLM namespace and continue referencing it through that namespace.
If the namespace is not currently exported by the runner module, add the
prescribed self-reexport there before updating the test import.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/core/src/integration.ts`:
- Around line 420-425: Update both compare-and-swap guards in the OAuth refresh
flow around the visible token comparisons to also compare the current OAuth
value’s methodID with oauth.methodID. Return the latest value whenever access,
refresh, expires, or methodID differs, preventing stale refresh results from
overwriting a concurrent OAuth-method change.

In `@packages/core/src/plugin/provider/anthropic-subscription.ts`:
- Around line 39-56: The shared request created by
refreshAnthropicSubscriptionToken must be bounded so a stalled token endpoint
cannot leave the cached Promise pending. Add one timeout around
tokenRequestPromise, abort the underlying request when it expires, ensure
cleanup still removes the matching pending entry, and add coverage using a
never-resolving endpoint to verify the Promise and integration Deferred settle.

In `@packages/core/src/session/runner/model.ts`:
- Around line 167-173: Update the credential guard in the model execution flow
to require both OAuth type and credential.methodID ===
AnthropicSubscriptionMethodID before forwarding the token; otherwise return the
existing CredentialRequiredError with the current providerID and modelID values.

In `@packages/llm/src/providers/anthropic-subscription.ts`:
- Line 3: Replace the star import for AnthropicMessages with a named namespace
import from the public anthropic-messages module, preserving the existing
AnthropicMessages references in the provider.
- Around line 161-165: Update restoreToolNames so only mcp_ names present in the
names mapping are restored; when no mapping exists, preserve the original mcp_
tool name unchanged. Keep the existing mapped-name replacement behavior and
avoid applying unprefixToolName as a fallback.

In `@packages/llm/test/provider/anthropic-subscription.test.ts`:
- Around line 7-11: Replace the layer-backed test usage in the “Anthropic
subscription provider” suite with the prescribed testEffect helper: import
testEffect from the existing effect test utility and update both test
declarations currently using it.effect, preserving their test bodies and layer
setup.

In `@packages/opencode/src/cli/cmd/providers.ts`:
- Around line 43-87: Three implementations of Anthropic subscription credential
persistence and legacy-auth retirement have diverged in rollback behavior. Add a
shared helper in anthropic-subscription-credential.ts that owns the locked
create-or-update, legacy-auth removal, and rollback semantics, then replace the
anthropic branch of putOAuth in packages/opencode/src/cli/cmd/providers.ts lines
43-87, the OAuth callback branch in packages/opencode/src/provider/auth.ts lines
219-260, and the storedAuth legacy-migration branch in
packages/opencode/src/provider/provider.ts lines 1388-1424 with calls to that
helper.
- Around line 604-633: Update the Anthropic subscription logout flow around the
stored credential collection and authSvc.remove rollback so every credential
removed from stored is recreated when removal fails, preserving each
credential’s integrationID, label, and value. Replace the single stored.at(-1)
rollback input with iteration over all stored rows while retaining the existing
credentialLayer and no-op behavior when none were removed.

In `@packages/opencode/src/plugin/anthropic/subscription.ts`:
- Around line 254-259: Update the 401 retry handling around getAuth so an
undefined latest credential is treated as disconnected and fails immediately.
Only call refresh and send with latest.refresh or auth.refresh when an existing
credential is available; preserve the access-token replacement path for valid
changed OAuth credentials.

In `@packages/opencode/src/provider/provider.ts`:
- Around line 1388-1424: Update Provider.storedAuth so the common
already-migrated OAuth credential path reads and returns the credential without
entering withAnthropicSubscriptionCredentialLock. Keep the lock only around
legacy-auth migration and its credential creation/removal writes, and remove any
unnecessary auth.get cleanup from the fast path; preserve existing behavior for
non-Anthropic providers and non-OAuth credentials.

In `@packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts`:
- Line 22: Update updateDurable and its related credential replacement flow to
snapshot the complete list returned by credentials.list rather than only
.at(-1), then perform removal and restoration atomically in a single transaction
where supported. If deletion or auth.remove fails, restore every original
credential and preserve the existing replacement behavior on success.

In `@packages/opencode/test/server/httpapi-provider.test.ts`:
- Line 450: Extend the final DELETE test around the auth credential request to
verify that the updated credential is actually removed from durable state, not
only that the response status is 200. Reuse the existing credential retrieval or
persistence assertion symbols in the test to confirm the credential is absent
after the DELETE.

---

Nitpick comments:
In `@packages/core/test/session-runner-anthropic-subscription.test.ts`:
- Line 25: Replace the star import of the LLM runner with the module’s exported
SessionRunnerLLM namespace and continue referencing it through that namespace.
If the namespace is not currently exported by the runner module, add the
prescribed self-reexport there before updating the test import.

In `@packages/opencode/src/cli/cmd/providers.ts`:
- Around line 306-321: Extract the duplicated subscription-auth injection logic
from the current command and ProvidersLogoutCommand into a shared helper such as
withSyntheticSubscriptionAuth(entries). Have the helper fetch the latest
Anthropic subscription credential, add the synthetic OAuth entry only when
appropriate and absent, then update both commands to use it while preserving
their existing results flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 31f8fb8c-af57-4093-ab68-19b6e293f863

📥 Commits

Reviewing files that changed from the base of the PR and between f572c91 and 9b6b371.

📒 Files selected for processing (30)
  • packages/core/src/integration.ts
  • packages/core/src/plugin/provider.ts
  • packages/core/src/plugin/provider/anthropic-subscription.ts
  • packages/core/src/session/runner/model.ts
  • packages/core/test/plugin/provider-anthropic-subscription.test.ts
  • packages/core/test/session-runner-anthropic-subscription.test.ts
  • packages/core/test/session-runner-model.test.ts
  • packages/llm/package.json
  • packages/llm/src/providers/anthropic-subscription.ts
  • packages/llm/src/providers/index.ts
  • packages/llm/test/provider/anthropic-subscription.test.ts
  • packages/opencode/src/cli/cmd/providers.ts
  • packages/opencode/src/plugin/anthropic/subscription.ts
  • packages/opencode/src/plugin/index.ts
  • packages/opencode/src/plugin/openai/codex.ts
  • packages/opencode/src/provider/anthropic-subscription-credential.ts
  • packages/opencode/src/provider/auth.ts
  • packages/opencode/src/provider/provider.ts
  • packages/opencode/src/provider/transform.ts
  • packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts
  • packages/opencode/src/server/routes/instance/httpapi/handlers/provider.ts
  • packages/opencode/src/session/llm/native-runtime.ts
  • packages/opencode/test/plugin/anthropic-subscription.test.ts
  • packages/opencode/test/plugin/codex.test.ts
  • packages/opencode/test/provider/provider.test.ts
  • packages/opencode/test/server/httpapi-provider.test.ts
  • packages/opencode/test/session/llm-native.test.ts
  • packages/tui/src/component/dialog-provider.tsx
  • packages/tui/src/context/sync.tsx
  • packages/tui/test/cli/cmd/tui/provider-options.test.ts

Comment thread packages/core/src/integration.ts
Comment thread packages/core/src/plugin/provider/anthropic-subscription.ts
Comment thread packages/core/src/session/runner/model.ts Outdated
Comment thread packages/llm/src/providers/anthropic-subscription.ts Outdated
Comment thread packages/llm/src/providers/anthropic-subscription.ts Outdated
Comment thread packages/opencode/src/cli/cmd/providers.ts
Comment thread packages/opencode/src/plugin/anthropic/subscription.ts
Comment thread packages/opencode/src/provider/provider.ts
Comment thread packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts Outdated
Comment thread packages/opencode/test/server/httpapi-provider.test.ts

@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

🧹 Nitpick comments (3)
packages/core/src/credential.ts (1)

133-153: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Metadata equality via JSON.stringify is key-order sensitive.

Two structurally-equal metadata objects with different key insertion order would incorrectly be treated as mismatched, causing compareAndSetOAuth to spuriously reject a legitimate refresh (returning stale current instead of persisting value). Since metadata objects flow through multiple code paths (initial OAuth exchange, refresh, manual updates), key ordering isn't guaranteed to stay stable.

♻️ Proposed fix using a stable comparison
-              if (
-                current?.type !== "oauth" ||
-                current.methodID !== expected.methodID ||
-                current.access !== expected.access ||
-                current.refresh !== expected.refresh ||
-                current.expires !== expected.expires ||
-                JSON.stringify(current.metadata) !== JSON.stringify(expected.metadata)
-              )
+              if (
+                current?.type !== "oauth" ||
+                current.methodID !== expected.methodID ||
+                current.access !== expected.access ||
+                current.refresh !== expected.refresh ||
+                current.expires !== expected.expires ||
+                !Equal.equals(current.metadata, expected.metadata)
+              )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/credential.ts` around lines 133 - 153, Update
compareAndSetOAuth’s metadata comparison to use a key-order-independent
structural equality check instead of JSON.stringify. Preserve the existing OAuth
field comparisons and ensure structurally equivalent metadata allows the
transaction to persist value and return it.
packages/opencode/src/cli/cmd/providers.ts (2)

569-596: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Logout removal/rollback still bespoke instead of reusing the credential module.

This branch reimplements list→remove→(on failure) create as its own rollback, separate from save/saveAnthropicSubscriptionCredential/migrateAnthropicSubscriptionCredential in anthropic-subscription-credential.ts, which already owns the lock and this exact remove/restore pattern for the save path. This is the same root cause previously flagged for the save/migrate paths (now fixed there); the removal path is a remaining site that still diverges and could drift further if either implementation changes independently. Consider adding a removeAnthropicSubscriptionCredential (or similar) helper to that module and calling it here instead.

Note: the previously-flagged concern about restoring only the last of "several" removed rows does not apply — Credential.Service.create() enforces at most one row per integration ID, so .at(-1) here is correct.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/src/cli/cmd/providers.ts` around lines 569 - 596, Replace
the bespoke list/remove/rollback logic in the AnthropicSubscriptionProviderID
logout branch with a shared removeAnthropicSubscriptionCredential-style helper
in anthropic-subscription-credential.ts. Move the credential lookup, removal,
failure restoration, and credential-layer handling into that module while
preserving the existing lock and single-row behavior, then call the helper from
the logout flow before reporting success.

Source: Learnings


60-79: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Nested service yield in withSyntheticSubscriptionAuth.

Line 64 does yield* (yield* Credential.Service).list(...) — a nested service yield. Bind the service to a variable first before calling the method.

♻️ Proposed fix
   const subscription = yield* Effect.gen(function* () {
-    return (yield* (yield* Credential.Service).list(AnthropicSubscriptionIntegrationID)).at(-1)
+    const credentials = yield* Credential.Service
+    return (yield* credentials.list(AnthropicSubscriptionIntegrationID)).at(-1)
   }).pipe(Effect.provide(credentialLayer))

As per coding guidelines, "In Effect generators, bind services to named variables before calling methods; do not use nested service yields."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/src/cli/cmd/providers.ts` around lines 60 - 79, Update
withSyntheticSubscriptionAuth so the Effect generator first binds
Credential.Service to a named local variable, then calls that variable’s list
method with AnthropicSubscriptionIntegrationID. Remove the nested yield
expression while preserving the existing credentialLayer provisioning and
subscription handling.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/opencode/src/provider/anthropic-subscription-credential.ts`:
- Around line 19-31: Update the save flow around services.credentials.list and
the previous credential branch to use compareAndSetOAuth(previous.id,
previous.value, next) instead of a plain update. Handle its undefined result
explicitly, preserving the existing create path when no previous credential
exists and ensuring the created credential result remains correct after a
compare-and-set failure or success.

---

Nitpick comments:
In `@packages/core/src/credential.ts`:
- Around line 133-153: Update compareAndSetOAuth’s metadata comparison to use a
key-order-independent structural equality check instead of JSON.stringify.
Preserve the existing OAuth field comparisons and ensure structurally equivalent
metadata allows the transaction to persist value and return it.

In `@packages/opencode/src/cli/cmd/providers.ts`:
- Around line 569-596: Replace the bespoke list/remove/rollback logic in the
AnthropicSubscriptionProviderID logout branch with a shared
removeAnthropicSubscriptionCredential-style helper in
anthropic-subscription-credential.ts. Move the credential lookup, removal,
failure restoration, and credential-layer handling into that module while
preserving the existing lock and single-row behavior, then call the helper from
the logout flow before reporting success.
- Around line 60-79: Update withSyntheticSubscriptionAuth so the Effect
generator first binds Credential.Service to a named local variable, then calls
that variable’s list method with AnthropicSubscriptionIntegrationID. Remove the
nested yield expression while preserving the existing credentialLayer
provisioning and subscription handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 29a694c4-8235-41e3-b4f9-caf935a288fa

📥 Commits

Reviewing files that changed from the base of the PR and between 9b6b371 and ba40db7.

📒 Files selected for processing (21)
  • packages/core/src/credential.ts
  • packages/core/src/integration.ts
  • packages/core/src/plugin/provider/anthropic-subscription.ts
  • packages/core/src/session/runner/llm.ts
  • packages/core/src/session/runner/model.ts
  • packages/core/test/credential.test.ts
  • packages/core/test/plugin/provider-anthropic-subscription.test.ts
  • packages/core/test/session-runner-anthropic-subscription.test.ts
  • packages/core/test/session-runner-model.test.ts
  • packages/llm/src/providers/anthropic-subscription.ts
  • packages/llm/test/provider/anthropic-subscription.test.ts
  • packages/opencode/src/cli/cmd/providers.ts
  • packages/opencode/src/plugin/anthropic/subscription.ts
  • packages/opencode/src/provider/anthropic-subscription-credential.ts
  • packages/opencode/src/provider/auth.ts
  • packages/opencode/src/provider/provider.ts
  • packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts
  • packages/opencode/test/plugin/anthropic-subscription.test.ts
  • packages/opencode/test/provider/anthropic-subscription-credential.test.ts
  • packages/opencode/test/provider/provider.test.ts
  • packages/opencode/test/server/httpapi-provider.test.ts
🚧 Files skipped from review as they are similar to previous changes (11)
  • packages/core/test/session-runner-anthropic-subscription.test.ts
  • packages/llm/test/provider/anthropic-subscription.test.ts
  • packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts
  • packages/opencode/test/server/httpapi-provider.test.ts
  • packages/opencode/test/plugin/anthropic-subscription.test.ts
  • packages/core/src/session/runner/model.ts
  • packages/opencode/src/plugin/anthropic/subscription.ts
  • packages/core/src/integration.ts
  • packages/llm/src/providers/anthropic-subscription.ts
  • packages/core/src/plugin/provider/anthropic-subscription.ts
  • packages/opencode/test/provider/provider.test.ts

Comment thread packages/opencode/src/provider/anthropic-subscription-credential.ts Outdated

@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

🧹 Nitpick comments (2)
packages/opencode/src/cli/cmd/providers.ts (1)

571-573: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Bind Credential.Service before constructing the helper arguments.

Avoid the nested service yield in the object literal.

Proposed change
 yield* Effect.gen(function* () {
-  yield* removeAnthropicSubscriptionCredential({ auth: authSvc, credentials: yield* Credential.Service })
+  const credentials = yield* Credential.Service
+  yield* removeAnthropicSubscriptionCredential({ auth: authSvc, credentials })
 }).pipe(Effect.provide(credentialLayer), Effect.orDie)

As per coding guidelines, “In Effect generators, bind services to named variables before calling methods; do not use nested service yields.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/src/cli/cmd/providers.ts` around lines 571 - 573, Update
the Effect.gen block around removeAnthropicSubscriptionCredential to bind
Credential.Service to a named variable before constructing the helper arguments,
then pass that variable as credentials. Keep the existing auth service and
credentialLayer provisioning unchanged.

Source: Coding guidelines

packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts (1)

30-40: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Lift Credential.Service out of the request callbacks. credentialLayer is module-scoped already, but Effect.provide(credentialLayer) still runs for every authSet/authRemove call. Yield Credential.Service once when building controlHandlers and close over it in both callbacks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts`
around lines 30 - 40, Update controlHandlers construction to yield
Credential.Service once using the existing module-scoped credentialLayer, then
close over that service in both authSet and authRemove callbacks. Remove the
per-request Credential.Service lookup and Effect.provide(credentialLayer) calls
from the Effect.gen block while preserving the existing credential save and
removal behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/opencode/src/provider/anthropic-subscription-credential.ts`:
- Around line 154-158: Update the existing-credential validation in the save
flow around replaceOAuth to reject OAuth credentials whose method ID differs
from the pending credential, matching the validation used by the
create-if-absent branch. Preserve the existing Auth.AuthError behavior and only
proceed to replaceOAuth when both the credential type and method ID match.

---

Nitpick comments:
In `@packages/opencode/src/cli/cmd/providers.ts`:
- Around line 571-573: Update the Effect.gen block around
removeAnthropicSubscriptionCredential to bind Credential.Service to a named
variable before constructing the helper arguments, then pass that variable as
credentials. Keep the existing auth service and credentialLayer provisioning
unchanged.

In `@packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts`:
- Around line 30-40: Update controlHandlers construction to yield
Credential.Service once using the existing module-scoped credentialLayer, then
close over that service in both authSet and authRemove callbacks. Remove the
per-request Credential.Service lookup and Effect.provide(credentialLayer) calls
from the Effect.gen block while preserving the existing credential save and
removal behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aa0c2ee5-2c27-40b0-b162-9d0c9267190e

📥 Commits

Reviewing files that changed from the base of the PR and between ba40db7 and cc16e1e.

📒 Files selected for processing (10)
  • packages/core/src/credential.ts
  • packages/core/src/integration.ts
  • packages/core/src/plugin/provider/anthropic-subscription.ts
  • packages/core/test/credential.test.ts
  • packages/core/test/plugin/provider-anthropic-subscription.test.ts
  • packages/opencode/src/cli/cmd/providers.ts
  • packages/opencode/src/plugin/anthropic/subscription.ts
  • packages/opencode/src/provider/anthropic-subscription-credential.ts
  • packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts
  • packages/opencode/test/provider/anthropic-subscription-credential.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/core/test/credential.test.ts
  • packages/core/src/integration.ts
  • packages/core/test/plugin/provider-anthropic-subscription.test.ts
  • packages/core/src/plugin/provider/anthropic-subscription.ts
  • packages/opencode/src/plugin/anthropic/subscription.ts

Comment thread packages/opencode/src/provider/anthropic-subscription-credential.ts Outdated
@CreatorGhost
CreatorGhost merged commit 2244207 into dev Jul 15, 2026
5 of 7 checks passed
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