mirror of
https://github.com/Gitlawb/openclaude.git
synced 2026-08-24 10:14:19 -05:00
320d63c812b9b76911b0f37dfc2cc9adf8487887
943
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
320d63c812 |
fix(compaction): skip microcompact when compaction is off (#1800)
* fix(compaction): respect disabled microcompact setting Skip automatic query-loop microcompact when the message-count compaction threshold is explicitly set to off, while preserving default, numeric, and explicit compact behavior. * test(query): isolate auto-compact config regression * test(query): type auto-compact deps fixture |
||
|
|
ab8645da9f |
fix: OpenClaude native launcher after Linux install (#1798)
* fix linux openclaude native install launcher Create the package-aware OpenClaude launcher for native installs while keeping the downloaded native payload name unchanged. Repair the native launcher after npm cleanup with a relink-only helper so npm uninstall cannot remove ~/.local/bin/openclaude. Update protocol registration and cleanup fallbacks to use the OpenClaude command name, with regression coverage for install surfaces. * test linux install repair flow |
||
|
|
13cf30afa4 |
fix(moonshot): Add verified Kimi effort metadata (#1796)
* Add verified Kimi effort metadata Add kimi-k2.7-code to the Kimi Code gateway catalog and the direct Moonshot catalog, ordered newest/highest first. Annotate Moonshot and Kimi Code Kimi models with context/output limits, reasoning capabilities, and reasoning_effort metadata limited to the verified low/medium/high levels. Add runtime metadata, effort resolution, and provider override tests covering catalog order, qualified aliases, stale xhigh clamping, and Kimi Code request serialization. Validation run locally: bun run build; bun run smoke; bun run check; bun run test:provider; bun run typecheck; bun run integrations:check; py -3.13 -m pytest -q -p no:cacheprovider python/tests; bun run typecheck:type-tests; bun run test:provider-recommendation; bun run security:pr-scan; focused effort/runtime/client/providerConfig tests; git diff --check. * Fix Kimi K2.7 PR review findings Clamp Kimi K2.7 max output metadata to 32,768 for Kimi Code, direct Moonshot, and Atlas Cloud so runtime limits do not advertise the context window as a generation cap. Update the Moonshot runtime metadata assertion to the corrected cap and keep Hicap's separate K2.7 cap unchanged. Restore globalThis.fetch in the Kimi Code providerOverride test with try/finally to avoid cross-test leakage. Validation: bun test --feature=UNATTENDED_RETRY src/integrations/runtimeMetadata.test.ts src/services/api/client.test.ts src/utils/effort.codex.test.ts; bun run integrations:check; bun run build; bun run test:provider; bun run typecheck; git diff --check; bun run check. * Fix Moonshot K2.6 and K2.5 output caps Raise direct Moonshot Kimi K2.6 and K2.5 max output metadata from the default token count to the provider context-budget ceiling. Add runtime metadata assertions so direct Moonshot K2.6 and K2.5 keep the 262,144 output cap while K2.7 Code remains capped at 32,768. Validation: bun test --feature=UNATTENDED_RETRY src/integrations/runtimeMetadata.test.ts; bun run integrations:check; bun run typecheck; git diff --check; bun run build. * Clamp Atlas Kimi K2.7 effort levels Remove xhigh from the Atlas Cloud moonshotai/kimi-k2.7-code metadata so the gateway no longer advertises an unverified effort level for that model. Update providerOverride and effort metadata tests to cover the exact Atlas K2.7 path and assert stale xhigh settings clamp to high. Validation: bun test --feature=UNATTENDED_RETRY src/services/api/client.test.ts src/utils/effort.codex.test.ts src/integrations/runtimeMetadata.test.ts; git diff --check; bun run integrations:check; bun run typecheck; bun run build. * Address Kimi metadata review findings |
||
|
|
6f794f4185 |
fix(xAI): Update xAI model metadata and effort handling (#1795)
* Update xAI model metadata and effort handling Refresh xAI direct provider catalog with live Grok 4.3, Grok Build, and Grok 4.20 model metadata, aliases, context windows, and output caps. Align Atlas Cloud xAI entries with the refreshed model aliases and verified effort semantics. Treat Grok Build as a coding model without reasoning effort support while keeping its xAI Responses endpoint override. Update xAI defaults, display names, alias descriptor lookup, and regression coverage for runtime metadata, effort selection, context limits, model options, and vision capability lookup. Validated with bun run integrations:check, focused provider/model tests, bun run typecheck, bun run build, and git diff --check. * Address xAI PR review findings Add the Grok 4.20 max output cap to both xAI descriptor paths and assert the runtime metadata. Keep route-less vision catalog lookup from resolving aliases globally while preserving route-specific alias resolution. |
||
|
|
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. |
||
|
|
0b4000042e |
fix(bash): correct off-by-one in truncated-line count (#1786)
* fix(bash): correct off-by-one in truncated-line count formatOutput reported one more truncated line than were actually omitted. The line straddling the cut has its head shown in the kept output, but counting newlines from maxOutputLength and adding one counted that partially-shown line as truncated. Drop the +1 so the "[N lines truncated]" marker counts only fully-omitted lines, and add a regression test for the previously untested truncation branch. * fix(bash): count the line omitted at a truncation boundary When the cut lands exactly on a line boundary (the char before maxOutputLength is a newline), the line before it is shown in full and the next line is fully omitted but has no preceding newline in the omitted suffix, so the newline count missed it. Add that line back when cutAtLineBoundary, leaving the mid-line (straddle) case unchanged. Adds a boundary regression test alongside the straddle one. |
||
|
|
9c298112dc |
feat(claude): add Opus 4.8 model support (#1769)
* feat(claude): add Opus 4.8 model support Adds Claude Opus 4.8 alongside 4.7 in the model registry, picker, pricing, integrations, and 1M-context support. Mirrors the established 4.7 pattern so longer suffixes resolve first in canonical-name matching. - configs: CLAUDE_OPUS_4_8_CONFIG + opus48 registry entry - model.ts: canonical resolver, default-model dispatch (1P -> 4.8, 3P bumped to 4.7), display & marketing names - modelOptions: getOpus48Option in PAYG 1P/3P, opusplan description - modelCost: COST_TIER_5_25 pricing - context: 1M-capable assertion - prompts: FRONTIER_MODEL_NAME -> Opus 4.8 - integrations: hicap gateway, nearai brand/vendor/model entries - claude brand catalog: new defineModel block Tests: extends modelSupports1M coverage for 4.8. * fix(models): gate Opus 4.8 out of PAYG 3P picker until rollout Opus 4.8 was being added to the third-party (3P) model picker while getDefaultOpusModel() keeps non-first-party usage on Opus 4.7. Remove the 3P option until 3P rollout is active; first-party picker is unaffected. Addresses CodeRabbit review on #1769. * test(integrations): cover NearAI anthropic/claude-opus-4-8 route Adds focused regression coverage for the new Opus 4.8 provider/model path: asserts the NearAI vendor catalog exposes the anthropic/claude-opus-4-8 entry and that it resolves through its modelDescriptorId to a registered NearAI model descriptor (vendor/brand nearai, correct default model + label), plus the NearAI route base URL. Addresses CodeRabbit's [Minor] request to test the exact route. * fix(models): wire Opus 4.8 into adaptive thinking, 3P fallback, and knowledge cutoff Addresses jatmn's review findings on #1769. - [High] thinking.ts: add opus-4-8 to the adaptive-thinking allowlist. Without it, claude-opus-4-8 hit the generic opus exclusion and returned false, dropping first-party Opus 4.8 into budget-based thinking instead of thinking: { type: 'adaptive' }. Adds a regression test (provider mocked to a non-1P value so the allowlist is the only reason 4.8 returns true). - [Medium] validateModel.ts: add an opus-4-8 -> opus47 entry to get3PFallbackSuggestion so an unavailable Opus 4.8 selection suggests 4.7. - [Medium] prompts.ts: getKnowledgeCutoff now returns "January 2026" for claude-opus-4-8 and claude-opus-4-7 instead of falling through to the stale generic "January 2025". - [Low] modelOptions.ts: update the PAYG 1P picker comment to include Opus 4.8. The betas.ts structured-outputs / auto-mode allowlists are intentionally left unchanged for this PR's scope (4.7 is also absent; auto mode is gated on PI safety probes) — to be revisited with safety-research before enabling. * fix(models): update remaining model-launch markers for Opus 4.8 default Addresses jatmn's follow-up review on #1769 — markers missed when Opus 4.8 became the default. - [P2] commitAttribution.ts: add explicit `opus-4-8` and `opus-4-7` branches to sanitizeModelName before the broad `opus-4` fallback, so commit/PR attribution shows the real model instead of `claude-opus-4`. Adds a focused regression test (commitAttribution.modelName.test.ts; mutation-checked). - [P2] attribution.ts: update the unknown-first-party-model co-author fallback from 'Claude Opus 4.6' to 'Claude Opus 4.8', and the matching test expectation. Also fixed a sibling de-dup test that was passing only by coincidence (it hit the 4.6 fallback): point it at a model the public-name map actually recognizes (dot form) so it exercises the real prefix-dedup path. - [P3] fastMode.ts: FAST_MODE_MODEL_DISPLAY 'Opus 4.6' -> 'Opus 4.8'. - [P3] context.test.ts: update the stale modelSupports1M test title/comment from Opus 4.7 to 4.8 (the current first-party default). * test(models): pin the claude-opus-4-7[1m] sanitizeModelName mapping too CodeRabbit follow-up on #1769: the test covered the suffixed 4.8 path but not the 4.7 branch with the same [1m] session suffix. Add the claude-opus-4-7[1m] case so both newly added mappings are pinned. * fix(models): extend fast-mode + default-effort gates to the current default Opus Addresses jatmn's review on #1769 — two predicates still gated to opus-4-6 only while the default Opus is now 4.8. - [P1] fastMode.ts: isFastModeSupportedByModel returned true only for opus-4-6, so for Max/Team Premium users on claude-opus-4-8 fast mode wouldn't actually enable even though FAST_MODE_MODEL_DISPLAY/the /fast command now say "Opus 4.8 only". Extend the predicate to the fast-mode-capable Opus models (4.8/4.7/4.6). - [P2] effort.ts: getDefaultEffortForModel applied the Pro/Max/Team `medium` default only for opus-4-6, so Pro/Max/Team sessions on the new default claude-opus-4-8 fell through to the generic effort path. Extend the branch to 4.8/4.7/4.6 (per the @[MODEL LAUNCH] marker). Adds regression tests for both (mutation-checked: reverting either predicate to opus-4-6 only fails them). * fix(models): wire Opus 4.8 into advisor, teammate fallback, skill vars, comments Addresses jatmn's follow-up model-launch markers on #1769. - [High] advisor.ts: modelSupportsAdvisor / isValidAdvisorModel only whitelisted opus-4-6 / sonnet-4-6, so first-party sessions on the new default claude-opus-4-8 reported the advisor tool unsupported. Add opus-4-8 and opus-4-7 to both (commands/advisor.ts and claude.ts use these centralized predicates, so they're covered). Adds a regression test (mutation-checked). - [Medium] swarm/teammateModel.ts: getHardcodedTeammateModelFallback hardcoded CLAUDE_OPUS_4_6_CONFIG -> CLAUDE_OPUS_4_8_CONFIG, so new teammates spawn on the current default. Adds a first-party test case (mutation-checked). - [Medium] skills/bundled/claudeApiContent.ts: SKILL_MODEL_VARS OPUS_ID/OPUS_NAME 4.6 -> 4.8 (the bundled claude-api skill docs don't hardcode 4.6 elsewhere). - [Low] effort.ts + figures.ts: refresh stale "max is Opus 4.6 only" comments to reflect the 4.8/4.7/4.6 runtime behavior. * test(swarm): assert provider-aware teammate fallback for Bedrock too CodeRabbit follow-up on #1769: add a non-first-party case so the provider-aware fallback is covered. Bedrock resolves to the Opus 4.8 Bedrock model id. * fix(models): give Opus 4.8/4.7 the elevated output-token limits and 3P fallback chain Addresses jatmn's review on #1769. - context.ts: getModelMaxOutputTokens only gave opus-4-6 the 64k/128k branch, so opus-4-7/4-8 fell through to the generic opus-4 branch and capped at 32k — including the new first-party default Opus 4.8. Extend the elevated branch to 4.8/4.7/4.6. Adds a regression test (mutation-checked). - errors.ts: get3PModelFallbackSuggestion had chains for opus-4-6/sonnet but not opus-4-8/4-7, so the error path suggested no fallback for the new default while validateModel.ts already does. Add opus-4-8 -> opus47 and opus-4-7 -> opus46 to mirror validateModel.ts. * fix(models): allow structured outputs on Opus 4.8/4.7 Addresses jatmn's finding #2 on #1769. modelSupportsStructuredOutputs whitelisted opus-4-1/4-5/4-6 but not 4-7/4-8, so first-party/Foundry requests on the new default Opus 4.8 lost the structured-output support that 4.6 had. Add claude-opus-4-7 and claude-opus-4-8 to the allowlist (4.6 supports it, so the newer Opus models do too). Adds a first-party regression test (mutation-checked). Auto-mode (modelSupportsExternalAutoMode) is intentionally left unchanged — it is gated on separate safety review and was not part of this finding. * fix(models): extend file-read mitigation exemption and effort callout to Opus 4.8/4.7 Addresses jatmn's remaining findings on #1769. - [P2] FileReadTool.ts: MITIGATION_EXEMPT_MODELS only held claude-opus-4-6, so the new default claude-opus-4-8 got the cyber-risk reminder appended to every file read that 4.6 did not — a behavioral regression. Add claude-opus-4-8 and claude-opus-4-7 so the recent Opus models inherit 4.6's exemption. - [P3] EffortCallout.tsx: shouldShowEffortCallout gated the medium-effort-default notification to opus-4-6 only; the same default now applies to opus-4-8, so users on the new default never saw it. Extend the gate to 4.8/4.7/4.6. Adds a regression test (mutation-checked). * fix(models): resolve Opus 4.6→4.8 drift in cost tracking, notifications, and picker strings Addresses jatmn's review on #1769 — remaining model-launch drift now that the first-party default is Opus 4.8. - [P1] modelCost.ts: getModelCosts only applied the elevated fast-mode tier to opus-4-6, so fast-mode Opus 4.8 was billed at the normal COST_TIER_5_25 rate while the picker advertised the fast-mode $30/$150 price. Extend the fast-mode cost check to the fast-mode-capable Opus models (4.8/4.7/4.6). Non-fast usage is unchanged (all three already map to COST_TIER_5_25). Adds a regression test (mutation-checked). - [P2] useModelMigrationNotifications.tsx: "Model updated to Opus 4.6" -> 4.8 (the migration lands users on the opus alias = 4.8 for first party). - [P2] commands/model/model.tsx: the 1M-unavailable error said "Opus 4.6"; made it generic ("Opus with 1M context...") since the gate matches any opus[1m]. - [P2] modelOptions.ts: getOpus46_1MOption is now provider-aware (3P → Opus 4.6, first-party → Opus 4.8); getMaxOpus46_1MOption (always first-party) → Opus 4.8. - [P3] migrateLegacyOpusToCurrent.ts: corrected the stale comment (opus alias resolves to 4.8, not 4.6). * docs(notifs): correct Opus default comment to 4.8 for 1P Comment said 4.6 but the migration notification text and the opus alias both resolve to Opus 4.8 for first-party users. Addresses jatmn P3 review note. * fix(integrations): remove duplicate claude-opus-4-8 descriptor A second claude-opus-4-8 entry (vendorId anthropic) with downgraded 200k/8192 specs duplicated the canonical 1M/128k descriptor. The artifact generator rejects duplicate (id, vendorId) pairs, so integrations:generate failed and smoke-and-tests could not pass. Removed the duplicate; the canonical entry and checked-in generated artifacts are unchanged. Addresses jatmn P1. * fix(models): address Opus 4.8 review — extra-usage label, callout test, stale copy - isBilledAsExtraUsage: recognize opus-4-7/4-8 1M variants, not just 4.6, so the "Billed as extra usage" label shows for the new default and 3P default - EffortCallout modelGate test: drop the unreliable `?ts=` cache-busting import and rely on mock.module live bindings, so the gate runs against the mocked deps on Linux CI (where the query-tagged specifier was not re-evaluated) - refresh stale "Opus 4.6+" effort help text and callout comments to reflect the recent Opus models (4.8/4.7/4.6) the gate now covers * test(effort): make Opus 4.8 callout regression deterministic via pure predicate The behavioral test mocked auth/config/effort and relied on the already-evaluated EffortCallout picking up those mocks, which is order-dependent and failed only in the full Linux CI suite (both the `?ts=` dynamic-import and the static-import variants regressed there). Extract the model check as a pure exported `effortCalloutCoversModel` and assert it directly with no module mocking, so the #1769 regression is covered deterministically on every platform. * test(effort): drop config-dependent 'opus' alias from callout regression test The bare 'opus' alias routes through getDefaultOpusModel(), whose result is environment/config-dependent, so `effortCalloutCoversModel('opus')` was false in a clean Linux CI environment even though the gate logic is correct — that single assertion was the only failure in smoke-and-tests (the explicit-id assertions passed). Assert the gate's opus-4-8/4-7/4-6 coverage with explicit canonical model ids (incl. a [1m] variant) instead, which is deterministic on every platform. |
||
|
|
4704cbc474 |
fix(core): join multi-block message text with a real newline (#1793)
extractTextFromContent joined text blocks with a literal "\\n" (backslash-n) instead of a newline. Assistant messages commonly arrive as multi-block content arrays, and the joined text feeds conversation-arc fact extraction whose regexes deliberately treat newlines as boundaries (e.g. env-var and URL values use [^\\s\\n"']+). With the literal separator a value at the end of one block absorbed the next block — e.g. API_KEY=secret123 across two blocks recorded the knowledge-graph value as "secret123\\nand..." instead of "secret123". Use a real newline so blocks stay separated. Adds a regression test asserting the extracted env-var value stops at the block boundary. |
||
|
|
2083d1cdff |
fix(openai-shim): recover GLM/Qwen XML tool calls emitted as text (#1791)
* fix(openai-shim): recover GLM/Qwen XML tool calls emitted as text GLM/Qwen-family models routed through OpenAI-compatible gateways emit tool calls as XML text (`<tool_call><function=…>`) instead of structured `tool_calls`. The shim had no parser, so these leaked into visible prose and never executed — the turn ended with no tool_use block and the agent appeared to "forget" and stop mid-task. Add `parseXmlToolCalls` covering the three dialects seen in the wild (function/parameter, GLM-native arg_key/arg_value, Hermes JSON), wired into both the streaming and non-streaming paths. The streaming path holds back text from `<tool_call>` onward (incl. an opener split across SSE deltas), converts it to tool_use blocks at finalize, and flips the finish reason to tool_calls — mirroring the existing Ollama fallback. Structured tool_calls and false-positive prose are handled losslessly. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * Update src/services/api/openaiShim.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> |
||
|
|
618e901dd8 |
fix(hicap): improve model catalog and effort routing (#1790)
* Improve Hicap model catalog and effort routing Update the Hicap gateway default to claude-opus-4.8 and add verified static catalog entries for DeepSeek V4 Pro, GLM 5.2, GPT-5.4, GPT-5.5, Grok 4.3, Kimi K2.7 Code, and MiniMax M3 with context, output, reasoning, and transport metadata. Force Hicap GPT-5.4/GPT-5.5 through the Responses transport when chat completions would reject reasoning_effort, and shape GLM 5.2 requests with the Z.AI-compatible thinking/tool-streaming behavior. Canonicalize discovered route aliases against static catalog entries so GLM and other aliased models do not duplicate or restart with the wrong API model, while preserving the session-level /model contract without mutating provider profile model lists. Merge static route catalog options with scoped/profile OpenAI-compatible discovery caches and ignore stale legacy OpenAI model caches for env-only Hicap routes. Add focused Atlas Cloud and OpenRouter regressions so these catalog changes do not break other gateways. Validation run locally: bun test --timeout 10000 --feature=UNATTENDED_RETRY src/utils/providerProfiles.test.ts src/utils/model/modelOptions.gateways.test.ts src/utils/model/modelOptions.hicap.test.ts src/utils/model/modelOptions.xiaomi-mimo.test.ts src/utils/model/modelOptions.github.test.ts; bun test --timeout 15000 --feature=UNATTENDED_RETRY src/commands/model/model.test.tsx src/commands/provider/provider.test.tsx src/services/api/client.test.ts; bun test --timeout 10000 --feature=UNATTENDED_RETRY src/services/api/providerConfig.test.ts src/services/api/providerConfig.local.test.ts src/integrations/runtimeMetadata.test.ts src/integrations/compatibility.test.ts src/integrations/routeMetadata.test.ts src/utils/providerFlag.test.ts src/utils/providerValidation.test.ts src/utils/effort.codex.test.ts; bun run typecheck; git diff --check. * Address Hicap PR review findings Rename the discovered model helper to reflect production use, seed ModelPicker focus from the canonical option value, normalize focused/selected picker aliases through the option list, and simplify requested API format support logic. Validation: bun test src/components/ModelPicker.test.tsx src/services/api/bootstrap.test.ts src/services/api/providerConfig.test.ts src/services/api/providerConfig.local.test.ts; bun run typecheck * Fix Hicap catalog id responses routing Resolve route catalog aliases before OpenAI shim runtime/support checks so Hicap catalog ids such as hicap-gpt-5.5 are probed as gpt-5.5 and keep their required Responses API transport. Validation: bun test src/services/api/providerConfig.local.test.ts src/services/api/providerConfig.test.ts src/services/api/openaiShim.test.ts; bun run typecheck * Add bootstrap regression for Hicap errored discovery Exercise fetchLocalOpenAIModelOptions with an errored Hicap discovery result so canonical catalog option mapping and duplicate removal are covered at the bootstrap payload level. Validation: bun test src/services/api/bootstrap.test.ts; bun run typecheck |
||
|
|
a5a8ccc378 |
fix: auto-continuation overly biased toward Claude-style output — breaks with non-Claude models (#1713)
* fix: auto-continuation overly biased toward Claude-style output When using non-Claude models (OpenAI, Gemini, local models, etc.), the continuation detection frequently fails, causing the agent to stop after each turn and wait for manual user input. Changes: - Expand CONTINUATION_SIGNALS verb list to include common verbs used by non-Claude models: process, download, upload, compile, train, evaluate, test, continue, generate, extract, merge, deploy, install, configure, refactor, optimize (plus their -ing forms) - Fix COMPLETION_MARKERS check to only block continuation when no continuation signal is present nearby (prevents false positives when words like 'complete' or 'done' appear mid-sentence) - Add soft fallback nudging when text has no terminal punctuation and no explicit completion signal (assumes model intends to continue by default) - Increase MAX_CONTINUATION_NUDGES from 3 to 20 for longer multi-step tasks - Pass 'interrupt' reason to abortController.abort() in the TUI control interrupt handler for consistent signal propagation Fixes #1707 * fix: add 429 retry, disable microcompact, disable command-queue abort - Add 429 rate limit retry with backoff for non-Claude providers (src/services/api/withRetry.ts) - Disable microcompact to prevent aggressive conversation trimming (src/query.ts) - Disable command-queue abort to prevent interrupting running tools (src/cli/print.ts) * fix: address PR review feedback - 429 retry: use separate counter instead of manipulating attempt - Soft fallback: remove aggressive fallback nudging all unpunctuated text - Microcompact: honor maxMessagesCompactionThreshold 'off' setting - Fix typecheck issues (pendingCacheEdits, imports) * fix: address second round of PR review - Remove 429 retry policy (out of scope for continuation fix) - Add presentProgressive check for 'complete. Now processing...' case - Restore command queue preemption (split to separate PR) - Fix PendingCacheEdits type for typecheck * fix: address third round of PR review - Fix completion guard condition (inverted logic — was blocking with continuation signals present) - Narrow presentProgressive to only 'Now [verb]ing' (avoid gerund false positives) - Restore original microcompact call (remove config gate) - Add 'interrupt' reason to permission deny abort calls * fix: address fourth round of PR review - [P1] Add 'continuing with' and 'proceeding to' to strongIntent regex so punctuated transition phrases survive terminal-punctuation gate - [P2] Pass 'interrupt' reason to abortController.abort() in PermissionContext.runHooks() for interactive permission hooks * fix: address fifth round of PR review - [P1] Add missing -ing gerund forms to line 15 (creating, writing, editing, running, checking, building, etc.) so the late gerund transition fix covers the original action verbs too - [P2] Add imperative/declarative signals for bare patterns: 'need to <verb>' ('Need to process files'), 'now <verb>' ('Now process files', with negative lookahead to avoid 'Now you' false positives), 'next (i|we) <verb>' ('Next I process files') * fix: address sixth round of PR review Addresses all 5 findings from jatmn's CHANGES_REQUESTED review: 1. (code-quality) Extract verb list to shared ACTION_VERBS array; build all continuation regexes from it via buildContinuationSignals() 2. (minor) Restrict presentProgressive to gerund forms of the same verb list instead of broad \w+ing 3. (nit) Remove accidental 'nul' entry from .gitignore 4. (medium) Add focused tests for new verbs, imperative patterns, present-progressive fallback, completion-marker guard, and verb-list deduplication 5. (minor) Add tests verifying MAX_CONTINUATION_NUDGES = 20 and the guard comparison * fix: address seventh round of PR review Addresses all 4 findings from jatmn's CHANGES_REQUESTED review: 1. [P1] Remove dead 'take' branch from VERB_ING gerund map 2. [P2] Pass 'interrupt' reason to abort() in print.ts SIGINT handler and PermissionContext.ts cancelAndAbort path 3. [P3] Fix tab indentation in query.ts, PermissionPromptToolResultSchema.ts, and permissions.ts 4. [P3] Update PR description to match actual code changes * fix: address PR review feedback (indentation, filter-based verb exclusion) - Fix indentation (2-space style) in PermissionPromptToolResultSchema.ts else-if block (jatmn review finding 1) - Use ACTION_VERBS.filter() instead of fragile v.replace(/^do\|/, '') for 'time to' regex (finding 2) - Clarify default fallback behavior in PR description (finding 4) - Note: queryLifecycle cleanup (finding 3) not applicable — field does not exist in this codebase * fix: punctuated imperative/declarative patterns now signal continuation intent The new imperative patterns (need to <verb>, now <verb>, next i/we <verb>) matched in the late-window signal check but were silently dropped when the text had terminal punctuation, because the punctuated branch only checked strongIntent / presentProgressive / endsWithColon. This left examples like 'Need to process files.' and 'Now create the component.' returning shouldNudge: false, even though the bare variants correctly returned true. Fix: add hasImperativeSignal to the punctuated gate, re-testing lowerText against the imperative patterns so punctuated action-intent signals are recognized. Also narrows the #1707 closure claim in the PR description: the default fallback remains shouldNudge: false (the broader inversion suggested in the issue introduced false positives in earlier rounds). Tests added for punctuated imperative variants. * fix: address ninth round of PR review - [Medium] Restore abortController.abort('interrupt') in onInterrupt() bridge/SDK callback to keep interrupt-reason fix consistent - [Minor] Normalize indent (tabs -> 2-space) in imperative/declarative test block in bugfixes.test.ts - [Nit] Use lateText (last 120 chars) for hasImperativeSignal check for consistency with surrounding late-window logic - queryLifecycle removal is intentional cleanup from earlier round; no longer referenced in callModel options * fix: tighten need-to pattern to exclude subject-led advice (You need to...) CodeRabbit review flagged that the `need to` continuation pattern was overmatching subject-led advice like "You need to update..." causing false-positive nudges. Added negative lookbehind `(?<!\b(?:you|i|we|...)\s+)` to both `CONTINUATION_SIGNALS` and `hasImperativeSignal` so only bare imperatives (no subject) trigger continuation. Added regression tests. Also confirmed three earlier findings were already fixed: - print.ts onInterrupt() already uses abort('interrupt') ✅ - Indentation in bugfixes.test.ts:172-200 already 2-space ✅ - hasImperativeSignal already uses lateText ✅ - queryLifecycle removal was intentional cleanup ✅ |
||
|
|
22fa5b4227 |
fix(copilot): auto-refresh Copilot token on 401 instead of only showing re-auth hint (#1766)
* fix(copilot): auto-refresh Copilot token on 401 instead of only showing re-auth hint When GitHub Copilot returns 401 'token expired', the existing flow only showed a hint to run /onboard-github (the #1042 fix) but never actually refreshed the token mid-session. Now _doRequest detects the 401 + 'token expired' body in GitHub mode, calls refreshCopilotTokenOn401() to exchange the stored OAuth token for a fresh Copilot token, updates process.env, and retries the request. Fixes #1746 * fix: promote refreshedCopilotToken in auth precedence and add 401 retry integration test - Fix auth value precedence in buildHeadersForAttempt so refreshedCopilotToken takes effect even when a CredentialPool is active (single key still creates a pool, shadowing the refreshed token on retry) - Add integration test verifying GitHub Copilot 401 'token expired' triggers refreshCopilotTokenOn401() and retries chat_completions with the new token (P2 for PR #1766 review) * test: add codex_responses 401 retry integration test (CodeRabbit P2) Adds a new test 'GitHub Copilot 401 codex_responses retries with refreshed token' that verifies the 401 retry path for the codex_responses transport (lines 2349-2390). Mocks performCodexRequest to throw APIError(401, 'token expired') on first call and return a valid SSE response on second, verifies refreshCopilotTokenOn401 is invoked and the retry uses the refreshed token. * review: shared token-expired helper, credential-pool test, edge case tests Finding 1: Promote token-expired detection into isCopilotTokenExpiredError() helper used by both chat_completions and codex_responses paths. Finding 2: Add credential-pool integration test verifying that refreshedCopilotToken takes precedence over credentialPool.next() when OPENAI_API_KEYS is active. Finding 3: Add edge case tests - 'token has expired' variant, 401 without expired substring (no refresh), and same-token refresh (retries with original token, exhausted, then fails). * review: credential source check in codex retry path, fix mock leak - Add credential source gate (apiKey === process.env.OPENAI_API_KEY) to codex_responses 401 retry path, matching the chat_completions path guard - Add same-token guard (newApiKey !== apiKey) after refresh in codex path - Remove leaking mock.module('./codexShim.js') from providerOverride test, use fetch mock instead so performCodexRequest converts 401 to APIError - Remove duplicate old test that was left behind by previous edit - Add providerOverride test verifying the credential source gate blocks refresh * review: P2 refreshedCopilotCodexToken, P3 guard continue on last attempt P2: Add refreshedCopilotCodexToken variable that takes precedence over providerOverride.apiKey on retry, mirroring the chat path's refreshedCopilotToken in buildHeadersForAttempt. Prevents stale override key from being reused after a successful refresh. P3: Guard the chat path's continue with attempt < maxAttempts - 1 so the original 401 is surfaced when the 401 lands on the final retry slot, instead of falling through to the generic 500 exit error. * review: P1 chat path credential gate, P3 log refresh failures P1: Add oldToken === (process.env.OPENAI_API_KEY ?? '') check in chat path before calling refreshCopilotTokenOn401(), matching the codex path guard. Prevents credential substitution when providerOverride.apiKey, route credential, or custom auth header was the failing credential. Add regression test for chat path with mismatched providerOverride. P3: Log caught error with logForDebugging before returning false in refreshCopilotTokenOn401() catch block, so secure storage read failures, token exchange rejections, and persistence failures are visible in debug logs instead of silently swallowed. * fix: restrict codex refresh path to Copilot/GHE endpoints only isGithubWithCodexTransport was using isGithubMode which is true for all GitHub modes including GitHub Models API and custom routes. Changed to isGithubCopilotEndpoint that checks githubEndpointType === 'copilot' || githubEndpointType === 'ghe', matching the chat path's isGithubCopilot guard. Prevents COPILOT_HEADERS and refreshCopilotTokenOn401 from being triggered for non-Copilot endpoints. * fix: move didRefreshCopilotToken assignment after credential source check The refresh flag was set before confirming oldToken matches process.env.OPENAI_API_KEY, burning the single refresh chance when the credential pool rotates to a non-matching key first. Moved inside the credential source check so it's only consumed for eligible credentials. * fix: add oldToken guard to chat-path credential source check Prevents '' === '' match when both Authorization and OPENAI_API_KEY are empty from burning the refresh flag on an unauthenticated request. --------- Co-authored-by: Gravirei <gravirei@users.noreply.github.com> |
||
|
|
0071454479 |
Update Xiaomi MiMo v2.5 catalog reasoning (#1788)
Limit the direct Xiaomi MiMo and token-plan catalogs to the supported v2.5 chat models, removing the deprecated v2/flash entries. Add OpenAI-compatible reasoning_effort metadata for low/medium/high on Xiaomi MiMo v2.5 models while keeping the provider on the existing OpenAI-compatible path. Add tests covering the token-plan catalog, raw api-key auth header, and reasoning_effort request serialization. Validation: bun run build; bun run smoke; bun run check; bun run test:full; bun run test:provider; bun run test:provider-recommendation; bun run typecheck; bun run typecheck:type-tests; python -m pytest -q python/tests; bun run integrations:check; focused Xiaomi/OpenAI shim tests; git diff --check; bun run scripts/pr-intent-scan.ts --base upstream/main. |
||
|
|
40faf256db |
fix(atlas-cloud): vendor to gateway catalog correction and added reasoning support (#1785)
* Fix Atlas Cloud gateway catalog Move Atlas Cloud from a direct vendor descriptor to a hosted OpenAI-compatible gateway while preserving the atlas-cloud preset, dedicated ATLAS_CLOUD_API_KEY handling, model env defaults, and generated integration metadata. Add shared upstream model descriptors needed by the Atlas gateway catalog so Atlas entries do not own model metadata or resolve through NearAI-scoped descriptors. Add conservative Atlas reasoning metadata for verified Kimi effort levels and Grok Build always-on/no-wire reasoning, plus resolver and OpenAI shim tests for top-level reasoning_effort serialization and no-wire behavior. * Add verified Atlas reasoning effort metadata Update the Atlas Cloud gateway catalog with route-specific reasoning controls verified through live Atlas probes. Enable top-level reasoning_effort metadata for models that accepted low/medium/high/xhigh, limit Doubao routes to low/medium/high, keep Grok Build as always-on no-wire reasoning, and leave OWL without effort controls after probes showed no reasoning output. Expand effort resolver coverage and OpenAI shim serialization tests so Atlas metadata exposes the expected effort levels and strips reasoning_effort for no-wire models. Validation: bun install; bun run build; bun run smoke; bun run check; bun run test:provider; bun run test:provider-recommendation; python -m pytest -q python/tests; bun run typecheck; bun run typecheck:type-tests; bun run doctor:runtime; bun run security:pr-scan -- --base HEAD --head 1878a1f05f7fcce7ab7098c890ff3091dafcaf66. * Add explicit Atlas catalog aliases Introduce opt-in catalog aliases for model entries and resolve those aliases through the active route catalog before sending OpenAI-compatible requests. Add Atlas aliases where descriptor IDs differ from Atlas API model IDs, including GLM, Claude coding variants, GPT, Kimi, MiniMax, Qwen, and Grok entries. Teach effort metadata lookup to use the same explicit aliases so /effort support matches routed model IDs. Cover Atlas alias routing plus OpenRouter and Hicap no-op routing in providerConfig tests, and add Atlas GLM alias effort coverage. * Strengthen Atlas routing regression tests Assert the Atlas gateway catalog resolves at least one shared model before checking for NearAI descriptor leakage. Add an Atlas full API-name fallback assertion so catalog alias routing cannot over-match provider-qualified model IDs. |
||
|
|
82fd23798e |
fix(env-file): collapse escaped backslashes in quoted values (#1773)
* fix(env-file): collapse escaped backslashes in quoted values unescapeQuotedValue only collapsed an escaped quote (\" -> "), but the closing-quote scanner (findClosingQuote/isEscapedQuote) already treats a quoted value as backslash-escaped: an odd-length backslash run escapes the following quote, so a \\ pair is consumed as a single escaped backslash when locating the closing quote. Because the unescaper left \\ untouched, the two stages disagreed and a value such as "a\\b" round-tripped to the doubled "a\\b" instead of "a\b". Collapse \\ to \ in unescapeQuotedValue so both stages share one escaping rule. Lone backslashes before ordinary characters are still preserved. * test(env-file): cover escaped backslash adjacent to closing quote Locks down the terminator interaction between findClosingQuote/isEscapedQuote and unescapeQuotedValue: a value written as "a\\" (two backslashes before the closing quote) parses to a single trailing backslash. |
||
|
|
13f7401541 |
perf(repl): batch streaming text, cache normalize, coalesce config writes (#1744)
Three independent runtime hot-path wins for long sessions and fast streams:
- Streaming text (REPL.tsx): the Ink root is a LegacyRoot, so every
per-delta setStreamingText committed a synchronous REPL render. The
preview hides the in-progress trailing line, so deltas between newlines
changed nothing on screen yet still re-rendered. Hold the full text in a
ref and publish to state only when the newline-truncated preview changes
(or on clear), dropping those no-op renders under 100-300 delta/sec
streams. Displayed text is byte-identical; the Esc-interrupt path reads
the ref so no trailing partial line is lost.
- Incremental normalize (messages.ts + Messages.tsx): normalizeMessages
flat-maps the whole transcript on every append (O(n) over 2,800+
messages). normalizeMessagesCached memoizes per-message output keyed on
(message identity, isNewChain entry flag) in a WeakMap, so unchanged
messages are reused with stable object identity (reviving downstream
memo/WeakMap bailouts and cutting GC pressure). It is an allocation/
identity optimization — the call still scans the list, it is not an O(1)
append. Proven equivalent to normalizeMessages in tests.
- Coalesced config writes (config.ts): saveGlobalConfig does a sync
lock+reread+backup+fsync per call. saveGlobalConfigDeferred queues the
updater, write-throughs the cache for read coherence, and folds the
batch into one locked write on a 500ms debounce (flushed on cleanup and
process exit). Auth-loss guard, lock and backup stay on the disk path.
Also fixes handleMessageFromStream's input_json_delta to update tool input
in place instead of reordering the updated tool to the array tail.
Review fixes (CodeRabbit + jatmn):
- REPL onStreamingText updates streamingTextRef BEFORE the showStreamingText
guard so reduced-motion / cursor-up-yank-bug terminals preserve partial
assistant output on Esc. The publish decision is extracted to a pure
helper (streamingTextPublish.ts) with streamingTextPublish.test.ts, and
replStreamingTextClear.test.ts source-scans REPL.tsx to assert both
turn-boundary paths fully clear the streaming refs (no stale re-append).
- saveGlobalConfigDeferred primes the cache (getGlobalConfig) before
enqueueing, and a direct saveGlobalConfig flushes pending deferred writes
first. Together these keep same-process reads coherent: the first deferred
counter update is visible immediately even on a cold cache, and a direct
save can no longer clobber a queued delta with a disk snapshot. Regressions
in deferredConfigWrites.test.ts.
- Extracted the deferred-write queue/debounce/write-through/drain into a
generic, disk-free engine (deferredConfigWrites.ts) with injected
storage/scheduler; deferredConfigWrites.test.ts exercises the real branch.
- config.deferredWrite.test.ts loads config via a query-suffixed specifier
so a leaked mock.module('./config.js') can no longer silently turn its
assertions into no-ops; it always exercises the real path.
- messages.streamingToolUses.test.ts covers interleaved input_json_delta
order preservation; messages.normalizeCached.test.ts now covers the
reused-message entryFlag true<->false cache transition.
- Corrected the normalizeMessagesCached comment (O(n) scan / allocation
optimization, not an O(1) append).
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
|
||
|
|
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> |
||
|
|
a1a3cfc31e |
perf(integrations): load descriptor catalog lazily on first registry read (#1742)
Split the generated output into integrationManifest.generated.ts (plain preset metadata, type-only deps) and integrationArtifacts.generated.ts (the ~10k-line descriptor graph). Add a setRegistryLazyLoader() hook in registry.ts so read getters evaluate the catalog on first access instead of eagerly at module load — cutting startup cost on paths that never read the registry (index.ts previously eval'd 60+ descriptor modules at import). Review fixes: - registry.ts ensureLoaded() keeps the lazy loader armed until it succeeds (re-entrancy guard + clear-on-success), so a throwing lazy import retries on the next read instead of leaving the registry empty. - providerSecrets.ts no longer statically imports the heavy descriptor artifacts. It sat on the bootstrap startup chain (bootstrap -> providerConfig -> providerProfile -> providerSecrets), so the static import undercut the lazy-loading goal. Import PROVIDER_PRESET_MANIFEST from the lightweight manifest module and lazily require() the descriptor arrays inside readDescriptorCredentialEnvKeys() (cached by getKnownProviderSecretEnvKeys(), so it runs at most once). - artifactGenerator.test.ts resolves artifacts by full src/integrations/generated/<name> path instead of tuple order; the last order-dependent test now uses splitGeneratedArtifacts(). - Added registry + providerSecrets regression tests (loader retry on failure; no static descriptor import on the bootstrap path). Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
6fdb1d0c46 | fix(bg): preserve provider env-file values during prompt detection (#1767) | ||
|
|
3157ee715b | Fix slash command suggestion filtering (#1664) | ||
|
|
cb689cc33a |
feat(effort): Add model-level reasoning effort routing (#1780)
* Add model-level reasoning effort metadata Introduce per-model reasoning control metadata on catalog entries and model descriptors so /effort support can be expanded without provider-wide inference. Resolve /effort through explicit model metadata first, preserve legacy allowlist behavior, and treat supportsReasoning-only entries as capability metadata that does not mutate requests. Guard OpenAI shim effort serialization with the model-level wire support check and add focused tests for capability-only, explicit metadata, opt-out, and toggle-mode cases. Document the reasoning metadata contract and provider follow-up workflow. * Expand reasoning effort routing Centralize OpenAI shim reasoning request planning so DeepSeek-compatible and Z.AI-compatible controls flow through the effort resolver instead of provider-specific shim helpers. Add compatibility metadata handling for DeepSeek and Z.AI routes while keeping supportsReasoning-only catalog entries non-controllable until exact wire formats are verified. Respect route removeBodyFields after compatibility serialization and make provider override support checks use the resolved override route/base URL instead of ambient provider metadata. Document temporary compatibility rules and add regression coverage for Atlas DeepSeek, Z.AI levels, Groq stripping, providerOverride OpenAI effort, providerOverride Groq stripping, and non-generic metadata opt-out. Verified: bun test --feature=UNATTENDED_RETRY src/utils/effort.codex.test.ts; bun test --feature=UNATTENDED_RETRY src/services/api/client.test.ts; bun test --feature=UNATTENDED_RETRY src/services/api/openaiShim.test.ts; node .\\node_modules\\typescript\\bin\\tsc --noEmit; bun run build; git diff --check. * Fix Responses API reasoning effort shape Send reasoning effort on OpenAI-compatible Responses requests using the nested reasoning object expected by the endpoint instead of flat reasoning_effort/reasoning_summary fields. Keep chat_completions behavior unchanged so OpenAI-compatible chat endpoints still receive top-level reasoning_effort. Add a regression test covering the Responses request body shape and verifying the flat fields are omitted. Validation: bun test --feature=UNATTENDED_RETRY src/services/api/openaiShim.test.ts; node .\\node_modules\\typescript\\bin\\tsc --noEmit; git diff --check. * Document reasoning effort metadata rules Make the new reasoning-effort guide discoverable from the integrations overview and reading order. Update model, gateway, and vendor onboarding docs to explain that supportsReasoning is descriptive only and does not enable /effort request mutation without verified per-model reasoning metadata. Clarify that gateway and vendor catalogs must annotate reasoning controls per exact route/model rather than provider-wide. Validation: git diff --check. * Stabilize effort resolver tests Add an optional reasoning control context so effort tests can inject provider, catalog, model descriptor, and shim metadata without mocking process-global integration/provider modules. Update effort.codex tests to use the injected context and restore only the remaining local mocks, preventing mock leakage into later full-suite provider tests. Validation: bun run check; bun run test:provider; bun run test:provider-recommendation; bun run typecheck:type-tests; bun run integrations:check; python -m pytest -q python/tests; bun run security:pr-scan -- --base upstream/main --head HEAD. * Address effort PR review findings Load integration registry before catalog reasoning lookup, isolate provider override route resolution from ambient routes, and carry explicit compat reasoning metadata into the OpenAI shim request planner. Clarify reasoning metadata documentation and add focused regression coverage for compat metadata and provider override route preference. * Fix Z.AI metadata high effort serialization Include high in the Z.AI-compatible metadata gate so high-only reasoning metadata emits reasoning_effort instead of silently dropping the user-selected effort. Add regression coverage for a high-only zai_compatible catalog entry flowing through the OpenAI shim request planner. * Fix provider override effort fallback Allow unrecognized providerOverride OpenAI-compatible routes to fall back to legacy effort support instead of dropping user-selected effort. Constrain compat metadata levels to wire-faithful high/xhigh values and clarify reserved reasoning wire formats in docs and descriptors. * Clamp provider override effort by route metadata Resolve providerOverride effort against the override model and route context before converting it for the OpenAI shim, so stale persisted effort values respect per-model metadata levels. Add regression coverage for high-only providerOverride metadata and explicit max filtering in compat metadata levels. |
||
|
|
263f07e2ab |
chore(main): release 0.20.1 (#1774)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>v0.20.1 |
||
|
|
db66f41071 |
fix(resume): tolerate malformed legacy attachment records (#1768)
* fix(resume): tolerate malformed legacy attachment records * test(resume): allow resume hook messages in recovery test * fix(resume): use validated attachment for skill listing |
||
|
|
b8c34645c9 |
chore(deps): clean npm install — fix CVEs, silence warnings (#1782)
* chore(deps): clean npm install — fix CVEs, silence warnings - bump undici 7.24.6 → 7.28.0 (7 high CVEs: TLS bypass, header injection, DoS, cache poisoning, SameSite downgrade, cross-origin routing) - bump ws 8.20.0 → 8.21.0 (2 high CVEs: uninitialized memory disclosure, memory exhaustion DoS) - add allowScripts for sharp + protobufjs to silence install-script warnings - vendor node-domexception shim (re-exports native DOMException) and override the deprecated polyfill pulled transitively by google-auth-library → gaxios → node-fetch@3 → fetch-blob Result: `npm install` reports 0 vulnerabilities, 0 warnings. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * chore(deps): update bun.lock for undici/ws bumps and node-domexception override CI runs `bun install --frozen-lockfile`, which requires bun.lock to match package.json. The previous commit bumped undici/ws and added the node-domexception shim override but didn't include the regenerated lockfile, causing frozen-lockfile CI to fail. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(publish): include vendor/node-domexception-shim in npm tarball The file: override in package.json points at vendor/node-domexception-shim, but the files array didn't list vendor/, so npm pack excluded it. End-user npm installs would fail resolving the override. Add vendor/node-domexception-shim/ to the files array. Verified via npm pack --dry-run: tarball now contains both shim files (12 → 14 files). Addresses reviewer finding #1. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
bd00b3b3c5 |
fix(bg): stream session logs with bounded memory (#1762)
* fix(bg): stream session logs with bounded memory * fix(bg): handle log follow cleanup edge cases * fix(bg): surface non-follow log read errors * test(bg): isolate log streaming temp dirs |
||
|
|
d32b6f0476 |
fix(update): stop false "development build" block on npm installs with NODE_ENV=development (#1781)
`/update` and `openclaude update` reported "Auto-update is unavailable for
a development build." even when OpenClaude was correctly installed from npm,
as long as the launching shell had `NODE_ENV=development` exported.
Root cause: `getCurrentInstallationType()` checked `NODE_ENV === 'development'`
as its first branch, before any path-based detection. A user's shell env var
then downgraded a real npm install to 'development', which routed
`resolveUpdateStrategy()` to `{ action: 'blocked', reason: 'development' }`.
Two-part fix:
1. doctorDiagnostic.ts — move the `NODE_ENV === 'development'` check to a
fallback position after all real-install path markers (bundled mode, local
npm, npm-global paths, /npm/, /nvm/, `npm config get prefix`). Path
detection runs first; NODE_ENV only classifies as 'development' when no
install path matches (i.e. an actual source-tree `bun run dev` run).
2. bin/openclaude — the heap-sizing relaunch previously used
`fileURLToPath(import.meta.url)`, which resolves symlinks. After relaunch,
`process.argv[1]` pointed at the real file target (repo path for
`npm install -g .`, package path inside node_modules for real installs),
defeating path-based detection. Preserve `process.argv[1]` (the original
invocation path, e.g. /usr/local/bin/openclaude or nvm bin symlink) so
npm-global path markers can match correctly.
Verified: `bun run typecheck` passes; `openclaude doctor` now reports
npm-global (not development) with NODE_ENV=development set on a real
npm global install.
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
|
||
|
|
28bbec4948 |
fix(memory): bound memory-directory scanning work (#1757)
* fix(memory): bound memory-directory scanning work * fix(memory): harden bounded memory scanning follow-up |
||
|
|
701b68c215 |
fix(query): prevent spurious Windows interruption prompt by passing 'interrupt' reason (#1733)
* fix(query): prevent spurious Windows interruption prompt by passing 'interrupt' reason
* fix(cli): pass 'interrupt' abort reason and add stop-hook regression tests
* test(query): exercise handleStopHooks abort-reason branch directly
Adds focused coverage that imports and runs handleStopHooks() while a
Stop-hook generator is being consumed, asserting abort('interrupt')
suppresses the synthetic '[Request interrupted by user]' message and a
default abort still yields it. Closes jatmn's P2 regression request.
|
||
|
|
669ecdfa8b |
fix: prevent recursive debounce infinite loop in team memory sync (#1726)
* fix: prevent recursive debounce infinite loop in team memory sync * fix: chain cap-reschedule push after in-flight promise instead of running concurrently * fix: preserve currentPushPromise when replaced during yield point + tests * fix: clear pending debounce timer in _resetWatcherStateForTesting * fix(teamMemorySync): prevent concurrent pushes, make clearing identity-safe, and avoid duplicate follow-ups * fix(teamMemorySync): guard capped follow-up push against suppression; fix test gaps Addresses jatmn's three P3 findings on #1726. - watcher.ts: the capped reschedule path queues a serialized follow-up executePush() without consulting pushSuppressedReason, whereas schedulePush() short-circuits on suppression. If a permanent failure set suppression while the in-flight push was running, the queued follow-up fired one redundant, identically-failing call. Skip executePush() when pushSuppressedReason is set, mirroring schedulePush(). Adds a regression test (mutation-checked: removing the guard makes it fail). - watcher.test.ts: the "resets to 0 when executePush completes" test was vacuous — rescheduleCount started at 0 and executePush() never owns that reset (onDebounceFire and _resetWatcherStateForTesting do). Renamed to "clears pushInProgress when executePush completes" and dropped the misleading rescheduleCount assertion; the reset path stays covered by the onDebounceFire cap test. - watcher.test.ts: the top-level mock.module('./index.js') is process-global and mock.restore() does not undo it. Follow the spawnCtxAgent.test.ts pattern — (re)register the mock in beforeEach and restore the real module in afterEach — so it can't silently bleed into a future test that imports teamMemorySync/index. |
||
|
|
02d43b6942 |
fix: surface swallowed error in plan file write (#1725)
* fix: surface swallowed error in plan file write * test: add regression test for plan file write failure path * test(ExitPlanModeV2Tool): scope fs mock to single test and pass full call signature Two CodeRabbit concerns: 1. The global fs/promises mock in beforeAll was leaking into unrelated test suites (e.g. loadAgentsDir.test.ts) because mock.module() cannot be cleanly undone mid-suite. Move the mock into the single test that needs it and call mock.restore() in a finally block so it is torn down as soon as the assertion completes. 2. ExitPlanModeV2Tool.call() was invoked with 2 args but the Tool base class signature requires 4 (input, context, canUseTool, parentMessage). Pass the two extra args so the test typechecks against the real signature. * fix(test): prevent fs/promises mock leak from ExitPlanModeV2Tool test Addresses jatmn's P2 on #1725. The previous test used mock.module('fs/promises', ...) in a try/finally with mock.restore(). On Linux CI the mock was still active when loadAgentsDir.test.ts ran, causing all five agent-fixture tests to fail with "write failed" traces pointing back to this file. Fix: add afterEach(mock.restore()) to guarantee the mock is torn down after each test, regardless of pass/fail path. Verified locally by running both test files in the same process — 6/6 pass. * fix(test): avoid fs/promises mock entirely to prevent CI leak The afterEach(mock.restore()) approach still leaked the fs/promises mock into loadAgentsDir.test.ts on Linux CI (CodeRabbit comment at 12:12 EST). Bun's mock.module on Linux replaces the module cache in a way that persists across test files in the same process. Fix: don't mock fs/promises at all. Instead, re-mock plans.js to point getPlanFilePath at a nonexistent directory, so the REAL fs/promises.writeFile rejects with ENOENT. Nothing to leak. Verified: both test files pass in both orderings with --max-concurrency=1. * Fix race on exiting plan mode by saving plan file before permission updates and add regression tests asserting no side effects on error * fix(test): completely isolate mock module by using teammate context APIs instead of module mock override * test(plan-mode): cover the React write-before-permission guard; drop global fs/promises mock Addresses jatmn's two P2 findings on #1725. - Extract the plan-file write guard from ExitPlanModePermissionRequest's handleResponse into an exported persistPlanFileBeforeExit() helper (jatmn suggested extraction for testability). The component path is unchanged: on write failure it stays in plan mode (returns early) and queues a 'plan-save-error' notification. Adds focused tests for both success and write-failure (mutation-checked: a helper that swallows the failure fails the test). Tests use real filesystem paths — a temp file for success and a path with a missing parent dir for failure — so they need no fs/promises mock. - ExitPlanModeV2Tool.test.ts: replace the process-global mock.module('fs/promises') in both write-failure tests with the same real-failing-path mechanism (a plan path whose parent dir does not exist → genuine ENOENT). This removes the fragile core-module mock jatmn flagged, which mock.restore() cannot undo and which could leak into other test files. * test(plan-mode): assert the specific ENOENT write failure in ExitPlanModeV2Tool tests Addresses CodeRabbit's review on #1725: the two write-failure tests asserted a generic rejects.toThrow(), which would pass on any error. Tighten both to rejects.toThrow(/ENOENT/) so they lock in the intended missing-parent-dir write failure rather than masking an unrelated error. The post-write side-effect assertions (persistFileSnapshotIfRemote / writeToMailbox / setAppState not called) are unchanged. * test(plan-mode): add rendered guard test for handleResponse write failure Addresses jatmn's P3 on #1725: the only coverage for the write-before-permission guard was at the persistPlanFileBeforeExit helper level; there was no test that handleResponse itself returns early on write failure. Renders ExitPlanModePermissionRequest (following the MonitorPermissionRequest harness), points the V2 plan file at a path whose parent dir is missing (real ENOENT), confirms the first accept option, and asserts the plan-save-error notification is queued while toolUseConfirm.onAllow / onReject and onDone are NOT called. Mutation-checked: removing the `if (!saved) return` guard makes it fail, so a future refactor cannot silently drop the guard. * fix(test): type addNotification mock so render test typechecks CodeRabbit (correctly) flagged that `mock(() => {})` infers a zero-arg tuple, so `call[0]` was TS2493 and `bun run typecheck` (CI) failed. Type the mock param with the real `Notification` type, which also lets the assertion drop its cast. |
||
|
|
adcf5e5839 |
fix(permissions): bound the speculativeChecks cache with FIFO eviction (#1724)
Internal/upstream defensive maintenance: cap the speculativeChecks Map at MAX_SPECULATIVE_CHECKS_SIZE (1000) and FIFO-evict the oldest entries after each insert, so the bash-classifier speculative cache can't grow unbounded when the classifier path is active (it is a stub in the open-source build; this guards the upstream build where it is reachable). Pure cache-bounding — no runtime or permission behavior change, and no new env vars. Adds focused FIFO-eviction regression tests via a small `_test` surface (mutation-checked: neutering eviction fails them). Rebased onto current main. |
||
|
|
3fb718f403 |
fix(worktree): base agent isolation worktree on parent HEAD, not origin/main (#1652)
* fix(worktree): base agent isolation worktree on parent HEAD, not origin/main createAgentWorktree delegated to getOrCreateWorktree with no base, so the non-PR path checked out origin/<defaultBranch> whenever that remote-tracking ref existed locally. An isolated agent (isolation: "worktree") is expected to see the same committed state as the parent session, but instead got an older tree and missed files that exist only on the active branch. Resolve the parent session's HEAD from the session cwd and thread it through getOrCreateWorktree as a new `baseRef` option (used verbatim, then rev-parsed to a SHA). Falls back to the prior origin-based behavior when HEAD can't be resolved (e.g. a repo with no commits). EnterWorktree/PR worktree paths are unchanged. Fixes #1586 * test(worktree): make agent-base regression test hermetic; add explicit cwd seam The integration test went through createAgentWorktree's ambient getCwd(), which reads process-global cwd state. bun runs test files concurrently in one process and a sibling test mutates that global cwd, so the agent-base test raced and failed in the full suite (worktree based on origin/main, missing the feature-only file). Add an optional `cwd` to createAgentWorktree, used for both the canonical git-root and the parent-HEAD lookups (defaults to getCwd()). The test pins it explicitly so it no longer depends on the raced global cwd. Verified it passes isolated and in the full src/utils suite, and still fails on the pre-fix code. * test(worktree): isolate agent-base regression from leaked module mocks Per review: the regression test imported createAgentWorktree (and thus worktree.ts's execFileNoThrow.js dependency) at module load, so it could bind process-global bun mock.module state left by a neighboring suite before establishing its own isolation. Hold the shared mutation lock for the whole test so it never runs interleaved with suites that mock execFileNoThrow.js, and import the worktree module only after the lock is held, via a cache-busted dynamic import, so the binding resolves against the real module. * test(worktree): use execa and a longer timeout in the agent-base regression Per AGENTS.md, src/utils tests shell out via execa rather than child_process, so swap the git() helper to execaSync. The test also performs real git work (init, commit, branch, worktree add) that can exceed Bun's default 5s timeout on slower/Windows runners, so give it an explicit 15s budget. * test: stop two suites leaking partial module mocks process-wide Bun's mock.module is process-global and is not reverted by mock.restore(), so a partial-surface mock left active by one suite breaks any later suite that imports the same module — it sees a module missing the un-mocked exports and fails to link with "named export not found". - setupGitHubActions.test.ts mocked config.js with only saveGlobalConfig. Spread the real surface (as it already does for execFileNoThrow/browser) and restore it in afterEach, so config.js keeps its full surface for later suites. - osc.test.ts mocked execFileNoThrow.js and tempfile.js with partial stubs and never restored them. Spread the real surfaces and gate each overridden export on an active-suite flag, so when osc's tests aren't running the exports delegate to the real implementation instead of leaking stubs. This unblocks worktree.agentBase.test.ts (which loads worktree.ts → config.js / execFileNoThrow.js) when it runs after either suite. * Revert mock-isolation changes that regressed the full test suite The previous commit reworked the leaking config.js / execFileNoThrow.js mocks in setupGitHubActions.test.ts and osc.test.ts (and swapped the worktree test helper to execa). While it fixed the two-file reproductions, it regressed the full smoke-and-tests run: createAgentWorktree began failing with "not in a git repository" in the combined suite. Revert to the last green state while a more robust isolation approach (process-level isolation for the real-git test) is worked out. * test(worktree): run the agent-base regression in an isolated process The previous in-suite isolation attempts couldn't survive the full test run: createAgentWorktree shells out to git via execFileNoThrow.js, which other suites mock with `mock.module` — a process-global override Bun cannot reliably revert, so any leaked stub made the test fail with "not in a git repository" depending on suite ordering. Move the actual createAgentWorktree call into a standalone child process (worktree.agentBase.fixture.ts) that loads only the real modules, and keep the git repo setup and assertions in the test using real git directly. Nothing the shared test process mocks can reach the child, so the test is now order- and leak-independent. Verified passing in isolation, alongside the two leaking suites that previously broke it, and in the full suite. * test(worktree): register agent-base fixture as a knip entrypoint The agent-base regression runs createAgentWorktree in a standalone child process (worktree.agentBase.fixture.ts) to escape leaked module mocks. knip sees no static importer for it — it is spawned via execFileSync — and the deadcode check fails it as an unused file. It is a genuine process entrypoint, so add the *.fixture.ts glob to knip's entry list. |
||
|
|
66ddbece19 |
fix(bridge): match loopback hostname exactly in HTTPS credential guard (#1760)
* fix(bridge): match loopback hostname exactly in HTTPS credential guard
The bridge requires HTTPS for non-localhost base URLs to protect OAuth
credentials in transit, but the guard decided "localhost" via substring
checks (`baseUrl.includes('localhost')` / `.includes('127.0.0.1')`). Any
remote URL that merely contains that text — `http://evil.example.com/localhost`,
`http://evil.localhost.com`, `http://127.0.0.1.evil.com` — slips past the
check and is allowed to carry credentials over plain HTTP.
Extract `isLocalhostBaseUrl`, which parses the URL and matches the hostname
component exactly (mirroring the logic already in `buildSdkUrl`), and route
both guard sites plus `buildSdkUrl` through it. A malformed URL is treated as
non-local so the HTTPS requirement still applies.
Add direct coverage for the helper, including the substring-bypass cases.
* fix(bridge): reject mixed-case HTTP scheme in credential guard
The HTTPS credential guard tested `baseUrl.startsWith('http://')`, which only
matches the exact lowercase scheme. `HTTP://example.com` / `Http://example.com`
slipped past — the URL parser normalizes the scheme to `http:`, so credentials
would still be sent over plaintext.
Fold the scheme test into the parse: add `isInsecureHttpBaseUrl`, which reads
`new URL(...).protocol === 'http:'` (case-normalized) and requires a non-local
host, and route both guard sites through it. Malformed URLs return false.
Extend coverage with mixed-case HTTP/HTTPS cases.
|
||
|
|
39604fe871 |
chore(main): release 0.20.0 (#1684)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>v0.20.0 |
||
|
|
adafde30fa |
feat(integrations): add GLM 5.2 as an Opengateway-routed model (#1772)
* feat(integrations): add GLM 5.2 as an Opengateway-routed model Add an `opengateway-glm-5.2` catalog entry (apiName `z-ai/glm-5.2`) to the gitlawb-opengateway gateway, reusing the existing `glm-5.2` model descriptor. Routes GLM 5.2 through the credit-billed Opengateway (OpenRouter upstream) alongside the existing direct Z.AI vendor path. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * test(model): add z-ai/glm-5.2 to the opengateway picker fixture The model-picker option-order assertion enumerates the gitlawb-opengateway catalog; add the new z-ai/glm-5.2 entry (after qwen/qwen3.7-max) so it matches the catalog. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
1b1dfcfdbc |
Feat/ads sponsored tips (#1674)
* initial commit * feat(ads): passive per-turn earning tips + mask earn code - Gitlawb earning tips now appear on a per-turn cadence (default every 2nd tip slot, OPENCLAUDE_ADS_TIP_EVERY-tunable) for opt-in users, bypassing the per-startup sponsored gate that only ever showed one ad per session. - Each shown earning tip fetches a real impression and confirms after the dwell, crediting opengateway credits; fails silent so ads never break the CLI. - Mark /ads isSensitive so the earn code is redacted from history. * feat(ads): mask earn code via paste dialog; never accept it inline /ads on always opens a masked TextInput dialog (mask="*") — the code is never typed inline, because the terminal echoes inline args as you type (isSensitive only redacts history after submission, not the live echo). An inline /ads on <code> now also opens the dialog and warns that the typed code is exposed and should be rotated. Converts /ads to a local-jsx command. * fix(ads): address CodeRabbit review on PR #1674 - ads.ts: hard 5s timeout (AbortController) on both fetchNextTip and confirmTip so a stalled connection can never hang the spinner-tip path; the abort timer is unref'd. (fetchWithProxyRetry forwards init.signal and treats AbortError as non-retryable.) - gitlawbEarn.ts: unref the best-effort confirm timer so it can't keep a short-lived CLI run alive for the dwell window. - ads.tsx: clear the stored earnCode on `/ads off` (it's a credential, no reason to keep it at rest after opt-out); fix the `/ads on` doc comment to match the always-masked-dialog behavior. - ads.test.ts: restore ADS_BASE_URL in afterEach; add submit/cancel coverage for the masked dialog (enables + persists code / cancellation message) and assert `/ads off` clears the code. - gitlawbEarn.test.ts: restore ADS_BASE_URL + OPENCLAUDE_ADS_TIP_EVERY in afterEach to stop env leaking across suites. - tipScheduler.test.ts: cover the earning-tip branch precedence in getTipToShowOnSpinner (driven via the existing config mock + env, no module mock — avoids the bun mock.module cross-file leak). Testing: tsc clean; tips+ads suite 36 pass; smoke green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * feat(ads): contextual sponsored tips — share the latest prompt for ad matching Switches the earning tips from generic to contextually-matched ads. When the viewer enables sponsored tips (the /ads on dialog now discloses this), the client sends their most recent prompt to the ads service, which matches a relevant ad via Gravity's contextual endpoint. - types.ts: TipContext gains `latestUserMessage` (opt-in earning tip only). - REPL.tsx: extract the latest user message at the spinner-tip pick site and thread the full TipContext into tip.content() (previously only {theme}). - gitlawbEarn.ts: pass ctx.latestUserMessage to fetchNextTip. - ads.ts: new sanitizeForAds() redacts secrets/JWTs/emails/long hex and truncates to 500 chars; fetchNextTip POSTs { context:{messages:[user]} } when a prompt is present (else GET, identity-only). - ads.tsx: /ads on dialog + enable message disclose that the recent prompt (secrets redacted) is shared with the ad partner — consent folded into enabling. Privacy: explicit disclosed consent, minimal context (last prompt only), sanitized client-side (the ads service re-bounds size server-side too). Testing: tsc clean; new ads.test.ts (7 sanitize cases); ads+tips suite 43 pass; smoke green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(ads): show the real advertiser + ad click URL in earning tips renderEarningTip hardcoded the Gitlawb name + gitlawb.com URL, so every served ad rendered as "Sponsored · Gitlawb — … gitlawb.com" and discarded the actual ad's `name` and `link`. Pass the served ad's advertiser name and click URL (Gravity's tracker — required for click attribution/payout) into the renderer; fall back to Gitlawb only for the static no-ad line. Testing: tsc clean; tips suite 16 pass; smoke green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * feat(ads): hyperlink the advertiser name instead of printing the tracker URL Sponsored/earning tips render the advertiser as a terminal hyperlink to the ad's click URL (Gravity's tracker) via a shared renderSponsorLink helper, rather than printing the long tracker URL inline. Clicks still route through the tracker, so attribution/payout are unchanged. Testing: tsc clean; tips/ads suite 46 pass (incl. tipLink); smoke green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(ads): address CodeRabbit review on the contextual ads changes Security: - tipLink.ts: sponsor name/url are advertiser-controlled (untrusted). Strip C0/C1 control chars from the name and accept only http(s) URLs (parsed via URL()), preventing terminal escape-sequence injection and javascript:/file: click targets. - ads.tsx: soften the consent copy to "best-effort secret redaction" (both the dialog and the enable message) — sanitizeForAds is heuristic, not absolute. Correctness: - ads.ts: clamp dwell_ms to a finite, non-negative integer (malformed values no longer yield NaN/Infinity in the confirm-delay math). - gitlawbEarn.ts: never point a third-party advertiser name at the Gitlawb URL — a real ad uses only its own click URL; the Gitlawb fallback is reserved for the static no-ad line. Tests + isolation: - ads.test.ts: cover fetchNextTip/confirmTip (POST-with-context vs GET, !ok→null, ad:null→null, dwell clamp, confirm normalization) via a stubbed fetch. - tipLink: renderSponsorLink takes an injectable `hyperlinks` flag so both branches are deterministically tested; added control-char/unsafe-URL cases. - gitlawbEarn.test.ts: restore global `ads` config in afterEach. - tipScheduler.test.ts: preserve OPENCLAUDE_ADS_TIP_EVERY in cleanup. Note: CodeRabbit's packages/memory/* findings are moot — that stray package was removed from the branch. Testing: tsc clean; ads+tips suite 56 pass; smoke green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(ads): address CodeRabbit follow-up review (round 2) - ads.tsx: remove warmOneEarn — it fetched AND confirmed a tip during /ads on that the user never saw, crediting an unshown impression (and could confirm after a quick opt-out). Earning now happens only on the per-turn rendered-tip path. Also drop the unsupported "Run /ads for your balance" instruction. - ads.ts: normalize confirm amounts (earned_micro/balance_micro) to finite integers via toFiniteInt, mirroring the dwell_ms clamp. - gitlawbEarn.ts: degrade to the static fallback when tip_text is blank, so a malformed ad never renders an empty line and credits a blank impression. - ads.test.ts (commands): restore global ads config in afterEach; assert the inline-code exposure warning (warnExposed) is shown. - ads.test.ts (services): build the AWS-shaped fixture at runtime so it isn't flagged by security:pr-scan; add a success-path test for buildEarningTip() rendering a fetched ad + a blank-ad fallback test. Testing: tsc clean; ads+tips suite 58 pass; smoke green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(ads): address CodeRabbit full review (round 3) + comments Blocking: - ads.ts: gate the serve response on a string `token` (the signed impression), not on an `ad` field. A served tip carries NO `ad` key, so CodeRabbit's suggested `data.ad == null` would have suppressed every real tip; the empty slot (`{ ad: null }`) and malformed responses both lack a token, so the token check covers them correctly. Documented the contract. - gitlawbEarn.ts: drop the dead try/catch around fetchNextTip — it is contractually non-throwing (catches everything, returns null), so the wrapper was unreachable/misleading. - ads.ts: make the base64-blob redaction boundary reliable — \b is meaningless around + and / (both \W), so bound the run with explicit look-around instead. Comments / clarity (per review): - ads.ts: note withAbortTimeout is a per-CALL deadline (shared across retries), not per-attempt. - tipLink.ts: clarify the CONTROL_CHARS_RE comment — the ESC/BEL literals above are intentional terminal-sequence constants, not part of the matcher. - config.ts: document that GlobalConfig.ads is managed via /ads and intentionally excluded from GLOBAL_CONFIG_KEYS (earnCode is a credential). (resetEarningCadenceForTesting export is an accepted in-repo pattern — no change.) Testing: tsc clean; ads+tips suite 58 pass; smoke green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- 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> |
||
|
|
3eb57c6d13 |
fix: upgrade shell-quote 1.8.3 -> 1.8.4 (CVE-2026-9277) (#1764)
Co-authored-by: Gravirei <gravirei@users.noreply.github.com> |
||
|
|
4dee44a542 |
feat(ux): long-turn visibility + default-on stream hang safety net (#1758)
Long-running turns looked frozen ("are you still working?") and a dropped
network stream could hang for the full 5-minute QueryGuard idle timeout.
Three focused changes:
- Spinner: show the elapsed-time counter at 5s instead of 30s (split the
timer gate from the token-count gate). The timer is wall-clock derived,
so it keeps ticking during long tool calls — proof of life even when no
tokens stream.
- Spinner: surface the currently-executing tool name in the status line
(reusing the spinnerSuffix channel; stop-hook progress still wins). A
long subagent/typecheck now reads "(↓ Bash · 1m 20s)" instead of a bare
spinner.
- claude.ts: enable the stream idle-timeout watchdog by default, matching
the always-on read-timeout already used by the OpenAI/Codex shims. A
silently dropped Anthropic-family stream now aborts and falls back to a
non-streaming retry within STREAM_IDLE_TIMEOUT_MS (90s) instead of
hanging to 5 minutes. Opt out with CLAUDE_DISABLE_STREAM_WATCHDOG=1.
Testing: tsc --noEmit clean; bugfixes.test.ts (31) and src/services/api
(835) pass; bun run smoke builds + runs.
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
|
||
|
|
bcf9421824 |
feat(permissions): allow npm/bun/tsc --version as read-only (#1759)
`node --version` and `python --version` are auto-approved as read-only Bash, but `npm`, `bun`, and `tsc` version queries were missing from the allowlist, so they fell through to a permission prompt. Add them in the same exact-anchored form (no trailing args) so a version flag can't smuggle a script-running suffix past the check (the `node -v --run <task>` class of bypass). Closes the only read-only gap that the now-superseded #787 classifier covered, without a parallel classification surface. Testing: new readOnlyValidation.test.ts (18 cases — allows -v/--version, rejects install/suffixed forms); tsc clean; BashTool suite 117 pass. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
dc6a7781bf |
feat: auto-detect and persist project conventions to wiki (#1010)
* feat: auto-detect and persist project conventions to wiki Adds a convention scanner that reads project config files (package.json, tsconfig.json, eslint, prettier, Dockerfile, CI workflows, lockfiles) on startup and saves extracted conventions to .openclaude/wiki/pages/conventions.md. Includes a fingerprint cache to avoid redundant writes and a /wiki scan command for manual re-scans. New modules: - src/services/wiki/conventions.ts — scanner + cache + save - src/services/wiki/identity.ts — project identity (name, languages, monorepo) - src/services/wiki/conventions.test.ts — 7 tests Modified: - paths/types/init/status — extended wiki infrastructure for conventions - wiki.tsx/index — added /wiki scan command - main.tsx — fires scan via startDeferredPrefetches - init.test.ts/status.test.ts — updated for new conventions page/fields Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(wiki): address PR #1010 review — trust gate, cache, pre-init, indexing Resolves the three blockers raised by @jatmn and @Vasanthdev2004, plus a post-rebase integration gap. 1. Trust gate (blocker): the startup convention scan ran git (via project identity) before workspace trust was established. Now gated behind the same check as prefetchSystemContextIfSafe — runs only when non-interactive (implicit trust) or the trust dialog has been accepted. 2. Cache fingerprint (blocker): computeFingerprint hashed only the detected config sections, so identity-only changes (project name, language counts, monorepo, default branch) left conventions.md stale. Identity inputs are now folded into the fingerprint. (Hashing the rendered markdown isn't viable — it embeds a "Last scanned" timestamp.) 3. /wiki scan pre-init crash (blocker): forceScanConventions caught the page write failure but still wrote the cache, throwing ENOENT before /wiki init. It now only writes the cache on a successful page write and reports saved=false; /wiki scan surfaces "run /wiki init first". 4. Index integration: the conventions page is written to pages/, but the wiki index (rebuildWikiIndex, added after this PR was opened) was never refreshed, so the new page wasn't listed. Both save paths now reindex on success. Testing: 3 new regression tests (identity-only re-save, pre-init no-crash/no-cache, reindex); wiki suite 14 pass; tsc clean; smoke + knip green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(wiki): address CodeRabbit review on PR #1010 - main.tsx: add a terminal .catch(logError) to the deferred conventions-scan promise chain so a failed dynamic import or scan can't become an unhandled rejection on the startup path. (Major) - conventions.ts: narrow both page-write catch blocks to ENOENT (= wiki not initialized → skip); rethrow other failures (EACCES, EROFS, …) instead of masking them as "not initialized". (Major) - identity.ts: replace blocking execFileSync('git', …) with async execa (the service-layer subprocess convention); getProjectIdentity is now async, awaited in scanProjectConventions. Keeps the startup scan off the event loop. - commands/wiki/index.ts: restore `ingest` in the argumentHint — `[init|status|scan|ingest <path>]` — it's still an implemented subcommand. Testing: tsc clean; wiki suite 14 pass; smoke + knip green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
a23014b260 |
fix(atlas-cloud): sync static catalog with live /models metadata (#1754)
- Enrich every entry with maxOutputTokens (from max_output_length) and
capabilities (function calling, json mode, vision, reasoning) pulled from
the live catalog. Addresses discoveryService metadata gap.
- Add transportOverrides.openaiShim.removeBodyFields for xai/grok-build-0.1
to drop reasoning_effort (fixes 400 on Atlas Cloud).
- Curate current model set:
- Add: Kimi K2.7 Code, GLM 5.2, Qwen3.7 Max/Plus, Doubao Seed 2.0 variants,
Claude Sonnet 4.6 / Haiku 4.5 (base + coding), latest GPT/Gemini/Grok.
- Drop: K2 Thinking/Instruct 0905, older MiniMax M2.1/M2, duplicate Qwen.
- Keep source: 'static' only. Entries sorted in descending version order
within each vendor family.
- Preserve notes: 'Free' on the owl model.
.gitignore: ignore .tmp-* directories (test artifacts such as replay-index tests).
Follows static-over-hybrid, catalog-model-ordering, and grok-build-0.1 notes.
|
||
|
|
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 |
||
|
|
c2467eedad |
feat(atlas-cloud): add GLM 5.2 to vendor catalog (#1755)
Adds zai-org/glm-5.2 with the same 202,752 context window as GLM 5.1 so the model is selectable through the Atlas Cloud provider. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
38b0e27333 |
fix(opencode-go): sync model catalog with opencode.ai/go (#1745)
* fix(opencode-go): sync model catalog with opencode.ai/go The OpenCode Go subscription page (https://opencode.ai/go) lists 13 models, but the catalog had 20. Remove the 7 models no longer offered: glm-5, kimi-k2.5, minimax-m2.5, qwen3.5-plus, mimo-v2-pro, mimo-v2-omni, hy3-preview. Catalog now matches the page exactly: - OpenAI-compatible: GLM 5.2, GLM 5.1, Kimi K2.7 Code, Kimi K2.6, DeepSeek V4 Pro, DeepSeek V4 Flash, MiMo V2.5 Pro, MiMo V2.5 - Anthropic messages: MiniMax M3, MiniMax M2.7, Qwen3.7 Max, Qwen3.7 Plus, Qwen3.6 Plus Updates gateway catalog, model descriptors, generated artifacts, and tests. * fix(opencode-go): reorder model catalog to match opencode.ai/go listing Reorder both the gateway catalog and model descriptor lists to match the order models appear on https://opencode.ai/go, so the in-app model picker mirrors the subscription page. No model added or removed — purely a reorder. GLM-5.2 → Qwen3.7 Max → Kimi K2.7 Code → MiMo V2.5 Pro → DeepSeek V4 Pro → Qwen3.7 Plus → MiniMax M3 → MiMo V2.5 → DeepSeek V4 Flash → GLM 5.1 → Kimi K2.6 → Qwen3.6 Plus → MiniMax M2.7 * test(opencode-go): assert exact model set matches opencode.ai/go catalog Address CodeRabbit review on #1745 — the count-only test wouldn't catch catalog drift. Add a strict set assertion verifying the 13 expected IDs are present and no removed/unexpected IDs remain. * test(opencode-go): update Anthropic Messages route test for refreshed catalog The direct-env-routing test listed minimax-m2.5 and qwen3.5-plus, which were removed from the opencode-go catalog. Replace with the five /messages-endpoint models that remain: minimax-m3, minimax-m2.7, qwen3.7-max, qwen3.7-plus, qwen3.6-plus. Unblocks the smoke-and-tests CI check on #1745. * fix: update OpenCode Go model references to 13 models and add assertions |
||
|
|
820015fbaf | fix(bg): prefer exact session names over ID prefixes (#1753) | ||
|
|
091571f643 |
fix(provider): Add Xiaomi MiMo token plan provider (#1751)
* Add Xiaomi MiMo token plan provider Add a descriptor-backed Xiaomi MiMo Token Plan gateway with the token-plan SGP/CN routing hosts, raw api-key OpenAI-compatible shim settings, and MiMo catalog defaults. Wire the new preset through generated integration artifacts, provider flag handling, active route/provider resolution, provider profile env mirroring, and ProviderManager preset ordering. Cover the new path with gateway metadata, compatibility, provider flag, provider profile, and focused ProviderManager tests. Validated with bun run build, bun run smoke, bun run check, typechecks, provider suites, integrations:check, security:pr-scan -- --base upstream/main, and doctor:runtime. * Address Xiaomi MiMo token plan review feedback Reset stale known OpenAI-compatible base URLs when xiaomi-mimo-token is explicitly selected so prior provider routing cannot survive the provider flag. Use hostname-based Xiaomi MiMo base URL detection when mirroring MIMO_API_KEY from provider profiles, avoiding substring matches in unrelated URL paths or query strings. Add focused regression coverage for stale base URL replacement and token-plan CN profile env mirroring. |
||
|
|
aed42df19b |
fix: treat 5xx HTML overload pages as retryable provider_unavailable (#1750)
Reorders classifyOpenAIHttpFailure so the status >= 500 branch runs before the isMalformedProviderResponse check. Gateway 502/504 overload pages have HTML bodies (matching "<!doctype html" / "<html") that previously tripped the malformed-provider-response path, marking the error non-retryable and surfacing "Provider returned a malformed response" even though the failure was transient — users had to retry manually. Now any 5xx is classified as provider_unavailable (retryable) regardless of body shape, matching the actual semantics. 4xx HTML responses still classify as malformed_provider_response since those are genuine protocol failures. Adds three regression tests covering 502 HTML, 504 HTML, and the unchanged 400 HTML path. |
||
|
|
8c15feaa5a |
test(install-surfaces): stop execFileNoThrow mock leaking into later suites (#1708)
The cleanupNpmInstallations test replaces execFileNoThrowWithCwd with a stub that simulates a failed npm uninstall (E404). bun's mock.module is process-wide, and re-mocking the module back to the real implementation in afterEach does not reliably undo it, so the stub leaked into later test files that shell out for real (e.g. git worktree add), making them fail with a bogus "npm ERR! code E404". Install the override once at module load and gate it on a module-level flag that is cleared in afterEach, so the persisted mock transparently falls through to the real implementation whenever the flag is off. The real module is snapshotted into a plain object before the mock is installed, because bun live-updates the imported namespace to the mock — delegating through the namespace inside the override would otherwise recurse infinitely. |
||
|
|
1be9b86607 |
feat: Add session replay timeline (#1705)
* Add session replay timeline Implement a /replay command backed by replay sidecar indexes that capture user requests, tool execution inputs/results, modified files, durations, real retry events, and repeated tool attempts. Add replay summaries to /resume, persist replay indexes during session cleanup, and include replay sidecars in retention cleanup. Add focused coverage for replay index retry/repeated-attempt accounting and replay modified-file detection, including simulated Bash sed edits. Validation run: bun install; bun run build; bun run smoke; bun run typecheck; bun run typecheck:type-tests; bun run security:pr-scan; bun run doctor:runtime; bun test src/utils/replayIndexBuilder.test.ts; bun test src/services/tools/toolExecution.test.ts. bun run check reached test:full but the full suite still reports 10 unrelated order/environment-sensitive failures in export, marketplace cache, and secure-storage tests; those files pass when rerun directly. * Address replay review findings Move replay tool-start tracking to the final executable input for allowed tool calls while preserving permission-denied replay records. Harden replay sidecar persistence with session-id based paths and owner-only file mode, and compute replay summary timestamps from true min/max values. Reuse the shared SessionSummary component in /replay and add focused regression coverage for replay storage, timestamp bounds, replay lifecycle records, and resume summary confirmation keyboard behavior. Validation: bun run typecheck; bun run build; bun test src/utils/replayIndexBuilder.test.ts src/utils/replayIndex.test.ts src/commands/resume/resume.test.tsx src/services/tools/toolExecution.test.ts. * Resolve replay follow-up findings Use the shared formatDuration helper in replay summary UI, replace replay timeline non-null assertions with optional chaining, and align the resume Escape test with the single-escape input pattern plus async polling. Validation: bun run typecheck; bun run build; bun test src/commands/resume/resume.test.tsx src/utils/replayIndexBuilder.test.ts src/utils/replayIndex.test.ts src/services/tools/toolExecution.test.ts. * Fix replay timing and cancellation status Preserve millisecond precision for sub-second replay displays, classify abort-shaped tool failures as cancelled in replay records, and derive replay summary duration from elapsed session bounds instead of summed tool runtime. Validation: bun run typecheck; bun run build; bun test src/utils/replayFormat.test.ts src/utils/replayIndexBuilder.test.ts src/utils/replayIndex.test.ts src/commands/resume/resume.test.tsx src/services/tools/toolExecution.test.ts. * Normalize denied replay tool inputs Use the same replay input normalization for permission-denied file tools that the allowed execution path uses, preserving model-emitted relative paths and repeated-attempt signatures across denied-then-allowed retries. Validation: bun run typecheck; bun run build; bun test src/services/tools/toolExecution.test.ts src/utils/replayFormat.test.ts src/utils/replayIndexBuilder.test.ts src/utils/replayIndex.test.ts src/commands/resume/resume.test.tsx. * Address replay review follow-ups * Fix replay follow-up test findings * Stabilize replay cleanup test * Address replay resume follow-ups * Harden replay resume validation * Address replay review coverage gaps * Close remaining replay review findings * Fix replay review follow-ups after rebase |