mirror of
https://github.com/Gitlawb/openclaude.git
synced 2026-08-24 10:14:19 -05:00
1f20e92c2ef0a164b63c24d0515479113fdbe6b5
105
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a32781537f |
fix(query): bound per-turn latency growth in long REPL sessions (#1949) (#1952)
* fix(query): bound per-turn latency growth in long REPL sessions (#1949) Addresses the progressive latency regression where consecutive prompts in a single session grow non-linearly (2nd prompt ~10s, 3rd 10+ min) due to unbounded message accumulation with no proactive compaction and no per-prompt turn cap on the main thread. - Cap the interactive REPL main thread at 50 turns per prompt (DEFAULT_REPL_MAX_TURNS). Headless/print mode and the SDK are unchanged (--max-turns flag / SDK callers still control it), preserving the SDK API contract. - Default maxMessagesCompactionThreshold to '200' so message-count compaction runs well before the context window fills, instead of 'off'. - Lower the auto-compact threshold buffer from 13k -> 30k so compaction fires earlier with less accumulated history. The effective-context floor buffer is kept at 13k and getAutoCompactThreshold() falls back to it for small-context models, so the threshold can never go negative (no #635 regression). Test updates: isolate the hard-cap override test from the new 200-message default, and correct an outdated constant reference in the autoCompact test. Co-Authored-By: Claude <noreply@anthropic.com> * fix(query): repair REPL latency guard * fix(query): cover resume and default guard paths * fix(query): enforce cap across interactive paths * docs(compaction): clarify disabled message limits * fix(query): retain explicit message thresholds * fix(query): enforce explicit threshold recovery * fix(query): honor legacy active-message limit * fix(doctor): report effective message compaction limit * fix(config): share message threshold validation * test(doctor): cover disabled message compaction * fix(compact): preserve latency guard coverage * test(repl): exercise turn cap defaults * fix(compact): honor disabled default message guard * fix(swarm): honor disabled auto compaction --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
e204d5ad36 |
feat(doctor): add WebSearch backend diagnostics (#1884)
* feat(doctor): add WebSearch backend diagnostics * fix(doctor): tighten Firecrawl cloud URL diagnostics * fix(firecrawl): align cloud URL detection * test(websearch): stabilize Brave timeout assertion * fix(firecrawl): handle bare cloud host casing * fix(doctor): align WebSearch auto diagnostics with fallback * fix(doctor): align custom preset diagnostics |
||
|
|
aa936cda11 |
Centralize credential redaction in src/utils/redaction.ts + channel gate tests (#1711)
* feat(utils): add centralized redaction utility Single source of truth for stripping API keys, tokens, and other secrets from strings and JSON. Provider env-var coverage is generated from getKnownProviderSecretEnvKeys() so adding a new provider cannot silently create an unredacted path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(Feedback): import redactSensitiveInfo from utils Remove the inline 40-line regex implementation in favor of the centralized redaction utility, eliminating drift between Feedback and the transcript share path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(submitTranscriptShare): import redactSensitiveInfo from utils Update import path to point at the centralized utility instead of the Feedback component. Removes the implicit re-export contract that required Feedback.tsx to keep redactSensitiveInfo exported. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(log,debug): redact secrets in default error and debug output Wire the centralized redaction utility into logError and logForDebugging so secrets cannot leak into in-memory error logs or the debug file even if a caller forgets to pass through redactSensitiveInfo. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(api/logging): redact error message in logAPIError Apply the centralized redaction utility to the error string passed to logEvent so analytics events cannot capture unredacted credentials from upstream API failures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve merge conflict from upstream sync Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(channelNotification): allow null in getEffectiveChannelAllowlist signature ChannelsNotice.tsx passes getSubscriptionType() which returns SubscriptionType | null, but the signature only accepted string | undefined. Widen to string | null so the call site typechecks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(redaction): exclude specific token fields from redaction process * fix(redaction): lower AIza minimum length to {10,} Real GCP/Gemini keys are 39 chars total (4 prefix + 35 suffix), but the {35} suffix bound missed short tokens like 'AIzaSyDUMMY-secret-token' (21 chars after AIza). Lower to {10,} to match the diagnostics module and catch any AIza-shaped value. Same precision trade-off the diagnostics redaction makes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(redaction,log): address review feedback - Drop quotes from ANTHROPIC/OPENAI key negative lookarounds so JSON-shaped values like "sk-ant-..." redact. - Add private_key pattern to GENERIC_HEADER_FIELD_PATTERN and privatekey to SENSITIVE_FIELD_SUBSTRINGS. - logError now builds a sanitized Error (redacted message + stack) before passing to the sink and queue, not just the in-memory log. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(redaction): consolidate into single module + add channel gate tests Address the three P2 review findings on the central-redaction PR: [1] Consolidate four redaction modules into src/utils/redaction.ts. Previously lived in: - src/utils/redaction.ts (logs/bug reports/transcript shares) - src/utils/urlRedaction.ts (URL display) - src/utils/statusRedaction.ts (/status output) - src/utils/diagnostics/redaction.ts (doctor reports) The four surfaces share the same regex set / credential lists but had drifted into separate per-domain files. Merged into one module; deleted the three shim files. Updated six direct consumers (openaiShim.ts, ProviderManager.tsx, status.tsx, requestSizeBreakdown.ts, diagnostics/issueReport.ts, scripts/system-check.ts) and three test files to import from redaction.js. [2] Add gateChannelServer() test coverage. src/services/mcp/channelNotification.test.ts: 13 cases for the six gate paths (capability, runtime, session, marketplace, plugin allowlist, server-entry dev) plus end-to-end register. Mocks channelAllowlist.js (GrowthBook-backed) so tests stay independent of feature-flag state. [3] Apply jsonRedactor in transcript share. src/components/FeedbackSurvey/submitTranscriptShare.ts now does redactSensitiveInfo(jsonStringify(data, jsonRedactor)) — the key-aware redaction applies during serialization, and the text pass stays as defense in depth for free-form fields. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test(channelNotification): cover findChannelEntry multi-candidate branch Regression test for the disambiguation path in `findChannelEntry` (channelNotification.ts:201-230): when two same-name plugin entries exist in the allowed-channels list with different marketplaces, `pluginSource` must select the matching entry before the marketplace and allowlist gates evaluate. Without this branch being exercised, the gate could lock onto whichever entry sorts first and either skip the user's real installation or wrongly authorize a typo-squatted one. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(redaction): align URL fallback regex + add path-prefix boundary check Two related redaction correctness fixes: [1] URL fallback regex covers the same parameter set as the primary path. The malformed-URL branch in `redactUrlForDisplay` previously had a hand-rolled alternation of credential parameter names that could drift behind `SENSITIVE_URL_QUERY_PARAM_TOKENS`. New `MALFORMED_URL_PARAM_PATTERN` derives from that same list, so the two paths can never diverge. Tests cover the full credential set (`api_key`, `access_token`, `refresh_token`, `signature`, `sig`, `secret`, `password`, `apikey`) plus a non-sensitive `model` that must survive. [2] `redactPathForStatus` now requires a path-separator boundary after the home prefix. The previous `startsWith` check matched `/home/alice2/project` against `/home/alice` and emitted `~2/project`. The fix requires the character at `normalizedCandidate.length` to be `/` or `\` so `alice` no longer matches `alice2` or `alice.bak`. Test pins the false-positive paths and the true-positive (`/home/alice/project` → `~/project`). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(channel,redaction): restore dev-channel warning + align URL fallback Two related security fixes: [1] Restore DevChannelsDialog when --dangerously-load-development-channels is passed and the channels feature is enabled. The previous logic skipped the dialog when OAuth was absent, which was safe only while gateChannelServer() blocked no-OAuth sessions. With the OAuth/org- policy gates removed in this PR, an API-key session could pass the flag, skip the warning, and still register the dev channel. The only remaining skip is the genuinely-disabled feature case (`!isChannelsEnabled()`), where the dialog is moot. [2] Malformed-URL fallback now uses the same substring predicate as the primary `URL` parser path. The previous regex matched only exact parameter names (`api_key=`, `access_token=`, …), so `my_api_key=SECRET` and `x_access_token=TOKEN` slipped through unchanged even though `shouldRedactUrlQueryParam` flags them as sensitive. New `redactMalformedQuery` walks the query pairs and runs the predicate on each key. Three new tests cover prefixed keys, non-sensitive keys, and fragment preservation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(redaction): widen key boundary class + tighten dev-channel comment Two small follow-ups from the latest CodeRabbit review: [1] Boundary class on key-prefix patterns widened from `[A-Za-z0-9]` to `[A-Za-z0-9_-]` so a raw key embedded in a JSON string value (`"sk-ant-..."`, `"AIza..."`, `"ghp_..."`, etc.) is still caught. Quotes act as delimiters, not blockers — the previous boundary class was correct for unquoted text but let quoted keys slip through. [2] Tighten the dev-channel dialog comment in interactiveHelpers.tsx so future readers don't misread the security boundary. Skip condition is `isChannelsEnabled()` (the channels feature flag gate), not KAIROS / KAIROS_CHANNELS as the previous wording implied. Comment now matches the code. Skipped with reason: - getEffectiveChannelAllowlist divergence from gateChannelServer allowlist — by design; the effective-list override is a UI hint consumed only by ChannelsNotice for the org-override indicator. Trust boundary is enforced by gateChannelServer() reading the hardcoded ledger. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(redaction,channel): address P1/P2 review findings P1 - malformed URL fallback secrets: - Decode percent-encoded query param keys via decodeURIComponent() before applying shouldRedactUrlQueryParam (e.g. %74oken -> token) - Stop userinfo regex at ? and # delimiters to avoid consuming query params when matching @ signs in email addresses or fragment delimiters P2 - channel notice/gate allowlist sync: - Remove org override path from getEffectiveChannelAllowlist() so ChannelsNotice startup guidance uses the same ledger source as gateChannelServer's runtime enforcement - Simplify ChannelsNotice to drop unused sub/policy params and the source === 'org' conditional * fix(channel): apply marketplace matching to permission relays, remove stale OAuth/org-policy blockers, add dev-channel dialog coverage P1: Thread runtime pluginSource through filterPermissionRelayClients so findChannelEntry disambiguates same-name plugin entries from different marketplaces before sending permission request previews. P2: Remove stale noAuth and policyBlocked branches from ChannelsNotice that would render '--channels ignored' before reaching the listening message, confusing non-OAuth users. P2: Add test coverage that mocks isChannelsEnabled() both true and false, verifies DevChannelsDialog appears with onAccept marking entries dev:true in the enabled case, and verifies the disabled branch registers entries directly without dialog. * test(dev-channel): clarify count assertion comment + add afterEach with mock.restore() * fix(channel): mirror marketplace gate in permission relay + restore mock Two follow-ups from the latest review: [1] Permission relay predicate no longer relies on findChannelEntry alone. After resolving the entry, the predicate now requires a runtime pluginSource whose marketplace matches the session entry's marketplace for plugin-kind entries — mirroring the gateChannelServer check at channelNotification.ts:303-312. A `plugin:slack@evilcorp` client whose session allows `plugin:slack@anthropic` is now rejected instead of piggy-backing on the approved entry to receive permission-request previews. Server-kind entries still match on bare name. [2] bugfixes.test.ts now re-registers the real channelAllowlist module in afterEach via a cache-busted reference, so the neighbor channelNotification.test.ts continues to import getChannelAllowlist after this suite runs. mock.restore() does not clear module-level mock.module() overrides in bun (the registry is process-global). Pattern matches compact.test.ts:27-36. Also expanded the dev-map count comment in bugfixes.test.ts to document the security invariant (a dev entry must never be confused with a production entry in the allowlist check) per CodeRabbit's request. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(redaction): consolidate into single module + add channel gate tests Address the three P2 review findings on the central-redaction PR: [1] Consolidate four redaction modules into src/utils/redaction.ts. Previously lived in: - src/utils/redaction.ts (logs/bug reports/transcript shares) - src/utils/urlRedaction.ts (URL display) - src/utils/statusRedaction.ts (/status output) - src/utils/diagnostics/redaction.ts (doctor reports) The four surfaces share the same regex set / credential lists but had drifted into separate per-domain files. Merged into one module; deleted the three shim files. Updated six direct consumers (openaiShim.ts, ProviderManager.tsx, status.tsx, requestSizeBreakdown.ts, diagnostics/issueReport.ts, scripts/system-check.ts) and three test files to import from redaction.js. [2] Add gateChannelServer() test coverage. src/services/mcp/channelNotification.test.ts: 13 cases for the six gate paths (capability, runtime, session, marketplace, plugin allowlist, server-entry dev) plus end-to-end register. Mocks channelAllowlist.js (GrowthBook-backed) so tests stay independent of feature-flag state. [3] Apply jsonRedactor in transcript share. src/components/FeedbackSurvey/submitTranscriptShare.ts now does redactSensitiveInfo(jsonStringify(data, jsonRedactor)) — the key-aware redaction applies during serialization, and the text pass stays as defense in depth for free-form fields. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): align malformed URL fragment expectation with preservation behavior * fix: address review findings P1 and P2 [P1] Enforce dev flag for server-kind entries in permission relay predicate, matching gateChannelServer() behavior. Add coverage for both dev and non-dev server relay paths. [P2] Drop fragments in malformed URL fallback (redactMalformedQuery) to match the valid-URL path, preventing credential leaks via fragment-carried tokens. Update existing tests and add regression for fragment-only malformed URLs. * test(relay): add plugin-kind marketplace regression tests * fix: address review findings P1 and P2 [P1] Add PEM private key redaction pattern to redactSensitiveInfo so multi-line PEM values are fully consumed instead of leaking after the first whitespace. Add [ to generic header pattern's value exclusion set to prevent re-consuming [REDACTED] tokens. [P2] Use truthy check (Boolean()) for claude/channel capability in filterPermissionRelayClients to match gateChannelServer's behavior, rejecting explicit false capabilities. * fix(debug): redact before JSON-stringify multiline messages Reorder logForDebugging so redactSensitiveInfo runs before jsonStringify, ensuring PEM/private-key patterns match the raw (unescaped) message text rather than the JSON-encoded form where colons and quotes are escaped. * test(debug): add end-to-end regression for multiline PEM redaction in logForDebugging Uses mock.module on process.js to capture stderr output and exercises the full logForDebugging path with multiline PEM private_key input, verifying the redact-before-JSON-stringify ordering produces redacted output. * fix(test): preserve original process.env.DEBUG and process.argv in logForDebugging test hooks * fix: address PR review findings P1-P3/P5-P7 - P1: clear isDebugMode/isDebugToStdErr memoize caches in test beforeEach + cache-busting query param for fresh debug.ts imports - P2: restore mock.module afterAll instead of leaking mock + mutate err in-place in logError to preserve name/cause - P3: post-processing regex absorbs trailing bracket content after [REDACTED] - P5: (was P3) expand jsonRedactor EXCLUDED_KEYS for maxTokens etc. - P7: capture HOME/USERPROFILE per-test instead of at module scope * fix: address CodeRabbit review findings - interactiveHelpers.tsx: update dev-channel comment — OAuth/org-policy gates removed from gateChannelServer(), org policy is not enforced - channelNotification.test.ts: add afterAll mock.restore() to clean up process-global channelAllowlist.js mock - channelNotification.ts: fix comments — isChannelsEnabled() still reads tengu_harbor, not always true - log.ts: sanitize err.message and err.stack separately so message doesn't get replaced with full stack trace - redaction.ts: add 'i' flag to redactHomePath regex for Windows case-insensitive path matching * fix: address second review round - interactiveHandler.ts: [P2] redact input_preview via redactSensitiveInfo before sending to channel servers - log.ts: [P3] copy error via Object.assign(Object.create(err), err) before sanitizing instead of mutating in-place * fix: address CodeRabbit second round - channelPermissions.ts: redact before truncate in truncateForPreview so partial credentials don't leak at the 200-char boundary - interactiveHandler.ts: remove outer redactSensitiveInfo — now handled inside truncateForPreview - log.ts: derive errorInfo.error from already-sanitized sanitizedErr; fix Object.assign comment to accurately describe what is copies * fix: improve permission relay client filtering and enhance redaction functions * fix: address third review round (P1, P2, P3) - P1: update test expectations for [REDACTED_*] output format - P2: add total_tokens, prompt_tokens, completion_tokens to jsonRedactor EXCLUDED_KEYS - P3: remove ) and } from GENERIC_HEADER_FIELD_PATTERN value capture to prevent content leak after embedded parens - Fix buildKnownEnvVarPattern capture group to preserve env-var separator ([REDACTED]) - Add & to GENERIC_CREDENTIAL_ENV_PATTERN value exclusion to prevent URL query over-consumption * fix: address latest reviewer P2/P3 findings (errorLogSink redaction, X_API_KEY/AUTHORIZATION patterns, regression tests) * fix: address reviewer P1/P2 — bracketed values and multi-word header values - P1: Remove and from value captures in X_API_KEY_PATTERN, AUTHORIZATION_PATTERN, GENERIC_HEADER_FIELD_PATTERN, GENERIC_CREDENTIAL_ENV_PATTERN so bracketed secrets like are fully redacted instead of passing through unchanged. - P2: Widen header-style value captures to include spaces by removing from exclusions, using as delimiter (stops at newlines and URL query separators). Fixes multi-word leaks: , , , . - GENERIC_CREDENTIAL_ENV_PATTERN: add to negative lookbehind to prevent matching inside when the latter is already redacted. - GENERIC_HEADER_FIELD_PATTERN replacer: skip values starting with to preserve specific labels from earlier passes. - Add 7 regression tests covering both finding categories. * fix: address reviewer findings P1-P4 P1: Custom enumerable error properties now redacted in log.ts logError iterates all own enumerable properties on the original error and applies redactSensitiveInfo to string values and jsonRedactor to object values, preventing credential-bearing custom fields from leaking through the sanitized error. Regression tests added in log.test.ts. P2: Soften single-source-of-truth claim; migrate easy call sites Header comment in redaction.ts updated to acknowledge that specialized scanners (secretScanner.ts, xaa.ts) are intentional exceptions. src/services/mcp/client.ts and src/services/mcp/auth.ts now use jsonRedactor for header redaction instead of ad-hoc key checks. P3: Fix mock.restore cleanup in channelNotification.test.ts Cache-bust the real channelAllowlist module at describe-entry and re-register it in afterAll, following the pattern from bugfixes.test.ts. mock.restore alone does not clear mock.module overrides in Bun. P4: Remove unused ChannelGateResult kinds Removed 'auth' and 'policy' from the skip kind union and removed corresponding dead branches in useManageMCPConnections.ts. * fix: extract sanitizeError() to fix CI test fragility The logError tests were failing in CI due to parallel test execution racing on the module-level errorLogSink singleton. Extract the inline sanitization logic into an exported sanitizeError() helper and test that directly — it's pure, has no env-var or sink dependencies, and doesn't interact with shared mutable state. * fix: use Object.getPrototypeOf(err) instead of err as prototype in sanitizeError Object.create(err) sets the original error instance as the prototype of the sanitized copy, leaking non-enumerable own properties through the prototype chain. Use Object.getPrototypeOf(err) instead so the prototype is the error constructor's prototype (e.g. TypeError.prototype), preserving instanceof checks without exposing the original error's non-enumerable fields. Add a regression test verifying non-enumerable properties do not leak and update the prototype-chain test to assert Object.getPrototypeOf result. * fix: apply key-aware redaction and fail closed on non-serializable error props - String properties: use jsonRedactor(key, value) instead of redactSensitiveInfo(value) so keys like apiKey with innocuous values (e.g. 'my-key') are still caught via SENSITIVE_FIELD_SUBSTRINGS. - Object path: catch now replaces non-serializable/circular references with '[REDACTED]' instead of leaving the original object reference. - Add 2 regression tests for key-aware redaction and fail-closed behavior. * Update src/utils/log.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix: redact bare auth header keys in JSON/header objects - Add 'auth' to SENSITIVE_FIELD_SUBSTRINGS in src/utils/redaction.ts:109 to match URL/diagnostic redactors treatment of auth - Add regression test for bare auth header keys in src/utils/diagnostics/redaction.test.ts:88 Co-authored-by: openhands <openhands@all-hands.dev> * fix: narrow auth matching, redact nested transcript JSONL, fix channel skip message * fix: address CodeRabbit nits — comment, hint, JSONL fallback redaction * fix: key-aware malformed JSONL fallback and auth/x-auth in free-form text * fix: strengthen redactJsonLines trailing rest redaction and auth test assertions * fix: preserve non-JSON prefix in redactJsonLines fallback and redact it * fix: tighten redactJsonLines prefix test to exact output assertion * fix: redact MCP log sink payloads and errorStr before writing to disk * fix: address P1 findings — URL #-in-password, ;-delimited query params, split channel trust-boundary - Allow in URL userinfo password on malformed-URL fallback path (new URL() fails when password contains fragment delimiter). - Redact -delimited sensitive query params by splitting on both & and ; in redactMalformedQuery, plus redactSemicolonQueryParams post-processor for valid-URL output. - Restore channelNotification.ts to upstream/main to fully split OAuth/org-policy trust-boundary changes from credential redaction PR. * fix: update callers to match upstream/main function signatures channelNotification.ts was restored to upstream/main to split trust-boundary changes from the redaction PR. This commit updates the three caller sites that previously passed extra arguments: - ChannelsNotice.tsx: pass getSubscriptionType() + undefined to getEffectiveChannelAllowlist (needs 2 args upstream) - interactiveHandler.ts, channelNotification.test.ts: drop 3rd pluginSource arg from findChannelEntry (takes 2 args upstream) * fix: address reviewer findings — OAuth mock, notice states, marketplace disambiguation P1: Mock getClaudeAIOAuthTokens and getSubscriptionType in channel notification tests so they pass on CI where no real OAuth exists. P2: Restore blocked-auth/org-policy notice states in ChannelsNotice.tsx so the UI shows the correct blocker when gateChannelServer rejects unauthenticated users or orgs without channelsEnabled. P2: Add pluginSource disambiguation to findChannelEntry so same-name plugin entries from different marketplaces are matched by runtime source rather than first-match order. Add regression test with non-matching marketplace first to cover the bug. * fix: address reviewer findings — relay gate parity and allowlist regression test - Replace filterPermissionRelayClients in interactiveHandler with inline gateChannelServer call so the relay predicate checks ALL gates including disabled-channel, auth, org policy, and approved-plugin allowlist, not just session entry + marketplace. - Clean up unused imports (getAllowedChannels, parsePluginIdentifier, findChannelEntry, filterPermissionRelayClients). - Add regression test: gateChannelServer rejects marketplace-matched plugin not on approved allowlist (full-gate path). * fix: redact mixed semicolon secrets in valid-URL path and route OpenAI shim through centralized redactor P1: Pre-redact semicolon-delimited sensitive query params from the raw query string in redactUrlForDisplay BEFORE URLSearchParams encodes ; as %3B. Previously model=ok;token=SECRET leaked because parsed.toString() reserialized to model=ok%3Btoken%3DSECRET, making it invisible to the post-process pass. P1: Route openaiShim's redactUrlForDiagnostics through the centralized redactUrlForDisplay so the semicolon fix, malformed-URL fallback, and all future redaction improvements apply to OpenAI-compatible diagnostic logs too. Keep redactSecretValueForDisplay as an additional safety net after the centralized pass. Add 3 regression tests for mixed-separator queries. * Update src/utils/redaction.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix: add fragment-query credential regression test and correct dev-channel gate comments P2: Add regression test for redactUrlForDisplay with query-like credential in fragment (e.g. #debug?token=SECRET). Fix raw-query pre-processing to only extract query before the first #, preventing fragment content from being treated as query parameters. P3: Update comments in interactiveHelpers.tsx to match the actual gate order — OAuth and org-policy gates still exist in gateChannelServer() after restoring to upstream/main; the --dangerously-load-development- channels flag only bypasses the allowlist gate. * fix: add port+fragment+@ fallback test and restructure dev-channels dialog tests * fix: registerDevChannels seam, bare-host #-in-password heuristic, and coverage restructure * fix: add OAuth and org-policy gate test coverage - Refactor auth module mock to use mutable variables per test - Auth gate test: empty OAuth tokens -> kind:auth - Policy gate test: team subscription without channelsEnabled -> kind:policy * fix: prefer exact server channel entries before plugin disambiguation - Return exact server-kind candidate first when candidates include both server and plugin entries with same name - Added regression test covering mixed server/plugin --channels entries to ensure exact server opt-in is not overridden by plugin candidate - This prevents a plugin marketplace mismatch from incorrectly rejecting a server the user explicitly selected via server:plugin:slack * fix: only trust exact [REDACTED] placeholder in generic header field pattern - Changed GENERIC_HEADER_FIELD_PATTERN to only bypass exact '[REDACTED]' canonical placeholder - Prevents non-canonical placeholders like '[REDACTED_API_KEY]' or '[REDACTED_actual_secret]' from leaking through - Updated tests to expect canonical '[REDACTED]' output for generic pattern * fix: handle bare hosts in malformed URL userinfo fallback - Added regex to recognize bare hostnames (with optional port) in the fragment heuristic - Added tests for //alice:sec#ret@host and //alice:sec#ret@host:443 * fix: add relay dispatch path test for non-allowlisted plugin - Added test using full gateChannelServer predicate in filterPermissionRelayClients - Mirrors the exact relay dispatch path used in interactiveHandler - Ensures marketplace-matched plugin not on allowlist is excluded from permission preview * fix: enhance URL redaction logic to handle valid hosts before fragment * fix: refine URL redaction logic to ensure valid host checks before fragment * fix: enhance redaction logic to handle embedded URLs in free-form text * fix: update redaction logic to remove user info from OpenAI base URL in diagnostic report * fix: ensure findChannelEntry returns undefined when no exact matches are found * fix: improve URL redaction logic to remove user info and ensure proper formatting * fix: enhance redactDiagnosticUrl to preserve query-param values and trailing slashes * fix: refine redaction logic to preserve meaningful path segments and handle trailing slashes correctly * fix: enhance redactDiagnosticUrl to preserve literal path segments and handle trailing slashes correctly * fix: preserve semicolon-delimited query params during redaction * fix: update redaction logic to support semicolon-delimited query parameters * fix: enhance redactUrlForDisplay to handle bare hosts and improve fragment redaction * fix: enhance redactUrlForDisplay to correctly handle username-only userinfo with fragments * fix: address privacy findings — URL redaction in jsonRedactor, base URL redaction, diagnostic object collapsing, structural channel previews, pluginSource telemetry * fix: preserve falsey env-presence values in diagnostic redaction - false, "", and 0 under isEnvPresenceKey keys are now preserved as-is instead of misrepresented as "[set]" - Added regression test for absent/falsey env-presence inputs * fix: address CodeRabbit findings — sync describe, heartbeat emitter, responsesBody filtering, dev entry precedence * chore: remove stray Windows path artifact * fix: update redaction import path in taskReport module * fix: address CodeRabbit P1-P3 findings and rebase regressions - F1: rebase onto upstream/main, fix taskReport.ts import path - F2: Ollama native chat code recovered via rebase (6 functions) - F3: &-truncation in credential regexes fixed via post-processing pass - F4: 'tokens' added to jsonRedactor EXCLUDED_KEYS - F5: redactHomePath case-sensitivity aligned with redactPathForStatus - F6: credential metadata object preserved in issue report (sensitive-key check moved inside type branches) - F7: heartbeat tests updated for pre-drain write behavior - F8: reportTask test expects [REDACTED] (matches centralized output) - rm: stray C:\repo\ Windows path artifact * fix: address reviewer findings — generic regex &-handling and diagnostic secret-key masking - Remove & from excluded char classes in 4 generic patterns so they consume full secret values (URL-query &-splitting belongs in redactUrlForDisplay). - Remove now-obsolete &-tail post-processor pass. - Remove credential from DIAGNOSTIC_SECRET_KEY_PATTERN so issue report credential metadata objects are traversed, not collapsed. - Restore broad isDiagnosticSecretKey check before type dispatch in redactDiagnosticObjectInternal so objects/arrays under secret-marked keys (auth, password, token, etc.) are masked. - Update issue report test baseUrl expectation (no trailing &mode=test after generic redactor consumes past &). * fix: address reviewer findings — URL delimiter safety, jsonRedactor #-drop, embedded URL query redaction - Restore &#; delimiters in generic pattern value classes (F1) so safe query tails (&mode=test) survive. Re-add &-tail post-processor for non-URL abc&def case. - Gate redactUrlForDisplay in jsonRedactor to https?:// strings only (F2) to prevent #-drop on ordinary text like 'fails after #setup'. - Add URL query redaction step to redactSensitiveInfo (F3) that extracts https?:// URLs from free-form text and routes them through redactUrlForDisplay, catching signature/sig params that generic patterns miss. Skip already-redacted URLs to avoid double-redaction. * fix: add Cookie/Set-Cookie semicolon-safe redaction pass, tighten &-tail regex * fix: COOKIE_PATTERN consume comma-joined multi-cookie values * fix: address P2 findings — URL redact skip, pre-drain write promise, permission truthy check * fix: update log.test.ts expectation, add protocol-relative URL support * fix: enhance redaction for provider env-vars in URLs, preserve safe query params * fix: enhance redaction for uppercase provider keys and cookie query params * fix: enhance redaction for bare Bearer and JWT tokens in sensitive info * fix: update report task test expectations for new redaction format * fix: limit token exemption to numeric values, protect semicolon cookie query tails * test: add tests for truncateForPreview to ensure sensitive data redaction --------- Co-authored-by: Gravirei <gravirei@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: openhands <openhands@all-hands.dev> |
||
|
|
2edec9a140 |
fix(deps): ship a zero-warning, minimal install (#1784)
* fix(deps): ship a zero-warning, minimal install
The published package declared 62 runtime `dependencies`, but `dist/cli.mjs`
is a fully-bundled esbuild output that inlines almost all of them. End users
therefore installed ~476 transitive packages — including three subtrees the
bundle never needs at install time, each emitting an install warning:
- node-domexception (deprecated) via google-auth-library
- protobufjs (allow-scripts) via @grpc/* (already bundled into dist)
- sharp (allow-scripts) native image module
The repo's `overrides`/`allowScripts` silence these locally, but those are
root-only npm settings and are ignored when the package is installed as a
dependency — so end users saw the warnings.
Core changes:
- package.json: runtime dependencies trimmed 62 -> 3 (@orama/orama,
@orama/plugin-data-persistence, @vscode/ripgrep). Bundled packages, plus
the optional sharp/google-auth-library, move to devDependencies so they
are built/tested but not shipped.
- package.json: @anthropic-ai/sdk, @modelcontextprotocol/sdk, react and
react-reconciler declared as OPTIONAL peerDependencies — externalized by
the ./sdk bundle but bundled into the CLI. Optional peers keep the CLI
install minimal and warning-free while still resolving for ./sdk consumers.
- externals.ts: sharp, google-auth-library and @anthropic-ai/bedrock-sdk
marked OPTIONAL_RUNTIME_EXTERNALS (loaded on demand, not shipped).
- validate-externals.ts: runtime deps validate against externals; bundled
deps validate against dependencies + devDependencies.
- client.ts: load @anthropic-ai/bedrock-sdk via the runtime importer so
esbuild no longer inlines it and hoists its static @aws-sdk import into
the CLI bundle (that was a startup crash for default installs).
Optional-dependency UX (consistent, actionable errors):
- New src/utils/optionalRuntimeModule.ts exports importRuntimeModule and
importOptionalRuntimeModule. The optional variant translates a missing
package (code === 'ERR_MODULE_NOT_FOUND', specifier present in message)
into "<feature> requires "<pkg>" ... Run `npm i -g <pkg>`". Generic so
typed call sites keep their module types.
- Routed ALL optional-package load sites through it (previously only one
did): google-auth-library (client.ts, auth.ts, geminiAuth.ts),
@anthropic-ai/foundry-sdk + @azure/identity (client.ts), and the
@aws-sdk/* Bedrock paths (model/bedrock.ts, tokenEstimation.ts, aws.ts).
- imageProcessor.ts: sharp-missing error now says `npm i -g sharp`.
- docs/advanced-setup.md: new "Optional provider packages" table and a
Vertex note documenting the on-demand installs.
- Unit test for the helper (friendly error, success path, specifier match,
raw passthrough).
- knip.json: ignore google-auth-library (now loaded via runtime string).
Verified on the current tree:
- tsc, build/validate-externals, knip, and tests all pass.
- npm pack + install --omit=dev adds 8 packages, zero deprecation/
allow-scripts/funding warnings; --version/--help/mcp list run.
- With packages absent, CLAUDE_CODE_USE_BEDROCK and CLAUDE_CODE_USE_VERTEX
print the friendly `npm i -g <pkg>` error (verified end-to-end).
- ./sdk imports once its optional peers are present (24 exports, no warns).
- Bundled ajv + ajv-formats validate with no ajv installed; no unguarded
native runtime requires (fsevents absent in chokidar 4; bun:sqlite Bun-only).
Trade-off: image reads, AWS Bedrock, Azure Foundry and GCP/Vertex now prompt
a one-time `npm i -g <pkg>` instead of being shipped to every user.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
Review fixes (CodeRabbit + jatmn):
- validate-externals: the INTENTIONALLY_BUNDLED exemption is now scoped per
bundle. The CLI exempts every bundled package; the SDK does NOT exempt
packages declared as peerDependencies (keyed on package.json, an independent
source of truth) so dropping react/@anthropic-ai/sdk from SDK_EXTERNALS now
fails validation instead of silently passing. Added an explicit minimal-
install contract check: bundled packages must be devDependencies-only — never
in `dependencies`, and only the SDK-external subset may be optional peers.
Validation logic extracted to scripts/externalsValidation.ts + tests.
- FileReadTool oversized-image fallback now loads via the shared
getImageProcessor() (not a raw import('sharp')) and re-throws
ImageProcessorUnavailableError, so a missing processor surfaces the
`npm i -g sharp` install hint instead of returning an over-budget image.
- optionalRuntimeModule: match the missing specifier as a QUOTED token, not a
raw substring, so a missing transitive package whose name contains the
requested one (sharp vs sharp-libvips, @aws-sdk/client-bedrock vs
@aws-sdk/client-bedrock-runtime) no longer triggers the wrong install hint.
Predicate extracted to isMissingSpecifierError() with regression tests.
- docs/advanced-setup.md: the Vertex auth section now shows both documented
paths (gcloud ADC and a GOOGLE_APPLICATION_CREDENTIALS service-account file).
Review fixes (round 2, CodeRabbit):
- validate-externals: assert the optional-peer install contract — every
peerDependency must be { optional: true } in peerDependenciesMeta
(validateOptionalPeers), so losing that flag fails the build instead of
silently reintroducing install warnings.
- validate-externals: hard-check OPTIONAL_RUNTIME_EXTERNALS placement
(validateOptionalRuntimeexternals). Anything esbuild can see statically must
stay external in BOTH bundles (dropping sharp/google-auth-library now fails);
the runtime-indirection-only subset (new RUNTIME_INDIRECTION_ONLY_EXTERNALS)
must stay OUT of externals so esbuild never re-exposes their static imports.
- Deeper-dig fix: @anthropic-ai/foundry-sdk was misclassified as
INTENTIONALLY_BUNDLED, but it is loaded only through the Function indirection
(esbuild never sees it, so it was never actually bundled) — its sole presence
in dist is the specifier string. Per the PR's own "Azure Foundry now prompts"
trade-off it is on-demand, so it now lives in OPTIONAL_RUNTIME_EXTERNALS +
RUNTIME_INDIRECTION_ONLY_EXTERNALS (mirroring bedrock-sdk). sandbox-runtime is
genuinely statically imported, so it stays bundled.
- Provider-routing coverage (scripts/optionalRuntimeSpecifiers.test.ts): a
static scan asserts every importOptionalRuntimeModule specifier is a declared
OPTIONAL_RUNTIME_EXTERNAL and never also INTENTIONALLY_BUNDLED — the
invariant that keeps a provider's optional package loadable on demand.
- All new validators extracted to scripts/externalsValidation.ts with tests.
Review fixes (round 3, CodeRabbit):
- client.ts: gate the Vertex google-auth-library import behind the non-skip
branch. CLAUDE_CODE_SKIP_VERTEX_AUTH (proxy/test) uses a mock GoogleAuth and
must not require the optional package; it was loaded unconditionally before.
- optionalRuntimeModule: drop the hard-coded `npm i -g`. The helper backs both
the global CLI and project-local ./sdk consumers, so the hint is now
context-neutral ("npm install <pkg>" / add -g for the global CLI).
- validate-externals: every SDK_ONLY_EXTERNALS entry must STAY a
peerDependency (a dropped peer leaves runtimeDeps while the SDK still
externalizes it); and OPTIONAL_RUNTIME_EXTERNALS must never be shipped (fail
on overlap with dependencies/peerDependencies). Both with tests + live-verified.
- optionalRuntimeSpecifiers.test: pin the EXACT set of optionally-loaded
specifiers instead of a >=5 count (a count passes even if a provider path
regresses).
- attachments: extract tryReadEditedImageAttachment() — background watched-file
image attachments DEGRADE to null on any failure (incl.
ImageProcessorUnavailableError) so a missing optional package never aborts a
turn, while the explicit FileReadTool path still surfaces the install hint.
Deterministic regression test (bad path -> null).
- docs: Bedrock row notes profile-based auth also needs
@aws-sdk/credential-providers; install-hint wording matches the new message.
Review fixes (round 4, CodeRabbit):
- attachments: stop sending the raw file path through the analytics
bypass-cast (tengu_watched_file_compression_failed). Send only the safe
file extension via getFileExtensionForAnalytics, matching the existing
tengu_file_read_dedup pattern, so no usernames/project paths can leak.
- externals.ts: corrected the OPTIONAL_RUNTIME_EXTERNALS header comment,
which still claimed all entries "remain in COMMON_EXTERNALS" — no longer
true since the indirection-only subset (bedrock/foundry) must stay OUT of
the externals lists.
(Other CodeRabbit comments on this push re-surface items already addressed in
prior commits: the peerDependenciesMeta-optional check (validateOptionalPeers),
the SDK-peers-present and optional-not-shipped validator rules, the
exact-specifier-set test, the attachments degrade contract + test, and the
context-neutral install hint are all present. The "assert every optional
external is a devDependency" suggestion is intentionally NOT applied: @aws-sdk/*
and @azure/identity are transitive devDeps via bedrock-sdk/foundry-sdk, so a
blanket assertion would be incorrect; source resolution is covered by the
build + tests that import these packages.)
Review fixes (round 5, CodeRabbit):
- attachments: stop leaking file paths via logError in the background-image
degrade path. readImageWithTokenBudget can throw path-bearing messages
(e.g. "Image file is empty: <path>") and logError persists message/stack, so
log only the error TYPE name now. (Analytics payload was already sanitized.)
- attachments: tryReadEditedImageAttachment takes an injectable reader so the
degrade contract is tested for the EXACT error types — ImageProcessorUnavailableError
and a path-bearing read error both degrade to null (not just ENOENT) — plus a
success case. No mocking.
- validate-externals: enforce the source-install half of the optional contract.
Non-transitive OPTIONAL_RUNTIME_EXTERNALS must be devDependencies so `bun
install` source builds resolve them. The new TRANSITIVE_OPTIONAL_EXTERNALS
documents the exemption (@aws-sdk/* via @anthropic-ai/bedrock-sdk, @azure/identity
via @anthropic-ai/foundry-sdk — provided transitively, not direct devDeps). A
blanket "all optionals are devDeps" check would have wrongly failed on those.
Tests + live-verified (dropping sharp from devDependencies now fails).
Review fixes (round 6, CodeRabbit + jatmn):
- optionalRuntimeSpecifiers.test: the call-site scan regex missed
generic-annotated calls (importOptionalRuntimeModule<...>(...)) in
model/bedrock.ts and tokenEstimation.ts, so the exact-set assertion was
incomplete. Regex now allows an optional generic; EXPECTED_SPECIFIERS adds
@aws-sdk/client-bedrock and @aws-sdk/client-bedrock-runtime (7 total).
- importOptionalRuntimeModule default generic is now <T = unknown> (was any),
so destructured imports are no longer silently any. Every call site now
supplies its module type — typeof import('<pkg>') where the package is
type-resolvable (bedrock-sdk, foundry-sdk, @aws-sdk/credential-providers,
google-auth-library), and a named minimal-shape alias for @azure/identity
(not a direct devDep, so typeof import can't resolve it). This gives
compile-time verification of each provider's module contract (export names,
shapes) — the structural answer to the "cover the provider branches" ask.
- attachments: tryReadEditedImageAttachment takes injectable {read,log,track};
a new test asserts the sanitized-telemetry contract directly — the logError
payload is path-free and the analytics payload carries only `ext`, never the
edited-image path.
* fix(deps): address optional runtime review findings
* test(deps): isolate optional runtime importer mocks
* fix(deps): clarify AWS optional auth labels
* fix(deps): close optional runtime review gaps
---------
Co-authored-by: jatmn <the@jat.mn>
|
||
|
|
fb40d49e68 |
feat: add repo map codebase intelligence (#1867)
* feat: add Codebase Intelligence — repo map with PageRank-ranked structural summaries
Adds a new module that builds a structural map of the repository by parsing
source files with tree-sitter, building a cross-file reference graph weighted
by IDF, ranking files with PageRank, and rendering a token-budgeted summary
of the most important files and their signatures.
Surface:
- RepoMap tool the model can call on-demand, with focus_files / focus_symbols
- /repomap slash command with --tokens, --focus, --stats, --invalidate
- Auto-injection into session system context, gated by REPO_MAP=1 env var
(compile-time feature('REPO_MAP') flag stays off in scripts/build.ts)
How it works:
git ls-files → tree-sitter WASM parse → extract defs/refs →
IDF-weighted directed graph → PageRank → render top files until token budget
Files imported by many others rank highest. Common symbol names (get, set,
map, value) are down-weighted via IDF. Results cached to disk keyed by
(path, mtime, size) — only changed files are re-parsed.
Supported languages: TypeScript, JavaScript, Python.
Tree-sitter tag queries are inlined as string constants in queries.ts so
they ship inside dist/cli.mjs and work after npm install — the .scm source
files are kept for readability/Aider attribution but are not required at
runtime. A drift-guard test (queries.test.ts) asserts byte-equality between
the inlined strings and the .scm source files.
Dependencies added: web-tree-sitter, tree-sitter-wasms, graphology,
graphology-pagerank, graphology-operators, js-tiktoken.
* fix(repomap): invalidate rendered cache on file edits + Windows test fix
- computeMapHash now folds per-file mtime+size into the cache key so a
source edit (without changing the file list) no longer returns the
prior rendered map. Adds a regression test that edits a file and
confirms the second build reflects the new symbol without manual
invalidateCache().
- queries.test.ts byte-for-byte drift guard normalizes CRLF -> LF when
reading the .scm source so Windows checkouts pass. .gitattributes
also pins *.scm to LF on future checkouts.
- Externals: declare web-tree-sitter, tree-sitter-wasms, graphology*,
and js-tiktoken in scripts/externals.ts so build validation passes.
* fix(repomap): expand directory focus paths
* fix(repomap): satisfy deadcode check
* Fix repo map review findings
* Resolve remaining repo map review findings
* fix(repomap): address review findings
* fix(repomap): address review findings
* fix(repomap): resolve smoke and review follow-ups
* fix(repomap): preserve cached tag order
* fix(repomap): resolve review follow-ups
* fix(repomap): satisfy query promise lint
* Fix repo map context timeout cleanup
* fix: address repo map review findings
* fix: cancel timed-out repo map context builds
* fix(repomap): preserve git file path whitespace
* fix(repomap): handle graph and parsing edge cases
* fix(repomap): preserve shell token positions
* fix(repomap): respect configured cache home
* fix(repomap): address review findings
- Add explicit 10000ms timeout to the feature-flag-off context test to avoid cold-import flakes.
- Add --focus-symbols flag to /repomap and forward it to buildRepoMap, matching the RepoMap tool.
- Add parsing/command tests and docs coverage for --focus-symbols.
---------
Co-authored-by: gnanam1990 <gnanasekaran.sekareee@gmail.com>
|
||
|
|
cd13a61537 |
fix(memory): recover from autocompact overflow failures (#1858)
* fix(memory): recover from autocompact overflow failures * fix(memory): address autocompact review findings * fix(memory): close autocompact recovery gaps * fix(memory): reduce OpenAI conversion pressure * test(memory): add long-session guard smoke * fix(memory): add runtime memory guard diagnostics * fix(memory): surface autocompact failure diagnostics * fix(memory): reuse hard-cap resolver in diagnostics * fix(memory): avoid hard-cap diagnostic drift * fix(memory): clarify hard-cap diagnostics |
||
|
|
203f05538e |
fix(build): shim jsxDEV when bundling production React — TUI rendered nothing (#1863)
Since
|
||
|
|
354feb483c |
fix(memory): prevent reported idle retention paths (#1856)
* fix(build): bundle production React in CLI * fix(memory): bound reported idle retention paths * fix(memory): address review feedback * fix(memory): keep fps average stable after sample cap * test(memory): cover heap dump filenames |
||
|
|
eea0a1a740 |
feat(cli): add headless heartbeat for print mode (#1789)
* feat(cli): add headless heartbeat for print mode * fix(cli): harden heartbeat validation and predicates * fix(cli): align print heartbeat phases * fix(cli): keep heartbeat payloads schema-valid * fix(cli): delay stream-json heartbeat until drain * test(sdk): cover heartbeat placeholder identifiers * fix(cli): clamp heartbeat durations * fix(cli): ignore file persistence final events * test(cli): cover post-turn final filtering * fix(cli): harden headless heartbeat follow-up Export the heartbeat SDK message type from generated core types. Keep heartbeat cleanup paired with setup and streaming failures, and cover timing/count edge cases with focused regression tests. * test(sdk): exercise generated heartbeat types Expose the SDK type generator as a pure helper so tests compare fresh output with the checked-in generated artifact. * fix(scripts): canonicalize sdk type generator entrypoint Compare real paths for direct script execution so symlinked invocations still run the generator. * test(sdk): harden generator import coverage Normalize generated type freshness checks across line endings and keep the SDK type generator import-safe for non-file entrypoints. * test(sdk): assert generator import has no write side effects Snapshot the generated SDK type artifact around the non-file import regression so importing the generator cannot silently rewrite the committed output. |
||
|
|
a723540163 |
perf(build): minify the CLI bundle (whitespace + syntax, keep identifiers) (#1743)
dist/cli.mjs shipped unminified at 21.7MB; whitespace+syntax minification cuts it to ~16MB (-26%) and shaves V8 parse time on every invocation. Identifier mangling stays off because the codebase matches constructor.name (errors.ts, toolExecution.ts, useCanUseTool). The SDK bundle stays unminified — its React/Ink leak check greps import syntax that minification would rewrite. The bundle guard's missing-module tripwire relied on Bun's `// missing-module-stub:<path>` module-boundary comments, which minification strips. The stub loader now also emits the marker as a side-effecting string push (survives treeshaking and syntax-minify), and the guard parses both forms. Review fix (CodeRabbit + jatmn): the marker parser previously truncated paths at the first backslash or space, so a JSON-escaped Windows marker like "missing-module-stub:C:\\Users\\Jane Doe\\...\\src\\...\\foo.js" was captured as a useless `C:` (or `C:\\Users\\Jane`) fragment and canonicalized to the wrong key — letting a newly stubbed module slip past the tripwire on Windows/spaced build hosts. Parse each marker form to its correct terminator instead: the string literal runs to its matching (back-ref) closing quote consuming escaped pairs, and Bun's comment runs to end of line. Extract canonicalStub() + the parser into scripts/stubMarkerGuard.ts so the logic is unit-testable, and add regression tests for Windows, spaced, comment-form, and multi-marker-per-line cases. Verified: build green, bundle ~16MB minified, guard passes against the real bundle, stub-guard tests pass, --version works through the minified bundle. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
9c0d5c61e2 |
fix(deps): remove deprecated uuid install path by replacing vertex-sdk with local client (#1771)
* fix(deps): remove deprecated uuid install path * fix(api): address PR #1232 review — type the local Vertex client surface Resolves the blocker raised by @Vasanthdev2004, @gnanam1990, and @jatmn: the in-repo AnthropicVertex replacement compiled under bun (no type-check) but added 4 `tsc --noEmit` errors that the upstream typed SDK did not. - Declare `messages`/`beta` as typed class fields (BaseAnthropic doesn't, but the upstream @anthropic-ai/vertex-sdk client did), so typed consumers — client.ts `new AnthropicVertex(...)` and the SDK calling `.messages` — keep the resource surface. (vertexClient.ts:145/146, test:53) - Widen the header-merge helpers to accept the base client's request header type (HeadersLike), and handle the NullableHeaders shape it actually passes so the merge stays correct, not just type-clean. (vertexClient.ts:182) Also drops the now-stale `@anthropic-ai/vertex-sdk` entries left behind by the dependency removal: - scripts/externals.ts INTENTIONALLY_BUNDLED (P3) - knip.json ignoreDependencies Testing: `tsc --noEmit` clean; vertex/client/gemini tests 51 pass; smoke green (INTENTIONALLY_BUNDLED back in sync, 57 entries); knip clean. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(api): address CodeRabbit review on PR #1232 — auth precedence + coverage - Security (vertexClient.ts): merge resolved Google auth headers LAST so a caller-supplied Authorization / x-goog-user-project can't override the Vertex credential and send the wrong token upstream. Other request headers still pass through unchanged. - Tests (vertexClient.test.ts): add focused regression coverage for the previously-unguarded routing/auth branches — * streaming → :streamRawPredict path (+ model stripped, stream preserved) * count_tokens → count-tokens:rawPredict path rewrite * auth-header precedence: caller Authorization does NOT override the Vertex token (guards the fix above + exercises the NullableHeaders merge branch). Testing: tsc clean; vertexClient tests 5 pass; full src/services/api 840 pass; smoke + knip green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(api): validate and encode Vertex model before building the URL CodeRabbit follow-up on PR #1232: `model` was interpolated straight into the Vertex endpoint path, so a missing/non-string model would silently route to `.../models/undefined:rawPredict` instead of failing fast. Now throw a clear error on a missing/empty model and encodeURIComponent the value before building the path. Adds a focused test for the missing-model case. Testing: tsc clean; vertexClient tests 6 pass; smoke + knip green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(api): address remaining review items from PR #1232 - [P2] Fix count_tokens method guard: apply method==='post' to both paths - [P3] Remove unused accessToken option from AnthropicVertex - [P3] Narrow batches type on messages/beta resources with Omit * test: add count_tokens?beta=true routing regression test --------- Co-authored-by: Kevin Codex <kevin@gitlawb.com> Co-authored-by: OpenClaude <openclaude@gitlawb.com> Co-authored-by: Gravirei <gravirei@users.noreply.github.com> |
||
|
|
dd4c4abc81 |
feat(api): add OpenAI-compatible credential pool failover (#1706)
* feat(api): rotate OpenAI credential pools * fix(api): align pooled credential discovery * fix(cache-probe): preserve GitHub credential precedence * fix(provider): honor pooled OpenAI fallbacks * fix(provider): validate pooled profile credential labels * fix(api): harden OpenAI credential pool handling Reject placeholder values in pooled OpenAI credentials before requests, discovery, diagnostics, and profile generation can use them. Normalize pooled credentials to a single usable key for model discovery, runtime cache partitions, cache probing, and NVIDIA NIM cache lookups. Preserve documented profile precedence by letting live shell credentials override saved pools, carrying OpenCode fallback pools through launch, and redacting individual pool members in profile display. Add regression coverage for pooled credential validation, profile launch/rebuild behavior, discovery/cache callers, diagnostics, provider autodetect, and shim failover semantics. * fix(provider): cover pooled key recommendation path Import the pooled OpenAI credential validator in provider-recommend and split invalid credentials from unset credentials in user guidance. Add a script-level regression that runs the OpenAI recommendation path with OPENAI_API_KEYS so the ts-nocheck script cannot regress with runtime ReferenceErrors. Scrub pooled OpenAI keys before xAI OAuth profile env construction and loosen the invalid-pool discovery test to assert auth header absence instead of exact header shape. * fix(tests): stabilize rebased provider checks * fix(provider): address pooled credential review findings * test(api): cover opencode go credential failover * fix(provider): share OpenAI credential usability checks * fix(provider): respect pooled credential precedence * fix(model): preserve pooled discovery credential precedence * fix(model): fall back from unusable pooled discovery keys |
||
|
|
29aea4969d |
fix(provider): centralize provider secret redaction (#1665)
* fix(provider): centralize provider secret redaction * fix(system-check): prefer base URL route credentials * fix(provider): avoid false credential matches * fix(provider): redact jwt-shaped tokens * fix(provider): redact embedded diagnostic secrets * test(system-check): isolate provider env keys |
||
|
|
a1b3346f65 |
feat(cli): add local background sessions (#1642)
* feat(cli): add local background sessions Add local detached background sessions backed by an OpenClaude-owned registry under the resolved config directory. - implement --bg spawning plus ps, logs, logs -f, kill, and an explicit attach limitation - harden registry metadata validation, atomic writes, ID/name collision handling, and terminal-name reuse - precreate child log files with precise ownership cleanup and register metadata only after spawn succeeds - verify live PIDs against the session command before treating registry entries as running - wait for process-tree termination and escalate to SIGKILL before marking sessions killed - skip live local background sessions during --continue transcript selection - preserve Node heap flags for detached children while avoiding stale launcher relaunch state - handle -- separators so dash-prefixed prompts remain positional - document storage, safety model, name reuse, and the current attach limitation Validation: - bun test - bun run typecheck - bun run smoke - isolated built-CLI --bg/ps/logs/kill smoke - CodeRabbit review findings addressed * test(utils): prevent bg registry mock leakage Restore complete bg registry and UDS module mocks after conversation recovery tests so Bun's process-global mock.module registry cannot leak partial module exports into later CLI tests. CI exposed this under Bun 1.3.13 when conversationRecovery.test ran before the bgRegistry and bg CLI test files. * test(utils): exercise bg registry without global mock Replace the conversation recovery bgRegistry module mock with real registry metadata backed by a short-lived live child process. This keeps UDS as the only mocked boundary and avoids leaking a mocked registry module into later CLI registry tests under Bun 1.3.13. * test(utils): isolate background registry state Stop the conversation recovery test from using process-wide bgRegistry mocks or real child processes by injecting the live-session dependencies directly. Pin and serialize the bg registry test config directory through the shared env mutation lock so path/cache state cannot leak from neighboring tests under Bun CI ordering. * test(utils): document Bun mock restoration Explain why conversation recovery tests re-register full module exports after mock.restore(), matching the CodeRabbit-requested Bun 1.3.13 isolation workaround. * test(cli): isolate background registry root Avoid relying on process-wide CLAUDE_CONFIG_DIR state in bgRegistry tests. Use a registry-local test root override so CI file ordering and mocked path modules cannot redirect background session metadata into another test's temp directory. * test(utils): cover live session fallback paths Add focused coverage for collectLiveBackgroundSessionIds when UDS discovery fails but registry data remains available, and when registry refresh fails but UDS data remains available. * fix(cli): harden background session management Validate persisted and newly-created background session PIDs before exposing them to management commands. Reserve named live sessions with an atomic registry write, release reservations when sessions become terminal, and cover concurrent duplicate-name attempts. Split local session management dispatch from background spawning so ps/logs/attach/kill avoid provider startup while --bg still inherits profile routing. * fix(cli): address background session review findings Preserve positional prompts when --bg is combined with optional-value flags such as --debug. Recover stale name reservations whose owner metadata is missing or terminal while preserving in-flight reservations from live creators. Cover both reviewer findings with focused parser and registry regression tests. * fix(cli): respect delimiter for background flags Limit background and print-mode flag detection to arguments before the -- delimiter so flag-shaped prompts remain positional. Keep optional resume/from-pr flags out of the required-value table and add regressions for delimiter and optional-flag prompt handling. * refactor(cli): share delimiter argument helper Move args-before-delimiter handling into the existing dependency-free CLI args utility. Use a dynamic import from the entrypoint so background flag routing shares the helper without adding top-level module load to version and management fast paths. * test(cli): cover background entrypoint routing Export the CLI entrypoint for controlled tests and add isolated importer injection so runtime routing tests do not leak global module mocks. Replace the delimiter source-layout assertion with execution-level coverage for management commands, real background flags, and flag-shaped prompt text after --. * fix(cli): preserve background resume selectors Keep space-separated --resume, -r, and --from-pr values attached when building background child args. Mark live background sessions stale when PID command identity cannot be read, avoiding termination of reused unrelated PIDs. * fix(cli): track unknown background session identity Represent unreadable live PID identity as a non-terminal unknown state so active sessions stay excluded from resume selection. Refuse to terminate unknown live PIDs because the process command cannot be positively matched to the background session. * fix(cli): honor background resume selectors Avoid adding a generated --session-id to non-forked background resume launches so the spawned print-mode child satisfies the existing resume/session-id contract. Pass --from-pr through headless print mode and resolve PR-linked sessions through the shared conversation recovery path. Add regression coverage for background resume launch args and PR selector matching. * fix(cli): treat PR resume as headless resume source Include --from-pr in print-mode resume guards so PR-linked headless resumes can run without a prompt and share resume-only options. Skip eager startup hooks for headless PR resumes and add explicit --session-id launch coverage. * fix(cli): keep background PR resumes live Resolve non-forked --from-pr background launches to the selected transcript id before writing registry metadata. Preserve PID identity refresh for PR-resume children by matching the stored invocation when argv does not carry the transcript id. Add regressions for launch registration and registry refresh. * test(cli): cover PR resume lookup failures Add regression coverage for non-forked background --from-pr launches when the selector cannot be resolved. Verify the launch planner returns the same clear error used by handleBgFlag(). |
||
|
|
d5588ea80d |
feat(context-collapse): opt-in between-turns context collapse (span summarization) (#1619)
* feat(context-collapse): implement context collapse for proactive context management
* feat(context-collapse): add turn-boundary helpers for span selection
* feat(context-collapse): deterministic turn-anchored span selection
* feat(context-collapse): code-computed span risk score
* feat(context-collapse): ctx-agent summarization instruction
* feat(context-collapse): implement ctx-agent span summarization spawn
* fix(context-collapse): make runtime activation opt-in (CLAUDE_CONTEXT_COLLAPSE)
* fix(context-collapse): address review feedback on restore state and test rigor
- restoreContextCollapseState now resets armed/lastSpawnTokens up front so a
snapshot-less restore cannot carry stale spawn state across sessions.
- projectView reuses a stable timestamp from the replaced span instead of
new Date(), keeping the read-side projection deterministic.
- Strengthen the disabled-state and turn-boundary assertions, drop an internal
renderToolUseMessage assertion, and isolate the operations/persist/spawn tests
from shared module and CLAUDE_CONTEXT_COLLAPSE env state.
* test(context-collapse): re-init enablement in persist.test hooks
resetContextCollapse() does not re-read CLAUDE_CONTEXT_COLLAPSE, so the
afterEach env delete left enabled=true in module state, leaking to the
next test file. Call initContextCollapse() in both hooks so module
enablement stays synced to the env var.
* test(context-collapse): stop spawnCtxAgent module stubs leaking across files
spawnCtxAgent.test.ts stubs shared modules (tokens, forkedAgent, messages,
analytics, log, spanSelection) via mock.module in beforeEach. bun's
mock.restore() does not undo mock.module, so the tokens stub (() => 100000)
bled into autoCompact/microCompact/runAgent tests run later in the full serial
suite, making them see every conversation as over-threshold (4 spurious
failures in test:full, all green in isolation).
Restore each stub to its real implementation in afterEach. The reals are
snapshotted into plain objects up front because 'import * as' yields a live
namespace that mock.module mutates in place, so holding the namespace would
restore the stub. autoCompact.js is deliberately not restored here since
autoCompact.test.ts re-imports it fresh via a cache-busting nonce.
Also reset+reinit the collapse module in afterEach so enabled state stays
synced to the now-unset env var.
* test(context-collapse): also restore autoCompact stub from spawnCtxAgent
The getEffectiveContextWindowSize stub on ../compact/autoCompact.js was the one
module the previous commit left unrestored, on the assumption that restoring it
would clash with autoCompact.test.ts's nonce re-import. It doesn't: the nonce
import uses a different specifier, and the snapshot restore is keyed by the
plain specifier. compressToolHistory imports getEffectiveContextWindowSize and
sizes tool-history truncation from it, so the leaked 20000-token window made it
fully omit tool results ('chars omitted') instead of mid-truncating
('[…truncated') for large-context models, failing the openaiShim compression
tests in the full serial suite. Restore all seven mocked modules.
* fix(context-collapse): re-arm after reset and gate ctx_inspect on opt-in
resetContextCollapse() left armed=false while enabled stayed true, so the
first /compact, main-thread compaction cleanup, or rewind permanently
disabled collapse for the rest of an opted-in session. Reset now mirrors
restoreContextCollapseState and sets armed=enabled.
CtxInspectTool.isEnabled() returned true unconditionally, advertising
ctx_inspect to the model in every default session even when the runtime
opt-in was off. It now returns isContextCollapseEnabled(). The opt-in is
also exposed as the contextCollapseEnabled global config key, so it is
reachable through /config instead of only the CLAUDE_CONTEXT_COLLAPSE env
var.
* refactor(context-collapse): drop no-op ternary in drainStaged persist call
The (stagedQueue.length > 0 ? 0 : 0) subtrahend always evaluated to 0, so
this is just persistCommits(processed.length).
* fix(context-collapse): persist commits before advancing the snapshot
drainStaged removed processed spans from the staged queue and then fired
persistCommits and persistSnapshot in parallel. If the snapshot write (which
no longer lists those spans as staged) landed while the commit write failed
or the process died between them, restore would find the spans neither staged
nor committed and the collapse would disappear on resume. Chain the snapshot
write after the commit write so the commit log is durable first.
* fix(context-collapse): project committed collapses on the query path, fix opt-in reach
Three issues from review:
- Committed collapses were never re-applied to the model input. The query path
calls applyCollapsesIfNeeded but only drained staged spans; projectView (which
replays the commit log) ran only in /context. Since messagesForQuery is rebuilt
from full REPL history each turn and the commit log is repopulated on resume,
the archived spans returned to the model on the next turn, undoing the collapse.
applyCollapsesIfNeeded now runs projectView first (idempotent). Adds a
regression that a committed collapse changes the next query input.
- Cache-safe params were saved only for exact repl_main_thread/sdk sources, but
the REPL tags non-default output styles as repl_main_thread:outputStyle:*, so
those sessions left the ctx-agent without params (empty spawns). Matches
repl_main_thread:* now, via a small tested helper.
- contextCollapseEnabled had no settings control. Adds a /config toggle that
refreshes runtime state (re-runs initContextCollapse) so it applies without a
restart.
* fix(context-collapse): clear already-committed staged spans; harden config toggle
After projecting committed collapses before draining, a span present in both the
commit log and the staged snapshot (a restore whose snapshot predates the
matching commit write) could not be drained — projectView had already removed
its messages — so it lingered in stagedQueue and distorted spawn/overflow
checks. drainStaged now drops staged spans that are already committed and syncs
the snapshot. Adds a regression covering the committed+staged overlap restore.
Also wraps the /config context-collapse refresh in try/catch so a failed
require/init can't crash the settings UI, and lists the toggle in the
save-and-close change summary like the neighboring compaction settings.
* fix(context-collapse): re-sync runtime state on config cancel
The context-collapse toggle's onChange refreshes the module-level
enabled/armed cache via initContextCollapse(). The revert path restored
the config key on disk but left that cache untouched, so enabling the
toggle and then pressing Escape kept collapse active for the rest of the
session. Re-init context collapse after the global config snapshot is
restored so cancel fully reverts runtime state.
* fix(context-collapse): keep collapsed summaries visible to the model
projectView and drainStaged replaced an archived span with a system
informational placeholder, but normalizeMessagesForAPI filters out every
system message that is not a local command. So once a collapse committed,
the next model request lost both the archived messages and the
<collapsed> summary meant to stand in for them, defeating the feature.
Mark the placeholder with isCollapseSummary and let it take the same
model-input path as local-command system messages (converted to a user
message), so the summary survives normalization. Added a regression that
runs the projected view through normalizeMessagesForAPI and asserts the
summary is still present.
* fix(context-collapse): avoid competing snapshot write after drain
After an immediate post-spawn drain, drainStaged(messages, true) starts
its own persistCommits().then(persistSnapshot) chain to guarantee commit
durability before the snapshot stops listing the staged spans. The
unconditional await persistSnapshot() that followed could win that race
and persist a snapshot with no staged spans before the commits landed,
reopening the crash window that drops collapses on restore. Only persist
directly when nothing was drained.
* fix(context-collapse): fall back, keep summaries non-snippable, gate /context
Three review findings:
- Suppress autocompact and the blocking preempt only when collapse holds a
real committed/staged reduction, not on mere enablement. Adds
hasActiveReduction(); a first over-threshold turn where spawnCtxAgent cannot
produce a span (getLastCacheSafeParams() still null) now falls back to
autocompact/blocking instead of sending an oversized transcript.
- Preserve isMeta when converting a collapse-summary placeholder to a user
message in normalizeMessagesForAPI, so the HISTORY_SNIP sweep cannot tag the
only replacement for an archived span as snippable.
- Gate the two /context projectView calls on isContextCollapseEnabled(), so a
disabled session does not under-report token usage from a lingering commit
log while the API receives the full transcript.
Adds regressions for hasActiveReduction and for the summary surviving
normalization as a non-snippable meta message.
* fix(context-collapse): scope collapse to the main thread that owns the store
The collapse store (commitLog/stagedQueue) is module-level and shared by
in-process subagents (agent:*) and the ctx-agent (marble_origami), which
run in the same process but do not own the main transcript.
applyCollapsesIfNeeded only skipped marble_origami, so a subagent could
stage or commit a span, flip the global hasActiveReduction(), and make
the next main-thread turn suppress autocompact and the blocking
prompt-too-long preempt while projectView() no-ops against the main
messages, sending an oversized transcript to the API.
Add isMainThreadSource() and gate both application (applyCollapsesIfNeeded,
isWithheldPromptTooLong, recoverFromOverflow) and fallback suppression
(autoCompact shouldAutoCompact, query collapseOwnsIt) to the owning
thread. Subagents now autocompact and preempt their own oversized turns
normally and never mutate the shared store.
Also adds the staged-only hasActiveReduction regression CodeRabbit
requested.
* fix(context-collapse): persist archived count so resumed stats stay accurate
restoreContextCollapseState rebuilt each commit with an empty archived
list, and getStats summed that list, so after a resume /context, the
context visualization, the token warning, and ctx_inspect reported
'N spans summarized (0 messages)' even though projectView was actively
removing the archived spans. The persisted-entry docstring claimed
projectView lazily refills the archive, but it only splices by boundary
uuid and never does.
The archived messages are never read back (only their count fed
getStats), so replace the per-commit Message[] with a persisted
archivedCount. It is written with each commit and restored on resume;
pre-field sessions restore as 0. getStats now reports the same figure
live and after resume.
* fix(context-collapse): keep collapse summary non-snippable across user merge
Preserving isMeta on the system->user conversion was not enough: when the
collapsed span ends right before the next user turn, normalizeMessagesForAPI
merges the summary into that real user message. Under HISTORY_SNIP
mergeUserMessages clears isMeta whenever an operand is real user content and
keeps the real turn's uuid, so the combined block — which carries the only
<collapsed> replacement for the archived span — got a snip id and the model
could queue it for removal.
Carry an isCollapseSummary marker onto the converted user message and through
mergeUserMessages (either operand), strip any snip id already baked into the
real turn when the merge absorbs a summary, and skip such blocks in
appendMessageTagToUserMessage. The merged block stays non-snippable
regardless of merge direction or isMeta being cleared.
* fix(context-collapse): preserve collapse marker on split, drop empty snip blocks
normalizeMessages split path now forwards isCollapseSummary so an array-backed
collapse summary keeps its non-snippable marker across API normalization.
stripSnipTagsFromContent drops a text block whose only content was the snip
marker, so the merge recovery path no longer emits an empty text block.
|
||
|
|
bd3ad89dd7 |
fix(security): bundle real sandbox runtime in open CLI (#1641)
* fix(security): bundle real sandbox runtime in open CLI * test(sandbox): cover fail-closed runtime diagnostics * fix(sandbox): report doctor inspection failures |
||
|
|
b036e9fa7c |
fix: startup provider validation fallback (#1658)
* fix startup provider validation fallback * test startup provider behavior |
||
|
|
d8dbf274b4 |
chore(runtime): align Node.js minimum version (#1644)
* chore(runtime): align Node.js runtime requirements * test(runtime): cover prefixed Node versions * fix(runtime): check node executable in doctor |
||
|
|
716c1d47f6 |
feat(compact): auto-compact prompt on /resume + determinate progress bar (#1386)
* feat(compact): auto-compact prompt on /resume + determinate progress bar On /resume, if the conversation exceeds 70% of the auto-compact threshold, a dialog appears offering to compact before continuing. Shows token count, context window usage, and effective window percentage. Also adds a determinate progress bar during compaction that advances as the summary streams in. - Add RESUME_COMPACT_PROMPT feature flag (enabled) - Add shouldPromptCompactOnResume() threshold gate - Add ResumeCompactPrompt dialog component - Emit compact_progress events during streaming in compact service - Render ProgressBar next to spinner during compaction in REPL - Add 5 tests for threshold gating logic * feat(compact): determinate progress bar via streamed text deltas Wire compact_progress events from streamed summary output (forkedAgent onStreamEvent forwards text deltas) and render a determinate ProgressBar in the REPL, replacing the indeterminate spinner during compaction. Extracts the bar into a CompactProgressBar component shared by manual /compact and the resume-triggered path. Emits coarse hooks/start ticks on the session-memory path so the bar moves immediately. * fix(compact): keep spinner visible and use bracketed progress bar Render the "Compacting conversation" spinner throughout compaction with the progress bar beneath it, instead of swapping the spinner out for the bar. Redraw the bar as a bracketed fill ([███····]) with no background rectangle, so it no longer reads as one solid block. * docs: remove resume-compact-prompt plan file Per PR review feedback — drop the planning doc from the PR. * fix(compact): prompt to compact on CLI --continue/--resume startup Startup resume paths (--continue, --resume <id>, ResumeConversation screen) install initialMessages via the initial useState rather than the resume() callback, so the threshold check never ran. Schedule the prompt from the mount-time initialMessages effect as well. * fix(compact): use MessageType alias for resumeCompactPending state main aliases the message type import as `Message as MessageType`; the rebased resumeCompactPending state still referenced the bare `Message`. * fix(compact): always close session-memory progress and fix progress-ratio units - Wrap the session-memory compaction attempt in try/finally so compact_end is always emitted, even when it returns null or throws, preventing a stuck progress bar/spinner. - Clear compactProgressRatio in resetLoadingState so an aborted or errored compaction does not leave the progress bar rendered in the idle UI. - Fix the progress denominator unit mismatch: estimatedOutputChars now uses a token-to-char converted estimate (preCompactTokenCount) instead of the token-scale preCompactTokenCount * 0.25, so progress no longer advances too fast and hits the cap prematurely. |
||
|
|
f4c3be850e |
chore: remove dead code and add knip gate to CI check (#1612)
Delete 32 unreferenced source files (~4,000 lines) verified dead by import-specifier grep and knip: test-only token utilities, orphaned hooks (useTaskListWatcher, useSkillImprovementSurvey + its component), the removed DevBar and ConfigTool UIs, unregistered bundled skills (stuck, verifyContent), unused analytics sinks, the benchmark command, and stale migrations/helpers. Remove unused dependencies code-excerpt, stack-utils, and tsx from package.json plus their entries in build stub/external lists. Add knip with a tuned knip.json (entrypoints, build-time stub targets, subprocess-launched fixtures, and runtime-string-imported SDKs ignored; providerAutoDetect kept intentionally as provider pre-wiring) and wire `bun run deadcode` into the `check` script so dead code stays dead. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
9755550137 |
Typecheck/zero tsc errors (#1597)
* ci(typecheck): add error-count ratchet toward zero tsc errors tsc --noEmit currently reports 697 pre-existing errors (issue #473), so PRs cannot be gated on a clean typecheck yet. This adds scripts/typecheck-ratchet.ts and a per-file baseline: CI fails when the count rises above the baseline (listing exactly which files regressed), passes at or below it, and --update lowers the baseline to lock in gains. Wired into pr-checks as its own step; once the baseline reaches zero the step becomes a plain `bun run typecheck`. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(typecheck): mechanical sweep — 697 → 624 tsc errors Type-only fixes with no runtime behavior change, except the deliberate NODE_ENV restorations: - Restore process.env.NODE_ENV comparisons that the source snapshot had baked into the literal "production", making the conditions constant (AutoUpdater dev/test skip, useTypeahead, ink devtools injection, interactiveHelpers onboarding skip, TestingPermissionTool.isEnabled — the last now correctly enables under bun test, +3 tests run green) - Type stream read helpers in openaiShim/codexShim as Bun.ReadableStreamDefaultReadResult<Uint8Array<ArrayBuffer>> and annotate throwClassifiedTransportError as never-returning, clearing the reader/response undefined cascades (29 errors) - Delete 14 stale @ts-expect-error directives - Widen useState/useRef/array generics inferred from null/[] literals - as-const notification priority/color literals to match Priority - Accept readonly Tool[] in checkLocalModelContextLoad/getCombinedTools Baseline lowered via typecheck:ratchet --update; full suite green (3690 tests), smoke + bundle guard green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(typecheck): recreate missing modules — 624 → 415 tsc errors The open snapshot never mirrored ~60 modules; the bundler noop-stubs them at build time (() => null named exports), so every recreated module here is runtime-inert by construction: no import-time side effects, gated features stay off (isAssistantMode/isSkillSearchEnabled → false, tools isEnabled → false, dialogs render null), lookups return empty, telemetry no-ops. Types are honest and derived from importer usage — no any. Highlights: - sdk: runtimeTypes re-exports/aliases, sdkUtilityTypes (NonNullableUsage), settingsTypes.generated; coreTypes.generated usage fields regenerated as a self-contained structural type (the consumer package ships without sdkUtilityTypes/@anthropic-ai/sdk, so the generated file must stay dependency-free — generator override updated to match, package-consumer-types tests green) - services: contextCollapse operations/persist/stats, compact cachedMicrocompact state/types + reactiveCompact, skillSearch (7 modules), oauth/types, lsp/types, sessionTranscript - cli/server/daemon: Transport interface, parseConnectUrl, server/* (7), daemon/*, bg/templateJobs/runners; assistant/* (KAIROS), ssh/* - tools/components: WorkflowTool trio, ReviewArtifact pair, OverflowTest/TerminalCapture/VerifyPlanExecution/DiscoverSkills, WebBrowserPanel, task dialogs, message variants, ink events/cursor - types: statusLine, fileSuggestion, notebook, messageQueueTypes; SerializedMessage rebuilt as distributed Omit-union so transcript guards narrow again; vitest-compat.d.ts mirrors Bun's runtime 'vitest' → 'bun:test' aliasing - TS2304 names: ant-model helpers imported from existing antModels.ts, inert Ultraplan/Gates/LogoV2 stubs, PromiseWithResolvers local type - build.ts: ACCEPTABLE_RUNTIME_STUBS emptied — both grandfathered bundle-reaching stubs (MonitorMcpDetailDialog, VerifyPlanExecutionTool/constants) are now real typed modules, so the degrade-on-use debt the guard tracked is retired Validation: full suite 3690 green, smoke + bundle guard green, typecheck:type-tests green, sdk package-consumer tests green; baseline lowered via typecheck:ratchet --update. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(typecheck): reconstruct Message discriminated union — 415 → 342 tsc errors src/types/message.ts was a stub where all ~40 message type aliases were 'export type X = any'. Bare-any aliases break the one thing the union is for: narrowing. Type predicates like isHookAttachmentMessage collapsed to 'never' in guard chains, cascading TS2339/TS2345 through utils/messages.ts, messageFilters.ts, groupToolUses.ts, collapseReadSearch.ts, REPL.tsx, compact.ts, stopHooks.ts and the message components. Envelope design (permissive-body discriminated union): - Each variant declares its literal discriminant(s) — message.type for the envelope union (user/assistant/attachment/progress/system), subtype for the 17-variant System family — plus the properties constructor functions in utils/messages.ts actually populate, with '[key: string]: any' as an escape hatch so unreconstructed properties never error. - UserMessage<C> / AssistantMessage<T> are generic over content shape so NormalizedUserMessage / NormalizedAssistantMessage<T> reuse the envelope without Omit (Omit over an index-signature type collapses keyof to string and silently drops the discriminant, breaking narrowing). - AssistantMessage.message is a structural AssistantMessageContent<T>, not the SDK's BetaMessage: synthetic constructors don't populate every SDK-required field (stop_details), and SDK-facing consumers need assignability to Record<string, unknown>-style bodies. - AttachmentMessage<T = Attachment> / ProgressMessage<T = Progress> stay generic over their payloads (utils/attachments.ts and Tool.ts types). - UI wrappers (GroupedToolUseMessage, CollapsedReadSearchGroup, CollapsibleMessage, RenderableMessage) and stream/control envelopes (StreamEvent over BetaRawMessageStreamEvent, RequestStartEvent, TombstoneMessage, ToolUseSummaryMessage) reconstructed from call sites. - logs.ts SerializedMessage switched from the Omit<Message, never> trick (only sound against an any stub) to an Extract-based distributed union, keeping TranscriptMessage assignable to Message. All other touched files are type-level-only adjustments (annotations on evolving arrays that inferred never[], predicate types, casts in SDK wire adapters and test fixtures) — no runtime logic changed anywhere; the full bun test suite passes 3690/0 before and after. Result: 415 → 342 tsc errors, every never-cascade in the message pipeline resolved, no file above its per-file baseline (ratchet updated). Part of issue #473. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(typecheck): narrow unknowns and fix signature drift — 342 → 94 tsc errors Clears every remaining non-test error. Honest fixes dominate: evolving array/let/useState/useRef annotations (the repo's noImplicitAny:false disables evolving types), real type guards over unknown wire payloads, hoisted react-compiler-style params annotated with their components' real Props, and callee signature corrections (useRegisterOverlay optional param, generic useVoiceState<T>, growthbook shim's accepted refresh-interval param) that each cleared several call sites. Targeted reason-commented casts only at SDK/stub/wire boundaries; no any, no new suppressions. Runtime deviations are confined to already-broken paths: benchmark.ts imported a function name that never existed (module-load crash), caches.ts called stub methods unguarded (TypeError for ant-gated users), messageActions returned undefined from a string function; CACHE_EDITING_BETA_HEADER is a best-effort reconstruction of a squash-lost constant, reachable only behind feature-gated first-party paths (flagged for review). Also: ConnectorTextBlock gains its wire-proven optional signature field; MCP server factory ambient types gain close(); ink render-node-to-output's nodeType cast fixed (intersection was collapsing the intended widening); upstreamproxy relay normalizes the socket data union. Validation: full suite 3690 green, smoke + bundle guard green; remaining 94 errors are all in test files (PR 5). Baseline lowered via typecheck:ratchet --update. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(typecheck): clean test typing, gate CI on zero tsc errors — 94 → 0 Closes the typecheck burn-down (issue #473): bun run typecheck now exits 0 across the whole repo and CI fails on any new error. Test typing: new src/test/typedMocks.ts centralizes the two bun:test gaps (asMockFetch — Mock<T> lacks fetch.preconnect; callArgs — argless-signature mocks collapse mock.calls to []). Beyond the helpers, fixes are honest: discriminated-union narrowing before member access, fixture typing with boundary casts, assertion-type corrections, and two tests realigned to production signatures they had drifted from (requestLogging logApiCallEnd args, incrementalTokenCounter tokenBudget rename) with identical assert outcomes. No assertion semantics changed; all touched suites pass. CI: the ratchet served its purpose and is retired — pr-checks now runs a plain `bun run typecheck` step; ratchet script and baseline deleted. Burn-down summary across the series: 697 → 624 (mechanical sweep) → 415 (recreate ~60 missing modules) → 342 (Message discriminated union) → 94 (narrowing + signature drift) → 0 (this PR). Validation: tsc --noEmit exit 0, full suite 3690 green, smoke + bundle guard green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(typecheck): reconcile with upstream parallel typecheck fixes Upstream landed #1591/#1592/#1595 while this series was in flight, fixing some of the same errors differently. Rebase resolutions prefer upstream where it is authoritative: their CACHE_EDITING_BETA_HEADER value ('cache-editing-2025-12-01', unconditional) replaces this series' feature-gated reconstruction; their cachedMicrocompact stub shapes (with their new test file) replace ours, with boundary casts in claude.ts where the stub's unknown[] edits meet the local pinned delete-edit shape; their reader/ReadResult stream typing in openaiShim replaces ours. MessageWithoutProgress now matches its name (Exclude<NormalizedMessage, ProgressMessage>), reconciling upstream's RenderableMessage GroupingResult with this series' message union; the @ts-expect-error upstream added for settingsTypes.generated is removed since the module now exists. tsc exit 0; full suite 3697 green (incl. upstream's new cachedMicrocompact tests); smoke + bundle guard green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(sdk): keep result usage counters required, fix assistant stub exports Addresses jatmn's and chioarub's review on the typecheck PR: 1. SDK usage contract restored: the generated result types' usage now keeps input_tokens, output_tokens, cache_creation_input_tokens, and cache_read_input_tokens as REQUIRED numbers — result messages are populated from QueryEngine.totalUsage (initialized from EMPTY_USAGE), so they are always present at runtime and strict consumers may sum them without undefined guards. The richer nested metadata (cache_creation, server_tool_use, service_tier) is modeled explicitly instead of hiding behind the index signature; the nested objects carry no index signature so the SDK's interface types stay assignable. Generator override updated and artifacts regenerated; a new package-consumer type test sums the counters and reads the nested fields so this contract cannot silently regress. The sessionHistory test fixture now carries all four counters, matching runtime shape. 2. Assistant install wizard stub mismatch fixed: dialogLaunchers imported NewInstallWizard/computeDefaultInstallDir through a module shape cast, but the assistant stub only exported default — a guaranteed runtime crash if the gated path lit up. The stub now provides real typed exports: a wizard that cancels immediately (so the launcher resolves null/user-cancelled instead of hanging on an empty dialog) and an inert computeDefaultInstallDir; the unsafe cast in dialogLaunchers is gone. Validation: tsc exit 0; full suite 3698 green (incl. the new consumer counters test); smoke + bundle guard green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
14036209cd |
Add configurable message-count compaction (#1587)
* Add configurable message-count compaction Add a /config setting for opting into message-count-based compaction thresholds and persist it in global config. Disable the legacy OPENCLAUDE_MAX_ACTIVE_MESSAGES default unless the new setting is off and the environment variable is explicitly set. Add a timeout around forked compact summaries using a child abort controller so timeouts and user aborts clean up without affecting the main thread. Document the diagnostic setting and normalize trailing line endings in Windows alias docs/script. * Address compaction PR review feedback Add a shared literal enum and normalizer for message-count compaction thresholds, use it in config, /config UI, and query threshold handling. Move the compact timeout constant to module scope and mark the /config docs snippet with a text fence. |
||
|
|
e6ce1037fe |
refactor(open-build): remove Ant employee gates (#1576)
* refactor(open-build): remove Ant employee gates * fix(open-build): address gate-removal review feedback * fix(open-build): address follow-up review findings * fix(hooks): remove stale remote fallback status * fix(open-build): keep pending background tasks visible * test(open-build): cover task footer hiding |
||
|
|
5c239eb601 |
fix(typecheck): declare bundled markdown and macro fields (#1562)
* fix(typecheck): declare bundled markdown and macro fields * fix(build): define version changelog macro |
||
|
|
7078853ea8 |
fix(typecheck): replace dead-code literal comparisons with isAntEmployee() (#1512)
* fix(typecheck): replace 'external' === 'ant' dead-code literals with isAntEmployee() The build system replaces process.env.USER_TYPE with the string literal 'external' at build time. Dead-code elimination then removes branches where 'external' === 'ant'. But TypeScript sees these as impossible comparisons (TS2367) because the narrowed literal type 'external' never equals 'ant', producing 90 type errors across 27 files. Replace all 'external === 'ant'' with isAntEmployee() and 'external !== 'ant'' with !isAntEmployee(). The function already exists in src/utils/buildConfig.ts and always returns false, so this is a behavioral no-op that makes the intent explicit and type-safe. The process.env.USER_TYPE === 'ant' pattern in other files is not touched; it will be addressed in a follow-up. Refs: #1486 * fix(build): replace isAntEmployee() calls with false at build time for DCE The bundler cannot dead-code-eliminate branches guarded by isAntEmployee() because it's an opaque function call. Extend the feature-flag preprocess plugin to also replace isAntEmployee() with false during bundling, so dynamic import() and require() calls gated behind ant-employee checks are eliminated from the external build. Also export IS_ANT_EMPLOYEE as a named constant for call-site readability and documentation, with the function kept as a convenience wrapper. * fix(build): use IS_ANT_EMPLOYEE constant for ant-only import/require guards CodeRabbit review identified that isAntEmployee() is a runtime function call that bundlers cannot evaluate for DCE. Replace all isAntEmployee() guards on dynamic import()/require() calls of ant-internal modules with the IS_ANT_EMPLOYEE boolean constant (exported as `false as const`), which the build-time source transform can replace with a literal `false` for DCE. Also extend the featureFlagPreprocessPlugin to replace IS_ANT_EMPLOYEE with false during bundling, and clean up the resulting dead imports/exports (`import { false, isAntEmployee }` → `import { isAntEmployee }`, `export const false = false as const` → removed). Affected ant-only modules (all missing from OpenClaude, must be DCE'd): - sessionDataUploader.js, eventLoopStallDetector.js, sdkHeapDumpMonitor.js - ccshareResume.js, cli/up.js, cli/rollback.js, cli/handlers/ant.js - useFrustrationDetection.js, useAntOrgWarningNotification.js - AntModelSwitchCallout.js, UndercoverAutoCallout.js |
||
|
|
07c1c56b4f |
Add Azure / Foundry launch support to VS Code extension (#1365)
* Enhance OpenClaude VS Code extension with Microsoft Foundry / Azure OpenAI support. Added configuration options for Azure API key, endpoint, and deployment settings. Updated README and documentation for new features, including a setup wizard for Azure integration. Improved terminal launch environment handling for Azure compatibility. * Fix packaged Windows helper runtime references * Use installed CLI from Windows helper aliases * Scope Windows helper env overrides to invocation * Align Windows alias docs with shipped helper |
||
|
|
492cde2619 |
Remediate audit findings, replace vulnerable Firecrawl SDK, and harden release validation (#1030)
* Harden release publish checks and remove vulnerable Firecrawl SDK Add a post-publish npm verification step to the release workflow so GitHub releases fail if the npm latest tag does not resolve to the expected version within the retry window. Update dependency pins to remediate the audit findings by moving axios to 1.16.0, upgrading the Anthropic SDK to 0.94.0, and bumping the Bedrock and Vertex wrapper packages so Bun installs dedupe onto the patched SDK. Replace the @mendable/firecrawl-js dependency with a small in-repo fetch-based Firecrawl client used by WebFetchTool and the Firecrawl web-search provider. Preserve self-hosted support, add transient 502 retry/backoff behavior, and cover the new client with focused tests. Validation: - bun test src/tools/firecrawl/client.test.ts src/tools/WebSearchTool/providers/firecrawl.test.ts - bun run build - bun run smoke - packed-install npm audit --omit dev --json returned 0 vulnerabilities * Harden Bun test isolation for release validation Fix shared-module test leaks that were breaking providerProfile in the full serialized Bun suite. - preserve full module surfaces when mocking env/provider modules - remove unnecessary env/envUtils mocks from user/install surface tests - use a fresh providerProfile module import for the Codex OAuth cleanup regression - relax the Windows-only permission assertion in providerProfile tests Validation: - bun install --frozen-lockfile - bun test --max-concurrency=1 - bun run smoke - bun run build - npm pack * Complete execa mock coverage in user test Fix the remaining cross-file Bun mock leak reported in review by expanding the persisted execa mock in src/utils/user.test.ts to include execaSync. This keeps later imports that touch secure-storage and exec helpers from failing or hanging when bun test runs files serially after user.test.ts. Validation: - bun test src/utils/user.test.ts src/utils/effort.codex.test.ts - bun test --max-concurrency=1 - bun run build - bun run smoke - npm pack * Preserve full module surfaces in user test mocks Convert the auth, config, cwd, and execa mocks in src/utils/user.test.ts into pass-through mocks with targeted overrides. This fixes the remaining Bun process-global mock leakage where later suites could fail or hang after user.test.ts because leaked partial mocks were missing exports such as auth/config helpers or execaSync. Validation: - bun test src/utils/user.test.ts src/utils/effort.codex.test.ts - bun test src/utils/user.test.ts src/utils/openclaudeInstallSurfaces.test.ts - bun test --max-concurrency=1 * Override ip-address to 10.2.0 Add a top-level override for ip-address and refresh bun.lock so the MCP SDK -> express-rate-limit path resolves to ip-address@10.2.0 instead of 10.1.0. This keeps the branch's audit-remediation scope aligned with the remaining transitive advisory path without changing the direct MCP SDK pin. Validation: - bun pm why ip-address - bun audit * fix: use cleanup-safe Firecrawl timeouts * Isolate attribution settings tests * test: remove stale provider profile import --------- Co-authored-by: JATMN <12479882+jatmn@users.noreply.github.com> |
||
|
|
3a308c11d4 |
fix(typecheck): restore control protocol type exports (#1497)
* fix(typecheck): restore control protocol type exports * fix(sdk): align control initialize contract * fix(sdk): expose control initialize response types |
||
|
|
cdc8057496 |
feat: enable HISTORY_SNIP — model-callable snip tool for context management (#1407)
* feat(snip): implement HISTORY_SNIP — model-callable snip tool for context management
- snipProjection.ts: boundary detection + view filter (isSnipBoundaryMessage, projectSnippedView)
- snipCompact.ts: pending registry, snipCompactIfNeeded, shouldNudgeForSnips, SNIP_NUDGE_TEXT
- SnipTool/: model-callable snip tool with Zod schema (prompt.ts + SnipTool.ts)
- types/message.ts: add SystemCompactBoundaryMessage export
- scripts/build.ts: enable HISTORY_SNIP: true
- QueryEngine.ts: fix snipReplay return type
* docs: add MCP_SKILLS implementation plan
* docs: add HISTORY_SNIP implementation plan
* fix(snip): prune headless store on snip-boundary replay
The snipReplay path called snipCompactIfNeeded with a {force:true} option
that the function never read, and the pending-snip set was already cleared
when the boundary was produced in query.ts — so the replay always reported
nothing removed and mutableMessages never shrank in long SDK sessions.
Prune the store by the boundary's own removedUuids via projectSnippedView
instead. Also drop two planning docs that were committed to the branch.
* fix(snip): persist snip boundary in SDK/headless transcripts
When a snip boundary was yielded in the SDK/headless path, snipReplay pruned
the in-memory mutableMessages store but the branch broke before adding the
boundary to the local messages array or calling recordTranscript. Later
transcript writes used the pre-snip messages copy, so the on-disk transcript
kept the removed messages and no snipMetadata boundary. After a restart or
--resume, loadTranscriptFile reconstructed the un-snipped history and the
context reduction was lost.
Mirror the boundary into the local messages copy and record it when the snip
executes, matching the compact_boundary path. recordTranscript is append-only
by UUID, so the pre-snip messages already on disk remain and the appended
boundary (carrying snipMetadata.removedUuids) lets applySnipRemovals prune
them on load.
Add a loadTranscriptFile round-trip test covering the previously-untested
snip replay: a persisted boundary prunes its removedUuids and relinks
survivors whose parentUuid pointed into the removed gap.
* fix(history-snip): record paired tool-result removals and scope pending snips per conversation
Two issues in the snip path:
1. Persist every removed message. snipCompactIfNeeded drops the paired
tool-result user messages of a snipped assistant tool-use message from the
live context, but the boundary only recorded the explicitly-marked UUIDs.
projectSnippedView / loadTranscriptFile replay solely from
snipMetadata.removedUuids, so on --resume the tool results came back orphaned
(their assistant message stayed removed) and part of the reduction was lost.
Record the paired tool-result UUIDs in removedUuids so replay drops the same
set the live snip dropped.
2. Scope pending snips per conversation. The pending registry was module-global
and stored model-facing short IDs, then cleared unconditionally on every
snipCompactIfNeeded pass. With concurrent in-process sessions, session B could
clear A's pending IDs (losing A's snip) or, on a short-ID collision, prune the
wrong message. Resolve short IDs to full UUIDs at mark time against the
snipping conversation's own messages, and consume only the UUIDs present in
the current message array. UUIDs are globally unique, so the registry
self-scopes: one session can no longer consume or mis-target another's.
* fix(history-snip): drop paired assistant tool-use when snipping a tool-result message
[id:] tags are appended to user messages only, so the model snips a
tool-result user message, not the assistant tool_use. The previous
pairing only ran assistant->user; snipping a tool-result left the
preceding assistant tool_use orphaned, so the next API-prep pass
synthesized a placeholder result and the tool interaction was never
actually removed from live context or from replay.
Pair in both directions: when a snipped user message's tool_results all
belong to an assistant turn, drop that assistant tool_use too (mirroring
the existing .every() guard so partially-snipped turns are kept), and
record its UUID in the boundary's removedUuids so replay drops the same
set.
* fix(history-snip): add SnipBoundaryMessage render component
HISTORY_SNIP ships enabled, so Message.tsx reaches the snip_boundary
render branch after the first snip. That branch requires
./messages/SnipBoundaryMessage.js and renders its named
SnipBoundaryMessage export, but no source file existed — the build
emitted a missing-module-stub exporting only a default noop, so the
named component was undefined and the render crashed right after a
successful snip.
Add the component, mirroring CompactBoundaryMessage: a single dimmed
line marking the snip with the removed-message count and the transcript
shortcut. The build now resolves the import (no stub) and the named
export is present in the bundle.
* build: guard against enabled-feature imports resolving to missing-module stubs
The missing-import scanner stubs any unresolved relative import to a noop
default export. For a require behind a DISABLED feature flag that is correct
(dead-code-eliminated, never bundled). But when a flag is ENABLED the gated
require becomes live and the stub silently degrades a real module to
() => null, so a named export resolves to undefined and crashes the first
time that path runs. SnipBoundaryMessage shipped exactly this way: build,
smoke, and unit tests all passed while the UI crashed on the first snip.
Feature-flag DCE removes disabled branches before bundling, so every
missing-module-stub marker left in dist/cli.mjs is reachable in the shipped
build. After the CLI bundle, fail the build on any stub marker not explicitly
grandfathered in ACCEPTABLE_RUNTIME_STUBS (seeded with the pre-existing
stubs), and warn on stale allowlist entries. Verified: removing the
SnipBoundaryMessage source makes the guard fail and name the module.
* fix(history-snip): drop unmirrored force-snip command registration
force-snip is gated on HISTORY_SNIP but its source (./commands/force-snip.js)
was never mirrored into this build, so require(...).default resolved to the
missing-module stub's noop. That truthy noop was spread into the command list
(commands.ts:252), registering a bare () => null as a command with no name,
description, or call — broken the moment anything enumerates commands. Enabling
HISTORY_SNIP turned this live, same class as the SnipBoundaryMessage crash.
Remove the registration rather than ship a phantom command: the implementation
is not present in this tree, so the honest behavior is to not register it. Drop
the matching ACCEPTABLE_RUNTIME_STUBS entry so the bundle guard stays strict.
* fix(history-snip): don't snip a tool result that would orphan a surviving tool_use
The result-side pairing dropped an explicitly-snipped tool-result user
message even when its paired assistant turn had other, un-snipped tool
calls. That left the assistant holding a tool_use with no matching result,
which the next API-prep pass repairs with a synthetic placeholder
(src/utils/messages.ts), so the snip never actually took effect and the
restored context still carried the stale interaction.
Block-level surgery on the surviving assistant is not an option: replay
(projectSnippedView / loadTranscriptFile) drops whole UUIDs, not blocks, so
the live store and a --resume would diverge. Instead, treat an unclean snip
as a no-op: a tool_use is safely removable only if its whole assistant turn
goes with it (the assistant is explicitly snipped, or every tool_use in it
has its result snipped). A tool-result user message whose results don't all
pair to a removable tool_use is kept, and no boundary is emitted when nothing
was cleanly removable, keeping live context and replay identical.
* docs(build): document the bundle-stub guard as a coarse tripwire
The guard rationale claimed every missing-module-stub marker left in
dist/cli.mjs is reachable in the shipped build and that each allowlisted
entry is latent runtime debt behind an enabled flag. That overstates it: the
scanner keys missing modules by specifier string, so a same-named specifier
missing in one importer (including a test file) can leave a marker even when
another importer resolves the real module, and a marker can sit on a path
that never runs. Reword the comment and error message so a flagged stub reads
as "inspect this", not "confirmed runtime crash"; the guard reliably catches
a NEW stub appearing where none was expected, which is its actual value.
* fix(build): canonicalize bundle stub markers before diffing the allowlist
The bundle guard compared raw `missing-module-stub:` marker text against
ACCEPTABLE_RUNTIME_STUBS, but the marker format is not stable across build
hosts: locally Bun emits the relative import specifier
(`./commands/fork/index.js`), while on the Linux CI merge run it emitted the
same grandfathered stubs as absolute source paths
(`/home/runner/work/openclaude/openclaude/src/commands/fork/index.ts`). The raw
diff therefore failed `bun run smoke` on CI for already-allowlisted stubs and
also reported them as stale.
Canonicalize both the bundle markers and the allowlist to a stable key (the
basename without extension) before diffing, so a stub matches in either form.
Basename is the only reduction that unifies a relative specifier of unknown
depth with an absolute path (a fixed path-segment count breaks single-segment
specifiers like `./dream.js`). The allowlist keeps the readable full specifiers;
diagnostics still print the raw marker. Guard against two allowlist entries
sharing a basename (which would let one silently cover an unrelated stub) by
failing the build if the canonical set is smaller than the allowlist.
* chore(build): drop allowlist stubs resolved by current main
Rebasing onto current main brings in the per-importer scanner (#1399)
and the real sources for four previously-stubbed modules, so they no
longer emit missing-module markers:
- ../../utils/hooks/ssrfGuard.js (per-importer keying, #1399/#1450)
- ./dream.js (/dream restored, #1399)
- ./UserForkBoilerplateMessage.js (source mirrored, #1451)
- ./commands/fork/index.js (unmirrored /fork dropped, #1451)
The bundle guard flagged all four as stale allowlist entries. Remove
them and refresh the guard rationale comment, which described the
pre-#1399 specifier-string scanner; the scanner now keys per importer.
* fix(history-snip): expose snip id on pure tool-result messages
appendMessageTagToUserMessage() only appended the [id:...] tag to a
string body or an existing text block. A user message that is purely
tool_result blocks (the normal shape for large Read/Bash outputs) has
no text block, so it returned unchanged and carried no visible id. Those
are exactly the highest-value snip targets the feature prompts the model
to remove, yet the model had no id to reference them by.
Append a dedicated text block holding the tag when a tool-result-only
message has no text block. The tool_result block is left intact, so snip
pairing is unaffected, and the tag lands on the API-bound copy only.
Export the function and add colocated tests covering string body, text
block, the pure tool_result case, and meta passthrough.
* fix(build): key bundle-stub guard on repo-relative path, not basename
The guard canonicalized every missing-module-stub marker to its basename
before checking the allowlist, so a future stub named constants.ts (or
cachedMCConfig.ts, MonitorMcpDetailDialog.ts) from any other directory
would be treated as allowlisted and slip past the guard — the exact
regression class the guard exists to catch.
Post-#1399 the per-importer scanner records each stub as the resolved
absolute source path, which differs across build hosts only by the
repo-root prefix. So key on the repo-relative path from src/ onward
(without extension): stable across hosts yet path-specific, so a stub
cannot mask a same-named file elsewhere. Drop the now-moot basename
collision guard and store the allowlist as repo-relative keys.
* fix(history-snip): describe snip as a queued, refusable request
SnipTool's tool result said "Marked N message(s) for removal. They will
be removed from context before the next model call" based only on the
count of input IDs. But snipCompactIfNeeded() can refuse the exact
request on the next turn: it keeps a tool_result whose paired tool_use
would survive (snipping it would orphan the tool call), freeing 0 tokens
and emitting no boundary. The model was told the output would be removed,
then saw it still in context with no failure signal, so it treated a
structural no-op as a successful context reduction.
Reword the tool result to describe the snip as a queued request that may
be refused, name the one refusal condition (would orphan a paired tool
call, e.g. one result from a parallel-tool turn), and give the model the
observable signal and repair: a kept message re-shows its [id:...] tag
next turn (tags are re-applied every API-prep pass), and snipping all of
that turn's tool results together removes them cleanly.
Add SnipTool.test.ts pinning the queued/refusable wording.
* test(history-snip): import UserMessage from its canonical module
messages.snipTag.test.ts imported UserMessage from ../query.js, which
imports the type but does not re-export it (TS2459). Import it from
../types/message.js, the canonical source messages.ts itself uses, so
the snip test files typecheck cleanly.
* fix(history-snip): make snip id tag injection idempotent
appendMessageTagToUserMessage() documents that it only mutates the
API-bound copy, but query.ts builds the next loop state's toolResults
from normalizeMessagesForAPI([update.message]) (query.ts:1589) and stores
that normalized, already-tagged output into state.messages
(query.ts:1976). With HISTORY_SNIP enabled the tag is carried forward as
conversation state, so the next turn re-normalizes it and appends the
same [id:...] a second time. In multi-tool agent loops every prior tool
result accumulates another duplicate tag each iteration, bloating context
and showing the model repeated IDs that are meant to be an API-projection
affordance only.
Guard the append: if the message already carries its own [id:<id>] token
(string body, last text block, or the dedicated tool_result text block),
return it unchanged. The token is derived from the message's own uuid, so
its presence means it was already tagged. Adds 3 idempotency tests.
* fix(history-snip): expose every parallel-tool sibling id before merge
normalizeMessagesForAPI tagged snip [id:] markers only after merging
consecutive user messages. A parallel-tool assistant turn yields several
adjacent tool_result user messages; the merge keeps just the first
operand's uuid, so on the resume/reload path (where the persisted
transcript is the untagged original) only the first sibling's id reached
the model. snipCompactIfNeeded refuses to drop one result of such a turn
(it would orphan the surviving tool_use), so the model needed every
sibling's id to request the whole-turn removal the snip prompt instructs,
and could never form it: a permanent no-op.
Inject the tag per user message before the merge instead, so each
sibling carries its own [id:] and joinTextAtSeam preserves them all,
matching the live path where each result is tagged at push time. The
post-merge sweep stays (idempotent) to tag user messages synthesized
during normalization (local_command, attachments).
Test: merging tagged parallel siblings keeps every sibling id and both
tool_result blocks.
* test(history-snip): type snip-replay test ids as UUID
loadTranscriptFile() returns Map<UUID, TranscriptMessage>, but the test
id() helper returned plain string, so every messages.has/get/
buildConversationChain call in the persisted-snip replay test raised a
TS2345 against the UUID-keyed map. Type id() as UUID (casting the literal
once at the source) so the new replay coverage does not add touched-path
typecheck debt. Also clears the same error cluster in the pre-existing
compact-boundary tests that share the helper.
* docs(history-snip): drop removed /force-snip from setMessages comment
The QueryEngine setMessages comment cited /force-snip as its example of a
message-mutating slash command, but that command was removed. Point the
example at /clear (src/commands/clear/conversation.ts), which still mutates
the message array via setMessages, so the comment stays accurate.
* refactor(history-snip): type SnipBoundaryMessage removedUuids as string[]
removedUuids holds message UUID strings throughout the snip feature, but
the SnipBoundaryMessage prop typed it as unknown[]. Narrow it to string[]
so the type carries intent and the test fixture no longer needs an
`as never` cast to satisfy the prop (the cast bypassed type checking and
could have hidden a real fixture/prop mismatch).
* fix(history-snip): drop stale cachedMCConfig stub-allowlist entry
cachedMCConfig.ts now exists in the tree and bundles as real code, so it
is no longer emitted as a missing-module stub. The grandfathered baseline
listed it among acceptable stubs, which made the new guard print a stale
warning and, worse, would silently accept a future reintroduced
cachedMCConfig stub as known debt instead of flagging it. Drop the entry so
the allowlist matches the actual bundle (VerifyPlanExecutionTool/constants
and MonitorMcpDetailDialog).
* fix(history-snip): guard paired snip drops and report queued count
Two CodeRabbit findings on the snip compaction path:
- Mixed-content turns: the inferred paired-drop ran its .every() check over
filtered tool blocks only, so an assistant turn like [text, tool_use] (or a
user [tool_result, text]) was treated as fully droppable and its text was
silently removed when the paired half was snipped. Require the whole message
to be tool blocks before an inferred drop; otherwise treat the snip as a
no-op (the explicit-snip path, where the model deliberately targets a message,
is unchanged and still removes wholesale).
- Queued count: markForSnip only enqueues short IDs it can resolve against the
conversation, but SnipTool reported sniped = input.message_ids.length, which
overstated the result when IDs were stale or unresolvable. markForSnip now
returns the distinct resolved UUIDs and SnipTool reports that length.
* fix(history-snip): align snip prompt with queued-not-guaranteed contract
The tool description told the model snipped IDs are "permanently remove[d]
... before the next model call", but snipCompactIfNeeded queues the request
and keeps a message when removing it would orphan a paired tool_use (the
tool_result already says so). Match the description to that contract so the
model does not treat a structural no-op as a guaranteed removal.
|
||
|
|
343cd1a2c9 |
fix(typecheck): restore AppState hook generics (#1503)
* fix(typecheck): restore AppState hook generics * test: enforce focused type assertions * fix: remove unused spinner api metrics prop |
||
|
|
3bf6ccd6d8 |
fix: preserve raw mode across component re-renders (issue #843) (#1198)
* fix: preserve raw mode across component re-renders (issue #843) * fix(input): only reset raw mode on explicit isActive=false, not on MCP re-render churn (issue #843) * fix: balance raw mode for isActive false transitions + add regression test Fixes the issue where cleanup closes over stale isActive=true and returns early without calling setRawMode(false), leaving rawModeEnabledCount incremented after UI no longer has active useInput. Changes: - Use a ref to track whether raw mode was actually enabled - Check the ref in cleanup instead of stale isActive closure value - Add 6 regression tests covering the true->false/unmount paths Addresses jatmn's review feedback: 'fix raw mode balance for isActive: false transitions' * fix(input): debounce raw-mode reset to survive MCP re-render churn (issue #843) * fix: add react-test-renderer dep and fix use-input test for CI - Add react-test-renderer devDependency (required by @testing-library/react-hooks) - Add @testing-library/react-hooks to INTENTIONALLY_BUNDLED in externals.ts - Fix use-input.test.ts 'MCP re-render churn' test to use isActive rerender instead of separate renderHook calls (refs don't persist across instances) * fix: address P1 raw-mode counter imbalance and P2 test-dep scope (PR #1196) P1 (use-input.ts:64-68): skip setRawMode(true) on isActive false->true when a deferred reset is pending, preventing counter over-increment that leaked raw mode on final unmount. Test updated to assert balanced 1-then-1 call pattern (no redundant setRawMode(true)). P2 (package.json, externals.ts): move @testing-library/react-hooks from dependencies to devDependencies; remove from INTENTIONALLY_BUNDLED. |
||
|
|
3be54de16b |
Make OpenGateway the default startup provider (#1493)
Default fresh installs to the Gitlawb OpenGateway profile, keep validation behavior for saved profiles, and mark OpenGateway as the recommended provider in the picker. Update setup docs and generated integration metadata to reflect the API-key-backed OpenGateway route, and add coverage for the fresh-install startup environment. |
||
|
|
353e306064 |
feat: add conversation cache and session persistence (#705)
* feat: add conversation cache and session persistence - ConversationCache: LRU cache for conversation history with TTL - Session persistence with encrypted save/load - Cross-device sync support - Integrated into sessionHistory * fix: address PR review feedback - Remove broken XOR encryption - store sessions as plain JSON - Fix key not being persisted issue - Integrate cacheSession into fetchLatestEvents for actual use - Remove dead code: no more unused integration functions - Use proper config directory path * test: add unit tests for conversationCache and sessionPersistence - conversationCache.test.ts: 8 tests (LRU, TTL, get/set, delete/clear) - sessionPersistence.test.ts: 7 tests (create, save/load, list, delete) * fix: use getClaudeConfigHomeDir for consistent config path - Replace custom path logic with getClaudeConfigHomeDir() from envUtils - Ensures consistency with rest of codebase (122 other usages) * fix: address PR #705 blockers * fix: fully address PR #705 blockers 1. Remove dead listPersistedSessions (no consumer) 2. Integrate loadCachedSession + cacheSession into fetchLatestEvents - fetchLatestEvents now checks cache first (loadCachedSession) - fetchLatestEvents now saves to cache + disk (cacheSession) 3. Add extractSessionId() function for session ID extraction 4. Proper serialization/deserialization with CacheMessage type * fix: address all non-blocking issues for PR #705 1. Fix O(n) accessOrder - use Map instead of array filtering (O(1)) 2. Remove maxMemoryMb - add deprecated function, memory limit not enforced 3. Add test override for session dir - OPENCLAUDE_TEST_SESSIONS_DIR env var All blockers and non-blockers now addressed. * fix: address PR #705 remaining blocker - Add timestamp to CacheMessage for SessionMessage compatibility - Replace as any with explicit cast for SessionMessage compatibility - Use serializeToCacheMessage consistently for both cache and persist * chore: remove PR705 review comment file * fix: preserve full SDKMessage fields in cache round-trip - Extend CacheMessage interface with id, type, model, created_at, stop_reason, usage, is_development, index - serializeToCacheMessage: preserve all relevant fields with type guards - deserializeFromCacheMessage: restore all preserved fields - Prevents data corruption on structured message history * fix: resolve PR 705 blocking issues - Fix cache-hit returns hasMore:true/firstId:null - now always fetch latest - Fix deserialize reconstructs structured content from JSON - Fix extractSessionId uses regex for robustness - Fix debounce saveSession - only persist on meaningful change (new count) Fixes reviewer feedback from gnanam1990 and Vasanthdev2004 * fix: use temp test directory in sessionPersistence test Non-blocking fix: use /tmp/openclaude-test-sessions instead of default to avoid touching real local state outside CI * fix: resolve PR 705 remaining blockers - fetchLatestEvents returns cached immediately for offline/restart support - Background fetch after returning cached - cacheSession checks message IDs not just count - Test uses temp directory * fix: resolve PR 705 remaining blockers - fetchLatestEvents returns fresh data, fixes firstId * fix: PR 705 - round-trip content type safety and pagination metadata Blocking: - Add contentIsArray flag to track whether content was originally string vs array - Serializer stores the flag; deserializer uses it instead of heuristic (startsWith '[') - Prevents corruption of string content like '[]' or '[1,2]' being parsed as JSON Non-blocking: - Wire OPENCLAUDE_TEST_SESSIONS_DIR in sessionPersistence.test.ts beforeEach - Add afterEach to clean up env var - Store hasMore/lastId metadata in cache, use real values on fallback instead of fabricating hasMore: true * fix: PR 705 - persist pagination metadata across restarts - Add pagination field to Session interface for hasMore/lastId - cacheSession() now saves pagination to persisted session - loadCachedSession() reconstructs sessionMetadataCache from persisted session - After restart/offline resume, fetchLatestEvents() returns correct hasMore from saved metadata * fix: preserve full SDKMessage shape in cache serializer Add missing type-specific payload fields to serialization/deserialization: - message (assistant/user/system payload) - uuid, session_id, parent_tool_use_id, tool_use_result (user messages) - subtype, result (result/system messages) - event (stream events) Previously only role/content were stored, dropping type-specific payloads needed by convertSDKMessage(). * fix: add error handling to PR intent scan entry point * fix: persist all SDKMessage variant fields through cache round-trip - Add error field for SDKAssistantMessage errors (was silently dropping) - Add errors field for SDKResultMessage error variant (was degrading to 'Unknown error') - Add status field for SDKStatusMessage ('compacting' was being dropped) - Add compact_metadata field for SDKCompactBoundaryMessage - Add tool_name and elapsed_time_seconds fields for SDKToolProgressMessage (was rendering undefined) - Add 11 regression tests verifying every variant round-trips correctly Fixes P1: Persisted history still does not round-trip the full SDKMessage union * fix: persist pagination cursor and use uuid for cache-dirty detection (PR review) |
||
|
|
db6017a8b7 | chore: replace strip-ansi with util.stripVTControlCharacters (#1380) | ||
|
|
64ad44abaf | chore(build): reject stale bundled external entries (#1275) | ||
|
|
1d48f8e855 |
test(build): assert WebFetch binds the real SSRF guard in the bundle (#1450)
#1399 already fixed the specifier-collision class by tracking missing relative imports per importer, which also resolves the WebFetch ssrfGuard case (the test-file string literal now only stubs the test importer, never WebFetch). The remaining gap is bundle-level coverage: the existing security-hardening test reads source only and would pass even if the shipped CLI bundle had stubbed the guard to a noop. Rebase onto current main (dropping the now-redundant scanner change) and add a dist/cli.mjs assertion alongside the /dream regression test: the real ssrfGuard blocked-address error is present and ssrfGuard is not replaced by a missing-module stub. |
||
|
|
479b0e8226 |
fix(sandbox): guard annotateStderrWithSandboxFailures against missing runtime method (fixes Bash on builds without sandbox-runtime) (#1452)
* fix(sandbox): guard annotateStderrWithSandboxFailures against missing runtime method
Fall back to a passthrough when BaseSandboxManager.annotateStderrWithSandboxFailures
is absent, so BashTool no longer throws "is not a function" on every command when the
underlying sandbox-runtime build doesn't provide the method. No behavior change when it
is present.
* fix(sandbox): complete the SDK SandboxManager stubs so they match the CLI's Proxy-noop
The SDK build stubs @anthropic-ai/sandbox-runtime two ways: the native-stub
namespace uses `new Proxy({}, { get: () => noop })` (every access is safe), but
defaultExportOverrides replaces SandboxManager/BaseSandboxManager with hollow
classes that omit annotateStderrWithSandboxFailures. The class form wins in the
SDK bundle, so SDK embedders crash on every Bash command
(`SandboxManager.annotateStderrWithSandboxFailures is not a function`) while the
CLI build — which keeps the Proxy-noop and ships the real native runtime — is
unaffected.
Add a passthrough `annotateStderrWithSandboxFailures` to both stub classes so
they behave like the Proxy form (return stderr unchanged when no real runtime is
present). Combined with the call-site `?? passthrough` guard, the SDK now
degrades gracefully on builds without sandbox-runtime instead of throwing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
276ec6ab0e | fix(ci): scan PR head for intent checks (#1461) | ||
|
|
f111eaa1b3 |
feat: enable MCP_SKILLS — discover skill:// resources as invocable skills (#1408)
* feat(mcp-skills): implement MCP skill discovery via skill:// resources
- mcpSkills.ts: fetchMcpSkillsForClient — lists MCP resources, filters skill://
URIs, reads each via resources/read, parses frontmatter, builds skill commands
with loadedFrom/source: 'mcp'. Memoized per server name (LRU, size 20).
- isSkillResource: pure helper to detect skill:// URI scheme
- deriveMcpSkillName: namespaced name builder (mcp__<server>__<name>)
- Enable MCP_SKILLS: true in scripts/build.ts
All call sites, cache-invalidation paths, and consumers were already wired
behind feature('MCP_SKILLS'). Only the module itself was missing. Fixes the
"fetchMcpSkillsForClient is not a function" crash (#856) when the flag was
force-enabled without the module present.
* fix(mcp-skills): discard hooks frontmatter from remote MCP skills
A skill:// resource's hooks frontmatter was carried through the
parseSkillFrontmatterFields spread into the Command, and the slash-command
path registered command.hooks as session hooks on invocation. This let any
connected MCP server install local command hooks that later run shell in the
user's workspace, bypassing the loadedFrom === 'mcp' inline-shell guard by
moving the payload into frontmatter hooks instead of the markdown body.
Discard hooks at the MCP construction site so untrusted remote skills can
never become registrable session hooks.
* fix(mcp-skills): discard allowed-tools frontmatter from remote MCP skills
Like hooks, a skill:// resource's allowed-tools frontmatter flowed through
the parseSkillFrontmatterFields spread into the Command. On the user-typed
slash path (/mcp__server__skill) those tools are written into
alwaysAllowRules.command, so a remote MCP server could auto-approve tool
calls (e.g. Bash) that its own skill body then drives the model to make —
with no permission prompt. The inline-shell guard for loadedFrom === 'mcp'
does not cover this.
Discard allowed-tools at the MCP construction site so remote skills can't
auto-grant tools; the model still prompts on each tool use. The model-invoked
SkillTool path already gates non-empty allowedTools via
skillHasOnlySafeProperties, but the slash path bypasses checkPermissions.
* fix(mcp-skills): skip @-mention attachment scanning for remote MCP skill bodies
A skill:// resource's markdown body flows through getMessagesForPromptSlashCommand
into getAttachmentMessages, which scans for @-mentions and MCP resource refs and
reads them before the model continues. skipSkillDiscovery only gates skill
discovery, not @-mention file reads, so a remote skill could embed @~/.ssh/config
or @.env and exfiltrate local file contents into the conversation with no tool
permission prompt — the same class as the already-stripped hooks/allowed-tools.
Gate the scan input on loadedFrom === 'mcp' (new attachmentScanInputForCommand
helper): the body still reaches the model verbatim, but its @-mentions are no
longer auto-read. Thread-level attachments are unaffected (input=null only gates
the user-input branch in getAttachments).
|
||
|
|
132539ff79 |
fix(build): restore /dream slash command in bundled CLI (#1399)
Scope missing-module stubs for relative imports to the importer file so the unmirrored KAIROS dream skill stub no longer replaces the real /dream command module during bundling. |
||
|
|
9190bd0c50 |
Harden test isolation and smoke checks (#1440)
* fix(test): isolate provider-related attribution and preconnect tests
Remove process-global provider mocks from apiPreconnect tests and exercise real env-based provider resolution with hermetic first-party setup.
Reset bootstrap/settings state around attribution tests and reload the attribution module per test so provider and client state cannot leak across suites.
Verification: bun test --max-concurrency=1 src/utils/apiPreconnect.test.ts src/utils/attribution.test.ts
* Fix full local check failures
Add a check script that runs smoke plus the full single-concurrency Bun test suite, and wire it into CONTRIBUTING, the PR template, and PR checks.
Fix Windows/full-suite failures by preferring Git Bash over the WSL bash launcher, normalizing settings paths before source matching, making path and warning-glyph tests platform-aware, and restoring persistent Bun module mocks for AgentTool and hook-chain tests.
Verified with bun test src\tools\BashTool\BashTool.errorOutput.test.ts --max-concurrency=1 and bun run check.
* fix(test): eliminate mock.module() leaks and platform-specific test failures
## Problem
The full test suite (bun test --max-concurrency=1) had 10 failing tests on
Windows. Investigation revealed 4 distinct root causes, all stemming from
bun's mock.module() not being fully reversible by mock.restore(). When a
test file replaces a shared module via mock.module(), stale bindings persist
in already-imported modules even after mock.restore() is called. This is a
known bun limitation.
The CI (Ubuntu) only showed 1 consistent failure (the attribution test),
but the Windows-local failures exposed real bugs that could surface in CI
under different test ordering.
## Changes
### src/utils/hookChains.integration.test.ts (root polluter)
This file was the biggest source of test pollution with 9 mock.module()
calls replacing shared modules (analytics, growthbook, policyLimits,
teammateMailbox, teammate, AgentTool, replBridge, etc.) with partial
surfaces. For example, the teammateMailbox mock only exported writeToMailbox
but the real module has 20+ exports including isIdleNotification,
createIdleNotification, readMailbox, etc. When mock.restore() didn't fully
undo these mocks, downstream tests got undefined for missing exports.
Fix: Import real modules via cache-busted dynamic imports before setting up
mocks, then spread the real module surface into each mock.module() call.
This way even if the mock leaks, downstream tests see the full module
surface with only the intended overrides. All 9 mock.module calls now
spread their real module counterparts.
Also fixed: the test was failing in isolation with SyntaxError because
attachments.ts transitively imports isIdleNotification from
teammateMailbox.js, which was missing from the partial mock.
### src/utils/settings/changeDetector.test.ts (Windows path normalization)
4 tests failed because getSourceForPath() normalizes paths using
path.normalize() which converts forward slashes to backslashes on Windows.
The test hardcoded Unix-style paths (/tmp/openclaude/user/settings.json)
but path.normalize produces \tmp\openclaude\user\settings.json on
Windows. The path comparison always failed, so handleChange() returned
early without triggering any callbacks or debounce timers.
Fix: Import normalize from 'path' and apply it to all test path constants
(pathsBySource, getManagedSettingsDropInDir). This matches what the
production code does.
### src/utils/exportFormats.test.ts (Windows path separator)
resolveExportFilepath() uses path.join() which produces backslash-separated
paths on Windows. The test expected forward-slash paths.
Fix: Import join from 'path' and use it in the expected value so the
assertion is platform-agnostic.
### src/utils/file.test.ts (growthbook mock leak)
importFileModuleWithKillswitchEnabled() mocked growthbook.js with only
getFeatureValue_CACHED_MAY_BE_STALE: () => killswitchEnabled. When
killswitchEnabled was false, this poisoned isAgentSwarmsEnabled() for all
downstream tests because agentSwarmsEnabled.ts has a static import of
getFeatureValue_CACHED_MAY_BE_STALE that captured the mock binding.
Fix: Import the real growthbook module and spread it into the mock, so
all exports remain available even if the mock leaks.
### src/utils/plugins/officialMarketplaceStartupCheck.test.ts (same pattern)
Same growthbook mock leak pattern. Top-level mock.module with only
getFeatureValue_CACHED_MAY_BE_STALE: () => true.
Fix: Import real growthbook module and spread into mock.
### src/tools/AgentTool/AgentTool.teammateModel.test.ts (transitive mock binding)
4 tests failed with 'Agent Teams is not yet available on your plan' because
isAgentSwarmsEnabled() returned false. The function checks
getFeatureValue_CACHED_MAY_BE_STALE('tengu_amber_flint', true) from
growthbook.js, but the static import binding in agentSwarmsEnabled.ts was
captured from a leaked mock that returned false.
Cache-busting the AgentTool.js import doesn't help because
agentSwarmsEnabled.ts is a transitive dependency that keeps its
already-loaded (mocked) growthbook binding.
Fix: Add mock.module for agentSwarmsEnabled.js in importAgentToolWithSpawnMock()
to pin isAgentSwarmsEnabled to true, matching the test's intent (it sets
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1).
## Verification
- bun run smoke: passes
- bun test --max-concurrency=1: 3019 pass, 0 fail (verified twice)
- No skipped tests (test.skip/it.skip/describe.skip), no test.todo,
no flaky markers, no test exclusions in config
## Known remaining risks
6 test files still have partial mock.module() calls on providers.js
(withRetry, officialRegistry, domainCheck, conversationRecovery, fastMode)
that don't spread the real module. These don't cause failures under current
test ordering but are latent risks if bun changes file execution order.
* Fix remaining provider mock leak risks
Address the known remaining risks from
|
||
|
|
363583faf5 |
fix(launcher): route direct Node launch paths through launcher (#1363)
Ensures package.json scripts (dev, start), scripts/provider-launch.ts, and Dockerfile route node executions through the bin/openclaude launcher rather than calling node directly on dist/cli.mjs. This resolves PR feedback: 1. Preserves the robust launcher relaunch guard, GC exposure, and test coverage already merged on main (from #1242). 2. Prevents hardcoded heap caps (--max-old-space-size=8192) from overriding user-provided NODE_OPTIONS or OPENCLAUDE_NODE_MAX_OLD_SPACE_SIZE_MB settings during development, start, or containerized runs. Co-authored-by: daltoncoder <daltoncoder@example.com> |
||
|
|
cb666c85d0 |
Fix launcher heap setup for long sessions (#1242)
Relaunch the package executable before loading dist/cli.mjs so OpenClaude starts with an effective V8 heap cap instead of setting NODE_OPTIONS after the current process has already started. The launcher now adds a default 8192 MB max-old-space-size and --expose-gc when they are missing, preserves flags supplied through process.execArgv or NODE_OPTIONS, and provides OPENCLAUDE_DISABLE_HEAP_RELAUNCH plus OPENCLAUDE_NODE_MAX_OLD_SPACE_SIZE_MB escape hatches. Update the headless loop GC hook to use Node global.gc when the launcher exposed it, while preserving the existing Bun.gc path. Clarify the entrypoint NODE_OPTIONS comment so it reflects child-process propagation rather than current-process heap sizing. Add scripts/openclaude-bin-heap.test.ts to guard launcher ordering and user override handling. Validation: bun test scripts/openclaude-bin-heap.test.ts src/entrypoints/cli.test.ts; node bin/openclaude --version returned 0.13.0 (OpenClaude). Earlier full build passed after bun install --frozen-lockfile. bun run typecheck remains blocked by existing repo-wide type errors unrelated to this change. |
||
|
|
f12eb1c9e8 |
Harden test isolation for smoke stability (#1192)
* Fix flaky smoke build checks Replace feature-flag build preprocessing with a Bun onLoad transform so smoke/build no longer rewrites tracked src files while tests may be reading them. Keep telemetry stubs ahead of the feature transform in both CLI and SDK builds, preserve non-empty text token counts in hybrid context splitting, and make the corrupted Orama stress test assert against the actual project directory used by the test. Verified with bun run smoke, bun test src/utils/hybridContextStrategy.test.ts, bun test src/utils/knowledgeGraph.stress.test.ts --rerun-each 3, and bun test --max-concurrency=1. * Harden KnowledgeGraph smoke stress isolation Give each KnowledgeGraph stress test its own temporary config directory and remove it during teardown so Orama, SQLite, and corrupted-file state cannot bleed between stress cases or later PR test runs. Reviewed at least 20 open PRs and found the recurring smoke-and-tests failure cluster is the full unit suite, especially KnowledgeGraph corrupted Orama recovery. Verified with bun test src/utils/knowledgeGraph.stress.test.ts --rerun-each 5, bun test --max-concurrency=1, and bun run smoke. * Harden smoke test isolation Audit and harden broad smoke-adjacent test suites for process-global leaks, including env/config restoration, shared registry/module mock cleanup, fetch/axios/mock restoration, and global MACRO/platform/sandbox mutations. Replace fragile render sleeps in interactive tests with output-driven waits, and isolate provider/model/profile tests behind the shared mutation lock so unrelated PRs do not inherit stale process state. Make SQLite knowledge graph cleanup clear closed on-disk databases before best-effort file cleanup, with coverage for the stale database reset path. Verified: bun run smoke; bun test --max-concurrency=1; python -m pytest -q python/tests; bun run security:pr-scan -- --base origin/main; bun run test:provider; npm run test:provider-recommendation. * Harden test isolation across smoke suite Guard process-global test mutations with the shared mutation lock across env, module mock, config cache, and storage tests.\n\nDeep-copy global config snapshots, restore transient globals precisely, and make plugin/LSP mocks expose compatible export surfaces so concurrent test loading does not poison unrelated suites.\n\nReplace fixed SDK cleanup sleeps with call polling to remove timing sensitivity.\n\nVerification:\n- bun test --max-concurrency=1\n- bun run smoke\n- python -m pytest -q python/tests\n- bun run test:provider\n- npm run test:provider-recommendation\n- bun run security:pr-scan -- --base origin/main\n- git diff --check * Close remaining test global-state leaks Guard remaining cache, plugin, console, and VS Code module-mock tests with the shared mutation lock.\n\nThis follow-up audit covers non-env process-global state that can leak across test files: tool schema cache, cache stats tracker state, plugin loader caches, console.error replacement, and VS Code mock.module usage.\n\nVerification:\n- leak-surface scans for env/global/mock.module/cache outliers\n- duplicate top-level mock collision cluster\n- affected tests cluster\n- bun test --max-concurrency=1\n- bun run smoke\n- git diff --check * Guard remaining mock restore cleanup Lock tests that call bun:test mock.restore without installing module mocks themselves.\n\nmock.restore is process-global, so these cleanup hooks can still tear down another test file's active module mocks when files run concurrently.\n\nVerification:\n- expanded leak scans for env, globals, module mocks, mock.restore, timers, argv, and caches\n- bun test src/components/useCodexOAuthFlow.test.tsx src/services/github/deviceFlow.test.ts\n- bun test --max-concurrency=1\n- bun run smoke\n- git diff --check * Harden test isolation for smoke stability Serialize tests that mutate process-global state behind the shared mutation lock, including process.env, transient globals, global config/cache state, storage mocks, and Bun module mocks. Add isolated env mutex instances for SDK mutex tests so timeout coverage no longer manipulates the live process-global mutex. Move top-level mock.module setup behind lock acquisition and restore mocks before releasing locks to prevent cross-file leakage under parallel smoke runs. Verified with: bun test --max-concurrency=1; bun test; bun run smoke. |
||
|
|
2c71e09394 |
chore(build): clean up external dependency validation warnings (#1124)
* chore(build): clean up external dependency validation warnings Remove 2 unused externals (@opentelemetry/sdk-trace-node, ink) and add 12 missing packages to package.json that are dynamically imported at runtime but weren't declared as dependencies. Also remove the unused @opentelemetry/sdk-trace-node dependency. This eliminates all 13 build validation warnings: - 8 missing OTel exporter deps (http, proto, grpc variants + prometheus) - 4 missing AWS SDK deps (bedrock, bedrock-runtime, sts, credential-providers) - 1 missing Azure dep (@azure/identity) - ink external pointed to local reimplementation, not npm package - sdk-trace-node was declared external but never imported Build validation now passes cleanly with 0 warnings. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * chore(build): eliminate external validation warnings Remove unused @opentelemetry/sdk-trace-node from externals and package.json (it's not imported anywhere in src/). Remove ink from SDK_ONLY_EXTERNALS (the project reimplements ink locally at src/ink/). Add OPTIONAL_RUNTIME_EXTERNALS list for packages that are dynamically imported but intentionally not direct deps — OTel protocol exporters and cloud provider SDKs are resolved from transitive deps or installed by users who need them. Validation now passes with 0 warnings instead of 13. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * feat(telemetry): full OpenTelemetry purge — remove all tracking dependencies Replace all @opentelemetry/* runtime dependencies with no-op stubs, delete OTel-only source modules, and remove 10 @opentelemetry packages from package.json plus @growthbook/growthbook. Key changes: - Delete 5 OTel-only modules (instrumentation, betaSessionTracing, bigqueryExporter, logger, firstPartyEventLoggingExporter) - Replace 9 modules with no-op stubs (sessionTracing, events, telemetryAttributes, firstPartyEventLogger, growthbook, index, sink, datadog, sinkKillswitch, perfettoTracing) - Remove all @opentelemetry/* imports from bootstrap/state.ts, entrypoints/init.ts, and ~20 caller files - Remove all OTel counter types, meter/provider state from state.ts - Clean externals.ts: remove 27 @opentelemetry/* entries - Clean build.ts: remove OTel native-stub namespace exports - Simplify no-telemetry-plugin.ts: remove redundant source-level stubs - Remove 10 @opentelemetry/* + @growthbook/growthbook from package.json - GrowthBook stub reads local ~/.claude/feature-flags.json for overrides Co-Authored-By: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com> * fix format * chore: regenerate lockfile after OTel dependency removal Co-Authored-By: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com> * fix(growthbook): route gate helpers through local flag overrides checkStatsigFeatureGate_CACHED_MAY_BE_STALE() and checkGate_CACHED_OR_BLOCKING() now resolve from ~/.claude/feature-flags.json like getFeatureValue_* does, so gates like tengu_thinkback, tengu_ccr_bridge, and VS Code upsells can be flipped on locally. Security gates (checkSecurityRestrictionGate) remain hard-false. Also adds 5 tests covering gate helper override behavior and unifies JSDoc wording for _getFlagValue-routed functions. Co-Authored-By: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
877b4dc886 | fix: replace raw abort signal timeouts (#1123) | ||
|
|
5873bc6714 |
feat(knowledge): introduce local Orama persistence (feature-flagged) (#1015)
* feat(knowledge): introduce local Orama persistence (clean phase 1) - Added @orama/orama and persistence plugin. - Implemented optional local-only Orama backend in knowledgeGraph.ts. - Gated Orama logic behind OPENCLAUDE_KNOWLEDGE_ORAMA=1. - Converted knowledge and conversation arc functions to async. - Fixed circular dependency between knowledgeGraph and sessionStorage by moving getProjectsDir to envUtils. - Updated all call sites and tests to handle async Knowledge API. - Verified build and tests pass on latest main. * fix: address PR review comments for knowledge feature (async finalizeArcTurn and Orama cleanup) * test: add comprehensive stress and edge case testing for Orama Knowledge Graph * fix: prevent test pollution by restoring Orama env flag in stress test * refactor: harden Knowledge architecture with concurrency locks, optimized I/O, and consolidated state |
||
|
|
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 |
||
|
|
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 |
||
|
|
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>
|