mirror of
https://github.com/Gitlawb/openclaude.git
synced 2026-06-01 15:27:41 +00:00
api-client-version
436
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
371641e40e |
fix: keep claude-cli identity for GitHub Copilot
GitHub Copilot whitelists 'claude-cli' but may not whitelist 'openclaude-cli'. Keep using the upstream-compatible identity for GitHub Copilot until confirmed safe to switch. Refs: PR #976 |
||
|
|
c2fecc6728 | Narrow Kimi Code user agent fallback | ||
|
|
580174e19d | Preserve Kimi Code compatibility user agent | ||
|
|
589eee4a43 |
Merge remote-tracking branch 'upstream/main' into api-client-version
# Conflicts: # src/services/api/client.ts |
||
|
|
ed7b6972f9 |
Feat: Add startup logo palette picker (#1072)
* Add startup logo palette picker * Address logo picker review feedback |
||
|
|
16726399d8 | fix plan mode branding and plan path (#1062) | ||
|
|
4830d6f778 |
fix(openai-shim): strip store for local providers (vLLM, custom) (#1048)
Local OpenAI-compatible servers (vLLM, llama.cpp, custom self-hosted gateways) often validate request bodies against a strict JSON schema and reject unknown fields with `400 Bad Request`. The shim already sends `store: false` (an OpenAI-only field for cloud conversation persistence) and strips it for known cloud hosts that share the same intolerance (Gemini, Cerebras). Local servers have no notion of remote conversation storage and fall in the same bucket. Add `isLocal` to `shouldStripResponsesStore` so any baseUrl resolved by `isLocalProviderUrl` (localhost / 127.0.0.1 / ::1 / 0.0.0.0) gets the field removed. Lenient locals (Ollama) already ignored it; this unblocks strict ones (vLLM Qwen) without behavior change for the former. Closes #672 (the `store: false` symptom; the separate `max_tokens` default vs. vLLM `max_model_len` collision is a different concern). |
||
|
|
4b1e516fc7 |
feat: incremental and cached token counting (#795)
* feat: incremental and cached token counting - Add IncrementalTokenCounter for performance (avoids recounting entire context) - Add getCacheTokens() to extract cache read/creation tokens - Add getNewTokensOnly() to get new tokens excluding cache - Add getTokenBreakdown() with cache efficiency percentage - Add comprehensive tests (10 passing) PR 1A: Token Counting Core (Features 1.6, 1.13) * refactor: extract IncrementalTokenCounter and tokenCache to separate files - Move IncrementalTokenCounter to incrementalTokenCounter.ts with stats tracking - Move cache utilities to tokenCache.ts with cost estimation and analytics - Remove duplicate implementations from tokens.ts - Update tokens.test.ts to import from new files - Add comprehensive tests for both new modules * fix: content-aware cache invalidation + high-precision costs Blocker fixes: - IncrementalTokenCounter: hash last message content for cache key - tokenCache: use 4-decimal precision for cost estimates (was 3, collapsed to $0) - exceedsBudget: use raw high-precision cost for budget comparisons Content hash prevents stale cache on same-length edits. * fix: PR 795 - fix cache invalidation and hash collisions Blockers: - getMessageHash now hashes ALL messages with full content (not just last message) - Prevents hash collisions when edits occur outside recent window - Incremental branch now verifies prefix hash matches before using cached count - If earlier messages mutated, full recalculation instead of stale increment * fix: PR 795 - fix prefix mutation + append invalidation Blocking: - Store both lastFullHash and lastPrefixHash separately - Compare actual prefix hash values, not just length (which always passes) - If prefix mutated, do full recalculation instead of stale increment Non-blocking: - Add regression tests for prefix mutation + append case * fix: PR 795 - fix isApproachingLimit to use tokenBudget not cache size Blocking: - Rename maxCacheSize to tokenBudget in IncrementalCounterConfig - isApproachingLimit now correctly compares against token budget (context window size) - Not cache entry size which was meaningless - Update CounterFactory to use appropriate tokenBudget values * fix: PR 795 - wire IncrementalTokenCounter into tokenCountWithEstimation - Integrate incremental counter into production token counting path - tokenCountWithEstimation now uses IncrementalTokenCounter.getCount() for rough estimation - Preserves exact usage baseline from last API response - getIncrementalTokenCounter() exported for external use - Uses lazy init to avoid circular dependency issues * fix: PR 795 - trim unused token cache surface - Remove tokenCache.ts (300+ line unused utility with no production caller) - Remove tokenCache.test.ts - Remove unused getCacheInfoFromUsage() from tokens.ts - Remove related tests in tokens.test.ts - PR now focused on IncrementalTokenCounter wired into tokenCountWithEstimation |
||
|
|
e438c89fbc |
fix: resolve two bugs making interactive mode unusable with plugin ecosystems (#825) (#830)
* fix(ink): import logForDebugging in App.tsx to prevent ReferenceError App.tsx used `logForDebugging()` in four call sites (XTVERSION async handlers, handleReadable/handleDataChunk stdin error-recovery branches) without importing it. When esbuild bundled this, the unresolved symbol collided with another identifier in scope; the bundler renamed most references to `logForDebugging2` but left the four in App.tsx pointing at the original name, which became undefined in the bundle. At runtime any modern terminal replying to the XTVERSION probe triggers an `unhandledRejection: logForDebugging is not defined`. Adding the missing import resolves the symbol before bundling, so the bundler emits a single consistent name for every call site. Refs #825 * fix(hooks): always close stdin after initial hook payload The conditional `if (!requestPrompt) child.stdin.end()` kept stdin open in interactive mode (where requestPrompt is always truthy while the REPL is mounted). Every plugin hook written against the Anthropic hook input contract reads stdin until EOF, so hooks blocked on the per-hook timeout (default 60s) on every user message — no HTTP request to the provider was made until every UserPromptSubmit hook had timed out. With ~10 plugins hooked to UserPromptSubmit (pipeline-orchestrator, superpowers, skill-advisor, episodic-memory, reflexion, etc.), a single prompt accumulated minutes of wait before any model call. Always closing stdin after the initial JSON payload restores the documented EOF-based contract. Verified locally: typical `oi` turnaround drops from ~60s to ~1s. Trade-off: the existing duplex-stdin path (hooks emitting prompt requests on stdout and receiving responses written back to stdin at hooks.ts:1237) is incompatible with an EOF contract by design and stops working with this change. Restoring that feature requires a separate IPC channel (named pipe, node IPC, or a second stream) rather than reusing the initial stdin; that refactor is out of scope for this fix. Given the blast radius of the current behaviour (every user with a plugin ecosystem sees unusable interactive mode), trading the rarely-used duplex path for the documented single-shot contract is the right short-term move. Closes #825 |
||
|
|
7cfc8d5dad |
feat(cli): honor --model alone without requiring --provider (#854)
Closes #808. Today `openclaude --model <name>` is parsed by Commander inside main.tsx but the startup banner and any provider-env-reading code run first, so the override is invisible until request time and saved-profile users see their stale model in the banner. Add applyModelFlagFromArgs that runs after saved-profile env application and before the banner. It routes the value to the env var matching the already-active provider (OPENAI_MODEL / GEMINI_MODEL / MISTRAL_MODEL / ANTHROPIC_MODEL) so the banner, resolution, and request payload all agree. Skipped when --provider is also present; that path is still handled by applyProviderFlagFromArgs. No writes to .openclaude-profile.json — override is process-scoped. |
||
|
|
402cd3dbe8 |
feat(websearch): add first-class Brave adapter; fix Google + Brave presets; restore Exa snippets (#1044)
This PR addresses three real bugs in WebSearch's provider layer plus adds Brave
as a first-class adapter so users with a BRAVE_API_KEY get auto-detection +
auto-chain inclusion (matching the ergonomics of TAVILY_API_KEY, EXA_API_KEY,
etc.).
## 1. New: Brave first-class adapter
`providers/brave.ts` — auto-detects `BRAVE_API_KEY`, slots into the auto chain
between Jina and Bing. Sends the bare token in `X-Subscription-Token` per
Brave's API contract. Mirrors the structure of `tavily.ts` / `bing.ts`.
Brave runs an independent web index (~30B pages), making it a useful
non-Google, non-Bing fallback. Bing's hosted API was sunsetted in Aug 2025
for new users, so Brave is a more practical default fallback in 2026.
## 2. Bug fix: Brave preset sent malformed auth header
The `WEB_PROVIDER=brave` preset in `custom.ts` declared
`authHeader: 'X-Subscription-Token'` but no `authScheme`, so the default
`'Bearer'` scheme prefix kicked in, producing:
X-Subscription-Token: Bearer <token> ← wrong, returns 401
Brave's API expects:
X-Subscription-Token: <token> ← bare token, no scheme
Fix: declare `authScheme: ''` on the preset; update
`buildAuthHeadersForPreset` to emit a bare token (no leading space) when the
scheme is empty.
## 3. Bug fix: Google preset never worked
`WEB_PROVIDER=google` was wired with `Authorization: Bearer <key>`, but the
Google Custom Search JSON API does not support Bearer auth. It requires:
- `?key=<API_KEY>` as a query param
- `?cx=<ENGINE_ID>` as a query param (Programmable Search Engine ID)
The preset previously had no slot for the engine ID at all, so any user
trying `WEB_PROVIDER=google` hit a 400/401 immediately.
Fix: extend `ProviderPreset` with two minimal fields — `authQueryParam` (key
goes in URL, not header) and `envQueryParams` (additional URL params sourced
from env vars). Rewire the `google` preset to use them; reading
`GOOGLE_CSE_ID` for `cx`.
A clear error fires fast if either `WEB_KEY` or `GOOGLE_CSE_ID` is missing,
instead of silently producing a 400 from upstream.
> Note: Google has announced the Custom Search JSON API will be discontinued
> on 2027-01-01 and is closed to new customers. The fix unbreaks existing
> users for the remaining ~8 months; the README includes a sunset notice and
> recommends Brave/Tavily/Exa for new setups.
## 4. Bug fix: Exa results had empty descriptions
`providers/exa.ts` never passed `contents` in the request body. Per the Exa
docs (https://docs.exa.ai/reference/search-api-guide-for-coding-agents):
> Use `highlights` for agent workflows. Highlights return 10x fewer
> tokens with the most relevant excerpts.
Without `contents: { highlights: true }`, the Exa response includes only
`{title, url, id, ...}` — no `text`, no `highlights`, no `summary`. The
adapter was then mapping `r.snippet ?? r.text` (neither field exists in the
default response shape), so every Exa hit came back with
`description: undefined`. Tavily/Brave/DDG all return snippets — Exa was
silently degraded.
Fix: request `contents: { highlights: true }` in the body, and map
`results[].highlights[]` (an array of strings) into the description by
joining up to 3 excerpts with ` … `. Falls back to `text` when present, then
`undefined` if neither field is populated.
## Tests
105 / 105 pass in `src/tools/WebSearchTool/providers/`:
- `providers/brave.test.ts` (new) — 7 tests: auth header, mapping,
domain filters, error paths
- `providers/exa.test.ts` (new) — 9 tests: contents request shape,
highlights mapping, fallback
chain, error paths
- `providers/custom.test.ts` (extended) — 6 new tests covering
`authScheme: ''` (Brave preset),
`authQueryParam` suppression
(Google preset), GOOGLE_CSE_ID +
WEB_KEY fail-fast errors, full
request shape via mocked fetch
205 / 205 pass across `src/tools/`. tsc clean for changed files.
## Docs
- `README_SEARCH_PROVIDERS.md` — promotes `BRAVE_API_KEY` to first-class,
documents `GOOGLE_CSE_ID` + sunset notice, fixes the provider table,
updates the auto-chain priority list and mode list
- `.env.example` — adds `BRAVE_API_KEY` line, documents `GOOGLE_CSE_ID`
requirement + sunset notice, updates auto-chain priority comment
## Migration / behavior changes
- `WEB_PROVIDER=google` users must now set `GOOGLE_CSE_ID` (was previously
unable to function at all, so this is a strict improvement).
- `WEB_PROVIDER=brave` users with `WEB_AUTH_SCHEME=""` workarounds can
drop the workaround — the preset now emits a bare token by default.
- Brave joins the auto-chain priority order
(firecrawl → tavily → exa → you → jina → **brave** → bing → mojeek →
linkup → ddg).
|
||
|
|
0adf97dc14 |
fix(openai-shim): strip store when baseUrl points at Cerebras (#1040)
Cerebras Cloud's chat-completions endpoint rejects requests with a `store` field — `400 store: property 'store' is unsupported`. The shim already strips `store` for Gemini hosts via `hasGeminiApiHost`; add the symmetric host check for Cerebras so users hitting `api.cerebras.ai` (directly or via the Custom provider preset) don't hit the same wall. Closes #1023. |
||
|
|
feb5791320 |
fix(effort): persist xhigh and send reasoning_effort on chat_completions (#857)
* fix(effort): persist xhigh and send reasoning_effort on chat_completions Fixes #853. Persistence: - EffortPicker.handleSelect normalizes the OpenAI-shaped `xhigh` to the standard `max` before writing AppState/settings. Previously `xhigh` fell through `toPersistableEffort` as undefined and the setting reverted on reload. - `/effort xhigh` takes the same normalization path. - `toPersistableEffort` maps `xhigh` -> `max` as a defensive backstop. - EffortPicker initialFocus reflects the user's stored selection (`max` shown as `xhigh`) instead of always snapping to the alias default. Payload: - openaiShim chat_completions body now emits `reasoning_effort` from `request.reasoning.effort`. Previously only codex_responses transport forwarded it, so Custom API users on chat_completions got no effort. - `getAnthropicClient` accepts `effortValue` and forwards it as `reasoningEffort` to `createOpenAIShimClient` (both providerOverride and direct-OpenAI paths) after converting `max` -> `xhigh` at the OpenAI boundary via `standardEffortToOpenAI`. - `claude.ts` threads the resolved effort through main streaming and both non-streaming fallback call sites. Tests: - openaiShim.test.ts: reasoning_effort emitted when override is passed, omitted otherwise, falls back to codex alias default. - effort.codex.test.ts: xhigh -> max normalization and standard<->OpenAI conversion. * fix(effort): skip max→high clamp for OpenAI/Codex models resolveAppliedEffort() unconditionally downgraded any non-Opus 'max' to 'high'. OpenAI/Codex models use 'max' as the standard internal form of 'xhigh' (the client shim converts on the wire), so the clamp was silently turning xhigh selections into high. The picker stored xhigh→max correctly and the shim emitted reasoning_effort, but the resolver in between rewrote max→high before the shim ever saw it. End result: UI showed xhigh, request sent high. Skip the clamp when modelUsesOpenAIEffort(model). Anthropic non-Opus clamp behavior unchanged. Adds two e2e tests in effort.codex.test.ts walking the full chain (persist → resolve → wire) so the regression can't recur silently. * test(effort): keep mocked module surfaces compatible with downstream tests bun:test's `mock.module()` is process-global and is not undone by `mock.restore()` (see comment in user.test.ts). When this file mocked `./auth.js`, `./thinking.js`, `../services/api/providerConfig.js`, etc. with reduced surfaces, later test files that imported the missing exports (refreshAndGetAwsCredentials, getRainbowColor, ...) crashed at module load with `SyntaxError: Export named 'X' not found`. Spread the real module exports into each mock factory so subsequent imports keep the full surface, while the targeted overrides for this file's tests still take effect. |
||
|
|
6af709e65e |
fix(agent): ensure main agent waits for subagent completion (#1032)
Updated the tool result instructions for async launched agents to explicitly command the model to end its turn. This prevents the main agent from prematurely continuing and duplicating work already delegated to a subagent, reducing redundant costs and improving orchestration reliability. |
||
|
|
c873725d90 |
fix(cli): replace createRequire with static import for teammate.js (#1026) (#1033)
In commit
|
||
|
|
094f04c803 |
fix(theme): remove stale memo wrappers from theme context hooks (#534)
* fix(theme): remove stale React Compiler memo wrappers from theme hooks Rebase on current main (includes #589 reconciler fix). The React Compiler memo caches (_c) in useTheme() and usePreviewTheme() use referential equality checks on destructured context values. These caches can return stale references when the ThemeProvider's useMemo recreates the context value object but the individual property references (setThemeSetting, setPreviewTheme, etc.) compare equal — the memo short-circuits and returns a cached tuple/object that still holds the old closure captures. This is a distinct bug from #589 (which fixed the ink reconciler's commitUpdate path for host prop updates). #589 ensures that when React _does_ re-render a component with new props, those props actually reach the DOM node. But the memo wrappers here prevent React from _even seeing_ the new context value in the first place — the hook returns the stale cached result. Removing the memo wrappers ensures useTheme() and usePreviewTheme() always read the current context value, eliminating the stale-reference path entirely. * test(theme): add regression tests for useTheme()/usePreviewTheme() stale-value bug These tests verify that context hooks always return fresh values after ThemeProvider re-renders, even when React Compiler memo caches are in play. - useTheme() must reflect currentTheme changes immediately after setThemeSetting is called (not return a stale cached tuple). - usePreviewTheme() must return functional actions after context re-renders (not stale closures from before the theme change). On current main (with _c memo wrappers), these tests expose the bug: the memo cache compares setThemeSetting by reference (stable across renders via useMemo) and short-circuits, returning the old cached result with stale currentTheme. * fix(test): correct import paths for ThemeProvider.test.tsx Fix relative paths for ink.js, KeybindingSetup, AppStateProvider, useStdin mock, systemTheme mock, and config mock to account for the test file being in src/components/design-system/ rather than src/components/. * fix(test): rewrite ThemeProvider tests using Ink renderer Use Ink's createRoot instead of react-dom/client, matching the pattern from ThemePicker.test.tsx. The tests now render through Ink's terminal renderer and check frame output for theme values, which is the same environment ThemeProvider actually runs in. * fix(test): correct all relative import paths for design-system/ depth - ink.js, KeybindingSetup, AppStateProvider: ../ → ../../ - StructuredDiff: same pattern as ThemePicker test adjusted for depth --------- Co-authored-by: root <root@vm7508.lumadock.com> |
||
|
|
7b02695b15 |
Feat/codex default provider (#1014)
* chore: add .openclaude/ to gitignore The .openclaude/ directory contains auto-generated project-local files (wiki pages, convention cache, local settings) that should not be committed to the repository. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * feat: make Codex + GPT 5.5 the default provider and model Changes the default provider to Codex and default model to GPT 5.5: - package.json: dev script now uses provider-launch.ts codex - providerRecommendation.ts: getGoalDefaultOpenAIModel returns gpt-5.5 for coding and balanced goals (was gpt-4o) - providerConfig.ts: fallback model changed from gpt-4o to codexplan (resolves to gpt-5.5) - ProviderManager.tsx: Codex OAuth option now shows green "★ Recommended" badge in the provider picker Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix: replace Box with nested Text in Codex label Ink's <Text> component cannot contain <Box>. The label is rendered inside a <Text> parent, so use nested <Text> elements instead. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix: default to Codex when no provider profile is saved When no persisted provider profile exists (fresh install / first run), buildStartupEnvFromProfile now injects Codex + GPT 5.5 env vars instead of returning process.env unchanged. Falls back gracefully — if Codex credentials are available (OAuth or existing), uses those; otherwise injects base URL and model defaults so the provider picker shows GPT 5.5 as the default. This closes the gap where node dist/cli.mjs (production start) would default to firstParty (Anthropic) when no profile or env vars were set. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * chore: resolve stash conflict markers from accidental stash pop Cleans up merge conflict artifacts left by a git stash pop from an unrelated branch (chore/add-atomic-chat-partner). Kept upstream (current branch) version in all cases. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix: restore memoize import and cleanup stash artifacts Restores the memoize import dropped during conflict resolution in modelSupportOverrides.ts. Removes duplicate originalEnv declaration and redundant delete statements in providerValidation.test.ts. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * revert change in package.json * fix broken test * fix color --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
1f66d322ad | fix flaky tests in full test run (#1020) | ||
|
|
40ae1e7200 |
fix(shims): strip x-anthropic-billing-header block before forwarding system prompt (#1019)
`getAttributionHeader()` (src/constants/system.ts) builds an `x-anthropic-billing-header: cc_version=...; cc_entrypoint=...; ...` line that gets prepended to the system-prompt block array in src/services/api/claude.ts:1390-1401. The Anthropic API path needs it (server _parse_cc_header consumes it), but the OpenAI / Codex shims joined every text block straight into the outbound `system` / `instructions` payload, so non-Anthropic providers received an Anthropic-only billing string in their prompt — token waste, plus a per-build fingerprint that churns local-model KV cache and any upstream prompt cache (the slowdown unsloth flagged for Claude Code). Fix the two `convertSystemPrompt` helpers (openaiShim.ts:241, codexShim.ts:124) to drop blocks whose text starts with `x-anthropic-billing-header`. Anthropic-bound traffic is unaffected — the block is built into Anthropic-shaped requests directly and never flows through these helpers. Tests: - openaiShim.test.ts: e2e capturedBody assertions on chat-completions + responses-API paths confirm the line is absent and the rest of the system prompt survives. - codexShim.test.ts: convertSystemPrompt is now exported (pure helper) and unit-tested for array + only-attribution + plain-string cases. Closes #607. |
||
|
|
1020663990 |
chore(engines): require Node >=22 to match runtime deps (#1018)
@mendable/firecrawl-js@4.18.1 (lazy-loaded by WebSearch + WebFetch) requires Node >=22, and CI runs Node 22 + 24, but package.json still advertised >=20.0.0. Result: npm install on Node 20 surfaced an EBADENGINE warning that users routinely ignored, then exploded with a cryptic syntax error the first time a web tool actually pulled firecrawl in. Bump engines.node to >=22.0.0 so npm refuses Node 20 up front, and refresh the stale comment in withResolvers.ts that still pointed at the long-gone >=18.0.0 baseline. Closes #1009 (engine half — the EACCES half is standard global-install perms, not openclaude's to fix). |
||
|
|
1c746750f6 |
fix(web-search): surface diagnostic when adapter returns 0 hits and no native fallback (#1006)
For openai-shim providers (minimax, moonshot, nvidia-nim, github copilot, etc.) hasNativeSearchFallback() is false, so when the DuckDuckGo adapter returns 0 hits in auto mode the call() path silently falls through to the native Anthropic web_search_20250305 tool. Those providers don't support that tool, producing a silent "Did 0 searches" with no signal that the default DDG backend is rate-limited or that no API-key backend is configured. Convert the silent fallthrough into an actionable result that names the active provider, the failing backend, and the env vars to set (FIRECRAWL_API_KEY / TAVILY_API_KEY / EXA_API_KEY / JINA_API_KEY / BING_API_KEY / MOJEEK_API_KEY / LINKUP_API_KEY / YOU_API_KEY) plus the native-provider escape hatch (Anthropic / Vertex / Foundry). Same root-cause family as the catch branch directly below — that already throws an actionable error for the throw-on-failure case; this matches it for the success-with-0-hits case. Auto mode + 0 hits + native fallback available is unchanged: still falls through so Anthropic/Vertex/Foundry/Codex can serve the result. Explicit adapter mode is unchanged. Adds bun:test coverage for the new helpers via the WebSearchTool __test export. Refs #614, #994 |
||
|
|
60c76b6599 |
feat: SDK Runtime — Query Engine, Sessions, and Build Pipeline (#984)
* feat(sdk): add SDK foundation — type declarations, errors, and utilities Adds standalone SDK building blocks with no SDK source dependencies: - sdk.d.ts: ambient type declarations for SDK bundle - coreSchemas.ts + coreTypes.generated.ts: Zod schemas and generated types - errors.ts: SDK-specific error classes - validation.ts: input validation utilities - messageFilters.ts: extracted message filter logic - handlePromptSubmit.ts: imports from messageFilters - 16 generated-types tests * fix(sdk): narrow assertFunction type from broad Function to callable signature Code review finding: assertFunction used `asserts value is Function` which accepts any function-like value without narrowing. Changed to `(...args: any[]) => any` for better type safety. * fix(sdk): update sdk.d.ts header — manually maintained, not generated Reviewer noted the header said "Generated from index.ts" but no generator produces this file. Updated to "Manually maintained — keep in sync with index.ts". Drift detection added in validate-externals.ts (PR 3). * fix(sdk): align sdk.d.ts types with canonical coreTypes.generated.ts Tighten SDK public type contract to resolve reviewer blockers: - PermissionResult: unknown[] → precise 6-shape discriminated union (addRules/replaceRules/removeRules/setMode/addDirectories/removeDirectories) - SDKSessionInfo: snake_case → camelCase (sessionId, lastModified, etc.) - ForkSessionResult: session_id → sessionId - SDKPermissionRequestMessage: uuid + session_id now required - SDKPermissionTimeoutMessage: added uuid + session_id - SessionMessage: parent_uuid → parentUuid - SDKMessage/SDKUserMessage/SDKResultMessage: replaced loose inline definitions with re-exports from coreTypes.generated.ts * feat(sdk): wire existing code modules + SDK shared utilities Modifies core modules for SDK integration: - QueryEngine, tools, state, commands: SDK type hooks - SDK shared utilities (shared.ts, permissions.ts) - 21 SDK tests (shared-utils, permissions) Stack: main ← pr1-foundation ← pr2-sdk-core * feat(sdk): add snake_case ↔ camelCase key mapping utilities casing.ts provides recursive key transformation for the SDK boundary layer. Internal runtime uses snake_case; public API exposes camelCase. Will be used by shared.ts, sessions.ts, query.ts at export boundaries. * test(sdk): add tests for snake_case ↔ camelCase mapping utilities Covers snakeToCamel, camelToSnake, mapKeysToCamel, mapKeysToSnake including nested objects, arrays, null/undefined, and round-trips. * feat(sdk): add SDK runtime — query engine, sessions, build pipeline Completes the SDK implementation: - SDK build target (dist/sdk.mjs) with TUI dependency stubbing - External dependency lists (scripts/externals.ts) - SDK type generation from Zod schemas (scripts/generate-sdk-types.ts) - External validation (scripts/validate-externals.ts) - SDK source: index, query, v2, sessions modules - agentSdkTypes: re-exports SDK functions (query, createSession, etc.) - 136 SDK tests + 7 build scanner tests Stack: main ← pr1-foundation ← pr2-sdk-core ← pr3-sdk-runtime * fix(sdk): align internal SDK types with camelCase public contract shared.ts: SDKSessionInfo, ForkSessionResult, SessionMessage fields now use camelCase matching sdk.d.ts. SDKPermissionRequestMessage and SDKPermissionTimeoutMessage gain required uuid + session_id fields. permissions.ts: onPermissionRequest/onTimeout callbacks now include uuid and session_id in emitted messages. * fix(sdk): update runtime modules to use camelCase field names sessions.ts: toSDKSessionInfo outputs camelCase keys, entryToSessionMessage uses parentUuid, forkSession returns sessionId. query.ts: reads sessionId from listSessions/forkSession results instead of snake_case session_id. * fix(test): update session tests to use camelCase field names session_id → sessionId in forkSession result assertions and getSessionMessages calls. * fix(sdk): prevent permission timeout race condition with once-only resolve wrapper Add createOnceOnlyResolve utility to prevent double-resolution of promises when timeout and host response happen simultaneously. This ensures deterministic behavior in the permission handling flow. * fix(sdk): improve race condition test robustness * fix(sdk): handle consecutive underscores in snakeToCamel conversion Changes: - Use _+([a-z]) regex to match multiple consecutive underscores before letters - Add lookahead (?=. ) to preserve underscore-letter pairs at string end - Handle dunder names (__proto__, __typename) by stripping wrapper and capitalizing - Add tests for consecutive underscores and trailing underscore preservation * fix(sdk): include original error message in permission callback denial When a canUseTool callback throws an error, the catch block now includes the original error message in the denial message, making debugging easier for SDK consumers. * feat(sdk): add optional timeout to env mutex for deadlock prevention Add timeout parameter to acquireEnvMutex() to prevent infinite waits in deadlock scenarios. The timeout is optional and defaults to no timeout (wait forever) for backward compatibility. Returns a MutexAcquireResult object with acquired status and optional timeout reason for failed acquisitions. * fix(sdk): remove timed-out callback from mutex queue to prevent deadlock * test(sdk): add missing error path and timeout scenario tests Add tests for timeout scenarios when host doesn't respond to permission requests, fallback behavior when no onPermissionRequest callback, and MCP connection edge cases for undefined/empty config. * fix(sdk): address code review issues - race conditions, validation, error handling - Add createPermissionTarget() factory that applies onceOnlyResolve at registration time, fixing race condition where timeout and host response could both try to resolve the same promise - Add try-catch to releaseEnvMutex() to prevent permanent lock if callback throws - Extract DEFAULT_PERMISSION_TIMEOUT_MS constant (30 seconds) - Add MCP config validation rejecting null, non-objects, and arrays - Preserve error stack traces in MCP connection failures - Add runtime validation to mapMessageToSDK for null/non-object/invalid type - Update tests to use createPermissionTarget and add validation tests * fix(sdk): syntax fixes and MCP connection error handling - Remove extra closing parenthesis in permissions.ts - Remove extra closing braces in shared.ts type definitions - Wrap MCP connection in try/catch to continue without MCP tools on failure * fix(sdk): syntax fixes, MCP error handling, and logic clarity - Remove extra closing parenthesis in permissions.ts - Remove extra closing braces in shared.ts type definitions - Wrap MCP connection in try/catch to continue without MCP tools on failure - Clarify thinkingConfig logic: use ?? true instead of !== false - Add explanatory comment about thinkingEnabled default behavior - Apply createOnceOnlyResolve wrapper in QueryImpl.registerPendingPermission * fix(sdk): comprehensive error handling and resource cleanup - Add try-catch around injectAgents() to gracefully handle plugin agent tool validation failures (prevents test crashes from unknown 'LS' tool) - Add console.warn logging to agent loading/injection catch blocks for debugging visibility (matches v2.ts pattern) - Add pendingPermissionPrompts.clear() to close() and interrupt() methods in both query.ts and v2.ts to prevent memory accumulation - Add close() method to SDKSession interface and SDKSessionImpl - Wrap MCP connection in query.ts with try-catch (matches v2.ts behavior) - Add timeoutQueue cleanup in finally blocks (query.ts + v2.ts) - Remove error.stack from MCP error messages to prevent internal path leak All 208 SDK tests pass. TypeScript errors are pre-existing. * fix(sdk): address code review non-blocking issues - Add SDKAgentLoadFailureMessage type for agent load failure events - Emit agent definition/injection failures to SDK message stream - Add tool name to permission timeout denial message - Replace 'as any' casts with proper typed state access - Fix supportedCommands to use correct mcp.commands/plugins.commands paths - Update test for correct AppState structure * fix(sdk): address code review blocking and non-blocking issues Blocking Issues Fixed: - MCP cleanup missing on session/query close - now disconnects MCP clients to prevent resource leaks in long-running processes with multiple sessions - Engine reference not cleared on close - now sets _engine = null to prevent memory leaks - Added MCP cleanup tests (9 new tests covering cleanup scenarios) Non-Blocking Issues Fixed: - Removed redundant catch block that just rethrew errors (query.ts) - Fixed inconsistent timeout denial message format (permissions.ts) - Fixed hardcoded tool name 'Bash' in test (permissions.test.ts) - Exported PermissionResolveDecision type for SDK consumers (index.ts) All 217 SDK tests pass. * fix(sdk): address code review type consistency issues - Add close() method to SDKSession interface (documented but missing from type) - Fix SDKSessionInfo, ForkSessionResult, SessionMessage field naming: snake_case → camelCase to match sdk.d.ts public contract and implementation - Add uuid and session_id to SDKPermissionTimeoutMessage for correlation - Fix JSDoc comment in forkSession to use sessionId (not session_id) These changes align internal types (shared.ts) with the public SDK contract (sdk.d.ts) and actual implementation output. The merge from origin/main introduced snake_case types that mismatched camelCase implementation and tests. * fix: restore openclaude.json comment in REPL.tsx Merge |
||
|
|
6636bce74b |
Add opt-in Karpathy guidelines skill (#909)
* Add opt-in Karpathy guidelines skill * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
de0e395467 |
Fix commit attribution configuration (#920)
* fix: configure commit attribution * fix: avoid duplicate commit-message usage * fix: clarify commit attribution command * fix: clarify set usage and default email * fix: address commit attribution review feedback * fix: use openclaude email for all attribution * fix: tighten attribution model formatting |
||
|
|
f5ec185609 | Store provider profiles in user config (#969) | ||
|
|
a133e7631a |
feat: support self-hosted Firecrawl via FIRECRAWL_API_URL (#949)
* feat: support self-hosted Firecrawl via FIRECRAWL_API_URL Adds FIRECRAWL_API_URL env var to enable self-hosted Firecrawl instances. Both WebFetchTool and firecrawl search provider now check for either FIRECRAWL_API_KEY (cloud) or FIRECRAWL_API_URL (self-hosted). The FirecrawlClient accepts apiUrl for custom endpoints. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove incorrect non-null assertion on FIRECRAWL_API_KEY Passing undefined to FirecrawlClient.apiKey is correct when using FIRECRAWL_API_URL without an API key. Also adds regression tests for isConfigured() covering all four env combinations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: trigger CI --------- Co-authored-by: Kunthawat Greethong <kunthawat@gmailcom> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
ca676affc4 |
feat: context partitioning and relevance-based pruning (#849)
* feat: context partitioning and relevance-based pruning PR 2B - Section 2.3, 2.4: - Add contextPartitioning.ts with priority zones - Add relevancePruning.ts with keyword overlap scoring - Add comprehensive tests (13 passing) * fix: resolve PR 849 blocking issues - Fix older system messages now appended in partitionContext() - Fix getAvailableSpace() accepts contextWindow param - Fix hasToolCalls/hasErrors() handle structured content blocks - Wire helpers into autoCompact.ts * fix: resolve PR 849 remaining blockers - Preserve system messages in pruning (not dropped) - Group messages by id to preserve transcript pairs - Add test for message ID group preservation * fix: rewrite pruneByRelevance to use API-round grouping - preserves tool_use/tool_result pairs - Add groupMessagesByApiRound() matching repo invariant from grouping.ts - Groups both recent and older messages at assistant message.id boundaries - Ensures tool_use + tool_result pairs stay together, not split across prune boundary - Add test for API-round grouping with real tool_use + tool_result transcript shape |
||
|
|
a8f71f3ac0 |
Fix user agent loading from OpenClaude config dirs (#972)
* Load OpenClaude agents from config dir * Address agent path review feedback |
||
|
|
6d0953a79c | fix(groq): strip unsupported store field (#983) | ||
|
|
884746dbe9 |
Provider: Add Hicap gateway provider (#979)
* Add Hicap provider and gateway auth presets * Fix Hicap compatibility preset coverage * Authenticate ripgrep download in PR checks * Use Opus 4.7 as Hicap default * Address Hicap review feedback * Address provider review blockers * Clarify gateway header UI docs * Remove Hicap endpoint from README |
||
|
|
3d791bf07f |
Disable feedback/mobile commands and refresh OpenClaude branding (#980)
- disable /feedback and /mobile from command availability while keeping implementation code in place - remove or rewrite lingering user guidance that pointed to /feedback or /mobile - switch HelpV2 to a public build version helper and fix the help dialog wrapper regression - update OpenClaude-facing links and prompt copy for issue reporting and branding consistency |
||
|
|
11f265c094 | test(api): cover first-party fetch wrapper runtime path (#990) | ||
|
|
7bb4c2e10d |
Merge configured and discovered provider profile models (#991)
Co-authored-by: Murali D <mdudaka@cisco.com> |
||
|
|
990a5a2afb |
fix(tests): resolve flakiness due to module leak and env state leakage (#988)
- StartupScreen.test.ts: Scope settings module mock to prevent process-global pollution. - providerConfig.github.test.ts: Explicitly scrub environment variables before each test. - providerProfiles.test.ts: Add beforeEach to clear RESTORED_KEYS and ensure fresh registry state. - providerValidation.test.ts: Ensure integrations registry is loaded and scrub environment to prevent leakage from host shell. This consolidates fixes from PR 944 and addresses additional failures found after rebasing onto main (v0.8.0). Co-authored-by: Kevin Codex <kevin@gitlawb.com> |
||
|
|
d948769dd5 |
feat: rework release notes around GitHub releases (#981)
* feat: rework release notes around GitHub releases - switch /release-notes from upstream changelog parsing to OpenClaude GitHub release data - add a public build version helper and use it for release URL/seen-version tracking - render release notes in-app with section headers like Features and Bug Fixes - cache serialized GitHub release notes locally for startup and command fallback paths - preserve snake_case identifiers while sanitizing markdown content - keep LogoV2 whats-new output within its display budget - add focused tests for parsing, formatting, version tags, and display slicing * Normalize release-please changelog versions Use normalizePublicVersion when parsing cached changelog headings so release-please markdown headings like ## [0.8.0](...) (2026-05-02) map to the expected version key. Also normalize direct version lookups consistently and add a regression test covering getReleaseNotesForVersion and getRecentReleaseNotes against the new CHANGELOG.md format. |
||
|
|
35f86a9580 |
fix(startup): make CLAUDE logo D distinct (#986)
* fix(startup): make CLAUDE logo D distinct Adjust the startup ASCII logo so the D in CLAUDE no longer reads as an O, and add a focused regression test for the rendered shape. Co-authored-by: Cursor <cursoragent@cursor.com> * test(startup): clear CI flag for logo rendering assertion Ensure the startup logo regression test exercises the interactive render path under GitHub Actions, where CI is set by default. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
dc3c065c4a |
fix(mcp): allow third-party providers to approve project-scope .mcp.json servers (#696) (#937)
When a user runs openclaude with a third-party provider, project-scope MCP servers added via `openclaude mcp add -s project ...` were silently dropped from `/mcp` and `openclaude mcp list`. Re-adding the same server printed "MCP server already exists in .mcp.json" but the server never actually loaded. Reporter @gbmerrall pinpointed the cause to the `if (usesAnthropicSetup)` gate around `handleMcpjsonServerApprovals` in src/interactiveHelpers.tsx. The MCP approval dialog and the CLAUDE.md external-includes warning are about workspace trust, not about Anthropic auth. Gating them on `usesAnthropicSetup` meant 3P-provider users never saw the dialog that writes `enableAllProjectMcpServers: true` and `enabledMcpjsonServers: [...]` to settings.local.json — without those settings, the MCP server isn't loaded for use. Drop the `usesAnthropicSetup` gate around the approval flow. The inner logic is unchanged and `handleMcpjsonServerApprovals` already early-returns when no servers are pending, so users without project `.mcp.json` see no new behavior. - src/interactiveHelpers.tsx: drop the gate, add a comment explaining why (and cite #696 so future readers can find context). - src/__tests__/bugfixes.test.ts: +2 regression tests asserting the gate is gone and the issue is referenced. Verified locally on Linux: build passes (v0.7.0), 1634 tests pass, the 4 remaining failures (StartupScreen.test.ts, thinking.test.ts) reproduce on main and are unrelated. The bundled dist/cli.mjs shows `handleMcpjsonServerApprovals(root2)` running directly after `setSessionTrustAccepted(true)` with no auth gate. |
||
|
|
95a817fdb0 |
fix(provider): apply Codex OAuth session switch correctly (#974)
* fix(provider): apply Codex OAuth session switch correctly Ensure Codex OAuth activation in an existing session does not briefly apply an empty OpenAI API key, preventing missing Authorization headers until restart. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(provider): preserve explicit env for Codex API profiles Limit the Codex session-switch override to OAuth profiles so explicit OpenAI environment settings keep taking precedence for regular Codex profiles. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(provider): isolate Codex OAuth env from ambient credentials Keep Codex OAuth profile activation from inheriting ambient Codex API credentials so CI and user shells cannot poison the in-session OAuth regression path. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
cc0dab60a3 |
fix(openai-shim): don't label transport failures as HTTP 503 (#971) (#975)
Network/transport failures from custom OpenAI providers (e.g. ENETDOWN,
EAI_AGAIN, generic fetch failed) were thrown via APIError.generate(503,
...). The OpenAI SDK formats the error message as "${status} ${message}",
so users saw output like:
503 OpenAI API transport error: fetch failed (code=ENETDOWN)
This is misleading: a 503 implies the upstream server returned Service
Unavailable, but no HTTP response was received at all -- the failure
happened at the transport layer.
Use APIError.generate(0, ...) instead. The SDK's own factory returns
APIConnectionError for status 0, which is the semantically correct class
for "no response received" and produces a message without the spurious
"503" prefix. APIConnectionError extends APIError, so existing
`instanceof APIError` branches in errors.ts and elsewhere keep working.
Add a regression test that asserts the constructed error is an
APIConnectionError, has no status, and the message does not start with
"503".
|
||
|
|
9f86de01ec |
Route API user agents by actual provider target
Split Anthropic-owned endpoint user agents from provider-routed API traffic. Keep compatibility-sensitive Anthropic requests on claude-cli/99.0.0 while using openclaude-cli with the current public build version for non-first-party/OpenAI-compatible connections. Wire getAnthropicClient() to pass its providerOverride-aware first-party decision into the user-agent helper so per-request routing stays correct. Expand focused tests to cover first-party compatibility traffic, Anthropic-owned endpoints under third-party env, non-first-party provider traffic, and explicit first-party override behavior. |
||
|
|
2d12fd46bc | Merge remote-tracking branch 'upstream/main' into api-client-version | ||
|
|
0a1c56cc6c |
Split public build version from compatibility headers
Keep compatibility-sensitive claude-cli/claude-code user agents on MACRO.VERSION while adding a shared public build version helper for MCP-facing metadata. Update MCP client metadata and MCP user-agent strings to report DISPLAY_VERSION where appropriate without reintroducing first-party version-gate regressions. Add focused regression tests covering: - public build version helper behavior - API client user-agent compatibility behavior |
||
|
|
7711ddae48 |
fix(worktree): surface git stderr in rev-parse failure message (#690) (#954)
When /statusline (or any AgentTool path that creates a worktree) hits
`git rev-parse HEAD` failure during base-branch resolution, the
previous error swallowed git's stderr and reported only:
Failed to resolve base branch "HEAD": git rev-parse failed
That message gives users no way to distinguish empty repos
('unknown revision or path'), detached HEADs pointing at missing
objects, or a missing git binary on PATH — all surfaced identically.
Extract the message construction into buildRevParseFailureMessage()
and include git's stderr in the thrown Error. When the failing ref is
literally 'HEAD' (the fallback path when fetching origin/<branch>
fails), append a short hint about the most common cause (no commits)
and PATH check.
Adds focused tests in worktree.test.ts covering the empty-repo case,
the empty-stderr fallback (exit code), the branch-ref path (no HEAD
hint), and stderr whitespace trimming.
Fixes #690
Co-authored-by: 0xfandom <nikhoriariteish@gmail.com>
|
||
|
|
5c4fdca217 |
fix(plugins): sanitize env before spawning git so /plugin marketplace add works (#751) (#934)
Git 2.30+ refuses to start when any environment value contains a NUL,
CR, or LF character ("Unsafe environment: control characters are not
allowed in values"). User shells frequently leak such values — a
copy-pasted API key with a trailing newline, a terminal-set variable
with embedded escape sequences — which made every /plugin marketplace
add and /plugin install fail with that error before git even ran.
Add a small shared helper that builds the env passed to git child
processes and drops keys whose name or value contains a control
character. The legacy GIT_NO_PROMPT_ENV overrides (terminal prompt
disabled, askpass cleared) move into the same helper. Apply it to
every git invocation in marketplaceManager.ts (5 sites: gitPull,
gitClone, sparse-checkout, post-sparse checkout, reconcileSparseCheckout)
and pluginLoader.ts (8 sites: clone, fetch, checkout in both gitClone
and installFromGitSubdir).
A debug-level warning is logged once per process listing the dropped
key NAMES (not values) so the user can clean them up in their shell.
- src/utils/plugins/gitEnv.ts (new): sanitizeEnvForGit + buildGitChildEnv
- src/utils/plugins/gitEnv.test.ts (new): 10 unit tests covering CR/LF/NUL
in values, control char in key name, undefined values, defaults,
extras override
- src/utils/plugins/marketplaceManager.ts: replace 5 inline env spreads
with buildGitChildEnv()
- src/utils/plugins/pluginLoader.ts: pass env: buildGitChildEnv() to 8
git exec sites that previously inherited process.env unfiltered
Verified locally on Linux: before fix, git --version with a leaked
control-char env value fails with "Unsafe environment"; after fix it
runs cleanly. Live marketplaceManager.gitClone against a real GitHub
repo with the same leaked env succeeds and the repo is materialized
on disk.
|
||
|
|
677d29ffd4 |
feat(lsp): add first-class code intelligence setup (#950)
* feat(lsp): add plugin candidate discovery * feat(lsp): add first-class setup command * fix(lsp): add bounded filesystem extension fallback * fix(lsp): repair official marketplace recommendations |
||
|
|
4fab8b913f |
fix(errors): show actual host in 404 message instead of Ollama hint (#926) (#931)
When an OpenAI-compatible provider returns a 404, the user-facing error message hardcoded "for Ollama: http://127.0.0.1:11434/v1" as a hint regardless of the configured base URL. Users on remote providers (NVIDIA NIM, OpenRouter, etc.) read this as the app ignoring their custom OPENAI_BASE_URL and routing to localhost. Plumb the request URL through the classifier and marker so the user-facing message can name the actual host. Localhost endpoints keep the existing Ollama-flavored guidance for backward compatibility. - classifyOpenAIHttpFailure now accepts an optional url and produces a host-aware hint for non-localhost 404s - the [openai_category=...] marker carries an optional host segment - mapOpenAICompatibilityFailureToAssistantMessage branches on host to show "Endpoint at <host> returned 404. Verify OPENAI_BASE_URL is correct and the selected model (<model>) is supported by this provider." for remote URLs - backward compatibility preserved when no URL is available |
||
|
|
a46b31c3ec |
feat: SDK Core — Permission System, Async Context, and Engine Extensions (#951)
* feat(sdk): add SDK foundation — type declarations, errors, and utilities
Adds standalone SDK building blocks with no SDK source dependencies:
- sdk.d.ts: ambient type declarations for SDK bundle
- coreSchemas.ts + coreTypes.generated.ts: Zod schemas and generated types
- errors.ts: SDK-specific error classes
- validation.ts: input validation utilities
- messageFilters.ts: extracted message filter logic
- handlePromptSubmit.ts: imports from messageFilters
- 16 generated-types tests
* fix(sdk): narrow assertFunction type from broad Function to callable signature
Code review finding: assertFunction used `asserts value is Function` which
accepts any function-like value without narrowing. Changed to
`(...args: any[]) => any` for better type safety.
* fix(sdk): update sdk.d.ts header — manually maintained, not generated
Reviewer noted the header said "Generated from index.ts" but no generator
produces this file. Updated to "Manually maintained — keep in sync with
index.ts". Drift detection added in validate-externals.ts (PR 3).
* fix(sdk): align sdk.d.ts types with canonical coreTypes.generated.ts
Tighten SDK public type contract to resolve reviewer blockers:
- PermissionResult: unknown[] → precise 6-shape discriminated union
(addRules/replaceRules/removeRules/setMode/addDirectories/removeDirectories)
- SDKSessionInfo: snake_case → camelCase (sessionId, lastModified, etc.)
- ForkSessionResult: session_id → sessionId
- SDKPermissionRequestMessage: uuid + session_id now required
- SDKPermissionTimeoutMessage: added uuid + session_id
- SessionMessage: parent_uuid → parentUuid
- SDKMessage/SDKUserMessage/SDKResultMessage: replaced loose inline
definitions with re-exports from coreTypes.generated.ts
* feat(sdk): wire existing code modules + SDK shared utilities
Modifies core modules for SDK integration:
- QueryEngine, tools, state, commands: SDK type hooks
- SDK shared utilities (shared.ts, permissions.ts)
- 21 SDK tests (shared-utils, permissions)
Stack: main ← pr1-foundation ← pr2-sdk-core
* feat(sdk): add snake_case ↔ camelCase key mapping utilities
casing.ts provides recursive key transformation for the SDK boundary
layer. Internal runtime uses snake_case; public API exposes camelCase.
Will be used by shared.ts, sessions.ts, query.ts at export boundaries.
* test(sdk): add tests for snake_case ↔ camelCase mapping utilities
Covers snakeToCamel, camelToSnake, mapKeysToCamel, mapKeysToSnake
including nested objects, arrays, null/undefined, and round-trips.
* fix(sdk): prevent permission timeout race condition with once-only resolve wrapper
Add createOnceOnlyResolve utility to prevent double-resolution of promises
when timeout and host response happen simultaneously. This ensures
deterministic behavior in the permission handling flow.
* fix(sdk): improve race condition test robustness
* fix(sdk): handle consecutive underscores in snakeToCamel conversion
Changes:
- Use _+([a-z]) regex to match multiple consecutive underscores before letters
- Add lookahead (?=. ) to preserve underscore-letter pairs at string end
- Handle dunder names (__proto__, __typename) by stripping wrapper and capitalizing
- Add tests for consecutive underscores and trailing underscore preservation
* fix(sdk): include original error message in permission callback denial
When a canUseTool callback throws an error, the catch block now
includes the original error message in the denial message, making
debugging easier for SDK consumers.
* feat(sdk): add optional timeout to env mutex for deadlock prevention
Add timeout parameter to acquireEnvMutex() to prevent infinite waits
in deadlock scenarios. The timeout is optional and defaults to no timeout
(wait forever) for backward compatibility.
Returns a MutexAcquireResult object with acquired status and optional
timeout reason for failed acquisitions.
* fix(sdk): remove timed-out callback from mutex queue to prevent deadlock
* test(sdk): add missing error path and timeout scenario tests
Add tests for timeout scenarios when host doesn't respond to permission
requests, fallback behavior when no onPermissionRequest callback, and
MCP connection edge cases for undefined/empty config.
* fix(sdk): address code review issues - race conditions, validation, error handling
- Add createPermissionTarget() factory that applies onceOnlyResolve at
registration time, fixing race condition where timeout and host response
could both try to resolve the same promise
- Add try-catch to releaseEnvMutex() to prevent permanent lock if callback throws
- Extract DEFAULT_PERMISSION_TIMEOUT_MS constant (30 seconds)
- Add MCP config validation rejecting null, non-objects, and arrays
- Preserve error stack traces in MCP connection failures
- Add runtime validation to mapMessageToSDK for null/non-object/invalid type
- Update tests to use createPermissionTarget and add validation tests
* test(sdk): add sequential timeout-then-host-response race condition tests
Adds two tests addressing reviewer request for proof that host response
after SDK timeout is safely handled with no double-resolve or leaked listener:
1. Integration test: stale host resolve called after timeout deny —
verifies no error, no mutation, map cleanup
2. Unit test: raw resolve called exactly once when timeout wins —
directly proves createOnceOnlyResolve prevents second execution
* fix: restore openclaude.json comment in REPL.tsx
Reviewer caught that the comment was incorrectly changed to
~/.claude.json during merge — project has already migrated to
~/.openclaude.json.
* fix(sdk): register pending permission before emitting onPermissionRequest
The previous code emitted onPermissionRequest before calling
registerPendingPermission, so a host responding synchronously from
the callback would find an empty map and its response was lost.
Swap the order so registration happens first.
Adds a regression test for the synchronous host response path.
* fix(sdk): make state setters context-aware for SDK isolation
When running inside runWithSdkContext(), setter functions (regenerateSessionId,
switchSession, setCwdState, setOriginalCwd) now write to the AsyncLocalStorage
context instead of global STATE. This prevents cross-session state leakage in
multi-session SDK scenarios.
Reads were already context-aware; this completes the isolation by making writes
consistent. Outside of SDK context, behavior is unchanged — all writes go to
global STATE as before.
* test(sdk): add context-aware state isolation tests
Tests verify that setters within runWithSdkContext() write to the SDK
context (not global STATE) and that parallel async contexts do not leak
state between sessions. Covers setCwdState, setOriginalCwd,
regenerateSessionId, switchSession, and an end-to-end parallel session
scenario.
* fix(sdk): selective tool schema cache invalidation for multi-engine isolation
Replace global clearToolSchemaCache() in QueryEngine.updateTools() with
selective invalidation that only removes cache entries for tools no longer
in the tool set. This preserves cached schemas for tools that remain,
avoiding unnecessary recomputation for concurrent QueryEngine instances
in multi-session SDK scenarios.
New function invalidateRemovedToolSchemas() handles both simple tool name
keys and schema-variant keys (format: "toolName:{...schemaJSON...}").
* docs(sdk): address PR2 non-blocking documentation and logging issues
- Document request_id vs tool_use_id relationship in shared.ts
(request_id for response correlation, tool_use_id for tracking)
- Add injectable SDKLogger interface to permissions.ts, replacing
direct console.warn calls with logger.warn (hosts can control noise)
- Document Node.js-only AsyncLocalStorage requirement in state.ts
(requires Node.js 12.17.0+ or 14.0.0+)
- Clarify env-mutex is host utility (SDK doesn't mutate process.env)
* fix(sdk): handle throwing onPermissionRequest and fix permission request shape
- Wrap onPermissionRequest in try-catch to clean up pending resolver on throw
- Add uuid and session_id to permission_request message to match SDK schema
- Add regression tests for throwing callback and message shape validation
* fix(sdk): use explicit no-session placeholder for standalone permission prompts
- Add NO_SESSION_PLACEHOLDER constant ('no-session') for permission requests
- Update SDKPermissionRequestMessage doc to explain session_id semantics
- Replace empty string fallback with explicit placeholder
- Add test verifying placeholder behavior when sessionId omitted
* docs(sdk): add example code to permission denial warning
Include canUseTool example in warning message to improve developer
experience and make SDK usage more discoverable for new users.
* fix(sdk): scope parentSessionId to SDK context for parallel isolation
regenerateSessionId({ setCurrentAsParent: true }) was writing to the
process-global STATE.parentSessionId even inside runWithSdkContext(),
allowing one SDK context to overwrite another's parent-session metadata.
Add parentSessionId to the SdkContext type and update both
regenerateSessionId and getParentSessionId to read/write from the
active context when one exists, using an explicit if-else pattern
rather than ?? to avoid undefined fallback leaking across contexts.
The non-SDK CLI path (no active context) continues to use STATE
directly, preserving existing behavior.
---------
Co-authored-by: Ali Alakbarli <ali.alakbarli@users.noreply.github.com>
|
||
|
|
b471745fb1 |
Registry-Based Integration Architecture for Providers, Gateways, and Models (#910)
* setting up
* updated plan with missing notes for discovery cache
* build out inital checklist and planning adjustments
* Phase 1A-1D
* Fix descriptor-backed provider profile routing
- preserve GitHub, Bedrock, and Vertex runtime flags during profile activation\n- serialize descriptor-backed startup profiles into legacy-compatible persisted kinds\n- add regression coverage for activation, restart round-trip, and saved-profile switching\n- guard integration registration so repeated imports stay idempotent in tests
* feat: finish phase 1 provider descriptor routing
Complete the Phase 1E CLI/usage migration work and the Phase 1F verification pass for descriptor-backed providers.
Details:
- derive valid --provider values from descriptor registry and compatibility mappings instead of a fixed list
- preserve special CLI semantics for ollama and minimax while allowing descriptor-backed OpenAI-compatible routes such as deepseek and openrouter to pick up descriptor base URLs
- add getUsageDescriptor() so /usage resolves vendor/gateway metadata and follows usage delegation
- switch Settings Usage rendering to descriptor-backed usage resolution for Anthropic, MiniMax, and neutral unsupported fallbacks
- make integration loading idempotent via ensureIntegrationsLoaded() so registry-backed helpers survive tests that clear the registry
- fix compatibility mapping for mistral so the preset routes through vendorId=openai with gatewayId=mistral rather than a nonexistent direct vendor route
- harden provider profile and startup tests so descriptor-backed providers, legacy OpenAI startup files, and unknown stored providers round-trip correctly
- remove a stale ollama model mock that was leaking across the full model test suite
- update plan/progress.md with the current 1E complete / 1F in-progress verification state and the note that repo-wide typecheck failures are pre-existing outside this migration slice
Verification:
- bun test src/commands/usage/index.test.ts src/integrations/compatibility.test.ts src/utils/providerFlag.test.ts src/utils/providerProfiles.test.ts src/utils/providerProfile.test.ts src/utils/model/modelCache.test.ts src/integrations/index.test.ts src/integrations/registry.test.ts
- filtered bun run typecheck output for the files changed in this branch is clean
* Phase 2 planning
* feat: complete phase 2A validation and discovery cache
* fix: address review findings for phase 2 cache and validation
Fixes the follow-up review issues from the Phase 2A / 2A.5 work.
Completed work:
- made discovery cache stale entries reachable through getCachedModels(..., { includeStale: true }) while keeping fresh-by-default behavior unchanged
- kept recordDiscoveryError stale-data preservation useful to later /model consumers by exposing stale and error-only entries through the public helper API
- extended descriptor-backed validation routing metadata with host alias matching support
- updated MiniMax validation routing to recognize both api.minimax.io and api.minimax.chat endpoints
- added regression coverage for stale cache reads, error-only cache entries, and MiniMax chat-host validation
- updated progress.md notes so the recorded 2A.5 helper behavior matches the implementation
* feat: complete phase 2B discovery and readiness migration
Implement descriptor-backed discovery and readiness routing for Phase 2B.
Highlights:
- add src/integrations/discoveryService.ts to execute declarative catalog.discovery configs with shared discovery-cache integration
- add hybrid merge behavior so curated descriptor catalog entries stay ahead of discovered duplicates
- add typed startup readiness metadata via ReadinessProbeKind and wire gateway descriptors for ollama, atomic-chat, lmstudio, and openrouter
- export probeOllamaModelCatalog() so discovery can distinguish unreachable Ollama from reachable-but-empty catalogs
- migrate ProviderManager and /provider flows to probeRouteReadiness() while preserving existing Ollama messaging
- route bootstrap local model discovery through descriptor-backed discovery for recognized local routes, while keeping legacy fallback for generic custom endpoints
- add resolveDiscoveryRouteIdFromBaseUrl() so bootstrap can share descriptor-backed discovery and local provider labels
- preserve explicit provider env precedence during applySavedProfileToCurrentSession() after focused verification exposed the regression
- update plan/progress.md to mark Phase 2B complete and record the verification notes
Verification:
- bun test src/integrations/discoveryService.test.ts
- bun test src/components/ProviderManager.test.tsx
- bun test src/commands/provider/provider.test.tsx
- bun test src/utils/providerDiscovery.test.ts src/integrations/registry.test.ts src/integrations/index.test.ts
- filtered bun run typecheck for the touched 2B files returned FILTER_CLEAN
* feat: complete phase 2c provider metadata migration
Finish the Phase 2C runtime metadata adoption work on cheeky-cooking-moon.
Provider UI metadata:
- add shared route metadata and provider preset UI metadata helpers
- move preset labels/defaults, route type labels, and custom-header capability checks onto descriptor-backed lookups
- update ProviderManager and /provider summaries/setup copy to read shared descriptor metadata instead of bespoke switches
- extend local gateway descriptors with default model metadata used by the shared UI helpers
Model discovery UX:
- add route catalog option builders for descriptor-backed /model rendering
- update /model to resolve the active route, read cached route catalogs before rendering, and trigger background refresh when cached discovery is stale
- add /model refresh plus in-picker refresh via modelPicker:refresh and the r keybinding
- clear discovery cache on manual refresh and surface non-blocking loading/success/stale-error states in ModelPicker
- keep descriptor-backed dynamic and hybrid routes on the shared discovery cache service
Verification and hardening:
- fix combined test pollution by isolating /model test module imports and using real OpenRouter descriptor metadata during shared runs
- update progress.md to mark Phase 2C complete with verification notes
- verified with bun test for provider profiles, ProviderManager, /provider, /model, discovery cache, and provider validation suites
* feat: complete phase 2d runtime provider alignment
Align descriptor-backed runtime provider behavior with the legacy APIProvider surface so active routes, OpenAI shim behavior, and resume handling all resolve through the same metadata path.
Add runtimeMetadata.ts to centralize active route detection, OpenAI shim overrides, and native-format inference. Update provider resolution to map descriptor-backed routes onto legacy provider categories while preserving existing compatibility fallbacks for Foundry, NVIDIA NIM, MiniMax, GitHub, Bedrock, and Vertex.
Move request-shaping rules onto descriptor metadata for DeepSeek, Moonshot, Kimi Code, Gemini, Mistral, GitHub, and local gateways, including reasoning_content preservation, deepseek-compatible thinking payloads, max_tokens field selection, and store field stripping. Treat GitHub Claude native transport as Anthropic-native during conversation recovery so thinking blocks survive resume flows.
Extend focused tests for provider resolution, OpenAI shim request shaping, and conversation recovery, and update phase tracking notes in progress.md to mark 2D complete with verification details.
* feat: complete phase 2e drift audit
Complete the Phase 2E verification and drift-audit packet for the descriptor migration branch.
Add representative provider-summary coverage for descriptor-backed OpenRouter routing plus Gemini and Mistral current-provider summaries in src/commands/provider/provider.test.tsx. Extend ProviderManager coverage with first-run Atomic Chat discovery-backed setup and a regression test proving the set-active picker now uses descriptor-backed provider-type labels.
Replace stale saved-profile picker wording in ProviderManager so saved profiles no longer collapse to a coarse anthropic/openai-compatible split and instead render the route's descriptor-backed provider type label.
Add plan/phase-2e-drift-audit.md documenting the remaining intentional switch sites and non-switch provider branches across provider summaries, active-route detection, OpenAI shim env remapping, auth/header exceptions, and conversation recovery. Update plan/progress.md to mark Phase 2 and 2E complete on-branch, record focused verification, and note the follow-up hardening completed during audit review.
Verification completed during this packet: bun test src/components/ProviderManager.test.tsx src/commands/provider/provider.test.tsx src/utils/providerValidation.test.ts src/integrations/discoveryService.test.ts src/commands/model/model.test.tsx and bun test src/utils/providerDiscovery.test.ts src/utils/model/providers.test.ts src/services/api/openaiShim.test.ts src/utils/conversationRecovery.test.ts. Filtered typecheck output still shows pre-existing baseline noise in src/services/api/openaiShim.ts and src/utils/conversationRecovery.ts only.
* fix: close phase 2 provider parity follow-through
Complete the skipped provider-surface follow-up discovered during the post-Phase-2 review.
- add focused status coverage for NVIDIA NIM and MiniMax sessions
- add Mistral entries to legacy teammate/model compatibility configs
- fill deprecation placeholders for the widened APIProvider surface
- add focused regression tests for status and teammate fallbacks
- update the Phase 2 drift audit and progress tracker with the compatibility-bridge notes and Phase 3 staging context
* phase 3 planning
* refactor: start phase 3a dead-switch cleanup
Begin the Phase 3 cleanup pass with the metadata-only dead-switch removals that are safe to land independently on cheeky-cooking-moon.
Completed work:
- updated plan/progress.md to move Phase 3 and Phase 3A into IN_PROGRESS, added slice-level checklists, and recorded what remains intentionally deferred to later packets
- removed duplicated OpenAI-compatible status-display branches in src/utils/status.tsx by routing openai/codex/nvidia-nim/minimax through shared metadata helpers
- replaced the pure transport-kind label switch in src/integrations/routeMetadata.ts with shared label metadata
- replaced the pure provider-label switch in src/components/CostThresholdDialog.tsx with a shared provider-label map
- added focused regression coverage in src/utils/status.test.ts, src/integrations/routeMetadata.test.ts, and src/components/CostThresholdDialog.test.ts
Verification:
- bun test src/utils/status.test.ts src/utils/swarm/teammateModel.test.ts src/utils/model/providers.test.ts
- bun test src/integrations/routeMetadata.test.ts src/utils/status.test.ts src/components/CostThresholdDialog.test.ts src/utils/model/providers.test.ts
- filtered bun run typecheck for the touched status/routeMetadata/CostThresholdDialog files returned FILTER_CLEAN
* refactor: complete phase 3b and 3c cleanup
Complete the uncommitted Phase 3B compatibility rename work and the Phase 3C env-shaping consolidation on cheeky-cooking-moon.
Phase 3B:
- introduce LegacyAPIProvider while keeping APIProvider as the public compatibility alias
- introduce LegacyProviderModelConfig and LEGACY_PROVIDER_MODEL_CONFIGS while keeping ModelConfig and ALL_MODEL_CONFIGS as compatibility exports
- switch modelStrings, deprecation helpers, and provider profile compatibility naming onto the legacy/compatibility terminology
Phase 3C:
- add shared managed-env clear/apply helpers in providerProfile.ts and route buildLaunchEnv through the shared compatibility env shaper
- route applyProviderProfileToProcessEnv through the same compatibility env shaper so config-backed profiles and startup/session env construction stay aligned
- preserve explicit exception behavior for github, mistral, bedrock, vertex, bankr aliasing, MiniMax fallback detection, and NVIDIA NIM mode markers
- reduce createOpenAIShimClient to the remaining credential alias hydration that resolveProviderRequest does not already cover
- fix applySavedProfileToCurrentSession so saved-profile switching can move away from stale GitHub env selections
- add regression coverage for NVIDIA NIM env stamping and stale Codex-managed env clearing
- update progress.md to mark Phase 3B and 3C complete on branch and record the verification notes
Verification:
- bun test src/utils/model/providers.test.ts src/utils/providerProfiles.test.ts src/utils/swarm/teammateModel.test.ts src/utils/status.test.ts
- bun test src/utils/providerProfile.test.ts src/utils/providerProfiles.test.ts src/services/api/openaiShim.test.ts
- filtered bun run typecheck confirmed no new hits in providerProfile.ts or providerProfiles.ts; remaining openaiShim.ts hits are existing repo baseline debt
* docs: complete phase 3d audit and architecture note
Complete the Phase 3D final audit/documentation packet on cheeky-cooking-moon.
Work completed:
- add plan/phase-3d-final-audit.md with the final post-Phase-3 inventory of remaining provider-specific runtime branches
- classify the remaining exceptions as intentional long-term runtime differences or temporary env/config compatibility bridges
- confirm the audit did not uncover new missed runtime migration work that requires additional Phase 3 code changes
- add docs/architecture/integrations.md to document the descriptor-first architecture, current constraints, known exceptions, and follow-on guidance for future cleanup
- update plan/progress.md to mark Phase 3D complete on branch, mark 3C merged on branch, and point the tracker at Phase 4A next
Key exception categories documented:
- github dual-mode transport behavior
- mistral dedicated route/runtime shaping
- bedrock/vertex/foundry native Anthropic-family paths
- Azure and Bankr request-auth/header differences
- Gemini, DeepSeek, and Moonshot/Kimi OpenAI-shim quirks
- MiniMax dedicated usage handling
- native web-search gating
- env-only MiniMax and NVIDIA NIM compatibility fallbacks
- env/config compatibility bridges such as route detection, --provider shaping, and startup/provider summaries
Notes:
- this packet is branch-local audit/documentation work only; no runtime code paths were changed
- no new tests were required for the audit/doc pass
* docs: stage phase 4 tracker and codex profile guard
Add the Phase 4 documentation/reference-samples plan to progress.md in the same packet/checkpoint structure as earlier phases, and reconcile the Phase 3 tracker summary with the completed cleanup state. Also fix applySavedProfileToCurrentSession so Codex saved-profile activation does not overwrite an already explicit live provider selection, while still clearing stale profile-managed markers when needed.
* docs: complete phase 4a and 4b guides
Expand the integrations architecture note with descriptor authoring, routing-contract, transport-boundary, and compatibility-layer guidance. Add overview and glossary docs under docs/integrations/, plus new how-to guides for adding vendors and gateways with one-file and two-file patterns, discovery cache guidance, token-field guidance, and compatibility follow-through. Update progress.md to mark Phase 4 in progress, Phase 4A complete, and Phase 4B complete with notes about the new docs structure and guide outputs.
* docs: complete phase 4 integration docs
Add the remaining descriptor contributor guides for models, anthropic proxies, and /usage support.
Add a reference sample pack and a common-pitfalls checklist, update the integrations overview, and reconcile plan/progress.md so Phase 4 is marked complete on cheeky-cooking-moon with the current implementation boundaries called out explicitly.
* docs: reconcile tracker waivers and checkpoints
Update plan/progress.md to formally waive the remaining repo-wide typecheck item for Phase 1F as pre-existing debt outside the descriptor migration scope, and mark the Phase 4 branch-local checkpoints as landed on cheeky-cooking-moon with the corresponding commit references.
* Align Z.AI merge fallout with descriptors
Reviewed the upstream main merge against plan/cheeky-cooking-moon.md and removed drift from the old switch/helper-based Z.AI provider path.
Moved Z.AI reasoning, context-window, and max-output metadata into the descriptor route catalog so thinking support can read catalog capabilities instead of URL/model helper checks.
Removed the standalone src/utils/zaiProvider.ts helper and updated startup/provider-discovery labeling to resolve known direct routes through descriptor route metadata.
Simplified --provider handling for Z.AI by letting descriptor defaults provide the base URL and default model through the generic OpenAI-compatible provider branch.
Updated startup and provider-discovery tests for descriptor-backed labels, added Z.AI descriptor-label coverage, and documented the post-main-merge reconciliation in plan/progress.md.
Verification before commit: bun test src/utils/providerFlag.test.ts src/utils/providerProfiles.test.ts src/utils/thinking.test.ts src/components/StartupScreen.test.ts src/utils/providerDiscovery.test.ts; bun test src/integrations/compatibility.test.ts src/integrations/index.test.ts src/integrations/registry.test.ts src/services/api/openaiShim.test.ts; git diff --check.
* fix: restore descriptor migration behavior and isolate provider tests
Restore the descriptor-era Anthropic/OpenAI boundary during conversation recovery by threading the legacy provider category into usesAnthropicNativeMessageFormat instead of relying on ambient env-only route detection.
Harden branch-added provider-facing tests so they do not inherit leaked bun mock.module state from neighboring suites. Status, thinking, teammate fallback, and GitHub model options tests now restore mocks and/or import fresh modules under explicit provider context.
Update bugfix assertions to validate the descriptor-backed openaiShim contract for removeBodyFields/store stripping instead of the pre-refactor inline conditionals.
Validation:
- focused status/thinking/conversationRecovery/bugfix suites pass
- full bun test --max-concurrency=1 is down to the existing conversationArc perf benchmark failure only
- bun run smoke
- bun run build
- npm pack
* fix: close descriptor review drift and provider regressions
Address the follow-up review against plan/cheeky-cooking-moon.md by fixing the remaining runtime drift and locking the behavior with focused coverage.
Completed work:
- make NVIDIA NIM descriptor-backed auth consistent across validation, --provider env shaping, and openaiShim request auth so NVIDIA_API_KEY works without requiring OPENAI_API_KEY
- resolve /usage from the active descriptor route instead of collapsing most OpenAI-compatible providers into the legacy openai bucket
- honor discoveryRefreshMode in /model so manual, on-open, background-if-stale, and startup catalogs no longer behave identically
- clarify docs/progress notes so the branch no longer overstates one-file additive onboarding while loader and preset/UI compatibility surfaces are still manual
Verification:
- bun test src/services/api/openaiShim.test.ts src/utils/providerValidation.test.ts src/utils/providerFlag.test.ts src/utils/model/providers.test.ts src/commands/usage/index.test.ts src/commands/model/model.test.tsx
* docs(plan): require descriptor-native gateway onboarding closure
Investigated the current descriptor onboarding flow and documented the remaining manual choke points in the loader, preset compatibility mapping, provider UI metadata, and handwritten preset typing.
Tighten cheeky-cooking-moon so additive onboarding is a hard requirement, add Phase 3E for descriptor-native onboarding closure, and update the progress tracker to reflect that follow-up work instead of treating the branch as fully complete.
* feat(integrations): close descriptor-native onboarding
Implement the Phase 3E generated-artifact workflow for integration onboarding.
- add integration artifact generation and check scripts
- generate loader inventory, preset manifest, and preset type from descriptors
- move preset participation onto descriptor preset metadata for preset-facing vendors and gateways
- derive compatibility and provider UI metadata from the generated manifest
- remove descriptor-level preset ordering and sort presets by description with standard alphanumeric ordering
- pin the custom preset to the bottom automatically in generated ordering
- add validation for duplicate preset ids and incomplete preset metadata
- add generator tests for representative gateway and direct-vendor onboarding
- refresh ProviderManager tests for generated preset ordering
- update architecture/how-to/reference docs and progress tracking for the new regeneration workflow
* Fix provider profile and discovery drift
Honor route-specific auth env vars across descriptor-backed OpenAI-compatible routes by centralizing credential resolution and using it in validation, bootstrap, discovery, and the OpenAI shim.
Persist Anthropic startup fallbacks as native anthropic profiles and restore them correctly at startup so the legacy startup file stays aligned with the active provider.
Wire discoveryRefreshMode='startup' into startup and provider activation flows, with LM Studio as a live startup-refresh example, and add regression coverage for validation, startup env shaping, discovery refresh, and shim auth handling.
* Pin Anthropic provider preset to the top
Keep the existing custom gateway preset pinned to the bottom while moving the Anthropic preset ahead of the description-sorted remainder.
Regenerate the integration preset manifest/order and extend the artifact generator coverage to lock in both ordering rules.
Validation: bun test src/integrations/artifactGenerator.test.ts src/components/ConsoleOAuthFlow.test.tsx; bun run build
* docs: refresh integration and setup guides
Update the new descriptor-era integration docs so they read as current contributor guidance instead of rollout notes, and align the authoring examples with the actual runtime metadata flow.
Highlights:
- add a CONTRIBUTING.md pointer to the integration overview and focused how-to guides
- remove branch/phase-specific wording from the integration docs
- fix OpenAI-compatible header guidance to use transportConfig.openaiShim headers and custom-header flags
- clarify anthropic proxy onboarding around generated loader support
- refresh advanced setup with current Codex, Gemini, Mistral, and profile-launch details
- fix LiteLLM /provider instructions and clarify local no-auth behavior
- tighten quick-start and non-technical cross-links so users can find the advanced provider docs
* fix: close descriptor integration drift
Apply descriptor-backed static headers to OpenAI-compatible request execution and model discovery, preserving request-specific header precedence.
Allow Gemini profile launch with API key, access-token, or ADC credentials, and align Gemini fallback defaults with the descriptor/docs default model.
Add regression coverage for descriptor header propagation, Gemini defaults, and discovery auth/header behavior.
* post-phase follow-up task added
* Fix xAI merge follow-ups
Route env-only XAI_API_KEY sessions through the OpenAI-compatible shim using descriptor-backed xAI defaults, and map the xAI key into OPENAI_API_KEY for shim auth.
Hydrate legacy profile: xai startup env with xAI descriptor defaults, preserving XAI_API_KEY and OpenAI-compatible launch behavior.
Update progress tracking for post-merge xAI descriptor inventory and clarify that profile-owned custom headers remain open despite adjacent auth/static-header plumbing.
Add regression coverage for env-only xAI client routing, legacy xAI launch env, shell key precedence, and the Gemini/OpenAI client test isolation issue.
* Complete profile custom headers follow-up
Add persisted provider-profile customHeaders support with shared parsing and sanitization for compact Name: value input. Reject malformed and reserved auth/internal headers before saving or applying profile-owned headers.
Expose a descriptor-gated /provider custom headers step, preserve headers during profile edit/update, and apply supported profile headers through ANTHROPIC_CUSTOM_HEADERS for active env and startup fallback profiles.
Propagate profile headers into descriptor discovery refresh and bootstrap model discovery while preserving descriptor/profile/auth merge order. Add focused regression coverage and mark the progress tracker packet complete.
* Allow api-key custom provider headers
Permit api-key in /provider custom header input and preserve it when OpenAI-compatible shim requests are built. This is intentional for gateway providers that require an api-key header in addition to, or instead of, standard bearer auth.
Keep managed credential headers protected by continuing to reject/strip authorization and x-api-key, plus Anthropic/Claude-owned headers. Add parser, profile env, and outgoing request coverage for the intended behavior.
* fix: restore API mode picker for OpenAI-compatible profiles
Use descriptor transport metadata instead of the legacy provider id when deciding whether provider profiles support OpenAI-compatible options. This restores the Chat Completions vs Responses picker for the Custom OpenAI-compatible preset after it moved to the descriptor-backed custom route.
Preserve apiFormat and custom auth header profile fields for all routes whose transportConfig.kind is openai-compatible, so selecting Responses is saved and applied as OPENAI_API_FORMAT=responses.
Tests: bun test src/components/ProviderManager.test.tsx; bun test src/utils/providerProfiles.test.ts; bun run build; bun run smoke
* fix: respect explicit provider routing with xAI env
Ensure env-only XAI_API_KEY fallback does not take over when Bedrock, Vertex, or Foundry has been explicitly selected. This preserves native transport routing while still allowing bare xAI env setup to use the OpenAI-compatible shim.
Restore api-key to the managed custom-header blocklist now that /provider exposes the API mode/auth-header controls for OpenAI-compatible profiles. The shim and provider override paths strip api-key again, while OPENAI_AUTH_HEADER=api-key remains available for explicit auth configuration.
Tests: bun test src/services/api/client.test.ts src/utils/providerCustomHeaders.test.ts src/utils/providerProfiles.test.ts src/services/api/openaiShim.test.ts; bun run build; bun run integrations:check; bun run smoke
* docs: fix integration drift
Align integration and setup docs with the current implementation.
- show model descriptor examples as array default exports, matching the generated MODEL_DESCRIPTOR_GROUPS loader contract
- document provider-scoped model env vars instead of implying OPENAI_MODEL globally overrides ANTHROPIC_MODEL
- clarify generated provider preset ordering: anthropic first, custom last, description-sorted middle entries
- update LiteLLM examples and /provider guidance to use the /v1 OpenAI-compatible base URL
Verification: bun run integrations:check
* Fix provider discovery cache isolation
* Stabilize provider env tests
* Stabilize provider test isolation
Completed work:
- Isolated GitHub model option tests from cached availableModels settings.
- Isolated startup discovery tests from live process.env provider flag races.
- Mocked teammate provider fallback tests at the provider helper boundary.
- Moved cost threshold provider labels into a pure helper for deterministic tests while preserving runtime active-provider behavior.
Validation:
- bun test src/components/CostThresholdDialog.test.ts src/integrations/discoveryService.test.ts src/utils/model src/utils/swarm
- bun run build
- bun run smoke
* test: isolate startup screen model settings
Clear the session settings cache and persisted global model around StartupScreen provider-detection tests.
This prevents earlier provider/model suites from leaking saved non-Anthropic models into the default Anthropic startup assertions.
Verified with: bun test src/components/StartupScreen.test.ts src/integrations/discoveryService.test.ts src/utils/model/modelOptions.github.test.ts
Full bun test now only fails the unrelated Conversation Arc sub-millisecond performance benchmark.
* test: isolate route discovery and github model options
Restore Bun module mocks around discoveryService tests before loading fresh route-discovery modules.
Pin the GitHub model-options test to a complete providers.js mock so cached provider mocks from other suites cannot hide Copilot options.
Verified with: bun test src/integrations/discoveryService.test.ts src/utils/model/modelOptions.github.test.ts
Also ran full bun test; only the unrelated Conversation Arc sub-millisecond performance benchmark fails locally.
* test: avoid startup discovery cache collision
Use the 127.0.0.1 LM Studio alias in refreshStartupDiscoveryForActiveRoute so it still resolves the active route from env but does not share the cache partition with the preceding startup refresh test.
This keeps the assertion on network refresh stable under Bun 1.3.11 serialized runs.
Verified with: bun test --max-concurrency=1 src/integrations/discoveryService.test.ts src/utils/model/modelOptions.github.test.ts
Also ran full bun test --max-concurrency=1; only the unrelated Conversation Arc perf benchmark fails locally.
* fix: isolate OpenAI-compatible route credentials
Restrict OpenAI-compatible shim auth to provider overrides, resolved route credentials, or explicit OPENAI_API_KEY instead of ambient provider-specific secrets.
Remove NVIDIA and Bankr compatibility fallbacks that could promote provider-specific API keys into unrelated OpenAI-compatible routes. Preserve Bankr base URL/model compatibility before route credential resolution so Bankr still resolves through descriptor credentials.
Clear stale NVIDIA_NIM and copied OPENAI_API_KEY values when switching away from NVIDIA NIM, Bankr, or xAI provider flags to avoid carrying provider secrets across route boundaries.
Add regressions for stale NVIDIA, MiniMax, and Bankr keys not leaking into OpenRouter-style routes, plus provider-flag cleanup for copied NVIDIA/Bankr/xAI keys.
Validation: bun test src/services/api/openaiShim.test.ts; bun test src/utils/providerFlag.test.ts; bun run build; bun run smoke.
* fix: guard model discovery privacy paths
Suppress descriptor and legacy model discovery while essential-only traffic mode is active.
Use the partitioned discovery cache key for /model cache reads, stale checks, and manual refresh clears, including route-specific credentials and custom headers.
Partition legacy local OpenAI additional model caches by credentials and routing headers to avoid catalog reuse across profiles.
Add coverage for OpenRouter route credentials, descriptor privacy suppression, legacy discovery privacy suppression, and local cache scope partitioning.
* Fix artifact checks and knowledge graph persistence
Normalize generated integration artifact comparisons so Windows line endings do not make checked-in artifacts appear stale.
Skip knowledge graph entity persistence when re-adding an existing entity with identical attributes, avoiding repeated disk writes during automatic fact extraction and restoring the conversation arc performance benchmark.
Verified with bun test src/integrations/artifactGenerator.test.ts --max-concurrency=1, bun test src/utils/conversationArc.perf.test.ts --max-concurrency=1, and bun test --max-concurrency=1.
* test: isolate privacy discovery cache path
The descriptor discovery privacy test could observe stale OpenRouter cache data populated by an earlier test and receive source=stale-cache instead of static. Use a test-specific API key so the privacy assertion gets its own discovery cache partition while still verifying that nonessential traffic disables network discovery.
Verified with bun test src/integrations/discoveryService.test.ts --max-concurrency=1 and bun test --max-concurrency=1.
* test: accept cached privacy discovery result
* test: set privacy gate before discovery import
* test: prevent discovery privacy mock bleed
Guard descriptor model discovery directly on CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC so nonessential traffic stays disabled even if the privacyLevel module is mocked in-process.
Reduce broad fastMode test mocks for shared modules and use real state/config test hooks, preventing Bun module mock namespaces from leaking into discovery and /model tests.
Verified with bun test src/utils/fastMode.test.ts src/utils/model/openaiModelDiscovery.test.ts src/integrations/discoveryService.test.ts src/commands/model/model.test.tsx --max-concurrency=1 and bun test --max-concurrency=1.
* test: prevent discovery privacy mock bleed
Add an env-level fallback guard to descriptor model discovery so disabled nonessential traffic cannot be bypassed by stale mocked privacy helpers.
Tighten the fastMode regression tests by setting real bootstrap/config state only after the tested module is imported, avoiding broad module mocks that can leak into unrelated discovery tests or behave differently under Bun in CI.
Verified with focused discovery/fastMode/model suites and the full serial bun test suite.
* fix: harden fast mode test isolation
Ignore non-string GrowthBook values when resolving the fast mode unavailable reason so boolean flag payloads cannot surface as false.
Make the affected regression tests install explicit provider mocks for their own scenarios and reset env state, preventing stale provider mocks from changing fastMode and conversation recovery behavior across the serial Bun test run.
* test: harden fast mode module mocks
Expand the fastMode GrowthBook and provider test mocks so later imports in the same Bun test process can resolve the named exports they expect. This prevents order-sensitive failures when model command tests run after fast mode tests.\n\nVerified with: bun test --max-concurrency=1
* feat: consolidate integration runtime metadata
Move OpenAI-compatible model runtime limits into descriptor-backed brand and model metadata, adding Gemini, GLM, MiniMax, Mistral, Nemotron, xAI, and OpenAI-compatible alias descriptor groups. Update generated integration artifacts, route catalog option handling, thinking capability lookup, and docs to use modelDescriptorId-backed runtime metadata.
Split OpenAI shim capability flags into supportsApiFormatSelection and supportsAuthHeaders, and update provider profile sanitization, ProviderManager forms, descriptor validation, and integration authoring docs so fixed routes do not preserve unsupported API format or auth-header settings.
Harden env-only MiniMax and xAI routing. Resolve shared route intent before client setup, reject conflicting OpenAI base URLs, preserve provider-specific base overrides, sanitize stale OpenAI shim knobs, copy provider credentials intentionally, and keep legacy provider labels, context windows, max output limits, model lists, and provider switching aligned.
Refresh MiniMax defaults and catalog entries, add descriptor-backed runtime limits for migrated models, preserve external OpenAI limit overrides, and add regression coverage for env-only MiniMax/xAI, provider-profile capability stripping, route catalog options, copied credential cleanup, and context/runtime limit detection.
Verification performed: bun test src/utils/providerFlag.test.ts; bun test src/services/api/client.test.ts src/utils/model/providers.test.ts src/integrations/routeMetadata.test.ts; bun test src/utils/context.test.ts src/utils/thinking.test.ts src/services/compact/autoCompact.test.ts; bun test src/integrations/routeMetadata.test.ts src/services/api/client.test.ts src/utils/model/providers.test.ts src/utils/providerValidation.test.ts src/integrations/index.test.ts src/utils/status.test.ts; bun run build; bun run smoke.
* test: isolate provider env in conversation recovery
Snapshot and restore all provider-selection environment variables used by the GitHub native Claude resume test instead of only restoring the GitHub flag and OPENAI_MODEL.
The full single-concurrency suite exposed that earlier tests can leave higher-priority provider flags in process.env, causing deserializeMessages to resolve a non-GitHub provider and strip thinking blocks even though the test intended to exercise GitHub native Claude transport.
The test now clears provider routing env before setting CLAUDE_CODE_USE_GITHUB=1 and OPENAI_MODEL=claude-sonnet-4-6, then restores the original env values in afterEach.
Verification: bun test src/utils/conversationRecovery.test.ts; bun test --max-concurrency=1.
* test: isolate conversation recovery provider state
* test: pin conversation recovery provider mock
* test: isolate knowledge graph persistence
* fix: make knowledge graph reset synchronous
* test: restore integration registry after unit tests
* remove plans dir
* delete plans
* Fix provider routing test failures
Restore the missing first-party Anthropic auth routing imports used by getAnthropicClient so OpenAI-compatible provider client creation no longer throws at runtime.
Keep GitHub provider resolution from inheriting OPENAI_API_FORMAT=responses so GitHub GPT-4 and gpt-5-mini models continue to use chat completions while Codex-flavored models still route to responses.
Reset OPENAI_API_FORMAT in the affected API provider tests to prevent environment leakage across serial Bun test runs.
Verified with: bun test --max-concurrency=1
* fix: restore provider-specific model routing
Resolve generic OpenAI-compatible profiles by their known descriptor base URLs so saved MiniMax, xAI, NVIDIA NIM, OpenRouter, and DeepSeek profiles use the correct route catalogs instead of the generic OpenAI model list.
Fix MiniMax defaults and display handling so provider-specific model IDs are not rendered as Claude Opus defaults, add current MiniMax M2.7 options, and cover the regressions with focused route/model tests.
Also clean up descriptor follow-ups from review: remove the dead OpenAI shim store-strip fallback list, preserve gateway vendor IDs for Bedrock/Vertex/GitHub profile resolution, and keep the ModelPicker compiled-form changes in this PR.
* test: cover provider precedence review fixes
Remove import-time ANTHROPIC_BASE_URL and ANTHROPIC_MODEL reads from the Anthropic descriptor so descriptor defaults stay static and live env handling remains in preset metadata.
Add getAPIProvider precedence coverage documenting that explicit Gemini/OpenAI flags beat env-only MiniMax API key inference.
Add a regression check to keep the removed openaiShim hardcoded descriptor route fallback list from returning.
---------
Co-authored-by: TechBrewBoss <dash@hicap.ai>
|
||
|
|
aae96aa52a | feat(cli): improve SSH interactivity detection via SSH_TTY and SSH_CONNECTION (#946) | ||
|
|
0f0fd266db |
fix(openai-shim): strip store when baseUrl points at Gemini (#959)
`isGeminiMode()` only consulted `CLAUDE_CODE_USE_GEMINI` and `process.env.OPENAI_BASE_URL`, so `providerOverride` flows that route to `generativelanguage.googleapis.com` (e.g. `primaryProvider: google` in `~/.claude.json`) kept `store: false` on the payload. Gemini's strict schema rejected it with `400 Invalid JSON payload received. Unknown name "store": Cannot find field.` Detect Gemini directly from `request.baseUrl` via `hasGeminiApiHost` in the strip predicate for both chat_completions and the responses body. Adds two regression tests covering each transport path. Fixes #664 |