Repository navigation
[CI only] feat(web-search): AI/ML API Toolbox search backend - #1
Closed
hugoaimlapi wants to merge 120 commits into
Closed
hugoaimlapi wants to merge 120 commits into
hugoaimlapi wants to merge 120 commits into
Conversation
* fix: retire settings locks before cleanup and stabilize watcher coverage * ci: scan fork pull requests with CodeQL * test: allow full PowerShell permission parser retries
…gpine#2307) Co-authored-by: OpenClaude CI Maintenance <ci@example.invalid>
* ci: drop duplicate provider test steps from PR checks * docs(contributing): drop provider suites from required preflight
…e#2311) Co-authored-by: Contributor <redacted>
Co-authored-by: Contributor <>
* ci: cache package downloads in PR checks * ci: cache package downloads in the suite job * ci: fix suite cache key and separate root and web cache prefixes
* ci: run unit test shards in parallel * fix(ci): address shard-selection review feedback * ci: cache package downloads in the shard and dedicated test jobs
* ci: skip unaffected pull request checks * fix(ci): run the PR intent scan on every pull request --------- Co-authored-by: Repository contributors <undisclosed>
* feat(gateway): add Merge Gateway provider Co-authored-by: MatthewFeroz <136640686+MatthewFeroz@users.noreply.github.com> * fix(merge-gateway): preserve scoped credentials across profiles Co-authored-by: MatthewFeroz <136640686+MatthewFeroz@users.noreply.github.com> * fix: complete Merge Gateway credential and catalog handoffs Co-authored-by: MatthewFeroz <136640686+MatthewFeroz@users.noreply.github.com> * fix: constrain Merge catalog and admit provider env-file credentials Co-authored-by: MatthewFeroz <136640686+MatthewFeroz@users.noreply.github.com> * fix: preserve Merge credential ownership across profile and host settings Co-authored-by: MatthewFeroz <136640686+MatthewFeroz@users.noreply.github.com> * fix(merge-gateway): scope ambient key adoption and per-page discovery timeouts Co-authored-by: MatthewFeroz <136640686+MatthewFeroz@users.noreply.github.com> --------- Co-authored-by: MatthewFeroz <136640686+MatthewFeroz@users.noreply.github.com>
…pine#2318) * fix: synchronize agent completion with output eviction Agent task completion could become externally observable before the output eviction and flush sequence completed, resulting in callers observing empty output files. Root Cause ---------- completeAgentTask() returned immediately while evictTaskOutput() continued asynchronously. Callers could observe task completion before the eviction/flush chain finished. Fix --- - Make completeAgentTask() async and await eviction - Await completeAsyncAgent() at both call sites - Clarify ORC-1337 vs gh-20236 in comments Testing ------- - AgentTool: 59/59 passing - LocalAgentTask: 6/6 passing Refs: ORC-1337, gh-20236 Co-authored-by: Zolow-kuni <285616838+Zolow-kuni@users.noreply.github.com> * fix: move output barrier before terminal state transition Review follow-up for ORC-1337 addressing both P1 findings: 1. Ordering: updateTaskState('completed') ran before the eviction await, so TaskOutput(block=true) readers released by the terminal status could still read a flushing file. The durable-output barrier now precedes the state transition. 2. Sidechain transcript: local-agent output is a symlink to the agent transcript written by the session-storage queue (recordSidechainTranscript) — no DiskTaskOutput entry exists, so evictTaskOutput alone resolved without waiting. completeAgentTask now awaits flushSessionStorage() (drains the queue and in-flight tracked chains) before evictTaskOutput and the status flip. Also per review: - Remove ORC-1337-FIX.md from the PR (P3) - Add deterministic regression test gating both barriers (completionBarrier.test.ts) — verified to fail on the previous ordering - Update call-site comments to describe the corrected sequence Testing: AgentTool + LocalAgentTask 67/67 (incl. 2 new); tsc clean. Refs: ORC-1337, gh-20236 Co-authored-by: Zolow-kuni <285616838+Zolow-kuni@users.noreply.github.com> * fix(agent): await scoped output writes before completion Co-authored-by: Zolow-kuni <285616838+Zolow-kuni@users.noreply.github.com> * fix: cancel agent transcript waits without delaying cleanup Co-authored-by: Zolow-kuni <285616838+Zolow-kuni@users.noreply.github.com> * fix: preserve agent output ownership across session changes Co-authored-by: Zolow-kuni <285616838+Zolow-kuni@users.noreply.github.com> * fix: await agent output link replacement after clear Co-authored-by: Zolow-kuni <285616838+Zolow-kuni@users.noreply.github.com> * fix: preserve agent output destination and finishing input admission Co-authored-by: Zolow-kuni <285616838+Zolow-kuni@users.noreply.github.com> * fix(agent): preserve output destinations for background and resumed sessions Co-authored-by: Zolow-kuni <285616838+Zolow-kuni@users.noreply.github.com> * fix: preserve agent output ownership across replacement and clear Co-authored-by: Zolow-kuni <285616838+Zolow-kuni@users.noreply.github.com> * fix(agent): bind retained output links to the recording producer Record successful sidechain append destinations by agent ID and require retained output links to match that producer record. Preserve the record across session clears and project overrides for retained resumes. Cover swapped agent and project targets for captured and live writes. Co-authored-by: Zolow-kuni <285616838+Zolow-kuni@users.noreply.github.com> --------- Co-authored-by: jatmn <jatmn@users.noreply.github.com> Co-authored-by: Zolow-kuni <285616838+Zolow-kuni@users.noreply.github.com>
…Twigpine#2321) * fix(shell): honor an abort that lands before the listener is attached * test: spawn node timer instead of sleep for Windows portability
Twigpine#2324) * test: consolidate vendor catalog tests into per-vendor contract tables * test: assert full descriptor limits and capabilities for nearai, longcat and xai
…les (Twigpine#2325) * test: consolidate gateway catalog tests into per-gateway contract tables * test: assert opencode-go catalog excludes removed model ids
…cess runs (Twigpine#2328) Co-authored-by: jatmn <12479882+jatmn@users.noreply.github.com>
…s (TA-001 slice 3) (Twigpine#2331) * test: restore leaked settings, config-home and path state in single-process runs (TA-001 slice 3) * test(printMaxTurns): release shared mutation lock if setup throws
…le mocks (TA-001 slice 2) (Twigpine#2332) * test: restore leaked provider, betas, sessionTitle and discovery module mocks (TA-001 slice 2) * test(model): restore real providerProfiles module after model command tests --------- Co-authored-by: jatmn <12479882.jatmn@users.noreply.github.com>
…ports are polluted (TA-001 slice 5) (Twigpine#2334)
* chore(deps): upgrade zod to 4.6.7 * test: normalize tool schema fixture paths to forward slashes * fix: omit AgentTool fields before cwd refinement and export settings schema via zodToJsonSchema
…ine#2406) * fix(proxy): restore direct fetch routing when clearing proxies * fix(proxy): preserve dispatcher ownership and lazy setup * fix(proxy): retire invalidated global TLS dispatchers * fix(proxy): preserve dispatchers retained by fetch callers
…wigpine#2407) * refactor(scripts): use Bun subprocess APIs for development commands * fix(scripts): pass the live process environment to captured subprocesses
* fix(git): honor discovery ceilings in worktree fixtures * test(git): type-check subprocess status assertions * fix(git): isolate fixtures and preserve literal ceilings --------- Co-authored-by: jatmn <jatmn@users.noreply.github.com>
…Twigpine#2414) * fix(types): narrow SDK response schema consumers * perf(types): bound mixed Zod schema comparisons
…ctually stop thinking buildOllamaChatBody() never set a top-level `think` field on outgoing requests. Ollama's hybrid-thinking models (e.g. Qwen3.x) interpret the field's absence as thinking enabled, so every Ollama request ran in thinking mode regardless of --effort, with no way to disable it. The existing client-side thinking filter only hides the trace from display — the model still generates the full reasoning chain either way, so the latency cost was paid regardless of whether it was visible. Mirrors the effort-handling already implemented for the Anthropic/Gemini paths just above this function: no --effort specified maps to think: false; an explicit effort level is passed through (xhigh/max/ ultracode collapse to Ollama's "high", matching its three-tier scale). Measured on qwen3.6:35b-a3b-coding (Ollama, Apple Silicon): - Before: 15-21s per trivial response, full visible reasoning trace - After: ~1.3s via the raw Ollama API with think=false, ~5s through the full CLI (Node startup + OpenAI<->Ollama format conversion overhead) - No visible reasoning trace in either case after the fix
* fix(lsp): synchronize document versions across restarts Track each open document by server generation and advance versions only after lifecycle notifications are delivered. This prevents replacement language servers from serving requests before receiving current file contents. * fix(lsp): harden restart synchronization Preserve active document URIs across Windows path aliases and coordinate connection replacement, failed startup cleanup, cross-generation retries, and shutdown with in-flight document work. * fix(lsp): settle restart lifecycle races Make failed starts and transport loss settle pending work, prevent obsolete generations from publishing responses, and keep diagnostics activity on the opened document identity. * fix(lsp): preserve lifecycle generation boundaries * fix(lsp): bound document recovery lifecycle * test(lsp): harden lifecycle regression coverage * fix(lsp): bound failed startup recovery * test(lsp): harden lifecycle failure coverage * test(lsp): restore current-main compatibility and isolation --------- Co-authored-by: jatmn <the@jat.mn>
…#2416) * fix(api): retry explicitly throttled OpenAI-compatible 403s * fix(api): allow explicit throttling messages that name an API key * fix(api): distinguish OAuth throttling from token faults * fix(api): preserve auth precedence and remote throttle retry headers Match account, key, and token faults across clause wording instead of a 20-character credential window. Preserve plural faults and structured error codes while allowing valid key and OAuth throttle messages. Keep classified throttle-403s out of the infrastructure-auth retry exception so their existing header and quota checks apply in remote mode. Cover actual shim retries, guidance, credential recovery, and native CCR retry behavior. * fix(api): distinguish credential predicates across comma clauses Keep unrelated disabled features from vetoing a valid-key throttle while preserving actual key faults continued through relative descriptions or parentheticals. Normalize only matching boundaries and retain raw error text. Cover the reported retry path and baseline credential-fault cases. * fix(api): require fault predicates in credential continuations An adverb alone does not connect a later disabled feature to an API key. Require an auxiliary/adverb sequence to reach the fault directly, while preserving relative descriptions and parentheticals of the credential. Cover the unrelated-subject regression through the real retry loop. * fix(api): exclude routing host metadata from quota detection Keep quota keyword matching on provider evidence and category markers, without allowing the configured hostname to suppress throttle retries. Preserve the original error message for diagnostics and host consumers. Cover real 403/429 retry paths and genuine quota/no-retry controls. * fix(api): preserve throttle fault precedence and retry categories * fix(api): recognize qualified credential absence
…gpine#2415) * fix(vscode): align session discovery with CLI storage * fix(vscode): discover mixed runtime session histories * fix(vscode): preserve selected history through resume * fix(vscode): cancel pending resume startup on abort * fix(vscode): resolve selected transcripts before CLI launch * fix(vscode): preserve exact-store resumes with relative config roots * fix(vscode): skip zero-byte transcripts during session discovery An empty exact-directory placeholder was treated as the selected session and hid a populated alternate history.
…bstring (Twigpine#2099) * fix(provider): match MiniMax and Venice base URLs by hostname, not substring The credential-mirroring chains route xAI, Cloudflare and Xiaomi through their exact-hostname predicates (isXaiBaseUrl, isCloudflareBaseUrl, isXiaomiMimoBaseUrl), but the MiniMax and Venice branches were left on raw baseUrl.toLowerCase().includes('minimax') / includes('api.venice.ai') even though isMiniMaxBaseUrl / isVeniceBaseUrl already exist. A proxy or lookalike host (minimax-proxy.example.com, api.venice.ai.evil.com) therefore mirrors the profile key into MINIMAX_API_KEY / VENICE_API_KEY; for MiniMax, providerAutoDetect then reroutes it to the real api.minimax.io the user never configured. Route all four sites through the hostname predicates, consistent with the neighbouring providers. * fix(provider): centralize dedicated-key mirroring across the profile lifecycle The host-detected dedicated credential mapping (xAI, MiniMax, Venice, MiMo) was reconstructed independently in three places and had drifted: - The startup env builder's strict-env branch returned before any of these mirrors, so a keyed generic OpenAI-compatible profile on a canonical Venice/MiniMax host persisted only OPENAI_API_KEY and relaunched without its dedicated credential; the fallback branch had no MiniMax mirror at all and matched MiMo by substring. - isProcessEnvAlignedWithProfile never checked MINIMAX_API_KEY, so once that key drifted after activation the env was still considered aligned and applyActiveProviderProfileFromConfig skipped reapplying — leaving MiniMax's dedicated-credential transport misrouted. Introduce getHostDedicatedCredentialEnv as the single source of truth for the exact-host (and, where available, route) credential mapping, and route live application, both startup-persistence branches, and the alignment check through it. Lookalike hosts still receive no dedicated key. * fix(provider): carry MiniMax dedicated key through launch reconstruction buildLaunchEnv rebuilt the startup environment from a fixed dedicated-key carry-over list that omitted MINIMAX_API_KEY, so a clean restart of a keyed MiniMax profile retained the generic OpenAI variables but dropped the dedicated credential. The MiniMax client only enters its native setup while MINIMAX_API_KEY is present, so the persisted profile did not deliver the intended restart behavior. Add MINIMAX_API_KEY to the carry-over contract alongside the other native-client vendor keys (MIMO/VENICE), preserving the same shell-over-persisted precedence. The client's own hostname/route detection gates activation, so an ambient key stays inert in an unrelated session. Regressions reconstruct the launch environment from a saved profile with an empty shell and assert both the surviving credential and the selected env-only route (via resolveEnvOnlyProviderRouteId), not just the persisted file: canonical MiniMax carries and reselects, a live shell key overrides the persisted one, and a generic profile never synthesizes the credential. * fix(provider): preserve keyless credential reconciliation * fix(provider): scope MiniMax launch credentials to the active route * fix(provider): admit saved Venice and MiMo keys only on their routes * test(provider): document saved dedicated-key precedence * fix(provider): restore test globals and scope profile docs * fix(provider): keep explicit MiniMax route credentials across reconcile Reconcile reapply rebuilt a generic OpenAI profile from the hostname only, so a launch that had already kept CLAUDE_CODE_PROVIDER_ROUTE_ID=minimax on a proxy lost MINIMAX_API_KEY when another env field drifted. Reapply now reuses that same-profile route instead of URL substring matching. * fix(provider): preserve saved credentials in pane teammates --------- Co-authored-by: jatmn <the@jat.mn>
…2193) * ' * feat(i18n): add zh-HK locale and fix extensionless imports * fix(i18n): restore original en/vi dictionaries and fix zh-HK exports * fix(i18n): resolve import paths, file duplication, and locale matching * fix(i18n): restore working import paths, localize() API, and session-based locale detection for zh-HK * fix(i18n): use Map for locale lookup to avoid prototype-key collisions * fix(i18n): restore working import paths, localize() API, exports, and session-based locale detection * fix(i18n): correct onboard-github key mismatch in zh-HK dictionary * test(i18n): clear locale env vars in beforeEach to isolate settings-based fallback tests * fix(i18n): preserve command meaning and POSIX locale normalization --------- Co-authored-by: jatmn <the@jat.mn>
…ine#2188) * fix(utils): make hasNodeOption handle --flag=value and quoted NODE_OPTIONS - Match exact flag or flag with =value suffix to support --max-old-space-size=4096, --inspect=0.0.0.0:9229 etc. - Strip surrounding single/double quotes for quoted NODE_OPTIONS tokens - Avoid false positives where --inspect incorrectly matched --inspect-brk - Filter empty tokens from whitespace split Fixes edge case where hasNodeOption('--max-old-space-size') returned false when NODE_OPTIONS contained '--max-old-space-size=4096' * fix(utils): align hasNodeOption with Node lexer, add regression tests - Only strip double quotes (Node's NODE_OPTIONS parser treats single quotes as literal) so NODE_OPTIONS="'--use-system-ca'" correctly returns false and does not incorrectly enable system CA roots (covers 145 vs 302 cert divergence reported in review) - Keep exact / --flag=value matching, double-quoted positives, prefix boundary (--inspect vs --inspect-brk), whitespace filtering - Add table-driven tests for both CA flags (--use-system-ca, --use-openssl-ca) covering exact, equals, double-quoted positive, single-quoted negative, repeated whitespace, and --inspect boundary; restores process.env.NODE_OPTIONS after each case Fixes CodeRabbit docstring coverage and jatmn P2 quote-grammar issue in PR Twigpine#2188 * fix(utils): quote-aware tokenization for hasNodeOption Parse NODE_OPTIONS respecting double quotes so option-like text inside a quoted value does not create spurious flags (e.g. --conditions "foo --use-system-ca bar" must not match --use-system-ca). Verified on Node 22.23.2 that --expose-gc inside a quoted --conditions value does not enable GC while an explicit flag does. Add table-driven cases for this grammar and keep previous single-quote literal + double-quote handling and prefix boundary fixes. Addresses CodeRabbit quote-grammar issue on c93a679 * fix(utils): precise NODE_OPTIONS parsing with escapes and value boundaries - Mirror Node's ParseNodeOptionsEnvVar: handle backslash escapes only inside double quotes, strip quotes, treat single quotes as literal, and correctly handle unterminated strings - Prevent false positives where option values look like flags: --conditions "--use-system-ca" and --conditions "foo \" --use-system-ca bar" no longer count as --use-system-ca - ALWAYS_CONSUMES_NEXT for --conditions/-C (value may be flag-like), VALUE_TAKING for other string options only when next token does not look like flag to avoid --inspect --use-system-ca false negatives - Add table-driven cases for exact --use-system-ca value, escaped-quote value, and equals form; 43 tests now pass Addresses CodeRabbit probe for quoted condition values and escaped quotes * fix(utils): fail closed on malformed NODE_OPTIONS and order CA negations * fix(utils): match Node tab grammar and fail closed on flag-like values * fix(utils): finish required-value validation and audit boolean option roles - Fail closed on missing-at-EOF, empty inline (=), and flag-like separate values for all modeled required-value options, before any CA matching, in both target-before-error and error-before-target directions (Node rejects the whole NODE_OPTIONS stream). - Add missing required-value aliases --loader, --inspect-port, --inspect-publish-uid; --conditions/-C now also fails closed on flag-like values (verified: --conditions -x exits 9). - Remove --experimental-import-meta-resolve from VALUE_TAKING: it is a no-value boolean in Node 22, so a following CA flag stays visible. - Update tests: 99 hasNodeOption cases + CA cache-rebuild coverage for EOF/empty/alias/boolean in both directions. * fix(utils): model Node's NODE_OPTIONS grammar in hasNodeOption Give each modeled option the value role Node gives it, and derive the CA state from one scoped parse instead of three loosely coupled passes. - Option roles are now explicit (boolean / value / inline value): the V8-owned heap flags accept an empty inline value and reject a separated one, while `--stack-trace-limit` requires a non-empty inline value. - The inspector family resolves through its aliases: `--inspect-port` and `--debug-port` take a value, the bare `--inspect`/`--inspect-brk`/ `--inspect-wait` forms are booleans, and the `=`-forms require a non-empty inline value. - Option processing stops at the first argument that is not an option, so an ignored suffix neither enables a CA flag nor creates a required-value error. - `--` and the negation of a non-boolean option are rejected, matching the streams Node refuses. Adds a parity script that derives the expected result from the Node 22 runtime instead of from the implementation's own assumptions. * test(utils): cover accepted heap and inspector forms through the CA selector * fix(utils): harden the NODE_OPTIONS parity check and negated queries - `hasNodeOption('--no-<flag>')` now reports the negation only when the option is actually present. Previously an unrelated option in NODE_OPTIONS made the negated query report `true`. - The parity script no longer accepts a `--flag` value it cannot measure: the Node probe and the matrix both observe `--use-openssl-ca`. - The parity script reports a usage error when an operand is missing or is another flag, instead of silently falling back to a default. - The parity script unsets NODE_EXTRA_CA_CERTS and NODE_USE_SYSTEM_CA in the child environment: both add certificates to the 'default' store and would make the observation signal unusable. - The parity script verifies that the runtime exposes `tls.getCACertificates` (Node >= 22.15) before running, counts timeouts separately, and fails when no case was checked, so a run that measures nothing can no longer report success. * fix(utils): preserve CA flags after supported test option values * fix(utils): resolve test-isolation alias through its value role --------- Co-authored-by: jatmn <the@jat.mn>
…igpine#2297) * feat(minimax): default to M3.1 Flash and refresh text suggestions * fix(minimax): preserve explicit selections and retire stale Zen suggestions * fix(minimax): preserve Ollama discovery and validate cached options * fix(minimax): preserve local discovery and validate picker caches * fix(minimax): preserve gateway-owned model availability --------- Co-authored-by: octo-patch <266937838+octo-patch@users.noreply.github.com> Co-authored-by: jatmn <the@jat.mn>
…gpine#1614) * feat(gemini-vertex): wire env-flag provider routing, selection and model defaults Builds on the native client PR. CLAUDE_CODE_USE_GEMINI_VERTEX=1 now selects the provider end-to-end: - integrations: gemini-vertex vendor descriptor, route/runtime metadata, Gemini model catalog additions, regenerated artifacts - client.ts: route to the native Vertex client on the env flag; the Anthropic-on-Vertex branch is only reached for real Claude-on-Vertex - providerConfig.ts: resolveProviderRequest Vertex model/project/location resolution; getAdditionalModelOptionsCacheScope returns null instead of 'firstParty' for Gemini Vertex sessions (bootstrap stays third-party) - model defaults / deprecation / context windows recognise the category - startup selection: hasProviderSelectionFlags + hasCompleteProviderSelection recognise the flag and its concrete project/model/location vars, so an explicit Vertex env wins over a saved profile like every other provider - drift sweep: auth (is3P, is1PApiCustomer), log, apiPreconnect, agentRouting, spawnUtils teammate forwarding, statusNotice, StartupScreen, onboard-github, providerFlag (--provider gemini-vertex, --model mapping), managedEnvConstants (managed; project ids deliberately NOT in SAFE_ENV_VARS, same policy as ANTHROPIC_VERTEX_PROJECT_ID), providerStartupOverrides, GCP credential prefetch, SDK provider type - durable guard: providerSelectionFlags.ts is the single source of truth for CLAUDE_CODE_USE_* flags; enumerating call sites derive from it and a parity test fails when a new provider flag appears unregistered Profile/UI integration follows in the next PR. Typecheck clean, test:provider 703/703 plus 353 targeted tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): sanitize smuggled thought-signature tool ids on foreign wires The Gemini Vertex client smuggles Gemini thought signatures into tool_use ids (toolu_vertex_x~~sig~~<base64>, up to ~1.8k chars) so they survive session persistence and can be replayed to Vertex, which requires them. Any other provider consuming that history then echoes the giant id and the backend rejects it - e.g. Codex: 400 "Invalid 'input[2].id': string too long" after a /provider switch or --resume from a Vertex session. Fix at every wire boundary, never in the stored history (Vertex keeps reading its signatures): - utils/toolUseIds.ts: shared dependency-free sanitizeToolUseIdForWire - cut at the first character outside [A-Za-z0-9_-] (no legitimate id contains one), cap with a deterministic FNV-1a hash tail so distinct overlong ids stay distinct - codexShim.normalizeToolUseId: sanitize before building fc_/call_ ids (Responses API limit 64) - openaiShim chat/completions: sanitize tool_calls[].id, tool_call_id and the correlation sets (OpenAI limit 40) - normalizeMessagesForAPI: same sanitation for the Anthropic wire, gated on getAPIProvider() !== 'gemini-vertex' Verified E2E on a real Vertex session containing 1.7k-char signed ids: resumed under the Codex profile -> completes (was the 400); resumed under the Vertex profile -> signatures preserved, completes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): address CodeRabbit review on #1614 - onboard-github: clear CLAUDE_CODE_USE_GEMINI_VERTEX in the fast-path (applyGithubOnboardingProcessEnv + buildGithubOnboardingSettingsEnv), so GitHub activation resets Vertex routing on both onboarding paths, not just the device-flow finalize path. Covered by an assertion in the existing test. - messages.normalizeMessages: guard against falsy/non-object slots before the switch reads message.type, matching the default arm's intent. - toolUseIds.sanitizeToolUseIdForWire: keep at least one prefix char if a caller passes maxLength < tail length. - providerFlag.test: add the Gemini Vertex env vars to the cleanup lists. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(gemini-vertex): add profile compatibility so the preset routes natively Addresses jatmn's P1 review on #1614: gemini-vertex is selectable in ProviderManager (it is in ORDERED_PROVIDER_PRESETS), but without a profile compatibility mode a saved profile fell through to the OpenAI-compatible path and was routed through the shim to aiplatform.googleapis.com instead of the native client. Brings the profile env-shaping into this PR (the /provider UI forms and launcher stay in the follow-up UI PR): - providerProfile.ts: 'gemini-vertex' compatibility/profile types, buildGeminiVertexProfileEnv, managed + secret env keys. - providerProfiles.ts: resolveProfileCompatibility mode, profile env application, startup-profile bootstrap, env/profile alignment. - client.ts: route a saved gemini-vertex profile to the native client (gated by shouldRouteToGeminiVertexFromProfile, yields to an explicit selection of another provider). - providerValidation.ts: Vertex project/credential validation messages. So a saved or preset-selected Gemini Vertex profile now sets CLAUDE_CODE_USE_GEMINI_VERTEX + the Vertex project/location/model env and reaches the native client. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): address jatmn review on #1614 (P1 test, P2 isolation, P3 startup) - ProviderManager.test.tsx: add 'Google Vertex AI Gemini' to PRESET_ORDER at its generated position (between Fireworks and Gemini) so navigation selects the intended preset and the ProviderManager suite is green again. (P1) - messages.toolUseIds.test.ts: clear the full provider-selection registry in before/afterEach so getAPIProvider() (which checks FOUNDRY before route resolution) reflects only the test's own flags — proven by passing under CLAUDE_CODE_USE_FOUNDRY=1. (P2) - StartupScreen detectProvider: when no Vertex project resolves, show a /projects/<set GEMINI_VERTEX_PROJECT>/locations/... project-required state instead of a project-less endpoint the native client never calls. (P3) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(gemini-vertex): make tool-id tests suite-order independent - providerSelectionFlags parity: scan only runtime source (skip *.test.*), so a flag a test fixture sets but the code never reads (e.g. a placeholder in discoveryService.test.ts from #1620) doesn't trip the gate. - messages.toolUseIds: mock getAPIProvider directly instead of relying on global env/route state, which other suite tests mutate via mock.module — makes the wire assertions hermetic. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): address CodeRabbit review on the routing PR - providerProfile.ts: honor an explicit shell GEMINI_VERTEX_AUTH_MODE over the persisted/default value (same precedence as model/project). - providerProfiles.ts: validate the active Vertex project in the profile alignment check so a project drift forces re-apply instead of routing to the wrong project (only when the profile pins a project; ADC-resolved stays free). - messages.ts: harden isNotEmptyMessage against malformed user/assistant slots ({ type } / { message: {} }) that previously crashed on message.message.content; add a regression test. - providerConfig.local.test.ts: clear a possibly-leaked CLAUDE_CODE_USE_OPENAI so the Gemini Vertex cache-scope assertion can't pass/fail for the wrong reason. The Critical "typecheck must pass" item is a false positive: both `bun run typecheck` and `bun run typecheck:type-tests` pass cleanly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): address jatmn re-review on #1614 (P2 content guard, P3 /provider summary) - messages.ts: isNotEmptyMessage now narrows content to string | array before the indexed reads, so a non-string/non-array content (object/number) no longer crashes on content[0].type. Added the object/number regression cases. - provider.tsx: buildCurrentProviderSummary now has a Gemini Vertex branch, so a CLAUDE_CODE_USE_GEMINI_VERTEX session reports the Vertex provider/model and the /projects/<project>/locations/<location> endpoint (project-required state when unset) instead of falling through to Anthropic. Added summary tests for both the resolved and project-required cases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): guard normalizeMessages payloads and forward all Vertex project aliases Addresses jatmn's third re-review on #1614: - messages.ts: normalizeMessages now drops malformed assistant/user slots (missing message, or non-array/non-string content) before the branches dereference message.message.content — so progress-derived messages routed through normalizeMessages (queryHelpers) can't crash the pipeline. Added a regression test for the missing/non-array content paths. - spawnUtils.ts: TEAMMATE_ENV_VARS now forwards every project alias Gemini Vertex accepts (adds GCLOUD_PROJECT and GOOGLE_PROJECT_ID), so an env-only teammate launched with any of them keeps the project hint instead of failing Vertex validation/routing. Added a spawn-env coverage test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): drop content arrays with malformed elements in normalizeMessages Addresses CodeRabbit follow-up: the array guard rejected bad content containers but arrays whose elements are null/primitive/typeless or a text block without text still reached block.type / isNotEmptyMessage and could crash the same malformed-history path. Drop the whole slot when any element is malformed, with regression cases. Also assert the normalized output shape in the well-formed cases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(gemini-vertex): use toMatchObject for normalized-shape assertion Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): use saved profile for project/model and validate credentials Addresses jatmn's re-review on #1614: - client.ts: when routing purely from the saved profile (no CLAUDE_CODE_USE_GEMINI_VERTEX flag), fall back to the profile's own project (stored in baseUrl) and model instead of only reading process.env — so a saved gemini-vertex profile no longer throws "project required" or routes an ADC session to the ambient/default project and gemini-2.5-flash. Env still wins when explicitly set. - providerValidation.ts: the Gemini Vertex branch now also resolves the selected credential path (mirroring getAnthropicClient's auth-mode mapping) and fails when none is available, so an access-token profile without GEMINI_ACCESS_TOKEN (or a missing ADC) is caught at setup/startup instead of on the first request. Added a credential-missing regression test and made the project-alias test inject a deterministic credential. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): wire the provider into doctor, /provider GitHub switch, and profile scripts Addresses jatmn's re-review on #1614 (provider-list drift in peripheral tools): - scripts/system-check.ts: doctor now dispatches Gemini Vertex — env checks (project/model/location/credential), reachability + generation readiness show a Vertex-specific skip instead of reporting the Anthropic login flow, currentBaseUrl and serializeSafeEnvSummary cover it. Test-only export + tests for the dispatch and safe-env summary. - ProviderManager.tsx: GitHub activation now clears every selection flag from the central PROVIDER_SELECTION_FLAGS list (including CLAUDE_CODE_USE_GEMINI_VERTEX) in both the settings patch and process.env, so switching to GitHub can't keep routing on a previously selected Vertex profile. - scripts/provider-launch.ts + provider-bootstrap.ts: accept gemini-vertex in the provider allowlists, build a real gemini-vertex profile (instead of a broken openai one), and fail loudly when project/credentials are missing. Verified end-to-end: `doctor:runtime` reports the Vertex provider; the launch script errors clearly without creds; bootstrap saves a gemini-vertex profile. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): address CodeRabbit review on PR-2 (8 findings) - client.ts: re-resolve the Vertex credential per request via getAccessToken so long-lived clients pick up refreshed ADC tokens instead of a stale one captured at construction (Critical); coerce geminiVertexModel to a plain string so the model fallback satisfies tsc and never passes undefined. - toolUseIds.ts: coerce non-string ids before .replace so loosely typed history blocks (number/object/undefined) cannot throw on the wire path. - system-check.ts / provider-launch.ts / provider-bootstrap.ts: resolve the Gemini Vertex project through the shared getGeminiVertexProjectId resolver instead of three drifting OR chains, matching the runtime validator/client contract and adding whitespace normalization. - providerSelectionFlags.test.ts: keep the parity scan non-tautological by excluding the registry and its own test. - bugfixes.test.ts: add a sectionBetween() helper that asserts both anchors resolve before slicing the drift-guard regions (a missing end anchor used to silently widen the slice and pass); fix the StartupScreen detectProvider end anchor it exposed. - deprecation.test.ts: lock the gemini-vertex bypass so Anthropic model retirement dates never leak into the Vertex deprecation banner. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(gemini-vertex): make deprecation bypass test hermetic The env-based variant failed deterministically in the full CI suite: getModelDeprecationWarning() resolves the provider through getAPIProvider(), which reads global env/route state that other suites mutate, so setting CLAUDE_CODE_USE_GEMINI_VERTEX on process.env did not reliably select the gemini-vertex route. Mock getAPIProvider directly (same pattern as messages.toolUseIds.test.ts) so the assertions are order-independent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(provider-flags): exclude all test files from the consumed-flag scan CodeRabbit follow-up: the "registered flags are actually consumed" check only skipped the registry and its own test, so a flag referenced solely by another test fixture would still look consumed. Skip every *.test.* file so the assertion proves real runtime consumption. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): address jatmn review — project/ADC/preset issues - providerProfiles.ts: never persist the Vertex API endpoint URL as the project id. The /provider preset seeds the shared baseUrl field with https://aiplatform.googleapis.com; a URL-shaped value would build invalid /v1/projects/https://.../locations/... requests. Add geminiVertexProjectFromProfile() to drop URL-shaped values and reuse it in both the profile-env builder and the alignment check. - providerUiMetadata.ts: base requiresApiKey on authMode === 'api-key' instead of requiresAuth, so the ADC-only Gemini Vertex preset is no longer pushed through the generic API-key step (ADC profiles have no key to enter). - provider-launch.ts: only require an upfront credential in access-token mode; ADC mode (explicit or default) relies on ambient Google ADC that only the runtime resolver can discover, matching doctor and the native client. - Tests: lock the endpoint-not-project guard (both URL and real-project cases) and the ADC-preset API-key gating; harden providerProfiles env cleanup with the Vertex project/credential keys. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): address CodeRabbit follow-up — credentials, URL guards, tests - providerProfiles.ts / providerProfile.ts: carry the active credential forward when applying a saved Gemini Vertex profile. buildCompatibilityProcessEnv clears every managed key (incl. GEMINI_ACCESS_TOKEN / GOOGLE_APPLICATION_CREDENTIALS), so a profile switch could relaunch unauthed; re-add the access token (access-token mode) or ADC file (adc mode). - providerProfiles.ts (startup-profile path) and provider-bootstrap.ts: extend the endpoint-URL guard to the remaining sites that fed baseUrl into the Vertex project — drop URL-shaped values via geminiVertexProjectFromProfile() and reject http(s) --base-url in the bootstrap script. - Tests: add applyModelFlagFromArgs GEMINI_VERTEX_MODEL coverage; lock access-token / ADC credential preservation on profile apply; isolate every provider-selection flag in the cache-scope test via PROVIDER_SELECTION_FLAGS. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): address jatmn round-3 — saved-profile-only path - messages.ts: sanitize Vertex tool_use ids by the effective provider (env flag OR saved active profile) via new isGeminiVertexEffectiveProvider(), not the env-only getAPIProvider(). On the saved-profile-only route the signed thought-signature ids were stripped before geminiVertexClient could decode them, re-triggering the 400 the smuggling avoids. - client.ts: run the saved-profile project through geminiVertexProjectFromProfile (shared "URL is not a project" sanitizer) instead of using activeProfile.baseUrl raw, so a preset profile can't build /v1/projects/https://.../. - providerProfiles.ts: carry the active credential into the persisted startup profile (buildStartupProfileFromActiveProfile), mirroring the live switch path, so a saved Vertex profile relaunches authenticated. - vendors/gemini-vertex.ts: credentialEnvVars now lists only real credentials (GEMINI_ACCESS_TOKEN / GOOGLE_APPLICATION_CREDENTIALS), not routing/config vars, so generic consumers don't surface the enablement flag/project id as an apiKey; defaultBaseUrl is a project placeholder so the preset routes through the full setup form to collect the project id. - Tests: saved-profile effective-provider preservation; startup-profile credential persistence (access-token + ADC); credentialEnvVars/apiKey gating; preset baseUrl placeholder. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): doctor validates the Vertex credential before launch The Vertex generation-readiness check unconditionally passed ("relying on ambient ADC"), so a fresh env with a project but no resolvable ADC passed the doctor that provider-launch runs as its preflight, then `node bin/openclaude` failed startup validation. Reuse the runtime resolveGeminiCredential() (mapping GEMINI_VERTEX_AUTH_MODE -> GEMINI_AUTH_MODE and clearing API keys, exactly like the native client) and fail when the credential cannot be resolved, so the preflight matches the provider that will actually run. Also clarify the GEMINI_VERTEX_AUTH line that ambient ADC is validated by the readiness check. Tests: readiness passes when a credential resolves and fails when none does. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): address CodeRabbit review (in-scope findings) - provider-bootstrap.ts: the saved-profile summary now prints GEMINI_VERTEX_MODEL for gemini-vertex profiles instead of falling through to a default. - provider-launch.ts: the usage string lists gemini-vertex and no longer repeats mistral, matching the accepted profile arguments. - StartupScreen.ts: detectProvider resolves Vertex model/location/project through the shared getGeminiVertexModel/Location/ProjectId helpers (same contract as runtime/provider summary) instead of manual env reads. - StartupScreen.test.ts: lock the alias fallback (GOOGLE_CLOUD_PROJECT resolves the endpoint project when GEMINI_VERTEX_PROJECT is unset). - client.test.ts: snapshot/clear/restore all three project aliases (GOOGLE_CLOUD_PROJECT, GCLOUD_PROJECT, GOOGLE_PROJECT_ID) so the Vertex tests can't leak host-environment project ids. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): thread saved-profile-only routing through provider identity The saved-profile-only Vertex route (active gemini-vertex profile, no CLAUDE_CODE_USE_GEMINI_VERTEX flag) was only honoured by the API client, so other env-flag-only "is this Vertex?" checks disagreed with it: - routeMetadata.ts: resolveActiveRouteIdFromEnv() now returns 'gemini-vertex' when activeProfileProvider is gemini-vertex and no provider env flag is set, so /model loads the Vertex catalog/discovery for the same session. - providerConfig.ts: getAdditionalModelOptionsCacheScope() scopes a saved gemini-vertex profile as third-party (null) instead of 'firstParty', so bootstrap/model-option traffic is not fetched through the Anthropic path. - provider.tsx: buildCurrentProviderSummary() reports the effective provider so a saved Vertex profile no longer shows "Anthropic"/api.anthropic.com. Tests cover each: saved-profile route resolution (and precedence when a flag is set), the cache-scope fallback, and the provider summary. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): resolve saved Vertex profile before env-only inference CodeRabbit follow-up: resolveActiveRouteIdFromEnv() placed the saved-profile gemini-vertex check after resolveEnvOnlyProviderRouteId(), so an ambient credential-only env (e.g. a stray XAI_API_KEY) won over the active profile and disagreed with getAnthropicClient (which routes saved gemini-vertex profiles unless a conflicting provider flag is set). Move the check above env-only inference, guarded on CLAUDE_CODE_USE_OPENAI to mirror the conflicting-flag rule. Add regressions: profile wins over ambient credential env, and yields to an explicit OpenAI flag. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): accept ADC-derived project; GitHub token over blank key - providerValidation.ts: resolve the Vertex credential before failing on a missing project, and accept credential.projectId (ADC-derived) as the project — mirroring getAnthropicClient, which falls back to the ADC project. A valid ambient-ADC setup with no project env var no longer fails startup validation. - provider-launch.ts: defer the project check to runtime in ADC mode (ADC can supply the project); access-token mode still requires an explicit project. - openaiShim.ts: GitHub hydration uses || (not ??) so a blank exported OPENAI_API_KEY no longer wins over GITHUB_TOKEN/GH_TOKEN and leaves GitHub requests unauthenticated. Pre-existing main behaviour, fixed at maintainer request; matches the Gemini/Mistral branches. - Tests: ADC-derived project acceptance + access-token project-required; GitHub-token fallback when OPENAI_API_KEY is blank. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): whitespace-safe Vertex config + ADC-only bootstrap CodeRabbit follow-up (all on this PR's Vertex code): - provider-bootstrap.ts: only require an explicit project for access-token profiles; ADC can supply credential.projectId at runtime, so ADC-only Vertex profiles can now be created. Pass the resolved authMode through. - providerProfile.ts: sanitize each shell/persisted candidate before applying precedence (model/project/location and the access-token re-add), so a whitespace-only shell value (e.g. GEMINI_VERTEX_PROJECT=' ') can't drop the real persisted config; normalize the auth-mode check too. - providerValidation.ts: trim GEMINI_ACCESS_TOKEN before inferring the default auth mode so a blank token doesn't force access-token over ADC. - Tests: assert the concrete 'openai' route in the precedence case; isolate the GCLOUD_PROJECT/GOOGLE_PROJECT_ID aliases in the env-diagnostics fallback tests; lock the whitespace-over-persisted precedence in buildStartupEnvFromProfile. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): saved profile wins over ambient env in profile-only routing getAnthropicClient's saved-profile-only Vertex branch resolved the project/model from process.env before falling back to the profile, contradicting its own comment. A saved gemini-vertex profile targeting one project/model could route to an ambient GOOGLE_CLOUD_PROJECT / stale GEMINI_VERTEX_MODEL when no CLAUDE_CODE_USE_GEMINI_VERTEX flag is set. profileProject/profileModel are only populated when routedFromProfileOnly, so leading the precedence with them makes the selected profile win in profile-only mode while env still wins for an explicit Vertex selection. Regression locks the precedence (profile project + model win over ambient env). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): align saved-profile route resolution with defined flag rule resolveActiveRouteIdFromEnv's saved-profile gemini-vertex branch only blocked on a *truthy* CLAUDE_CODE_USE_OPENAI, but the client's shouldRouteToGeminiVertexFromProfile treats any *defined* conflicting provider flag as explicit intent. With a saved gemini-vertex profile and CLAUDE_CODE_USE_OPENAI=0, route metadata reported gemini-vertex while the client refused the saved-profile Vertex route, so /model loaded the Vertex catalog for a session whose requests would not use Vertex. Mirror the client exactly via PROVIDER_SELECTION_FLAGS (any defined flag other than the Vertex one blocks the route). Regression covers the falsey OpenAI flag. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): single source of truth for Vertex auth mode + edge cases CodeRabbit follow-up — the recurring "infer auth mode from token" logic was duplicated inline (and inconsistently) across the client/doctor/profile paths, so a token-only or whitespace-only config was classified differently per site. - geminiAuth.ts: add resolveGeminiVertexAuthMode() — explicit GEMINI_VERTEX_AUTH_MODE (case/space-normalized) wins, else a present (sanitized) token implies access-token, else adc. Wire it into providerProfiles.ts (apply + startup builders) and system-check.ts (env check, generation-readiness, safe summary), fixing the token-only case that previously forced adc and dropped the bearer. - system-check.ts: don't hard-fail a missing project in ADC mode (ADC supplies credential.projectId; only access-token mode requires an explicit project); normalize the credential-set boolean with trim instead of ??. - statusNoticeLocalModel.ts: handle the gemini-vertex route id explicitly so a saved-profile Vertex route never resolves to an OPENAI base URL. - Tests: resolveGeminiVertexAuthMode unit cases; token-only auth-mode regressions for apply + startup persistence; deterministic access-token validator stub; exact provider-summary assertions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(gemini-vertex): document the Gemini-on-Vertex provider and setup The PR adds a first-class native Gemini Vertex route but the user-facing docs didn't mention it (repo guidance requires doc updates for provider/setup changes). - README.md: add a "Google Vertex AI (Gemini)" row to the supported-provider table (enable flag, ADC vs access-token auth, project aliases, model/region), and clarify the existing Gemini row is Google AI Studio (API key). - docs/advanced-setup.md: add a "Gemini on Vertex AI" section with ADC and access-token setup, distinct from Claude-on-Vertex, and cross-link it from the Claude-on-Vertex section (which previously only pointed to the API-key Gemini provider for Gemini models). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): add CLAUDE_CODE_USE_GEMINI_VERTEX to isUsing3PServices() The exported isUsing3PServices() helper was missing the Gemini Vertex flag, causing /login //logout to remain visible and auth status to report as first-party when CLAUDE_CODE_USE_GEMINI_VERTEX=1. The local is3P check inside isAnthropicAuthEnabled() was already correct; this aligns the exported helper to match. Adds a dedicated drift guard test targeting isUsing3PServices() directly, since the existing test only covered the separate local is3P block. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(gemini-vertex): address CodeRabbit review findings - providerProfile.ts: use firstSanitizedProviderValue for Google alias vars (GOOGLE_CLOUD_PROJECT, GCLOUD_PROJECT, GOOGLE_PROJECT_ID, GOOGLE_APPLICATION_CREDENTIALS) so a whitespace-only shell value doesn't shadow a real persisted value - providerProfiles.ts: wrap project alias env lookups with trimOrUndefined() to prevent whitespace-only values blocking the fallback chain - providerProfiles.ts: add GOOGLE_APPLICATION_CREDENTIALS and GEMINI_VERTEX_AUTH_MODE=adc to isEnvOnlyProviderProfile so ADC-only configurations are correctly detected - providerValidation.ts: use resolveGeminiVertexAuthMode() instead of duplicating the auth-mode inference inline - provider.tsx: simplify Gemini Vertex branch condition to effectiveRouteId check only (resolveActiveRouteIdFromEnv already covers the env flag) - system-check.ts: add project validation after credential resolution so the doctor catches missing project in ADC mode (mirrors getProviderValidationError) - provider-bootstrap.ts: use resolveGeminiVertexAuthMode() to infer access-token mode when GEMINI_ACCESS_TOKEN is set without explicit GEMINI_VERTEX_AUTH_MODE - docs/advanced-setup.md: clarify that ADC project derivation only applies in ADC mode; access-token mode requires an explicit project env var Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(gemini-vertex): use resolveGeminiVertexAuthMode in provider-launch Replace duplicated inline auth-mode inference with the shared helper, consistent with provider-bootstrap and system-check (CodeRabbit #3417084093). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(gemini-vertex): persist Vertex creds, sanitize project fallback, harden env test Round-3 CodeRabbit findings on #1614: - provider-bootstrap.ts: carry the active credential into the saved profile. PROFILE_ENV_KEYS clears GEMINI_ACCESS_TOKEN / GOOGLE_APPLICATION_CREDENTIALS on relaunch, so an access-token profile must persist the bearer (and fail fast if absent) and an ADC profile must persist the credentials file — otherwise the saved profile relaunches unauthed (thread 3460713116). - providerProfiles.ts: sanitize each Vertex project candidate with trimOrUndefined() + ?? at the startup-profile site so a whitespace-only GEMINI_VERTEX_PROJECT no longer shadows a real GOOGLE_CLOUD_PROJECT / GCLOUD_PROJECT / GOOGLE_PROJECT_ID (thread 3460713146; the compatibility builder site was already fixed in 0501694). - security-hardening.test.ts: assert env classification via runtime isProviderManagedEnvVar() / SAFE_ENV_VARS.has() instead of slicing source text, so the policy test tracks behavior, not formatting (thread 3460713129). Validation: tsc --noEmit, type-tests, security-hardening.test.ts (13/13), knip — all green. providerProfiles getAPIProvider() timeouts are pre-existing (identical on the unmodified tree). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(provider-flags): derive flag-agnostic checks from the registry Address jatmn review 4558518184 on PR #1614: - Export hasAnyTruthyProviderSelectionFlag / hasConflictingProviderFlag from providerSelectionFlags.ts so the flag-agnostic call sites derive from the single source of truth instead of hand-enumerating CLAUDE_CODE_USE_* flags. Adopted by hasExplicitProviderSelection, hasNoExplicitNonOpenAICompatibleProvider, isUsing3PServices, and hasConflictingProviderFlagsForProfile (via an exhaustive activeProviderFlagForCompatibilityMode switch that preserves the github-enterprise -> CLAUDE_CODE_USE_GITHUB mapping and is compile-checked). Per-provider checks (hasConcreteProviderSelection, hasCompleteProviderSelection) stay as-is since they are not flag-agnostic. - Give the two source-scanning parity tests an explicit 30s timeout so they no longer flake under parallel test load. - Add unit tests for both helpers covering every registered flag. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(gemini-vertex): align access-token startup precedence and use isEnvTruthy Address jatmn review 4565330236 on PR #1614: - [P2] providerProfiles.ts: hasCompleteProviderSelection now treats GEMINI_ACCESS_TOKEN (and GEMINI_VERTEX_AUTH_MODE='access-token') as a concrete Gemini Vertex selection, matching the sibling hasConcreteProviderSelection. Without this, CLAUDE_CODE_USE_GEMINI_VERTEX=1 + GEMINI_ACCESS_TOKEN (no project) was not "complete", so a saved profile could override the user's explicit access-token env selection at startup — unlike the ADC path. Regressions added for both the token and the auth-mode signals. - [P3] providerFlag.ts (applyModelFlagFromArgs) and StartupScreen.ts (detectProvider): recognize the Vertex flag via isEnvTruthy() instead of an exact '1'/'true' match, so 'yes'/'on'/whitespace-normalized values get the same model routing and startup banner as the rest of the codebase. Regressions added for both sites. Validation: tsc --noEmit clean; providerFlag, StartupScreen, providerProfiles, client, routeMetadata, compatibility, providerSelectionFlags suites green. Branch rebased on latest main (integrations artifacts regenerated). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(gemini-vertex): lock isUsing3PServices drift guard to the flag registry After the round-4 refactor (jatmn review 4558518184) isUsing3PServices() derives from hasAnyTruthyProviderSelectionFlag() instead of hand-enumerating CLAUDE_CODE_USE_* flags, so the source-text drift guard that scanned the function body for the literal CLAUDE_CODE_USE_GEMINI_VERTEX no longer matched and failed under test:full. Update the guard to lock the real post-refactor guarantee: (1) the function routes through the registry helper, and (2) the Vertex flag is registered in PROVIDER_SELECTION_FLAGS (the single source of truth). This tracks the behavior the refactor introduced instead of regressing to the literal-scan drift it eliminated. Validation: bugfixes.test.ts Gemini Vertex guards 5/5 green; tsc --noEmit clean. Remaining local test:full failures are environmental only (FR-locale Intl thousands separator and the CLI-bundle build artifact) and pass on CI. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(gemini-vertex): wire flag into env-file allowlist, test scrubs and truthiness Addresses maintainer review on PR #1614: - envFile.ts: add CLAUDE_CODE_USE_GEMINI_VERTEX plus GEMINI_VERTEX_* / GOOGLE_APPLICATION_CREDENTIALS / GCLOUD_PROJECT / GOOGLE_PROJECT_ID to ALLOWED_ENV_FILE_KEYS so --provider-env-file .env no longer silently rejects the documented Vertex setup. Adds a PROVIDER_SELECTION_FLAGS-derived parity test plus a full Vertex .env load test. - Add CLAUDE_CODE_USE_GEMINI_VERTEX to the provider env scrub/restore lists in betas, conversationRecovery, model, withRetry and promptCacheBreakDetection tests to prevent cross-test flag leakage. - providerFlag.applyModelFlagFromArgs and StartupScreen.detectProvider: use isEnvTruthy for all provider flags instead of literal '1'/'true' checks, removing the Vertex-only inconsistency. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(gemini-vertex): address CodeRabbit review — drift, malformed-history and credential hardening Verified each CodeRabbit finding against current code; applied the valid ones, skipped two with documented reasons. Provider-flag drift / registry adoption: - auth.ts: isAnthropicAuthEnabled's is3P gate now derives from hasAnyTruthyProviderSelectionFlag() instead of a hand-maintained 8-flag list. - routeMetadata.ts: reuse the shared hasConflictingProviderFlag() helper instead of an inline PROVIDER_SELECTION_FLAGS scan. - withRetry/promptCacheBreakDetection tests: derive env scrub lists from PROVIDER_SELECTION_FLAGS so new flags are covered automatically. - bugfixes drift guard updated to assert the registry derivation rather than the now-removed literal in auth.ts. Credential / managed-env correctness: - managedEnvConstants.ts: treat GOOGLE_APPLICATION_CREDENTIALS as provider-managed so host-managed sessions can't have ADC creds swapped via settings. - providerStartupOverrides.ts: clear the Google ADC/project aliases (GOOGLE_APPLICATION_CREDENTIALS, GOOGLE_CLOUD_PROJECT, GCLOUD_PROJECT, GOOGLE_PROJECT_ID) so a cleared Vertex override can't stay partially active. - agentRouting.ts: clear the Vertex-specific selectors before forcing an OpenAI-compatible override, mirroring the existing GEMINI_MODEL/BASE_URL clears. - providerProfile.ts: treat GEMINI_VERTEX_AUTH_MODE as a concrete Vertex signal in hasConcreteProviderSelection (mirrors hasCompleteProviderSelection). - client.ts: reuse resolveGeminiVertexAuthMode() instead of an inline auth-mode computation (trims whitespace tokens, validates the mode). - system-check.ts: use getGeminiVertexLocation() so doctor output matches runtime. - openaiShim.ts: pick the first non-blank credential so a whitespace-only OPENAI_API_KEY no longer suppresses a real GITHUB_TOKEN/GH_TOKEN (+regression). Malformed-history hardening: - messages.ts: validate the first content block before reading .type/.text; sanitize non-string tool_result.tool_use_id to mirror the assistant id path. - normalizeMessages malformed test: cover falsy slots and unknown message types. - providerConfig.envDiagnostics test: clear CLAUDE_CODE_USE_GITHUB in Vertex tests. Skipped (documented): - log.ts saved-profile gate: importing isGeminiVertexEffectiveProvider would create a log -> providerProfiles -> config -> log cycle (config.ts imports log.ts). - providerProfiles.ts hasCompleteProviderSelection access-token gate: CodeRabbit asked to require a project, which contradicts the maintainer's explicit P2 request to treat a bare access-token Vertex selection as complete. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(gemini-vertex): require project for token-only, gate logs, validate region Addresses jatmn's round-7 review (6 findings): - P1 hasCompleteProviderSelection: an access-token / token-only Vertex env (GEMINI_ACCESS_TOKEN or GEMINI_VERTEX_AUTH_MODE=access-token) with no project is unusable — the native client throws "project is required" since only ADC can derive a project. Such a stale shell no longer counts as complete, so it stops suppressing the saved active profile. Reverses the round-5 tests that encoded the opposite (now corrected). - P1 logError gate: use isGeminiVertexEffectiveProvider() so a saved-profile-only Vertex session (no env flag) gets the same no-error-reporting treatment. - P2 buildCurrentProviderSummary: merge the saved Vertex profile env so the /provider summary shows the saved project/model/location in profile-only mode. - P2 detectProvider: honor profile-only Vertex routing via the effective provider/profile logic (mirrors getAnthropicClient) instead of the raw flag. - P2 global-only models: reject gemini-3.5-flash on a regional GEMINI_VERTEX_ LOCATION via isGlobalOnlyVertexModel() rather than building an unusable URL. - P3 isNotEmptyMessage: reject typeless first content blocks ({ foo: 'bar' }) before the non-text branch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(gemini-vertex): tighten ADC completeness, neutral project placeholder, hermetic tests Addresses CodeRabbit review on 6236de2 (5 findings): - hasCompleteProviderSelection: use the *effective* auth mode (resolveGeminiVertexAuthMode) so a GEMINI_ACCESS_TOKEN alongside a GOOGLE_APPLICATION_CREDENTIALS file (which resolves to access-token and still needs a project) is no longer treated as complete. The credentials file only counts when the effective mode stays ADC. +2 regressions (token+creds, ADC). - StartupScreen / buildCurrentProviderSummary: show a neutral "<ADC-resolved project>" placeholder when an explicit ADC credential is present (the client derives credential.projectId at runtime) instead of the imperative "<set GEMINI_VERTEX_PROJECT>"; keep the actionable hint for access-token/ambient setups. +startup regression. - client.test.ts: add an ADC-only routing regression asserting the generated Vertex URL uses the credential-discovered project (no env/profile project). - Test isolation: snapshot a structuredClone of the global config before mutating it (StartupScreen), and save/restore DISABLE_ERROR_REPORTING + essential-traffic env so the logError gate control case is hermetic in CI. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(gemini-vertex): centralize ADC-resolves-project check, fix placeholder drift Addresses CodeRabbit re-review on 365cc2e (2 findings, same class as the prior raw-vs-effective auth-mode issue): The ADC project placeholder in StartupScreen.detectProvider and buildCurrentProviderSummary checked GOOGLE_APPLICATION_CREDENTIALS directly, so a GEMINI_ACCESS_TOKEN alongside a credentials file (effective mode: access-token, which still needs an explicit project) wrongly showed "<ADC-resolved project>". - Extract geminiVertexAdcWillResolveProject(env) in utils/geminiAuth.ts as the single source of truth: explicit auth_mode=adc, or effective mode resolves to ADC AND a credentials file is present (a bare/ambient flag is not ADC- resolvable). Reused by hasCompleteProviderSelection (replacing the inline copy) and both placeholders, so the gate and the display cannot drift apart. - StartupScreen regression: GEMINI_ACCESS_TOKEN + GOOGLE_APPLICATION_CREDENTIALS with no project keeps the actionable "<set GEMINI_VERTEX_PROJECT>" hint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(gemini-vertex): honor explicit model override and make profile tests order-independent Addresses jatmn's round-7c review (3 findings): - P1 smoke-and-tests isolation: the full suite failed with provider-detection assertions returning "Gemini Vertex" for unrelated providers because providerProfiles.test.ts installs mock.module('./config.js') and bun's mock.restore() does NOT revert mock.module(), so the dead config mock leaked into every later test file in the process. Add an afterAll that re-installs the real config module. StartupScreen.test.ts and log.errorReportingGate.test.ts stop mutating the shared global config and instead spyOn getActiveProviderProfile / isGeminiVertexEffectiveProvider, so their saved-profile-only assertions no longer depend on a possibly-leaked config mock and become order-independent across OSes. - P2 client.ts model precedence: in saved-profile-only Vertex routing an explicit model override (runtime/--model) now wins over the profile's saved model, matching the startup display and the other provider paths; the saved model still wins over ambient GEMINI_VERTEX_MODEL. Env-flag routing keeps an explicit GEMINI_VERTEX_MODEL ahead of a generic model arg. - P2 runtimeMetadata.test.ts: add the no-env-flag case where activeProfileProvider: 'gemini-vertex' alone selects the Vertex catalog limits, protecting the saved-profile-only path this PR relies on. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(test): stop ProviderManager providerProfiles mock from leaking across files Root cause of the failing smoke-and-tests full-suite run (jatmn round-7c P1): ProviderManager.test.tsx installs a partial mock.module('../utils/ providerProfiles.js') via mockProviderProfilesModule() and only calls mock.restore() in afterEach. bun's mock.restore() does NOT revert mock.module(), so the partial stub stayed installed for every later test file in the process. The stub omits exports the gemini-vertex routing now depends on (notably isGeminiVertexEffectiveProvider) and returns getActiveProviderProfile() = null, so once ProviderManager ran before the provider-detection suites the leaked stub turned StartupScreen.detectProvider, the logError reporting gate and client.ts Vertex routing order-dependent — the exact "provider detection returns Gemini Vertex / wrong provider" failures jatmn saw in CI (and that pass in focused runs). main stayed green only because its detectProvider didn't yet read those exports. - Capture the real providerProfiles module before any mock runs and spread it into the stub, so exports the tests don't explicitly override keep their real implementations during ProviderManager's own tests. - Add an afterAll that re-installs the real module, mirroring the restore pattern in providerProfiles.test.ts / messages.toolUseIds.test.ts / user.test.ts. Verified: ProviderManager + StartupScreen + log gate + client run sequentially (the CI failure order) now pass 108/0; ProviderManager alone stays 24/0; tsc clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(gemini-vertex): pin getActiveProviderProfile in StartupScreen detectProvider specs The full-suite smoke-and-tests run failed because StartupScreen.detectProvider specs depended on shared global-config state to decide Gemini Vertex routing. A prior test file can leave a mock.module('./config.js') installed (bun's mock.restore() does not revert mock.module()), silently turning the saveGlobalConfig()-based setup into a no-op and making detection order-dependent. Spy getActiveProviderProfile() in beforeEach to return "no active profile" by default so a leaked gemini-vertex profile can't make these assertions return "Gemini Vertex"; the profile-only spec overrides it for its single case. afterEach restores the spy. detectProvider's real routing still runs. Verified: ProviderManager + StartupScreen + log gate + client (the CI failure order) pass 108/0; all detectProvider-bearing files + polluter pass 145/0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(gemini-vertex): fix provider-profile mock leak with spyOn messages.toolUseIds.test.ts mocked isGeminiVertexEffectiveProvider via mock.module(), then "restored" by re-spreading the `import * as` namespace. Because that namespace is a live binding, after the mock it already reflects the mocked exports, so the restore re-installs the mock instead of reverting it. Bun's mock.module() is also not undone by mock.restore(), so the mocked isGeminiVertexEffectiveProvider (pinned true) leaked into every later test file in the full suite. When that leak reaches StartupScreen.test.ts, detectProvider() reads the leaked isGeminiVertexEffectiveProvider() === true and labels every provider as "Gemini Vertex", which is the source of the smoke-and-tests failures (30 detectProvider specs + the client/log-gate Vertex specs) seen only in CI's Linux file-discovery order. Switch to spyOn(...).mockReturnValue() restored via mockRestore() in afterEach — the same cross-module-binding-safe pattern StartupScreen.test.ts uses for getActiveProviderProfile. spyOn is properly restorable and never calls mock.module, so the file can no longer leak the provider-profile module. Verified: the leak reproduces deterministically on bun 1.3.9 and 1.3.13; the spyOn version restores cleanly on both. * test: stop mock.module restore leaks via static real-module snapshots Two more test files reinstalled their real modules through an `import * as` namespace, which is a live binding: once mock.module() has run, the namespace already reflects the mocked exports, so "restoring" by returning that namespace re-installs the stub. bun's mock.restore() does not revert mock.module() either, so the stub leaks into every later file in the full suite (only manifesting in CI's Linux file-discovery order). - ProviderManager.test.tsx: the leaked partial providerProfiles stub left getActiveProviderProfile() returning null, so client routing could not see a saved Gemini Vertex profile and fell back to the generic OPENAI_MODEL — failing "Gemini Vertex provider uses GEMINI_VERTEX_MODEL instead of generic OPENAI_MODEL". - mcpValidation.test.ts: the leaked log.js stub ({ logError: () => {} }) made logError a no-op, so the logError error-reporting gate recorded nothing and the control case ("records errors when not routing to a cloud provider") failed. Capture a frozen { ...realModule } snapshot at import time, before any mock.module() mutates the live binding, and restore from that snapshot. Verified with isolated repros that the live-binding restore leaks on bun 1.3.9/1.3.13 and the snapshot restore reverts cleanly; both files' own suites still pass. * test(model): reinstall real providerProfiles in afterAll to stop mock leak model.test.tsx mock.module()'d providerProfiles.js with a partial stub (getActiveProviderProfile -> undefined) and only called mock.restore() in afterEach. Since bun's mock.restore() does not revert mock.module(), the stub leaked into later files in the full suite; client.test.ts then could not resolve the saved Gemini Vertex profile and fell back to the generic OPENAI_MODEL, failing "Gemini Vertex provider uses GEMINI_VERTEX_MODEL instead of generic OPENAI_MODEL" (the last remaining smoke-and-tests failure after the earlier ProviderManager/mcpValidation fixes). Capture a frozen { ...realProviderProfilesModule } snapshot at import time and reinstall it via mock.module() in a new afterAll, matching the pattern now used in ProviderManager.test.tsx. model.test.tsx's own 37 tests still pass. * test(client): isolate GEMINI_VERTEX_AUTH_MODE across Gemini Vertex specs The "ADC-only Vertex routing" test set process.env.GEMINI_VERTEX_AUTH_MODE='adc' but the suite's beforeEach/afterEach never cleared or restored it (only GEMINI_AUTH_MODE was managed). The very next test, "Gemini Vertex provider uses GEMINI_VERTEX_MODEL instead of generic OPENAI_MODEL", then inherited GEMINI_VERTEX_AUTH_MODE='adc'. resolveGeminiVertexAuthMode() treats an explicit 'adc' as authoritative over a present GEMINI_ACCESS_TOKEN, so resolveGeminiCredential took the ADC path, found no ADC credentials, and threw "Gemini Vertex authentication requires GEMINI_ACCESS_TOKEN or Google ADC credentials" — the last remaining smoke-and-tests failure on CI (on Windows the same leak instead hangs the ADC credential lookup until the 5s test timeout). Capture, clear (beforeEach) and restore (afterEach) GEMINI_VERTEX_AUTH_MODE the same way GEMINI_AUTH_MODE is already handled, so each spec starts from a clean auth mode. Verified: the model spec now passes even when the ADC spec runs immediately before it. * test(client): assert explicit model overrides saved Vertex profile model Covers the saved-profile-only Gemini Vertex precedence contract at the client boundary: with CLAUDE_CODE_USE_GEMINI_VERTEX unset and an active gemini-vertex profile, an explicit model passed to getAnthropicClient() must drive the native Vertex request instead of the profile's saved model (addresses CodeRabbit's requested regression). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(status): render Gemini Vertex provider route in /status getAPIProvider() can return 'gemini-vertex' (env flag or saved-profile routing), but /status had no API_PROVIDER_LABELS entry or render branch, so it showed an 'API provider' row with an undefined value and no endpoint/model. Add the label and a Vertex branch mirroring the /provider summary: project/location endpoint, model, and credential (access token vs Google ADC). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(gemini-vertex): stop saved Vertex profile hijacking an explicit OpenAI flag resolveActiveRouteIdFromEnv's saved-profile guard already rejects a gemini-vertex profile when a conflicting provider flag is defined, but the two generic profile-route fallbacks below it re-resolved the profile provider without re-checking that conflict. With CLAUDE_CODE_USE_OPENAI set, resolveProfileRoute('gemini-vertex') returned 'gemini-vertex', so an explicit OpenAI flag (truthy) yielded 'gemini-vertex' instead of 'openai', and a defined-but-falsey flag (=0) yielded 'gemini-vertex' instead of falling back to 'anthropic'. gemini-vertex is a native (non-OpenAI-compatible) route reached only via its explicit flag or the guarded saved-profile branch, so exclude it from both generic fallbacks. Mirrors shouldRouteToGeminiVertexFromProfile. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(gemini-vertex): preserve routing and privacy on current main * fix(gemini-vertex): address review routing and credential findings * fix(gemini-vertex): preserve routing and request contracts * fix(gemini-vertex): restore saved configuration and routing precedence Restore matching active startup settings before marking plural profiles applied, reuse saved Vertex bearers, and honor complete custom Anthropic selection. Align native model and source-launch precedence, normalize doctor location, and restore diagnostics test environment state with regression coverage. * fix(gemini-vertex): preserve credential identity and routing boundaries * fix: isolate Vertex credentials when switching to keyless profiles * fix(gemini-vertex): reject global-only models on regional locations before the first request Startup validation and doctor used a narrower Vertex readiness check than the native client, so a global-only model with a regional location looked ready and then failed on the first call. Share one location rule across the client, validator, and doctor. * fix(gemini-vertex): complete model fallbacks and redact shared diagnostics * test(gemini-vertex): isolate default-model regression environment * test(provider): remove duplicate environment snapshot import * fix(gemini-vertex): preserve profile identity and native payloads Keep valid project IDs and ADC locators during native profile shaping, honor prospective user-profile ADC files, and reject implicit workspace filename adoption before credential validation. Preserve image/PDF bytes at native wire conversion and resolve installed diagnostic reports from native fields. * fix(gemini-vertex): validate native URL components and profile provenance Reject endpoint and resource-path injection at native request and validation boundaries while preserving project numbers and domain-scoped identifiers. Keep the loaded profile's source through startup so concurrent user-profile writes cannot admit a workspace ADC filename. * fix(gemini-vertex): align validation and saved profile switching * fix(gemini-vertex): restore original env-only routing scope * fix(gemini-vertex): preserve ambient ADC selection and tool identities --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: jatmn <the@jat.mn> Co-authored-by: jatmn <12479882+jatmn@users.noreply.github.com>
* commit1 * fix(attachments): resolve reminder thresholds per field and validate env strictly - Resolve turnsSinceWrite/turnsBetweenReminders independently with per-field precedence (settings -> env -> default) so partial todoReminder settings are honored instead of discarded. - Parse OPENCLAUDE_TODO_REMINDER_* via whole-string positive-safe-integer validation so suffixed, fractional, scientific, and oversized values fall back to the default instead of being truncated. - Degrade invalid todoReminder thresholds or a malformed object to undefined in the settings schema so one bad value cannot invalidate the entire settings source. - Replace the copy-pasted JSDoc above getEnvNumber with an accurate one. - Extend focused tests: settings precedence, partial/mixed settings, malformed env values, and schema fallback behavior. * test(attachments): isolate reminder settings and cover consumer boundaries --------- Co-authored-by: jatmn <the@jat.mn>
…igpine#2066) (Twigpine#2171) * fix(opencode): promote bare Codex alias launches to the Codex backend endpoint * fix(opencode): normalize [1m] tag before Codex alias classification (Twigpine#2171) * fix(opencode): centralize [1m] tag normalization inside isCodexAlias (Twigpine#2171) * chore: remove pnpm lockfile (repo uses bun) * fix(provider): share Codex model identity parsing for tagged routes --------- Co-authored-by: jatmn <the@jat.mn>
…ine#2255) (Twigpine#2256) * fix(launcher): tolerate missing bin helpers on distro installs (Twigpine#2255) * test(launcher): add heap fallback/canonical parity plus robust siblingless fixture * test(launcher): isolate heap env in launcher fixture --------- Co-authored-by: Gravirei <gravirei@users.noreply.github.com>
…tor (Twigpine#2244) * fix(sdk): wire options.hooks callbacks into the in-process hook executor QueryOptions.hooks was declared but never consumed by the in-process query() path — SDK consumers passing PreToolUse/PostToolUse/Stop callbacks got zero invocations, silently. The kernel hook executor already supports callback-form hooks: utils/hooks.ts merges getRegisteredHooks() into every hook event, and bootstrap/state.ts exposes registerHookCallbacks() for exactly this purpose (the CLI path wires the same mechanism via the control protocol in print.ts). This registers options.hooks (converted to HookCallbackMatcher form) during query() setup so in-process SDK consumers get the same hook semantics as CLI users: - PreToolUse deny/ask decisions now gate tool execution - Stop hooks fire when the agent attempts to finish - All HookEvents flow through the standard executor Verified against dist/sdk.mjs with GLM gateway end-to-end: - PreToolUse callback fires and its deny decision blocks the tool call - Stop callback fires on result Co-Authored-By: Claude <noreply@anthropic.com> * fix(sdk): isolate query hook callbacks and clean up their lifecycle Keep options.hooks on each query's existing async context instead of registering callbacks globally. Release callbacks on close and terminal iteration, and cover matching, denial output, concurrent queries, and cleanup without affecting process-level hooks. * fix(sdk): validate hook matchers and preserve callback arguments Skip malformed matcher values before registration without broadening them to wildcards. Preserve omitted, undefined, and empty matchers, and forward the executor's full callback tuple including cancellation and state access. Cover both boundaries through the SDK query lifecycle and real hook executor, including live abort propagation and valid optional matchers. * test(sdk): isolate query hook fixtures from persisted settings --------- Co-authored-by: renjie.yin <renjie.yin@ronds.com.cn> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: jatmn <the@jat.mn>
* fix(hooks): reject unsupported command hook args * fix(settings): preserve forbidden hook args diagnostics --------- Co-authored-by: jatmn <the@jat.mn>
Add an `aimlapi` web search provider that runs the AI/ML API Toolbox tool `web/search` (POST https://tools.aimlapi.com/v1/tools/run). It reuses the key the AI/ML API model provider already has (AIMLAPI_API_KEY, or the route's OPENAI_API_KEY fallback while OPENAI_BASE_URL is the canonical AI/ML API endpoint), so a user on that provider has nothing to paste. Every run is billed to the user's AI/ML API balance, so the backend is explicit-only: it is not in the auto chain, and an AIMLAPI_API_KEY alone never routes searches through it. Each run carries max_cost_usd 0.025. - Map output.results[] (url/title/snippet/published_at) onto search hits, prefixing the snippet with the publication date. - Send allowed_domains as include_domains; apply both domain lists to the results like the other backends (the tool has no exclude field). - Poll GET /v1/tools/runs/{id} after a 202 until the run ends, within the web search timeout; report is_error runs and HTTP error codes. - Send the key and the X-AIMLAPI-Source / X-AIMLAPI-Partner-ID attribution headers only to tools.aimlapi.com, checked by exact hostname. - /search aimlapi selects it only when the key is present and states the billing; /search key|remove-key aimlapi refuse, because saving AIMLAPI_API_KEY to the global env would also switch the model provider. - Document the backend next to the other search providers.
…olbox An aimlapi provider profile that points at the user's own proxy exports the proxy's credential as AIMLAPI_API_KEY, with CLAUDE_CODE_PROVIDER_ROUTE_ID=aimlapi and OPENAI_BASE_URL set to the proxy. The search provider returned AIMLAPI_API_KEY unconditionally, so /search aimlapi would have sent that proxy credential to tools.aimlapi.com. Resolve no key in that case: the backend reports itself as not configured, /search aimlapi refuses the selection, and a search fails with the not-configured error before any request is made.
| assertNoLineBreak(command); | ||
| // launchCommand is trusted user configuration (and can include fixed flags). | ||
| // Dynamic CLI values must remain arguments, including through npm's %* shim. | ||
| return `"${[command, ...args.map(quoteWindowsArgument)].join(' ')}"`; |
| assertNoLineBreak(command); | ||
| // launchCommand is trusted user configuration (and can include fixed flags). | ||
| // Dynamic CLI values must remain arguments, including through npm's %* shim. | ||
| return `"${[command, ...args.map(quoteWindowsArgument)].join(' ')}"`; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CI validation run for the upstream PR to Twigpine/openclaude. Not for merge; will be closed once PR Checks finish.