Skip to content

fix(opencode-go): surface clear error on subscription quota exhaustion - #1749

Merged
kevincodex1 merged 8 commits into
Twigpine:mainfrom
euxaristia:fix/opencode-go-usage-errors
Jul 7, 2026
Merged

kevincodex1 merged 8 commits into
Twigpine:mainfrom
euxaristia:fix/opencode-go-usage-errors

Conversation

@euxaristia

@euxaristia euxaristia commented Jun 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The opencode.ai/zen/go gateway returns 429 with GoUsageLimitError in the response body when a user's Go subscription quota runs out. Previously these fell through to the generic Request rejected (429) path, causing the CLI to "mysteriously stop working" with no actionable hint for the user.

This PR adds detection for the opencode-go-specific error markers and surfaces a clear, actionable message. It also fixes an endless "thinking..." hang by intercepting JSON error payloads returned on streaming paths and throwing them cleanly instead of silently ending the stream.

What an opencode-go user now sees

OpenCode Go subscription limit hit:

OpenCode Go subscription limit reached · See https://opencode.ai/workspace/go · Limit: weekly · Workspace: euxaristia-personal · Resets in 2d

Changes

  • src/services/api/openaiShim.ts

    • Intercepts application/json responses at the beginning of openaiStreamToAnthropic streaming reader loop. If it's a JSON error, it parses it and throws it cleanly, preventing endless thinking spinner hangs when the stream ends empty.
  • src/services/api/errors.ts

    • parseOpenCodeGoLimitError() — detects GoUsageLimitError in the error body, scoped to the opencode-go gateway via the x-opencode-request-url header (with OPENAI_BASE_URL env fallback for direct-mode usage). Extracts limitName, workspace, and retry-after seconds.
    • formatResetDuration() — formats seconds as 2d, 2d 3h, 2h 6m, etc.
    • OPENCODE_GO_USAGE_LIMIT_ERROR_MESSAGE exported constant.
    • getAssistantMessageFromError() — new branch that calls parseOpenCodeGoLimitError() before the generic 429 handler, returns the clear message with reset time, workspace, and limit name.
    • classifyAPIError() — new opencode_go_quota_exhausted analytics bucket (distinct from generic rate_limit).
  • src/services/api/openaiErrorClassification.ts

    • Introduces quota_exhausted category mapping for 402 status codes and client errors with billing/credit/quota keywords.
  • src/services/api/withRetry.ts

    • shouldRetry() — treats opencode-go quota errors as terminal. Retrying burns the same 429 and hides the actionable message behind repeated retries.
    • isQuotaExhausted() — updated to abort retries for 402 and quota-related errors.
  • src/services/api/errors.opencodeGo.test.ts (new)

    • Tests covering paid branches, reset duration formatting, env-var fallback, and non-opencode-go 429s that must NOT be mapped.
  • src/services/api/openaiErrorClassification.test.ts and src/services/api/withRetry.test.ts

    • Added coverage for quota_exhausted classification and retry loop termination.

Why this approach

  • Mirrors the canonical implementation in anomalyco/opencode packages/opencode/src/session/retry.ts.
  • Gated on opencode.ai/zen/go in the request URL or OPENAI_BASE_URL, so it cannot misfire on other OpenAI-compatible providers.
  • Avoids infinite thinking spinner hangs when streaming endpoints return JSON errors.

Test plan

  • bun test src/services/api/errors.opencodeGo.test.ts — pass
  • bun test src/services/api/openaiErrorClassification.test.ts src/services/api/withRetry.test.ts — pass
  • bun run typecheck — clean
  • bun run smoke (will run in CI)
  • bun run check (will run in CI)

Summary by CodeRabbit

Release Notes

  • New Features

    • Added quota_exhausted classification for OpenAI-compatible HTTP errors (402/403/429) as non-retryable guidance.
    • Added Opencode Go quota-exhaustion assistant messaging (free vs subscription), including optional reset/workspace/limit details.
    • Improved non-streaming application/json handling by converting it into consistent Anthropic-style streaming events.
  • Bug Fixes

    • Ensured Opencode Go quota errors are detected early and treated as terminal (retry/guidance precedence fixed).
    • Updated OpenAI shim error/header handling for better request-url context.
  • Tests

    • Expanded Bun regression coverage for quota classification, Opencode Go parsing/precedence, retry behavior, and JSON fallback mappings (tool use, stop reasons, <think> stripping).

@coderabbitai

coderabbitai Bot commented Jun 22, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds quota_exhausted handling for OpenAI-compatible failures, OpenCode Go quota messaging, retry gating updates, and a JSON fallback path for streamed responses. Expands tests for quota handling, retry behavior, and stream conversion.

Changes

OpenCode Go Quota Exhaustion Handling

Layer / File(s) Summary
OpenAI compatibility quota classification
src/services/api/openaiErrorClassification.ts, src/services/api/errors.ts, src/services/api/openaiErrorClassification.test.ts
Adds quota_exhausted to the compatibility category union and runtime set, detects quota and billing exhaustion text, maps matching 402/400/403/429 failures to non-retryable quota errors, and adds tests for the new classification cases.
OpenCode Go quota error parsing and message routing
src/services/api/errors.ts, src/services/api/errors.opencodeGo.test.ts
Exports OpenCode Go quota message constants, detects OpenCode Go origin from request headers or OPENAI_BASE_URL, distinguishes free-tier and paid usage-limit errors, extracts limitName and workspace, formats reset durations, returns assistant messages, and classifies matched errors as opencode_go_quota_exhausted.
Retry enforcement and JSON stream fallback
src/services/api/withRetry.ts, src/services/api/withRetry.test.ts
Broadens quota-exhaustion detection in retry logic, exempts OpenCode Go errors from the terminal quota path, makes OpenCode Go quota errors non-retryable in shouldRetry, exports shouldRetry, and adds regression coverage for retry behavior and terminal quota handling.
OpenAI stream JSON fallback
src/services/api/openaiShim.ts, src/services/api/openaiShim.test.ts
Adds a JSON fast-path in openaiStreamToAnthropic that re-emits non-streaming responses as Anthropic events, centralizes non-streaming conversion, and adds regression tests for tool calls, stop reasons, think-tag stripping, array content normalization, and raw-text tool-call recovery.

Estimated code review effort: 4 (Complex) | ~55 minutes

Possibly related PRs

  • Gitlawb/openclaude#1750: Also changes src/services/api/openaiErrorClassification.ts failure categorization for OpenAI-compatible responses.
  • Gitlawb/openclaude#1076: Also changes src/services/api/openaiShim.ts response handling and tool-call conversion paths.
  • Gitlawb/openclaude#1595: Also touches the OpenAI shim streaming flow in src/services/api/openaiShim.ts.

Suggested labels: bug

Suggested reviewers: jatmn, kevincodex1

🚥 Pre-merge checks | ✅ 6 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (6 passed)
Check name Status Explanation
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.
Risk Surface Disclosed ✅ Passed Risk surface was called out: gateway/provider gating is explicitly limited to opencode.ai/zen/go, and no blocker is indicated.
No Hidden Policy Change ✅ Passed All behavior changes are explicit in error, retry, and JSON-fallback paths; no unrelated cleanup hides product/trust/permission/network policy changes.
Title check ✅ Passed The title is concise and matches the main diff around OpenCode Go quota exhaustion handling.
Description check ✅ Passed The description covers Summary, Changes, Why, and Testing, but omits the template's Impact and Notes sections.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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[bot]
coderabbitai Bot previously requested changes Jun 22, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/services/api/errors.opencodeGo.test.ts (1)

1-159: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add focused regression coverage for the new terminal retry branch.

This suite validates message/classification paths, but it does not cover the shouldRetry change in src/services/api/withRetry.ts (Line 815-Line 830). Please add targeted tests for:

  • OpenCode-Go FreeUsageLimitError → non-retryable
  • OpenCode-Go GoUsageLimitError → non-retryable
  • Non-OpenCode 429 with similar body markers → existing retry policy preserved

As per coding guidelines, "Add or update tests when the change affects behavior" and "Block when risky runtime changes lack focused regression coverage or tests assert implementation details while missing the user-visible behavior."

🤖 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 `@src/services/api/errors.opencodeGo.test.ts` around lines 1 - 159, The current
test suite validates error message generation and classification paths but lacks
focused regression tests for the retry behavior logic in the shouldRetry
function from withRetry.ts. Add three new targeted test cases to this file:
first, test that OpenCode-Go FreeUsageLimitError errors should not be retried
(shouldRetry returns false), second, test that OpenCode-Go GoUsageLimitError
errors should not be retried, and third, test that non-OpenCode 429 errors with
similar error body structures should follow the existing retry policy (not be
treated as non-retryable). Each test should create an appropriate error using
makeGoError or APIError.generate and pass it to the shouldRetry function to
verify the expected retry 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 `@src/services/api/errors.ts`:
- Around line 453-458: The OpenCode-Go detection logic currently checks both the
request URL header and the environment variable fallback independently with OR
semantics, causing misclassification when the header contains a non-OpenCode URL
but the environment variable contains an OpenCode URL. Fix this by establishing
proper precedence: only use envBaseUrl as a fallback when the url from the
x-opencode-request-url header is empty, not when it's present but doesn't match
OpenCode. Change the isOpencodeGo assignment to check url first, and only fall
back to checking envBaseUrl if url is empty, so that the actual request URL
takes precedence over the environment configuration.

In `@src/services/api/withRetry.ts`:
- Around line 823-827: The condition checking the OpenCode-Go gateway at lines
823-827 has a precedence issue where the OPENAI_BASE_URL environment variable
check can incorrectly override the header-based check. To fix this, restructure
the condition to prioritize the header check first by using `&&` logic to ensure
that if a header-based check determines it is not OpenCode-Go, the environment
variable check should not override that decision. The header from
error.headers?.get?.('x-opencode-request-url') should be checked as the primary
condition, and only if the header doesn't explicitly indicate opencode.ai/zen/go
should the fallback to
process.env.OPENAI_BASE_URL?.includes('opencode.ai/zen/go') be evaluated.

---

Outside diff comments:
In `@src/services/api/errors.opencodeGo.test.ts`:
- Around line 1-159: The current test suite validates error message generation
and classification paths but lacks focused regression tests for the retry
behavior logic in the shouldRetry function from withRetry.ts. Add three new
targeted test cases to this file: first, test that OpenCode-Go
FreeUsageLimitError errors should not be retried (shouldRetry returns false),
second, test that OpenCode-Go GoUsageLimitError errors should not be retried,
and third, test that non-OpenCode 429 errors with similar error body structures
should follow the existing retry policy (not be treated as non-retryable). Each
test should create an appropriate error using makeGoError or APIError.generate
and pass it to the shouldRetry function to verify the expected retry 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 39f3d9f6-eb68-4162-8016-9e577f15c002

📥 Commits

Reviewing files that changed from the base of the PR and between 1be9b86 and c27b13d.

📒 Files selected for processing (3)
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/errors.ts
  • src/services/api/withRetry.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (13)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with strict mode and ESM imports

Files:

  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/errors.ts
{src/commands/**/*.ts,src/services/**/*.ts,src/entrypoints/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use chalk for terminal color in CLI code

Files:

  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/errors.ts
{src/services/**/*.ts,src/utils/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use execa for child processes

Files:

  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/errors.ts
{src/integrations/**/*.ts,src/services/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Test the exact provider/model path you changed when possible for provider modifications

Files:

  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/errors.ts
**/*.{ts,tsx,js,jsx,py,json,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Follow the existing code style in the touched files

Files:

  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/errors.ts
**/*.{ts,tsx,js,jsx,py}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Keep comments useful and concise

Files:

  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/errors.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Follow TypeScript strict mode and type safety practices by running typecheck before submitting

Files:

  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/errors.ts
**/*

⚙️ CodeRabbit configuration file

**/*: Apply the OpenClaude maintainer review rubric from AGENTS.md. Review the current diff, not stale discussion context. Separate real blockers from suggestions. Do not request changes for vague style churn. Treat approval as merge-ready from CodeRabbit's side, pending required human review and GitHub Checks. If checks are failing or unavailable, say so clearly instead of implying the PR is fully ready.

Files:

  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/errors.ts
{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}

⚙️ CodeRabbit configuration file

{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}: Review provider routing, model selection, env precedence, auth/token handling, OpenAI-compatible shims, retries, proxy behavior, and outbound HTTP behavior with high scrutiny. Block on silent default changes, hidden fallback expansion, credential reuse mistakes, hardcoded provider assumptions, or new network reach that is not intentional and documented.

Files:

  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/errors.ts
**

⚙️ CodeRabbit configuration file

**: # AGENTS.md - AI Agent Coding Guide

This guide is for AI coding agents working in the OpenClaude repository. Read it before changing code, and also follow CONTRIBUTING.md for contributor policy, PR expectations, review follow-up, and project scope.

Project Snapshot

OpenClaude is a coding-agent CLI for cloud and local model providers. It supports OpenAI-compatible APIs, Anthropic, Gemini, DeepSeek, Ollama, MCP, local backends, slash commands, tools, agents, and a React/Ink terminal UI.

The installed CLI runs on Node.js >=22.0.0. Bun is used for source builds, scripts, dependency management, and tests.

Work Style

  • Keep changes focused on one problem.
  • Prefer existing patterns in the file or nearby module.
  • Avoid unrelated formatting, renames, dependency changes, or broad rewrites.
  • Add or update tests when behavior changes.
  • Update docs when setup, commands, provider behavior, or user-facing behavior changes.
  • For new features, larger refactors, dependencies, or runtime changes, follow the issue-first guidance in CONTRIBUTING.md.

Stack And Conventions

  • TypeScript with strict mode and ESM imports.
  • React + Ink for terminal UI.
  • Bun lockfile and Bun scripts for development workflows.
  • Node runtime for the built CLI.
  • Python exists for legacy/local-provider helper code. Do not add new Python code or expand Python-based features unless a maintainer explicitly approves that direction.

Common libraries and patterns:

  • chalk for terminal color.
  • commander for CLI argument parsing.
  • execa for child processes.
  • Existing service, provider, settings, permission, and UI patterns over new abstractions.

Repository Map

  • src/commands/ - slash and CLI command implementations.
  • src/components/ - React/Ink UI components.
  • src/services/ - API, MCP, OAuth, wiki, voice, and other service integrations.
  • src/tools/ - tool implementations.
  • src/utils/ - shared utilities.
  • `src/integration...

Files:

  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/errors.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add or update tests when the change affects behavior

Files:

  • src/services/api/errors.opencodeGo.test.ts
**/*.test.{ts,tsx,js}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Test the exact provider/model path you changed when possible

Files:

  • src/services/api/errors.opencodeGo.test.ts
{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}

⚙️ CodeRabbit configuration file

{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}: Review tests for meaningful coverage of the changed behavior, isolation of global/env/config state, async cleanup, fake timers, provider profile leaks, and Windows-compatible assumptions. Block when risky runtime changes lack focused regression coverage or tests assert implementation details while missing the user-visible behavior.

Files:

  • src/services/api/errors.opencodeGo.test.ts

Comment thread src/services/api/errors.ts Outdated
Comment thread src/services/api/withRetry.ts Outdated
jatmn
jatmn previously requested changes Jun 22, 2026

@jatmn jatmn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I found an issue that needs to be addressed before this is ready.

Findings

  • [P2] Complete CodeRabbit's request to make the request URL authoritative
    src/services/api/errors.ts:453
    src/services/api/withRetry.ts:823
    CodeRabbit's two header-precedence comments are still valid. Both the message parser and retry gate currently OR x-opencode-request-url with OPENAI_BASE_URL, so if a real response says it came from a non-OpenCode URL while stale env/profile state still has OPENAI_BASE_URL=https://opencode.ai/zen/go/v1, the PR misclassifies the unrelated 429 as OpenCode Go quota exhaustion. I reproduced that shape with x-opencode-request-url: https://api.openai.com/v1/messages: getAssistantMessageFromError emits the OpenCode Go subscription message, and withRetry stops after one attempt instead of preserving the existing retry policy. Please complete CodeRabbit's request by making the explicit request URL authoritative, only falling back to OPENAI_BASE_URL when that header is absent, and add regression coverage for the retry path as well as the message path.

coderabbitai[bot]
coderabbitai Bot previously requested changes Jun 22, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 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 `@src/integrations/runtimeMetadata.test.ts`:
- Around line 242-257: The test cases for resolveOpenAIShimRuntimeContext are
incomplete and only assert preserveReasoningContent and thinkingRequestFormat,
but do not validate the other newly inferred shim configuration fields. Add
expect assertions for the maxTokensField, removeBodyFields,
requireReasoningContentOnAssistantMessages, and reasoningContentFallback
properties in both the GLM paths test case (testing openrouter/zhipu/glm-5.2
model) and the direct glm model names test case (testing glm-5.2 model). These
assertions should validate that result.openaiShimConfig contains the correct
values for each of these fields to prevent silent regressions in GLM request
shaping behavior.

In `@src/services/api/openaiShim.ts`:
- Around line 1580-1633: The non-streaming JSON handler in the
openaiStreamToAnthropic function is missing logic to emit tool_use content
blocks from the choice.message.tool_calls array. After handling
reasoning_content and content blocks but before yielding message_delta, add code
to iterate through any tool_calls present in choice.message.tool_calls and yield
corresponding content_block_start, content_block_delta, and content_block_stop
events with type 'tool_use' for each tool call. Reference the
_convertNonStreamingResponse function to see the correct pattern for extracting
and formatting tool_calls, then apply that same logic here to ensure tool calls
are not dropped when a provider returns a complete JSON response instead of a
stream.

In `@src/services/api/withRetry.test.ts`:
- Around line 214-244: The test modifies the global
`process.env.OPENCLAUDE_RETRY_DELAY_MS` variable without restoring it afterward,
which can cause state leakage to subsequent tests. Save the original value of
`process.env.OPENCLAUDE_RETRY_DELAY_MS` before setting it to '1', then restore
it after the test completes using either a try/finally block or by storing and
resetting it at the end of the test function. This ensures the global
environment state is properly isolated and does not affect other tests.

In `@src/tools/FileReadTool/FileReadTool.ts`:
- Line 818: In the FileReadTool.ts file, locate the system reminder message
around line 818 that discusses reading files and malware analysis. This message
currently lacks an explicit refusal clause that prevents improving or augmenting
malware. Add back the explicit refusal-to-improve-malware sentence that
strengthens this security control, or if this change was intentional, coordinate
with maintainers for explicit approval of the policy change since this is a
security-sensitive path.

In `@tests/sdk/query-lifecycle.test.ts`:
- Around line 223-238: The test for interrupt() with reason "interrupt"
currently aborts the AbortController immediately before the query begins
iteration, which bypasses the stopHooks branch that checks signal.reason. Modify
the test to start consuming the query first (by iterating through at least one
message from the query) before calling ac.abort('interrupt'), then continue
draining the remaining messages and verify that the user cancellation message is
still suppressed in this in-flight interruption scenario. This ensures the test
actually exercises the signal.reason check within the stopHooks path rather than
just testing early abort 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 62f14ac3-b45f-4ff7-8503-0e99e5d38e5d

📥 Commits

Reviewing files that changed from the base of the PR and between c27b13d and 98c6161.

📒 Files selected for processing (14)
  • src/cli/print.ts
  • src/constants/prompts.ts
  • src/hooks/useReplBridge.tsx
  • src/integrations/runtimeMetadata.test.ts
  • src/integrations/runtimeMetadata.ts
  • src/query/stopHooks.ts
  • src/services/api/errors.ts
  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/openaiShim.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/tools/FileReadTool/FileReadTool.ts
  • tests/sdk/query-lifecycle.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (15)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with strict mode and ESM imports

Files:

  • src/hooks/useReplBridge.tsx
  • src/integrations/runtimeMetadata.ts
  • src/cli/print.ts
  • src/services/api/openaiErrorClassification.test.ts
  • src/integrations/runtimeMetadata.test.ts
  • src/tools/FileReadTool/FileReadTool.ts
  • src/query/stopHooks.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/openaiShim.ts
  • src/constants/prompts.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.ts
  • src/services/api/openaiErrorClassification.ts
**/*.{ts,tsx,js,jsx,py,json,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Follow the existing code style in the touched files

Files:

  • src/hooks/useReplBridge.tsx
  • src/integrations/runtimeMetadata.ts
  • src/cli/print.ts
  • src/services/api/openaiErrorClassification.test.ts
  • src/integrations/runtimeMetadata.test.ts
  • src/tools/FileReadTool/FileReadTool.ts
  • src/query/stopHooks.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/openaiShim.ts
  • tests/sdk/query-lifecycle.test.ts
  • src/constants/prompts.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.ts
  • src/services/api/openaiErrorClassification.ts
**/*.{ts,tsx,js,jsx,py}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Keep comments useful and concise

Files:

  • src/hooks/useReplBridge.tsx
  • src/integrations/runtimeMetadata.ts
  • src/cli/print.ts
  • src/services/api/openaiErrorClassification.test.ts
  • src/integrations/runtimeMetadata.test.ts
  • src/tools/FileReadTool/FileReadTool.ts
  • src/query/stopHooks.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/openaiShim.ts
  • tests/sdk/query-lifecycle.test.ts
  • src/constants/prompts.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.ts
  • src/services/api/openaiErrorClassification.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Follow TypeScript strict mode and type safety practices by running typecheck before submitting

Files:

  • src/hooks/useReplBridge.tsx
  • src/integrations/runtimeMetadata.ts
  • src/cli/print.ts
  • src/services/api/openaiErrorClassification.test.ts
  • src/integrations/runtimeMetadata.test.ts
  • src/tools/FileReadTool/FileReadTool.ts
  • src/query/stopHooks.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/openaiShim.ts
  • tests/sdk/query-lifecycle.test.ts
  • src/constants/prompts.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.ts
  • src/services/api/openaiErrorClassification.ts
**

⚙️ CodeRabbit configuration file

**: # AGENTS.md - AI Agent Coding Guide

This guide is for AI coding agents working in the OpenClaude repository. Read it before changing code, and also follow CONTRIBUTING.md for contributor policy, PR expectations, review follow-up, and project scope.

Project Snapshot

OpenClaude is a coding-agent CLI for cloud and local model providers. It supports OpenAI-compatible APIs, Anthropic, Gemini, DeepSeek, Ollama, MCP, local backends, slash commands, tools, agents, and a React/Ink terminal UI.

The installed CLI runs on Node.js >=22.0.0. Bun is used for source builds, scripts, dependency management, and tests.

Work Style

  • Keep changes focused on one problem.
  • Prefer existing patterns in the file or nearby module.
  • Avoid unrelated formatting, renames, dependency changes, or broad rewrites.
  • Add or update tests when behavior changes.
  • Update docs when setup, commands, provider behavior, or user-facing behavior changes.
  • For new features, larger refactors, dependencies, or runtime changes, follow the issue-first guidance in CONTRIBUTING.md.

Stack And Conventions

  • TypeScript with strict mode and ESM imports.
  • React + Ink for terminal UI.
  • Bun lockfile and Bun scripts for development workflows.
  • Node runtime for the built CLI.
  • Python exists for legacy/local-provider helper code. Do not add new Python code or expand Python-based features unless a maintainer explicitly approves that direction.

Common libraries and patterns:

  • chalk for terminal color.
  • commander for CLI argument parsing.
  • execa for child processes.
  • Existing service, provider, settings, permission, and UI patterns over new abstractions.

Repository Map

  • src/commands/ - slash and CLI command implementations.
  • src/components/ - React/Ink UI components.
  • src/services/ - API, MCP, OAuth, wiki, voice, and other service integrations.
  • src/tools/ - tool implementations.
  • src/utils/ - shared utilities.
  • `src/integration...

Files:

  • src/hooks/useReplBridge.tsx
  • src/integrations/runtimeMetadata.ts
  • src/cli/print.ts
  • src/services/api/openaiErrorClassification.test.ts
  • src/integrations/runtimeMetadata.test.ts
  • src/tools/FileReadTool/FileReadTool.ts
  • src/query/stopHooks.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/openaiShim.ts
  • tests/sdk/query-lifecycle.test.ts
  • src/constants/prompts.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.ts
  • src/services/api/openaiErrorClassification.ts
**/*

⚙️ CodeRabbit configuration file

**/*: Apply the OpenClaude maintainer review rubric from AGENTS.md. Review the current diff, not stale discussion context. Separate real blockers from suggestions. Do not request changes for vague style churn. Treat approval as merge-ready from CodeRabbit's side, pending required human review and GitHub Checks. If checks are failing or unavailable, say so clearly instead of implying the PR is fully ready.

Files:

  • src/hooks/useReplBridge.tsx
  • src/integrations/runtimeMetadata.ts
  • src/cli/print.ts
  • src/services/api/openaiErrorClassification.test.ts
  • src/integrations/runtimeMetadata.test.ts
  • src/tools/FileReadTool/FileReadTool.ts
  • src/query/stopHooks.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/openaiShim.ts
  • tests/sdk/query-lifecycle.test.ts
  • src/constants/prompts.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.ts
  • src/services/api/openaiErrorClassification.ts
{src/integrations/**/*.ts,src/services/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Test the exact provider/model path you changed when possible for provider modifications

Files:

  • src/integrations/runtimeMetadata.ts
  • src/services/api/openaiErrorClassification.test.ts
  • src/integrations/runtimeMetadata.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/openaiShim.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.ts
  • src/services/api/openaiErrorClassification.ts
src/integrations/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Check existing provider implementations before adding a new pattern

Files:

  • src/integrations/runtimeMetadata.ts
  • src/integrations/runtimeMetadata.test.ts
{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}

⚙️ CodeRabbit configuration file

{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}: Review provider routing, model selection, env precedence, auth/token handling, OpenAI-compatible shims, retries, proxy behavior, and outbound HTTP behavior with high scrutiny. Block on silent default changes, hidden fallback expansion, credential reuse mistakes, hardcoded provider assumptions, or new network reach that is not intentional and documented.

Files:

  • src/integrations/runtimeMetadata.ts
  • src/services/api/openaiErrorClassification.test.ts
  • src/integrations/runtimeMetadata.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/openaiShim.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.ts
  • src/services/api/openaiErrorClassification.ts
{src/commands/**/*.ts,src/services/**/*.ts,src/entrypoints/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use chalk for terminal color in CLI code

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/openaiShim.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.ts
  • src/services/api/openaiErrorClassification.ts
{src/services/**/*.ts,src/utils/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use execa for child processes

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/openaiShim.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.ts
  • src/services/api/openaiErrorClassification.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add or update tests when the change affects behavior

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/integrations/runtimeMetadata.test.ts
  • src/services/api/withRetry.test.ts
  • tests/sdk/query-lifecycle.test.ts
**/*.test.{ts,tsx,js}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Test the exact provider/model path you changed when possible

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/integrations/runtimeMetadata.test.ts
  • src/services/api/withRetry.test.ts
  • tests/sdk/query-lifecycle.test.ts
{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}

⚙️ CodeRabbit configuration file

{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}: Review tests for meaningful coverage of the changed behavior, isolation of global/env/config state, async cleanup, fake timers, provider profile leaks, and Windows-compatible assumptions. Block when risky runtime changes lack focused regression coverage or tests assert implementation details while missing the user-visible behavior.

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/integrations/runtimeMetadata.test.ts
  • src/services/api/withRetry.test.ts
  • tests/sdk/query-lifecycle.test.ts
src/{components/permissions,utils/permissions,hooks/toolPermission,tools,entrypoints/sdk}/**

⚙️ CodeRabbit configuration file

src/{components/permissions,utils/permissions,hooks/toolPermission,tools,entrypoints/sdk}/**: Review permission prompts, auto-allow logic, sandbox behavior, SDK permission schemas, shell/PowerShell execution, and background execution paths as security-sensitive. Block on bypasses, unclear trust boundaries, unsafe path handling, missing user visibility, or changes that broaden allowed behavior without an explicit maintainer decision.

Files:

  • src/tools/FileReadTool/FileReadTool.ts
🔇 Additional comments (10)
src/constants/prompts.ts (1)

22-24: LGTM!

Also applies to: 289-292, 303-307, 363-367, 736-740

src/integrations/runtimeMetadata.ts (1)

201-211: LGTM!

src/cli/print.ts (1)

2852-2852: LGTM!

src/hooks/useReplBridge.tsx (1)

396-396: LGTM!

src/query/stopHooks.ts (1)

325-329: LGTM!

tests/sdk/query-lifecycle.test.ts (1)

240-268: LGTM!

src/services/api/openaiErrorClassification.ts (1)

8-8: LGTM!

Also applies to: 41-41, 204-220, 338-352, 470-495

src/services/api/errors.ts (1)

125-130: LGTM!

src/services/api/withRetry.ts (1)

123-140: LGTM!

src/services/api/openaiErrorClassification.test.ts (1)

225-260: LGTM!

Also applies to: 270-300

Comment thread src/integrations/runtimeMetadata.test.ts Outdated
Comment thread src/services/api/openaiShim.ts Outdated
Comment thread src/services/api/withRetry.test.ts
Comment thread src/tools/FileReadTool/FileReadTool.ts Outdated
Comment thread tests/sdk/query-lifecycle.test.ts
@euxaristia
euxaristia force-pushed the fix/opencode-go-usage-errors branch from 98c6161 to 349eff6 Compare June 22, 2026 20:28

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
src/services/api/withRetry.test.ts (1)

213-244: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Restore OPENCLAUDE_RETRY_DELAY_MS after this test to prevent env leakage.

This test mutates global env state and leaves it set, which can affect subsequent retry tests.

As per path instructions, "Review tests for meaningful coverage of the changed behavior, isolation of global/env/config state...".

Suggested fix
 test('does not retry quota/allotment exhaustion failures', async () => {
-  process.env.OPENCLAUDE_RETRY_DELAY_MS = '1'
-  const { CannotRetryError, withRetry } =
-    await importFreshWithRetryModule('openai')
-  const error = APIError.generate(
-    402,
-    undefined,
-    'OpenAI API error 402: Payment Required [openai_category=quota_exhausted,host=opencode.ai] Hint: Provider quota or usage allotment has run out.',
-    new Headers(),
-  )
-  let attempts = 0
-
-  await expect(
-    drainAsyncGenerator(
-      withRetry(
-        async () => ({} as Anthropic),
-        async () => {
-          attempts++
-          throw error
-        },
-        {
-          maxRetries: 2,
-          model: 'glm-5.1',
-          thinkingConfig: { type: 'disabled' },
-        },
-      ),
-    ),
-  ).rejects.toBeInstanceOf(CannotRetryError)
-
-  expect(attempts).toBe(1)
+  const prevRetryDelay = process.env.OPENCLAUDE_RETRY_DELAY_MS
+  process.env.OPENCLAUDE_RETRY_DELAY_MS = '1'
+  try {
+    const { CannotRetryError, withRetry } =
+      await importFreshWithRetryModule('openai')
+    const error = APIError.generate(
+      402,
+      undefined,
+      'OpenAI API error 402: Payment Required [openai_category=quota_exhausted,host=opencode.ai] Hint: Provider quota or usage allotment has run out.',
+      new Headers(),
+    )
+    let attempts = 0
+
+    await expect(
+      drainAsyncGenerator(
+        withRetry(
+          async () => ({} as Anthropic),
+          async () => {
+            attempts++
+            throw error
+          },
+          {
+            maxRetries: 2,
+            model: 'glm-5.1',
+            thinkingConfig: { type: 'disabled' },
+          },
+        ),
+      ),
+    ).rejects.toBeInstanceOf(CannotRetryError)
+
+    expect(attempts).toBe(1)
+  } finally {
+    if (prevRetryDelay === undefined) {
+      delete process.env.OPENCLAUDE_RETRY_DELAY_MS
+    } else {
+      process.env.OPENCLAUDE_RETRY_DELAY_MS = prevRetryDelay
+    }
+  }
 })
🤖 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 `@src/services/api/withRetry.test.ts` around lines 213 - 244, The test function
"does not retry quota/allotment exhaustion failures" sets
process.env.OPENCLAUDE_RETRY_DELAY_MS but does not restore it after execution,
causing environment pollution for subsequent tests. After the final expect
assertion, add cleanup code to delete or restore the original value of
process.env.OPENCLAUDE_RETRY_DELAY_MS so that the global environment state is
not modified for other tests.

Source: Path instructions

src/services/api/openaiShim.ts (1)

1580-1633: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

JSON fast-path still drops tool_calls, causing missed tool execution.

The non-streaming application/json branch emits thinking/text then ends the message, but never converts choice.message.tool_calls into tool_use content blocks. Tool-calling responses from providers that ignore streaming are silently degraded.

Suggested fix
     if (content) {
       yield {
         type: 'content_block_start',
         index: contentBlockIndex,
         content_block: { type: 'text', text: '' },
       }
       yield {
         type: 'content_block_delta',
         index: contentBlockIndex,
         delta: { type: 'text_delta', text: content },
       }
       yield {
         type: 'content_block_stop',
         index: contentBlockIndex,
       }
       contentBlockIndex++
     }

+    // Preserve non-streaming tool calls in JSON fallback responses.
+    const toolCalls = Array.isArray(choice?.message?.tool_calls)
+      ? choice.message.tool_calls
+      : []
+    for (const tc of toolCalls) {
+      const fnName = tc?.function?.name
+      const argsRaw = tc?.function?.arguments
+      if (!fnName) continue
+      let input: unknown = {}
+      if (typeof argsRaw === 'string' && argsRaw.length > 0) {
+        try {
+          input = JSON.parse(argsRaw)
+        } catch {
+          input = { arguments: argsRaw }
+        }
+      }
+      yield {
+        type: 'content_block_start',
+        index: contentBlockIndex,
+        content_block: {
+          type: 'tool_use',
+          id: tc?.id ?? `tool_${contentBlockIndex}`,
+          name: fnName,
+          input,
+        },
+      }
+      yield { type: 'content_block_stop', index: contentBlockIndex }
+      contentBlockIndex++
+    }
+
     yield {
       type: 'message_delta',
🤖 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 `@src/services/api/openaiShim.ts` around lines 1580 - 1633, The non-streaming
response conversion in the openaiShim.ts function currently handles reasoning
and content blocks but completely ignores tool_calls from the parsed response.
After the content block handling (the if (content) block), add a new section
that checks for choice.message.tool_calls and converts each tool call into a
tool_use content block. For each tool call in the array, emit
content_block_start with type 'tool_use', content_block_delta with the tool use
details, and content_block_stop events, incrementing contentBlockIndex after
each tool call, following the same pattern used for reasoning and content
blocks.
🤖 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.

Duplicate comments:
In `@src/services/api/openaiShim.ts`:
- Around line 1580-1633: The non-streaming response conversion in the
openaiShim.ts function currently handles reasoning and content blocks but
completely ignores tool_calls from the parsed response. After the content block
handling (the if (content) block), add a new section that checks for
choice.message.tool_calls and converts each tool call into a tool_use content
block. For each tool call in the array, emit content_block_start with type
'tool_use', content_block_delta with the tool use details, and
content_block_stop events, incrementing contentBlockIndex after each tool call,
following the same pattern used for reasoning and content blocks.

In `@src/services/api/withRetry.test.ts`:
- Around line 213-244: The test function "does not retry quota/allotment
exhaustion failures" sets process.env.OPENCLAUDE_RETRY_DELAY_MS but does not
restore it after execution, causing environment pollution for subsequent tests.
After the final expect assertion, add cleanup code to delete or restore the
original value of process.env.OPENCLAUDE_RETRY_DELAY_MS so that the global
environment state is not modified for other tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e1878e52-fa2a-4c7f-b68c-12de8161602f

📥 Commits

Reviewing files that changed from the base of the PR and between 98c6161 and 349eff6.

📒 Files selected for processing (7)
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/errors.ts
  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/openaiShim.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (13)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with strict mode and ESM imports

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/errors.ts
{src/commands/**/*.ts,src/services/**/*.ts,src/entrypoints/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use chalk for terminal color in CLI code

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/errors.ts
{src/services/**/*.ts,src/utils/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use execa for child processes

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/errors.ts
{src/integrations/**/*.ts,src/services/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Test the exact provider/model path you changed when possible for provider modifications

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/errors.ts
**/*.{ts,tsx,js,jsx,py,json,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Follow the existing code style in the touched files

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/errors.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add or update tests when the change affects behavior

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/errors.opencodeGo.test.ts
**/*.test.{ts,tsx,js}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Test the exact provider/model path you changed when possible

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/errors.opencodeGo.test.ts
**/*.{ts,tsx,js,jsx,py}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Keep comments useful and concise

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/errors.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Follow TypeScript strict mode and type safety practices by running typecheck before submitting

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/errors.ts
**

⚙️ CodeRabbit configuration file

**: # AGENTS.md - AI Agent Coding Guide

This guide is for AI coding agents working in the OpenClaude repository. Read it before changing code, and also follow CONTRIBUTING.md for contributor policy, PR expectations, review follow-up, and project scope.

Project Snapshot

OpenClaude is a coding-agent CLI for cloud and local model providers. It supports OpenAI-compatible APIs, Anthropic, Gemini, DeepSeek, Ollama, MCP, local backends, slash commands, tools, agents, and a React/Ink terminal UI.

The installed CLI runs on Node.js >=22.0.0. Bun is used for source builds, scripts, dependency management, and tests.

Work Style

  • Keep changes focused on one problem.
  • Prefer existing patterns in the file or nearby module.
  • Avoid unrelated formatting, renames, dependency changes, or broad rewrites.
  • Add or update tests when behavior changes.
  • Update docs when setup, commands, provider behavior, or user-facing behavior changes.
  • For new features, larger refactors, dependencies, or runtime changes, follow the issue-first guidance in CONTRIBUTING.md.

Stack And Conventions

  • TypeScript with strict mode and ESM imports.
  • React + Ink for terminal UI.
  • Bun lockfile and Bun scripts for development workflows.
  • Node runtime for the built CLI.
  • Python exists for legacy/local-provider helper code. Do not add new Python code or expand Python-based features unless a maintainer explicitly approves that direction.

Common libraries and patterns:

  • chalk for terminal color.
  • commander for CLI argument parsing.
  • execa for child processes.
  • Existing service, provider, settings, permission, and UI patterns over new abstractions.

Repository Map

  • src/commands/ - slash and CLI command implementations.
  • src/components/ - React/Ink UI components.
  • src/services/ - API, MCP, OAuth, wiki, voice, and other service integrations.
  • src/tools/ - tool implementations.
  • src/utils/ - shared utilities.
  • `src/integration...

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/errors.ts
**/*

⚙️ CodeRabbit configuration file

**/*: Apply the OpenClaude maintainer review rubric from AGENTS.md. Review the current diff, not stale discussion context. Separate real blockers from suggestions. Do not request changes for vague style churn. Treat approval as merge-ready from CodeRabbit's side, pending required human review and GitHub Checks. If checks are failing or unavailable, say so clearly instead of implying the PR is fully ready.

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/errors.ts
{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}

⚙️ CodeRabbit configuration file

{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}: Review provider routing, model selection, env precedence, auth/token handling, OpenAI-compatible shims, retries, proxy behavior, and outbound HTTP behavior with high scrutiny. Block on silent default changes, hidden fallback expansion, credential reuse mistakes, hardcoded provider assumptions, or new network reach that is not intentional and documented.

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/errors.ts
{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}

⚙️ CodeRabbit configuration file

{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}: Review tests for meaningful coverage of the changed behavior, isolation of global/env/config state, async cleanup, fake timers, provider profile leaks, and Windows-compatible assumptions. Block when risky runtime changes lack focused regression coverage or tests assert implementation details while missing the user-visible behavior.

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/errors.opencodeGo.test.ts
🔇 Additional comments (9)
src/services/api/withRetry.ts (1)

123-140: LGTM!

Also applies to: 830-846, 1068-1069

src/services/api/errors.opencodeGo.test.ts (1)

10-10: LGTM!

Also applies to: 161-210

src/services/api/openaiErrorClassification.test.ts (1)

234-263: LGTM!

src/services/api/openaiErrorClassification.ts (4)

1-16: LGTM!


41-41: LGTM!


204-220: LGTM!


338-352: LGTM!

src/services/api/errors.ts (2)

125-130: LGTM!


449-485: LGTM!

@jatmn jatmn added the enhancement New feature or request label Jun 22, 2026
jatmn
jatmn previously requested changes Jun 22, 2026

@jatmn jatmn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I found issues that need to be addressed before this is ready.

Findings

  • [P2] Preserve tool calls in the streaming JSON fallback
    src/services/api/openaiShim.ts:1580
    CodeRabbit's tool-call comment is still valid. The new application/json fallback inside openaiStreamToAnthropic converts only reasoning_content and message.content, then returns before the normal stream parser or _convertNonStreamingResponse can handle choice.message.tool_calls. When an OpenAI-compatible provider ignores stream: true and returns a full JSON response containing tool_calls, the shim now emits no tool_use block and ends the message instead, so the requested tool is never executed. Please complete that review request by reusing the existing non-streaming conversion behavior or adding equivalent tool_calls handling in this fallback path.

  • [P2] Normalize JSON fallback stop reasons before emitting stream events
    src/services/api/openaiShim.ts:1621
    The same JSON fallback passes choice.finish_reason directly into the Anthropic-shaped message_delta. That leaks OpenAI values such as stop, length, and tool_calls instead of the rest of this shim's end_turn, max_tokens, and tool_use values. I reproduced the fallback with stream: true JSON responses and saw stop_reason: "length" and stop_reason: "tool_calls" emitted verbatim. Downstream code expects the Anthropic stop-reason vocabulary, so max-token truncation and tool-use turns from providers that return JSON on a streaming request are misclassified. Please map these finish reasons the same way the streaming parser and _convertNonStreamingResponse already do.

  • [P2] Convert array content before emitting text deltas
    src/services/api/openaiShim.ts:1582
    The JSON fallback also assigns choice.message.content directly to content and then emits it as delta: { type: 'text_delta', text: content }. OpenAI-compatible JSON responses can use array content parts, and _convertNonStreamingResponse already joins text parts before returning Anthropic content. In the new fallback, the same response shape produces a stream event whose text_delta.text is an array of content objects rather than a string, which is not a valid Anthropic text delta and can break consumers that concatenate or render streamed text. Please normalize array content to text blocks before emitting the delta, or route the fallback through the existing non-streaming converter.

  • [P2] Keep think-tag filtering on JSON fallback text
    src/services/api/openaiShim.ts:1582
    Both the regular streaming parser and _convertNonStreamingResponse strip <think>...</think> blocks before surfacing assistant text, and this file has regression tests for that behavior. The new JSON fallback bypasses both paths and emits choice.message.content verbatim; with a JSON response containing <think>private plan</think>visible answer, the streamed text delta includes the entire <think> block. Providers that ignore streaming but return JSON can therefore expose hidden reasoning that the shim normally filters. Please run JSON fallback text through the same think-tag filter/stripper before emitting it.

  • [P2] Preserve raw text tool-call fallback behavior for JSON responses
    src/services/api/openaiShim.ts:1582
    The established streaming and non-streaming converters also detect raw tool-call JSON embedded in assistant text, such as {"name":"Bash","arguments":{"command":"pwd"}}, and convert it to a tool_use block so local/Ollama-style providers can still execute tools. The new JSON fallback emits that raw JSON as ordinary text and ends the turn, so providers that return a non-SSE JSON response on a streamed request lose the tool call even when the existing converter logic would have recovered it. Please reuse the existing raw text tool-call parsing or delegate this fallback to the non-streaming converter.

jatmn
jatmn previously requested changes Jun 23, 2026

@jatmn jatmn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I found an issue that needs to be addressed before this is ready.

Findings

  • [P2] Preserve the new OpenCode Go guidance through the retry wrapper
    src/services/api/withRetry.ts:299
    The early isQuotaExhausted(error) block runs before the PR's OpenCode Go-specific retry/message handling can surface the new guidance. I reproduced an OpenCode Go FreeUsageLimitError with x-opencode-request-url: https://opencode.ai/zen/go/v1/messages through withRetry; instead of preserving OpenCode Go free usage exhausted · Subscribe at https://opencode.ai/go, the thrown CannotRetryError wraps the old generic text: API quota exhausted or not enabled. Fix: Enable billing for your provider.... That means at least the free-tier exhaustion path can still show the unclear message this PR is meant to replace. Please let the OpenCode Go quota APIError survive as the original error, or otherwise preserve the specific OpenCode Go assistant message before the generic quota guard converts it.

  • [P2] Complete CodeRabbit's request to preserve non-stream JSON conversion semantics
    src/services/api/openaiShim.ts:1580
    CodeRabbit's current JSON-fallback review item is still valid. The new application/json branch inside openaiStreamToAnthropic hand-rolls a much smaller converter than the existing non-streaming path: it only emits reasoning_content and message.content, then forwards choice.finish_reason directly. That drops choice.message.tool_calls, turns finish_reason: "tool_calls" or "stop" into invalid Anthropic stop reasons instead of tool_use/end_turn, skips array-content normalization, bypasses <think>...</think> stripping, and loses the raw text tool-call recovery that _convertNonStreamingResponse already applies. A provider that ignores stream: true and returns a normal JSON chat completion can therefore appear successful while silently ending a tool turn as plain text or exposing hidden reasoning. Please complete that review request by reusing the established non-stream converter for this fallback, or by mirroring its tool-call, stop-reason, text-normalization, think-filtering, and raw-tool-call behavior before yielding stream events.

euxaristia added a commit to euxaristia/openclaude that referenced this pull request Jun 24, 2026
…verter in JSON fallback

Addresses the two remaining P2 review items on Twigpine#1749.

withRetry: the early isQuotaExhausted guard wrapped OpenCode Go
FreeUsageLimitError/GoUsageLimitError 429s in the generic "API quota
exhausted or not enabled" message, clobbering the actionable subscribe/
reset guidance. Skip the generic guard for OpenCode Go quota errors so they
fall through to the standard shouldRetry=false terminal path, which rethrows
the original APIError and lets getAssistantMessageFromError surface the
specific message. Consolidate detection in a shared isOpenCodeGoQuotaError
predicate (errors.ts) and drop the duplicated inline header check in
shouldRetry.

openaiShim: the application/json fallback in openaiStreamToAnthropic
hand-rolled a thin converter that dropped tool_calls, forwarded raw OpenAI
finish_reason values as Anthropic stop reasons, skipped array-content
normalization, bypassed <think> stripping, and lost raw text tool-call
recovery. Extract the established non-streaming conversion into a shared
convertNonStreamingResponseToAnthropicMessage and route the fallback through
it, re-emitting the result as stream events. _convertNonStreamingResponse
now delegates to the same function.

Adds regression coverage: a withRetry test proving the OpenCode Go message
survives the retry loop, and JSON-fallback tests for tool_calls, stop-reason
mapping, <think> stripping, array content, and raw text tool-call recovery.
@euxaristia

Copy link
Copy Markdown
Contributor Author

Addressed both remaining P2 items in d502556.

1. Preserve the OpenCode Go guidance through the retry wrapper (withRetry.ts)
The early isQuotaExhausted(error) guard was matching OpenCode Go FreeUsageLimitError/GoUsageLimitError 429s (their bodies contain "usage limit"/"quota") and throwing the generic API quota exhausted or not enabled… CannotRetryError before the OpenCode Go message could surface. Fixed by skipping that generic guard for OpenCode Go quota errors (isQuotaExhausted(error) && !isOpenCodeGoQuotaError(error)), so they fall through to the standard shouldRetry=false terminal path, which rethrows the original APIError and lets getAssistantMessageFromError produce OpenCode Go free usage exhausted · Subscribe at https://opencode.ai/go. Detection is now a single shared isOpenCodeGoQuotaError predicate in errors.ts; the duplicated inline header check in shouldRetry is gone.

2. Reuse the established non-stream converter in the JSON fallback (openaiShim.ts)
Extracted _convertNonStreamingResponse's body into a shared convertNonStreamingResponseToAnthropicMessage, and routed the application/json fallback in openaiStreamToAnthropic through it (re-emitting the resulting message as stream events). _convertNonStreamingResponse now delegates to the same function, so the fallback and the non-stream path are guaranteed identical: tool_calls → tool_use, finish_reason tool_calls/length → tool_use/max_tokens, array content joined to text, <think>…</think> stripped, and raw Tool calls requested: text recovered as tool_use.

Verification (mutation-proofed): reverting both production changes while keeping the new tests makes them fail — the withRetry regression (asserts the original APIError survives and the OpenCode Go message is recoverable, not the generic text) fails, and all five JSON-fallback tests fail — then they pass again with the fix restored. Full files green: openaiShim.test.ts 151/151, withRetry.test.ts 40/40. tsc --noEmit shows no new errors in the changed files.

coderabbitai[bot]
coderabbitai Bot previously requested changes Jun 24, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@src/services/api/openaiShim.test.ts`:
- Around line 7335-7353: The collectFallbackEvents helper leaves a global fetch
stub installed, which can leak state across tests. Update collectFallbackEvents
in openaiShim.test.ts to save the existing globalThis.fetch before overriding
it, then restore it in a finally block after the stream is consumed. Keep the
fix localized to the helper and ensure createOpenAIShimClient and
makeJsonChatCompletion still work with the temporary stub.

In `@src/services/api/openaiShim.ts`:
- Around line 1544-1547: Treat empty tool call arrays as missing in the raw
recovery paths. In openaiShim.ts, update the logic around
parseRawToolCallsRequestedText so that the check uses the actual length of
choice.message.tool_calls instead of relying on truthiness, and apply the same
hasStructuredToolCalls-style condition in both the strippedContent branch and
the array-content branch. This ensures raw “Tool calls requested” recovery only
skips when structured tool calls are truly present.

In `@src/services/api/withRetry.ts`:
- Around line 299-304: OpenCode Go quota errors are currently reaching the
fast-mode 429 fallback in withRetry before the terminal quota path, which can
trigger an unnecessary retry or cooldown instead of surfacing the quota
guidance. Update the retry handling in src/services/api/withRetry.ts so that
isOpenCodeGoQuotaError(error) is intercepted before the fast-mode branch and
immediately returned as a CannotRetryError(error, retryContext). Keep the
existing quota-exhausted flow for non-OpenCode-Go errors so shouldRetry() and
getAssistantMessageFromError still behave as intended.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 56dcc5af-b046-4a41-94ac-bd7fcc48b44d

📥 Commits

Reviewing files that changed from the base of the PR and between eee3097 and d502556.

📒 Files selected for processing (5)
  • src/services/api/errors.ts
  • src/services/api/openaiShim.test.ts
  • src/services/api/openaiShim.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (13)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with strict mode and ESM imports

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/errors.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
{src/commands/**/*.ts,src/services/**/*.ts,src/entrypoints/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use chalk for terminal color in CLI code

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/errors.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
{src/services/**/*.ts,src/utils/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use execa for child processes

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/errors.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
{src/integrations/**/*.ts,src/services/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Test the exact provider/model path you changed when possible for provider modifications

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/errors.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
**/*.{ts,tsx,js,jsx,py,json,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Follow the existing code style in the touched files

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/errors.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add or update tests when the change affects behavior

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.test.ts
**/*.test.{ts,tsx,js}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Test the exact provider/model path you changed when possible

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.test.ts
**/*.{ts,tsx,js,jsx,py}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Keep comments useful and concise

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/errors.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Follow TypeScript strict mode and type safety practices by running typecheck before submitting

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/errors.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
**

⚙️ CodeRabbit configuration file

**: # AGENTS.md - AI Agent Coding Guide

This guide is for AI coding agents working in the OpenClaude repository. Read it before changing code, and also follow CONTRIBUTING.md for contributor policy, PR expectations, review follow-up, and project scope.

Project Snapshot

OpenClaude is a coding-agent CLI for cloud and local model providers. It supports OpenAI-compatible APIs, Anthropic, Gemini, DeepSeek, Ollama, MCP, local backends, slash commands, tools, agents, and a React/Ink terminal UI.

The installed CLI runs on Node.js >=22.0.0. Bun is used for source builds, scripts, dependency management, and tests.

Work Style

  • Keep changes focused on one problem.
  • Prefer existing patterns in the file or nearby module.
  • Avoid unrelated formatting, renames, dependency changes, or broad rewrites.
  • Add or update tests when behavior changes.
  • Update docs when setup, commands, provider behavior, or user-facing behavior changes.
  • For new features, larger refactors, dependencies, or runtime changes, follow the issue-first guidance in CONTRIBUTING.md.

Stack And Conventions

  • TypeScript with strict mode and ESM imports.
  • React + Ink for terminal UI.
  • Bun lockfile and Bun scripts for development workflows.
  • Node runtime for the built CLI.
  • Python exists for legacy/local-provider helper code. Do not add new Python code or expand Python-based features unless a maintainer explicitly approves that direction.

Common libraries and patterns:

  • chalk for terminal color.
  • commander for CLI argument parsing.
  • execa for child processes.
  • Existing service, provider, settings, permission, and UI patterns over new abstractions.

Repository Map

  • src/commands/ - slash and CLI command implementations.
  • src/components/ - React/Ink UI components.
  • src/services/ - API, MCP, OAuth, wiki, voice, and other service integrations.
  • src/tools/ - tool implementations.
  • src/utils/ - shared utilities.
  • `src/integration...

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/errors.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
**/*

⚙️ CodeRabbit configuration file

**/*: Apply the OpenClaude maintainer review rubric from AGENTS.md. Review the current diff, not stale discussion context. Separate real blockers from suggestions. Do not request changes for vague style churn. Treat approval as merge-ready from CodeRabbit's side, pending required human review and GitHub Checks. If checks are failing or unavailable, say so clearly instead of implying the PR is fully ready.

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/errors.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}

⚙️ CodeRabbit configuration file

{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}: Review provider routing, model selection, env precedence, auth/token handling, OpenAI-compatible shims, retries, proxy behavior, and outbound HTTP behavior with high scrutiny. Block on silent default changes, hidden fallback expansion, credential reuse mistakes, hardcoded provider assumptions, or new network reach that is not intentional and documented.

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/errors.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}

⚙️ CodeRabbit configuration file

{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}: Review tests for meaningful coverage of the changed behavior, isolation of global/env/config state, async cleanup, fake timers, provider profile leaks, and Windows-compatible assumptions. Block when risky runtime changes lack focused regression coverage or tests assert implementation details while missing the user-visible behavior.

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.test.ts
🔇 Additional comments (5)
src/services/api/withRetry.test.ts (1)

245-301: Same env-leak feedback applies here.

This test sets process.env.OPENCLAUDE_RETRY_DELAY_MS without restoring it, so later tests can inherit the forced retry delay. Wrap the mutation in try/finally or restore it in local cleanup.

src/services/api/errors.ts (1)

489-495: LGTM!

src/services/api/openaiShim.ts (1)

1741-1798: LGTM!

Also applies to: 3635-3638

src/services/api/openaiShim.test.ts (1)

7355-7504: LGTM!

src/services/api/withRetry.ts (1)

829-829: 🎯 Functional Correctness

No change needed for shouldRetry
The test intentionally narrows shouldRetry to a one-argument type, so the direct call does not create a strict typecheck issue.

			> Likely an incorrect or invalid review comment.

Comment thread src/services/api/openaiShim.test.ts
Comment thread src/services/api/openaiShim.ts Outdated
Comment thread src/services/api/withRetry.ts Outdated
euxaristia added a commit to euxaristia/openclaude that referenced this pull request Jun 24, 2026
…calls as absent

Addresses CodeRabbit's review on Twigpine#1749.

- [Major] withRetry.ts: throw CannotRetryError for isOpenCodeGoQuotaError BEFORE
  the fast-mode 429 fallback. Previously the guard only *skipped* the generic
  quota throw, so an OpenCode Go 429 while fast mode was active hit the fast-mode
  retry/cooldown path instead of surfacing the quota message immediately.
  Wrapping the original APIError still preserves the OpenCode Go assistant
  message via getAssistantMessageFromError. Adds a fast-mode regression test
  (mutation-checked) and a forceFastMode option on the test helper.

- [Minor] openaiShim.ts: an empty tool_calls array is truthy, which skipped the
  raw "Tool calls requested" recovery in convertNonStreamingResponseToAnthropicMessage.
  Gate on a single hasStructuredToolCalls (length > 0) check across both the
  string- and array-content raw-recovery paths and the structured loop. Adds a
  JSON-fallback regression test for tool_calls: [] (mutation-checked).

- [Minor] openaiShim.test.ts: collectFallbackEvents now saves and restores
  globalThis.fetch in a finally block so the stub can't leak past the helper.
coderabbitai[bot]
coderabbitai Bot previously requested changes Jun 24, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/services/api/withRetry.test.ts (1)

311-355: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Restore OPENCLAUDE_RETRY_DELAY_MS after this test.

Line 315 mutates global env state and never restores it, so later retry tests can inherit the shortened delay and become order-dependent.

Suggested fix
 test('terminates OpenCode Go quota 429 immediately in fast mode (no fast-mode retry/cooldown)', async () => {
+  const previousRetryDelay = process.env.OPENCLAUDE_RETRY_DELAY_MS
   // Regression for `#1749` (CodeRabbit): the OpenCode Go terminal throw must run
   // BEFORE the fast-mode 429 fallback, otherwise fast mode retries/cooldowns a
   // quota-exhausted subscription instead of surfacing the quota message.
   process.env.OPENCLAUDE_RETRY_DELAY_MS = '1'
-  const { CannotRetryError, withRetry } =
-    await importFreshWithRetryModule('openai', { forceFastMode: true })
-  const error = APIError.generate(
-    429,
-    undefined,
-    JSON.stringify({
-      error: { type: 'GoUsageLimitError', message: 'subscription limit reached' },
-    }),
-    new Headers({
-      'x-opencode-request-url': 'https://opencode.ai/zen/go/v1/messages',
-    }),
-  )
-  let attempts = 0
-
-  let caught: unknown
   try {
-    await drainAsyncGenerator(
-      withRetry(
-        async () => ({} as Anthropic),
-        async () => {
-          attempts++
-          throw error
-        },
-        {
-          maxRetries: 2,
-          model: 'glm-5.1',
-          thinkingConfig: { type: 'disabled' },
-          fastMode: true,
-        },
-      ),
-    )
-  } catch (e) {
-    caught = e
+    const { CannotRetryError, withRetry } =
+      await importFreshWithRetryModule('openai', { forceFastMode: true })
+    const error = APIError.generate(
+      429,
+      undefined,
+      JSON.stringify({
+        error: { type: 'GoUsageLimitError', message: 'subscription limit reached' },
+      }),
+      new Headers({
+        'x-opencode-request-url': 'https://opencode.ai/zen/go/v1/messages',
+      }),
+    )
+    let attempts = 0
+
+    let caught: unknown
+    try {
+      await drainAsyncGenerator(
+        withRetry(
+          async () => ({} as Anthropic),
+          async () => {
+            attempts++
+            throw error
+          },
+          {
+            maxRetries: 2,
+            model: 'glm-5.1',
+            thinkingConfig: { type: 'disabled' },
+            fastMode: true,
+          },
+        ),
+      )
+    } catch (e) {
+      caught = e
+    }
+
+    expect(caught).toBeInstanceOf(CannotRetryError)
+    expect(attempts).toBe(1)
+    expect((caught as { originalError?: unknown }).originalError).toBe(error)
+  } finally {
+    if (previousRetryDelay === undefined) {
+      delete process.env.OPENCLAUDE_RETRY_DELAY_MS
+    } else {
+      process.env.OPENCLAUDE_RETRY_DELAY_MS = previousRetryDelay
+    }
   }
-
-  expect(caught).toBeInstanceOf(CannotRetryError)
-  // Fired exactly once — fast mode did not retry or enter cooldown.
-  expect(attempts).toBe(1)
-  expect((caught as { originalError?: unknown }).originalError).toBe(error)
 })

As per path instructions, “Review tests for meaningful coverage of the changed behavior, isolation of global/env/config state...”.

🤖 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 `@src/services/api/withRetry.test.ts` around lines 311 - 355, This test mutates
the global OPENCLAUDE_RETRY_DELAY_MS environment variable and leaves it changed,
which can leak into later retry tests. Update withRetry.test’s fast-mode quota
test to save the previous env value before setting it, then restore it in a
cleanup path after the test (for example via a try/finally or test hook) so the
suite stays isolated. Use the existing withRetry and importFreshWithRetryModule
test setup as the place to apply and undo the env change.

Source: Path instructions

🤖 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 `@src/services/api/openaiShim.test.ts`:
- Around line 7512-7542: The empty tool_calls regression is only covered for
string-based assistant content, so the array-content recovery path in the
fallback logic is still untested. Add a companion test alongside
collectFallbackEvents that uses an assistant message with content as an array
and tool_calls: [] , then assert the same tool_use recovery via the existing
content_block_start lookup so the array branch in openaiShim cannot regress.
Keep the new case aligned with the current JSON fallback test naming and reuse
the same assertions around the recovered Bash tool call.

---

Duplicate comments:
In `@src/services/api/withRetry.test.ts`:
- Around line 311-355: This test mutates the global OPENCLAUDE_RETRY_DELAY_MS
environment variable and leaves it changed, which can leak into later retry
tests. Update withRetry.test’s fast-mode quota test to save the previous env
value before setting it, then restore it in a cleanup path after the test (for
example via a try/finally or test hook) so the suite stays isolated. Use the
existing withRetry and importFreshWithRetryModule test setup as the place to
apply and undo the env change.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 10d64b6c-4d41-4bf5-acde-bfb5bc30c187

📥 Commits

Reviewing files that changed from the base of the PR and between d502556 and d5a2198.

📒 Files selected for processing (4)
  • src/services/api/openaiShim.test.ts
  • src/services/api/openaiShim.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (13)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with strict mode and ESM imports

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
{src/commands/**/*.ts,src/services/**/*.ts,src/entrypoints/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use chalk for terminal color in CLI code

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
{src/services/**/*.ts,src/utils/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use execa for child processes

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
{src/integrations/**/*.ts,src/services/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Test the exact provider/model path you changed when possible for provider modifications

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
**/*.{ts,tsx,js,jsx,py,json,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Follow the existing code style in the touched files

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add or update tests when the change affects behavior

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.test.ts
**/*.test.{ts,tsx,js}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Test the exact provider/model path you changed when possible

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.test.ts
**/*.{ts,tsx,js,jsx,py}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Keep comments useful and concise

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Follow TypeScript strict mode and type safety practices by running typecheck before submitting

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
**

⚙️ CodeRabbit configuration file

**: # AGENTS.md - AI Agent Coding Guide

This guide is for AI coding agents working in the OpenClaude repository. Read it before changing code, and also follow CONTRIBUTING.md for contributor policy, PR expectations, review follow-up, and project scope.

Project Snapshot

OpenClaude is a coding-agent CLI for cloud and local model providers. It supports OpenAI-compatible APIs, Anthropic, Gemini, DeepSeek, Ollama, MCP, local backends, slash commands, tools, agents, and a React/Ink terminal UI.

The installed CLI runs on Node.js >=22.0.0. Bun is used for source builds, scripts, dependency management, and tests.

Work Style

  • Keep changes focused on one problem.
  • Prefer existing patterns in the file or nearby module.
  • Avoid unrelated formatting, renames, dependency changes, or broad rewrites.
  • Add or update tests when behavior changes.
  • Update docs when setup, commands, provider behavior, or user-facing behavior changes.
  • For new features, larger refactors, dependencies, or runtime changes, follow the issue-first guidance in CONTRIBUTING.md.

Stack And Conventions

  • TypeScript with strict mode and ESM imports.
  • React + Ink for terminal UI.
  • Bun lockfile and Bun scripts for development workflows.
  • Node runtime for the built CLI.
  • Python exists for legacy/local-provider helper code. Do not add new Python code or expand Python-based features unless a maintainer explicitly approves that direction.

Common libraries and patterns:

  • chalk for terminal color.
  • commander for CLI argument parsing.
  • execa for child processes.
  • Existing service, provider, settings, permission, and UI patterns over new abstractions.

Repository Map

  • src/commands/ - slash and CLI command implementations.
  • src/components/ - React/Ink UI components.
  • src/services/ - API, MCP, OAuth, wiki, voice, and other service integrations.
  • src/tools/ - tool implementations.
  • src/utils/ - shared utilities.
  • `src/integration...

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
**/*

⚙️ CodeRabbit configuration file

**/*: Apply the OpenClaude maintainer review rubric from AGENTS.md. Review the current diff, not stale discussion context. Separate real blockers from suggestions. Do not request changes for vague style churn. Treat approval as merge-ready from CodeRabbit's side, pending required human review and GitHub Checks. If checks are failing or unavailable, say so clearly instead of implying the PR is fully ready.

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}

⚙️ CodeRabbit configuration file

{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}: Review provider routing, model selection, env precedence, auth/token handling, OpenAI-compatible shims, retries, proxy behavior, and outbound HTTP behavior with high scrutiny. Block on silent default changes, hidden fallback expansion, credential reuse mistakes, hardcoded provider assumptions, or new network reach that is not intentional and documented.

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/openaiShim.ts
{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}

⚙️ CodeRabbit configuration file

{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}: Review tests for meaningful coverage of the changed behavior, isolation of global/env/config state, async cleanup, fake timers, provider profile leaks, and Windows-compatible assumptions. Block when risky runtime changes lack focused regression coverage or tests assert implementation details while missing the user-visible behavior.

Files:

  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.test.ts
🔇 Additional comments (4)
src/services/api/withRetry.ts (1)

299-307: LGTM!

src/services/api/withRetry.test.ts (1)

76-97: LGTM!

src/services/api/openaiShim.ts (1)

1524-1654: LGTM!

src/services/api/openaiShim.test.ts (1)

7338-7357: LGTM!

Comment thread src/services/api/openaiShim.test.ts
@euxaristia

Copy link
Copy Markdown
Contributor Author

Addressed all three CodeRabbit findings in d5a2198.

  • 🟠 Major — bypass fast-mode retry before falling through (withRetry.ts): You're right. My earlier change only skipped the generic quota throw for OpenCode Go, so a Go 429 while fast mode was active still hit the fast-mode 429 fallback (retry/cooldown) before shouldRetry() ran. Now I throw new CannotRetryError(error, retryContext) for isOpenCodeGoQuotaError(error) before the fast-mode branch — terminal immediately, and wrapping the original APIError still preserves the OpenCode Go message via getAssistantMessageFromError. Added a fast-mode regression test (forceFastMode mocks isFastModeEnabled → true) asserting a Go 429 fires exactly once with no cooldown; mutation-checked (reverting to the skip approach makes it fail).

  • 🟡 Minor — treat empty tool_calls as absent (openaiShim.ts): tool_calls: [] is truthy, so raw "Tool calls requested" recovery was skipped. Replaced the truthiness checks with a single hasStructuredToolCalls = (tool_calls?.length ?? 0) > 0, applied to both the string- and array-content raw-recovery paths and the structured loop. Added a JSON-fallback regression test for tool_calls: []; mutation-checked.

  • 🟡 Minor — restore globalThis.fetch (openaiShim.test.ts): collectFallbackEvents now saves the previous fetch and restores it in a finally block, so the stub can't leak past the helper (independent of the file-level afterEach).

Validation: openaiShim.test.ts 152/152, withRetry.test.ts 41/41, typecheck clean on the changed files.

euxaristia added a commit to euxaristia/openclaude that referenced this pull request Jun 24, 2026
…nt too

Addresses CodeRabbit's follow-up on Twigpine#1749: the empty-tool_calls regression only
exercised the string-content path, but the hasStructuredToolCalls fix also gates
the array-content branch. Add a companion JSON-fallback test with array-form
message.content and tool_calls: [] so the array branch can't regress silently.
Mutation-checked: reverting hasStructuredToolCalls to a truthiness check fails it.
@euxaristia

Copy link
Copy Markdown
Contributor Author

Addressed the follow-up in the latest commit — added a companion JSON fallback: empty tool_calls does not block raw-text recovery on array content test that drives message.content as an array with tool_calls: [] and asserts the same tool_use recovery, so the array-content branch of the hasStructuredToolCalls fix can't regress silently. Mutation-checked (reverting to the truthiness check fails it). openaiShim.test.ts 153/153.

jatmn
jatmn previously approved these changes Jun 24, 2026

@jatmn jatmn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No findings. The current code implements the stated goal and the prior valid-reviewer requests. Validation is clean.

@kevincodex1 LGTM

euxaristia added a commit to euxaristia/openclaude that referenced this pull request Jun 27, 2026
…verter in JSON fallback

Addresses the two remaining P2 review items on Twigpine#1749.

withRetry: the early isQuotaExhausted guard wrapped OpenCode Go
FreeUsageLimitError/GoUsageLimitError 429s in the generic "API quota
exhausted or not enabled" message, clobbering the actionable subscribe/
reset guidance. Skip the generic guard for OpenCode Go quota errors so they
fall through to the standard shouldRetry=false terminal path, which rethrows
the original APIError and lets getAssistantMessageFromError surface the
specific message. Consolidate detection in a shared isOpenCodeGoQuotaError
predicate (errors.ts) and drop the duplicated inline header check in
shouldRetry.

openaiShim: the application/json fallback in openaiStreamToAnthropic
hand-rolled a thin converter that dropped tool_calls, forwarded raw OpenAI
finish_reason values as Anthropic stop reasons, skipped array-content
normalization, bypassed <think> stripping, and lost raw text tool-call
recovery. Extract the established non-streaming conversion into a shared
convertNonStreamingResponseToAnthropicMessage and route the fallback through
it, re-emitting the result as stream events. _convertNonStreamingResponse
now delegates to the same function.

Adds regression coverage: a withRetry test proving the OpenCode Go message
survives the retry loop, and JSON-fallback tests for tool_calls, stop-reason
mapping, <think> stripping, array content, and raw text tool-call recovery.
euxaristia added a commit to euxaristia/openclaude that referenced this pull request Jun 27, 2026
…calls as absent

Addresses CodeRabbit's review on Twigpine#1749.

- [Major] withRetry.ts: throw CannotRetryError for isOpenCodeGoQuotaError BEFORE
  the fast-mode 429 fallback. Previously the guard only *skipped* the generic
  quota throw, so an OpenCode Go 429 while fast mode was active hit the fast-mode
  retry/cooldown path instead of surfacing the quota message immediately.
  Wrapping the original APIError still preserves the OpenCode Go assistant
  message via getAssistantMessageFromError. Adds a fast-mode regression test
  (mutation-checked) and a forceFastMode option on the test helper.

- [Minor] openaiShim.ts: an empty tool_calls array is truthy, which skipped the
  raw "Tool calls requested" recovery in convertNonStreamingResponseToAnthropicMessage.
  Gate on a single hasStructuredToolCalls (length > 0) check across both the
  string- and array-content raw-recovery paths and the structured loop. Adds a
  JSON-fallback regression test for tool_calls: [] (mutation-checked).

- [Minor] openaiShim.test.ts: collectFallbackEvents now saves and restores
  globalThis.fetch in a finally block so the stub can't leak past the helper.
euxaristia added a commit to euxaristia/openclaude that referenced this pull request Jun 27, 2026
…nt too

Addresses CodeRabbit's follow-up on Twigpine#1749: the empty-tool_calls regression only
exercised the string-content path, but the hasStructuredToolCalls fix also gates
the array-content branch. Add a companion JSON-fallback test with array-form
message.content and tool_calls: [] so the array branch can't regress silently.
Mutation-checked: reverting hasStructuredToolCalls to a truthiness check fails it.
@euxaristia
euxaristia force-pushed the fix/opencode-go-usage-errors branch from 6fc4c8f to 360037c Compare June 27, 2026 19:00
coderabbitai[bot]
coderabbitai Bot previously requested changes Jun 27, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (1)
src/services/api/withRetry.test.ts (1)

221-355: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Restore OPENCLAUDE_RETRY_DELAY_MS in these tests.

Lines 222, 257, and 315 mutate global env state without cleanup, so retry timing can leak into later tests. Wrap each test in try/finally or use a small restore helper. This was already flagged on the earlier quota test and now applies to the added OpenCode Go cases too.

Proposed helper pattern
+async function withRetryDelayEnv<T>(value: string, fn: () => Promise<T>): Promise<T> {
+  const previous = process.env.OPENCLAUDE_RETRY_DELAY_MS
+  process.env.OPENCLAUDE_RETRY_DELAY_MS = value
+  try {
+    return await fn()
+  } finally {
+    if (previous === undefined) {
+      delete process.env.OPENCLAUDE_RETRY_DELAY_MS
+    } else {
+      process.env.OPENCLAUDE_RETRY_DELAY_MS = previous
+    }
+  }
+}

As per path instructions, “Review tests for meaningful coverage of the changed behavior, isolation of global/env/config state...”.

🤖 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 `@src/services/api/withRetry.test.ts` around lines 221 - 355, The three retry
tests mutate OPENCLAUDE_RETRY_DELAY_MS but never restore it, which can leak
retry timing into later tests. Update each affected test in withRetry.test.ts to
save the prior env value before setting it and restore it afterward with
try/finally or a shared cleanup helper. Use the existing test blocks around
withRetry, CannotRetryError, and drainAsyncGenerator to keep the fix local and
consistent.

Source: Path instructions

🤖 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 `@src/services/api/errors.ts`:
- Around line 449-464: The OpenCode Go detection in parseOpenCodeGoLimitError is
reading a header that the openaiShim error path does not populate, so the
classification can miss provider/profile requests to opencode.ai/zen/go. Fix
this by sourcing the request URL from the existing compatibility metadata
already available on the APIError path, or update openaiShim to attach the
x-opencode-request-url header when constructing errors, and make sure
parseOpenCodeGoLimitError uses that same source consistently.
- Around line 788-814: The OpenCode Go quota handling in
getAssistantMessageFromError is still bypassed on the shim path because
classifyOpenAIHttpFailure() marks these 429s as quota_exhausted before the new
parseOpenCodeGoLimitError branch runs. Move the OpenCode Go detection ahead of
the generic OpenAI-compatibility marker handling, or add a special-case inside
that marker block so FreeUsageLimitError and GoUsageLimitError are preserved.
Keep the existing createAssistantAPIErrorMessage flow and reuse
parseOpenCodeGoLimitError, APIError, and the quota_exhausted handling in
src/services/api/errors.ts.

In `@src/services/api/openaiErrorClassification.ts`:
- Around line 204-218: The quota classification in isQuotaExhaustedMessage is
too broad because it matches the standalone token billing, causing unrelated
400/403/429 responses to be misclassified as quota_exhausted. Tighten the checks
in this helper and the related early branch in openaiErrorClassification so they
only match quota-specific phrases like credit, quota, payment required, or usage
limit, and avoid treating generic billing-related bad requests as exhausted
quota.

In `@src/services/api/withRetry.ts`:
- Around line 305-307: The quota-handling logic in withRetry is still wrapping
OpenAI-compatible quota APIErrors in a generic Error before they reach
getAssistantMessageFromError, so preserve them just like the existing
isOpenCodeGoQuotaError branch. Update the isQuotaExhausted guard to detect the
OpenAI-compatible quota category and throw a CannotRetryError with the original
APIError instead of wrapping it generically. Keep the handling aligned with the
existing retryContext flow so both quota-preservation branches behave
consistently.

---

Duplicate comments:
In `@src/services/api/withRetry.test.ts`:
- Around line 221-355: The three retry tests mutate OPENCLAUDE_RETRY_DELAY_MS
but never restore it, which can leak retry timing into later tests. Update each
affected test in withRetry.test.ts to save the prior env value before setting it
and restore it afterward with try/finally or a shared cleanup helper. Use the
existing test blocks around withRetry, CannotRetryError, and drainAsyncGenerator
to keep the fix local and consistent.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d08ed620-1a46-45ee-935d-dbfc217603dd

📥 Commits

Reviewing files that changed from the base of the PR and between 6fc4c8f and 360037c.

📒 Files selected for processing (8)
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/errors.ts
  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/openaiShim.test.ts
  • src/services/api/openaiShim.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/withRetry.ts
📜 Review details
⚠️ CI failures not shown inline (4)

GitHub Actions: PR Checks / typecheck: fix(opencode-go): surface clear error on subscription quota exhaustion

Conclusion: failure

View job details

##[group]Run bun run typecheck
 �[36;1mbun run typecheck�[0m
 shell: /usr/bin/bash -e {0}
 ##[endgroup]
 $ tsc --noEmit
 src/services/api/openaiShim.test.ts(8599,1): error TS1005: '}' expected.
 ##[error]Process completed with exit code 2.

GitHub Actions: PR Checks / smoke-and-tests: fix(opencode-go): surface clear error on subscription quota exhaustion

Conclusion: failure

View job details

##[group]src/components/agents/new-agent-creation/wizard-steps/wizardSteps.test.tsx:
 (pass) ColorStep does not create a final agent when required wizard data is missing [35.00ms]
 (pass) MemoryStep falls back to an empty system prompt when wizard data is incomplete [30.00ms]
 (pass) ConfirmStep renders a fallback instead of a blank screen for incomplete wizard data [6.00ms]
 ##[endgroup]
 1 tests failed:
  5096 pass
  1 fail
  1 error
  13123 expect() calls
 Ran 5097 tests across 488 files. [41.52s]
 error: script "test:full" exited with code 1
 error: script "check" exited with code 1
 �[?1006l�[?1003l�[?1002l�[?1000l�[>4m�[<u�[?1004l�[?2004l�[?25h�]9;4;0;��[?1006l�[?1003l�[?1002l�[?1000l�[>4m�[<u�[?1004l�[?2004l�[?25h�]9;4;0;��[?1006l�[?1003l�[?1002l�[?1000l�[>4m�[<u�[?1004l�[?2004l�[?25h�]9;4;0;��[?1006l�[?1003l�[?1002l�[?1000l�[>4m�[<u�[?1004l�[?2004l�[?25h�]9;4;0;�
 ##[error]Process completed with exit code 1.

GitHub Actions: PR Checks / 0_typecheck.txt: fix(opencode-go): surface clear error on subscription quota exhaustion

Conclusion: failure

View job details

##[group]Run bun run typecheck
 �[36;1mbun run typecheck�[0m
 shell: /usr/bin/bash -e {0}
 ##[endgroup]
 $ tsc --noEmit
 src/services/api/openaiShim.test.ts(8599,1): error TS1005: '}' expected.
 ##[error]Process completed with exit code 2.

GitHub Actions: PR Checks / 1_smoke-and-tests.txt: fix(opencode-go): surface clear error on subscription quota exhaustion

Conclusion: failure

View job details

##[group]src/components/agents/new-agent-creation/wizard-steps/wizardSteps.test.tsx:
 (pass) ColorStep does not create a final agent when required wizard data is missing [35.00ms]
 (pass) MemoryStep falls back to an empty system prompt when wizard data is incomplete [30.00ms]
 (pass) ConfirmStep renders a fallback instead of a blank screen for incomplete wizard data [6.00ms]
 ##[endgroup]
 1 tests failed:
  5096 pass
  1 fail
  1 error
  13123 expect() calls
 Ran 5097 tests across 488 files. [41.52s]
 error: script "test:full" exited with code 1
 error: script "check" exited with code 1
 �[?1006l�[?1003l�[?1002l�[?1000l�[>4m�[<u�[?1004l�[?2004l�[?25h�]9;4;0;��[?1006l�[?1003l�[?1002l�[?1000l�[>4m�[<u�[?1004l�[?2004l�[?25h�]9;4;0;��[?1006l�[?1003l�[?1002l�[?1000l�[>4m�[<u�[?1004l�[?2004l�[?25h�]9;4;0;��[?1006l�[?1003l�[?1002l�[?1000l�[>4m�[<u�[?1004l�[?2004l�[?25h�]9;4;0;�
 ##[error]Process completed with exit code 1.
🧰 Additional context used
📓 Path-based instructions (13)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with strict mode and ESM imports

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/errors.ts
  • src/services/api/openaiShim.ts
{src/commands/**/*.ts,src/services/**/*.ts,src/entrypoints/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use chalk for terminal color in CLI code

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/errors.ts
  • src/services/api/openaiShim.ts
{src/services/**/*.ts,src/utils/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use execa for child processes

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/errors.ts
  • src/services/api/openaiShim.ts
{src/integrations/**/*.ts,src/services/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Test the exact provider/model path you changed when possible for provider modifications

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/errors.ts
  • src/services/api/openaiShim.ts
**/*.{ts,tsx,js,jsx,py,json,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Follow the existing code style in the touched files

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/errors.ts
  • src/services/api/openaiShim.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add or update tests when the change affects behavior

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/openaiShim.test.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/withRetry.test.ts
**/*.test.{ts,tsx,js}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Test the exact provider/model path you changed when possible

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/openaiShim.test.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/withRetry.test.ts
**/*.{ts,tsx,js,jsx,py}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Keep comments useful and concise

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/errors.ts
  • src/services/api/openaiShim.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Follow TypeScript strict mode and type safety practices by running typecheck before submitting

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/errors.ts
  • src/services/api/openaiShim.ts
**

⚙️ CodeRabbit configuration file

**: # AGENTS.md - AI Agent Coding Guide

This guide is for AI coding agents working in the OpenClaude repository. Read it before changing code, and also follow CONTRIBUTING.md for contributor policy, PR expectations, review follow-up, and project scope.

Project Snapshot

OpenClaude is a coding-agent CLI for cloud and local model providers. It supports OpenAI-compatible APIs, Anthropic, Gemini, DeepSeek, Ollama, MCP, local backends, slash commands, tools, agents, and a React/Ink terminal UI.

The installed CLI runs on Node.js >=22.0.0. Bun is used for source builds, scripts, dependency management, and tests.

Work Style

  • Keep changes focused on one problem.
  • Prefer existing patterns in the file or nearby module.
  • Avoid unrelated formatting, renames, dependency changes, or broad rewrites.
  • Add or update tests when behavior changes.
  • Update docs when setup, commands, provider behavior, or user-facing behavior changes.
  • For new features, larger refactors, dependencies, or runtime changes, follow the issue-first guidance in CONTRIBUTING.md.

Stack And Conventions

  • TypeScript with strict mode and ESM imports.
  • React + Ink for terminal UI.
  • Bun lockfile and Bun scripts for development workflows.
  • Node runtime for the built CLI.
  • Python exists for legacy/local-provider helper code. Do not add new Python code or expand Python-based features unless a maintainer explicitly approves that direction.

Common libraries and patterns:

  • chalk for terminal color.
  • commander for CLI argument parsing.
  • execa for child processes.
  • Existing service, provider, settings, permission, and UI patterns over new abstractions.

Repository Map

  • src/commands/ - slash and CLI command implementations.
  • src/components/ - React/Ink UI components.
  • src/services/ - API, MCP, OAuth, wiki, voice, and other service integrations.
  • src/tools/ - tool implementations.
  • src/utils/ - shared utilities.
  • `src/integration...

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/errors.ts
  • src/services/api/openaiShim.ts
**/*

⚙️ CodeRabbit configuration file

**/*: Apply the OpenClaude maintainer review rubric from AGENTS.md. Review the current diff, not stale discussion context. Separate real blockers from suggestions. Do not request changes for vague style churn. Treat approval as merge-ready from CodeRabbit's side, pending required human review and GitHub Checks. If checks are failing or unavailable, say so clearly instead of implying the PR is fully ready.

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/errors.ts
  • src/services/api/openaiShim.ts
{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}

⚙️ CodeRabbit configuration file

{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}: Review provider routing, model selection, env precedence, auth/token handling, OpenAI-compatible shims, retries, proxy behavior, and outbound HTTP behavior with high scrutiny. Block on silent default changes, hidden fallback expansion, credential reuse mistakes, hardcoded provider assumptions, or new network reach that is not intentional and documented.

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/openaiShim.test.ts
  • src/services/api/withRetry.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/openaiErrorClassification.ts
  • src/services/api/withRetry.test.ts
  • src/services/api/errors.ts
  • src/services/api/openaiShim.ts
{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}

⚙️ CodeRabbit configuration file

{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}: Review tests for meaningful coverage of the changed behavior, isolation of global/env/config state, async cleanup, fake timers, provider profile leaks, and Windows-compatible assumptions. Block when risky runtime changes lack focused regression coverage or tests assert implementation details while missing the user-visible behavior.

Files:

  • src/services/api/openaiErrorClassification.test.ts
  • src/services/api/openaiShim.test.ts
  • src/services/api/errors.opencodeGo.test.ts
  • src/services/api/withRetry.test.ts
🪛 GitHub Actions: PR Checks / 0_typecheck.txt
src/services/api/openaiShim.test.ts

[error] 8599-8599: TypeScript compilation error TS1005: '}' expected.

🪛 GitHub Actions: PR Checks / typecheck
src/services/api/openaiShim.test.ts

[error] 8599-8599: TypeScript compilation error: TS1005: '}' expected.

🔇 Additional comments (7)
src/services/api/openaiShim.ts (3)

1636-1766: LGTM!


4040-4043: LGTM!


1870-1923: 🩺 Stability & Availability

No issue: JSON fallback still emits message_start first. The fallback path starts after message_start, so re-emitting content_block_* events here preserves the stream shape.

			> Likely an incorrect or invalid review comment.
src/services/api/openaiShim.test.ts (2)

8356-8380: LGTM!


8382-8598: LGTM!

src/services/api/withRetry.ts (1)

48-48: LGTM!

Also applies to: 123-140, 839-845, 1067-1068

src/services/api/withRetry.test.ts (1)

76-97: LGTM!

Comment thread src/services/api/errors.ts
Comment thread src/services/api/errors.ts Outdated
Comment thread src/services/api/openaiErrorClassification.ts
Comment thread src/services/api/withRetry.ts Outdated
jatmn
jatmn previously requested changes Jun 27, 2026

@jatmn jatmn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Findings

  • [P1] Fix the failing typecheck and smoke checks before this is ready
    src/services/api/openaiShim.test.ts:8599
    The current head does not parse: openaiShim.test.ts ends after the final JSON fallback test without closing the surrounding block, so both GitHub's typecheck check and a local bun run typecheck fail with TS1005: '}' expected. The focused test run also stops on the same unexpected EOF after the other changed API tests pass. Please fix the syntax error and rerun the affected checks before addressing the behavioral review items below.

  • [P2] Complete CodeRabbit's request to preserve the OpenCode Go message on shim errors
    src/services/api/errors.ts:776
    The OpenCode Go-specific branch is still unreachable for shim-generated quota errors that carry the new OpenAI compatibility marker: getAssistantMessageFromError() maps any [openai_category=quota_exhausted,...] message through the generic quota handler before it ever calls parseOpenCodeGoLimitError(). The shim error path builds exactly that marker from the request URL, but passes through provider response headers rather than the synthetic x-opencode-request-url header that the parser expects, so OpenCode Go users can still get the generic "Provider quota or usage allotment has run out" guidance instead of the PR's OpenCode Go free usage exhausted / subscription-limit message. Please complete CodeRabbit's still-valid request by making the shim path and assistant-message ordering use one consistent source for the OpenCode Go request URL and by testing that a real shim-shaped FreeUsageLimitError/GoUsageLimitError preserves the OpenCode Go message.

@euxaristia

Copy link
Copy Markdown
Contributor Author

Fixed P1: added missing to close the GitHub Copilot test before the JSON fallback regression block. and both pass (176 tests). P2 (OpenCode Go message preservation in shim errors) still needs behavioral work — flagged for author.

jatmn
jatmn previously requested changes Jun 28, 2026

@jatmn jatmn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Findings
[P2] Complete CodeRabbit's request to preserve the OpenCode Go message on shim errors
src/services/api/errors.ts:776
The OpenCode Go-specific branch is still bypassed for shim-generated quota errors. openaiStreamToAnthropic() throws an APIError with the [openai_category=quota_exhausted,...] marker, and getAssistantMessageFromError() maps that generic marker before it calls parseOpenCodeGoLimitError(). The thrown shim error also uses the provider response headers rather than a synthetic x-opencode-request-url header, and the message uses parsed.error.message when present, so the OpenCode Go parser may not see either the request URL or the original FreeUsageLimitError / GoUsageLimitError type. OpenCode Go users can still receive the generic provider-quota guidance instead of the PR's actionable OpenCode Go free/subscription message. Please complete CodeRabbit's still-valid request by wiring the shim request URL and original error type through one consistent source, moving the OpenCode Go detection ahead of the generic marker mapping, and adding a shim-shaped regression test.

[P2] Preserve OpenAI-compatible quota APIErrors through the retry wrapper
src/services/api/withRetry.ts:308
CodeRabbit's quota-preservation request is still valid for non-OpenCode OpenAI-compatible quota errors. isQuotaExhausted() matches [openai_category=quota_exhausted], but the retry loop then wraps that APIError in a new generic Error, so the later getAssistantMessageFromError() path cannot use the new quota_exhausted category mapping added in this PR. That means the new provider-specific compatibility guidance can be replaced by the older generic API quota exhausted or not enabled message. Please preserve the original APIError for OpenAI-compatible quota_exhausted markers the same way the OpenCode Go branch preserves its original error.

[P2] Tighten quota classification so generic billing 4xx responses are not terminal
src/services/api/openaiErrorClassification.ts:204
The new isQuotaExhaustedMessage() treats the standalone word billing as quota exhaustion, and classifyOpenAIHttpFailure() applies that branch to 400/403/429 responses before the existing invalid-request/auth handling. A provider response such as a malformed billing header/field error can now become non-retryable quota_exhausted guidance even though the user needs to fix the request/provider setup, not enable billing. Please narrow this to quota-specific phrases such as billing limit, quota, credits, payment required, or usage limit, and add a negative test for a non-quota billing-related 4xx body.

[P3] Clean up diff-check failures in the changed tests
src/services/api/errors.opencodeGo.test.ts:234
git diff --check origin/main...HEAD reports trailing whitespace in errors.opencodeGo.test.ts and an extra blank line at EOF in openaiErrorClassification.test.ts. Please remove those whitespace issues so the patch stays clean and does not trip diff hygiene checks.

euxaristia added a commit to euxaristia/openclaude that referenced this pull request Jun 28, 2026
…nt too

Addresses CodeRabbit's follow-up on Twigpine#1749: the empty-tool_calls regression only
exercised the string-content path, but the hasStructuredToolCalls fix also gates
the array-content branch. Add a companion JSON-fallback test with array-form
message.content and tool_calls: [] so the array branch can't regress silently.
Mutation-checked: reverting hasStructuredToolCalls to a truthiness check fails it.
@euxaristia
euxaristia force-pushed the fix/opencode-go-usage-errors branch from 2bfe3ef to d4e75e2 Compare June 28, 2026 01:00
@jatmn
jatmn marked this pull request as draft July 7, 2026 03:49
@jatmn jatmn self-assigned this Jul 7, 2026
euxaristia and others added 8 commits July 6, 2026 20:50
The opencode.ai/zen/go gateway returns 429 with FreeUsageLimitError or
GoUsageLimitError in the body when a user's Go subscription quota runs
out. Previously these fell through to the generic "Request rejected
(429)" path, causing a mysterious stop with no actionable hint.

Detect the opencode-go-specific error markers, surface a clear message
with the upgrade URL (free tier) or reset duration + workspace + limit
name (paid tier), and skip retry — the quota is terminal until reset.

Mirrors the canonical implementation in anomalyco/opencode
packages/opencode/src/session/retry.ts.
…verter in JSON fallback

Addresses the two remaining P2 review items on Twigpine#1749.

withRetry: the early isQuotaExhausted guard wrapped OpenCode Go
FreeUsageLimitError/GoUsageLimitError 429s in the generic "API quota
exhausted or not enabled" message, clobbering the actionable subscribe/
reset guidance. Skip the generic guard for OpenCode Go quota errors so they
fall through to the standard shouldRetry=false terminal path, which rethrows
the original APIError and lets getAssistantMessageFromError surface the
specific message. Consolidate detection in a shared isOpenCodeGoQuotaError
predicate (errors.ts) and drop the duplicated inline header check in
shouldRetry.

openaiShim: the application/json fallback in openaiStreamToAnthropic
hand-rolled a thin converter that dropped tool_calls, forwarded raw OpenAI
finish_reason values as Anthropic stop reasons, skipped array-content
normalization, bypassed <think> stripping, and lost raw text tool-call
recovery. Extract the established non-streaming conversion into a shared
convertNonStreamingResponseToAnthropicMessage and route the fallback through
it, re-emitting the result as stream events. _convertNonStreamingResponse
now delegates to the same function.

Adds regression coverage: a withRetry test proving the OpenCode Go message
survives the retry loop, and JSON-fallback tests for tool_calls, stop-reason
mapping, <think> stripping, array content, and raw text tool-call recovery.
…calls as absent

Addresses CodeRabbit's review on Twigpine#1749.

- [Major] withRetry.ts: throw CannotRetryError for isOpenCodeGoQuotaError BEFORE
  the fast-mode 429 fallback. Previously the guard only *skipped* the generic
  quota throw, so an OpenCode Go 429 while fast mode was active hit the fast-mode
  retry/cooldown path instead of surfacing the quota message immediately.
  Wrapping the original APIError still preserves the OpenCode Go assistant
  message via getAssistantMessageFromError. Adds a fast-mode regression test
  (mutation-checked) and a forceFastMode option on the test helper.

- [Minor] openaiShim.ts: an empty tool_calls array is truthy, which skipped the
  raw "Tool calls requested" recovery in convertNonStreamingResponseToAnthropicMessage.
  Gate on a single hasStructuredToolCalls (length > 0) check across both the
  string- and array-content raw-recovery paths and the structured loop. Adds a
  JSON-fallback regression test for tool_calls: [] (mutation-checked).

- [Minor] openaiShim.test.ts: collectFallbackEvents now saves and restores
  globalThis.fetch in a finally block so the stub can't leak past the helper.
…nt too

Addresses CodeRabbit's follow-up on Twigpine#1749: the empty-tool_calls regression only
exercised the string-content path, but the hasStructuredToolCalls fix also gates
the array-content branch. Add a companion JSON-fallback test with array-form
message.content and tool_calls: [] so the array branch can't regress silently.
Mutation-checked: reverting hasStructuredToolCalls to a truthiness check fails it.
@jatmn
jatmn force-pushed the fix/opencode-go-usage-errors branch from d4e75e2 to 369af89 Compare July 7, 2026 04:13
@jatmn
jatmn dismissed stale reviews from coderabbitai[bot], coderabbitai[bot], coderabbitai[bot], coderabbitai[bot], coderabbitai[bot], and themself July 7, 2026 04:29

Stale after follow-up commits: current head resolves the requested changes, review threads are resolved, and current checks are passing.

@jatmn
jatmn marked this pull request as ready for review July 7, 2026 04:37

@jatmn jatmn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@kevincodex1
kevincodex1 merged commit b8c8b24 into Twigpine:main Jul 7, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants