mirror of
https://github.com/Gitlawb/openclaude.git
synced 2026-08-24 10:14:19 -05:00
1aabe261dbb4e49c4dff995b8b5a62456f9607e1
856
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1aabe261db |
feat(bughunter): make /bughunter public + add /bughunter-security & /bughunter-perf with robust fallback prompts (#1621)
* feat(bughunter): split into /bughunter, /bughunter-security, /bughunter-perf
Replace the single /bughunter command with three siblings that share a
common prefix:
/bughunter — general bug hunt (existing prompt, untouched)
/bughunter-security — OWASP-aligned, exploit-driven, confidence ≥ 8
/bughunter-perf — hot-path complexity, sync I/O, leaks, N+1
Both new subcommands are prompt commands built with
createMovedToPluginCommand so they migrate to the bughunter marketplace
plugin unchanged once it ships. While the marketplace is private they
inline the full audit prompt (frontmatter + !`git ...` blocks) just like
the existing /bughunter.
All three stay in the public COMMANDS list (not INTERNAL_ONLY_COMMANDS)
so non-ant users can invoke them. clearCommandMemoizationCaches() now
also flushes the zero-arg COMMANDS() and builtInCommandNames() memos so
tests can switch USER_TYPE mid-run without poisoning the cache.
Adds regression tests in src/commands.test.ts covering:
- bughunter stays public for non-ant users
- bughunter-security and bughunter-perf are in the public list
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore(bughunter): remove orphan index.js after .js → .ts rename
The bughunter command directory was renamed from a single .js file to
index.ts in the previous commit, but git tracked them as separate paths
so the old .js was left in the tree. Drop it.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(bughunter): enhance fallback prompts for robustness in non-git environments
- Add graceful error handling to all git commands in fallback prompts (|| echo fallbacks)
- Add explicit non-git fallback guidance in Phase 1 for all three commands
- /bughunter: search for entry points, core business logic, recently modified files
- /bughunter-security: search for auth/middleware, validation, DB, config, upload code
- /bughunter-perf: search for handlers, loops, data access, serialization, build configs
- Improve context labels to clarify git context may be empty
* fix(bughunter): address CodeRabbit feedback
- Fix test isolation: restore USER_TYPE/IS_DEMO env vars in finally blocks
- Add non-git fallback test cases for all three bughunter commands
- Fix bash pipeline issue: replace if/then/else subshells with simple git commands + static fallback text in template
- Fix output format contradiction: remove LOW confidence from scoring (Phase 3 drops LOW, so scoring only includes Critical/Medium)
* fix(test): correct case and prefix in git fallback assertions for bughunter-security and bughunter-perf tests
* fix(test): add missing opening parenthesis in bughunter test assertions
* fix(bughunter): complete non-git fallback and propagate allowedTools
- Fix git commands in all three prompts to always succeed with fallback text (using || echo)
- Modify createMovedToPluginCommand to accept allowedTools parameter
- Add allowedTools to all three bughunter commands so slash-command turn grants declared tools
- Parse allowed-tools from frontmatter at command creation time
* fix(bughunter): complete non-git fallback and allowedTools propagation
- Fix git commands in prompts to always succeed with fallback text (using || echo)
- Modify createMovedToPluginCommand to accept allowedTools parameter
- Add allowedTools to all three bughunter commands so slash-command turn grants declared tools
- Fix RECENTLY COMMITTED FILES command to avoid command substitution (permission check rejects )
- Update tests to accept shell tool's '(Bash completed with no output)' for empty results
- Use runWithCwdOverride and additionalWorkingDirectories for proper test isolation
* fix(bughunter): prevent shell injection via user-provided args
The user-provided scope was interpolated into the prompt template BEFORE
executeShellCommandsInPrompt() ran, so any !command or ```! block
syntax in the args would be interpreted and executed as shell commands.
Fix: parse frontmatter from the raw template and run shell execution first
(with {{ARGS}} still in place — inert to shell patterns), then replace
{{ARGS}} with the user scope on the processed output. This ensures args
are never fed through the shell command parser.
* refactor(bughunter): use createGetAppStateWithAllowedTools helper
Replaces duplicate inline getAppState overrides across all three bughunter
commands (bughunter, bughunter-security, bughunter-perf) with the shared
helper from src/utils/forkedAgent.ts. This:
- Eliminates ~30 lines of duplicated permission context modification
- Merges allowedTools with existing alwaysAllowRules.command (vs overwrite)
* fix(bughunter): address jatmn review - String.replace special patterns + test isolation
- Replace '{{ARGS}}' with a replacer function () => scope instead of
the plain string 'scope'. JavaScript's String.replace treats $&, $',
$', , 32855 specially even in string replacements, so a scope like
'src/auth $&' would render as 'src/auth {{ARGS}}' instead of literal
text. The replacer function bypasses all special patterns.
- Restore USER_TYPE and IS_DEMO env vars in the injection regression
test's finally block, matching the isolation pattern used by all other
bughunter tests.
* fix(bughunter): make fallback prompt generation work on Windows
Wrap executeShellCommandsInPrompt() in a try/catch in all three bughunter
commands. On platforms where bash is unavailable (e.g. Windows without Git
Bash), the bash-specific shell syntax (2>/dev/null, | head -N) would cause
executeShellCommandsInPrompt to throw MalformedCommandError, preventing the
prompt from being generated at all.
The catch handler replaces the !`command` inline patterns with a static
placeholder, allowing the LLM to still receive the full audit instructions
and non-git search strategies in Phase 1.
* fix(bughunter-perf): remove Low severity contradiction
The summary line included Low: L but Phase 3 drops non-measurable findings
and exclusions remove micro-optimizations. Low findings (measurable but
not user-visible) would never survive the filter, so remove Low from the
severity categories and summary line.
fix(bughunter-security): align log-forging exclusion with A9 criteria
Exclusion #11 blocked all log spoofing/forging, but A9 says to flag
log injection when it enables audit-trail forgery. Narrowed the exclusion
to allow concrete audit-trail attacks through while still excluding
generic non-exploitable logging suggestions.
* fix(bughunter-security): tighten log-forging exclusion threshold
Reword exclusion #11 to require concrete evidence of a log-entry or
structured-field forgery path, not merely unsanitized user input.
* fix(bughunter): preserve fallback text on Windows/no-bash path
Replace generic '(Shell execution unavailable)' placeholder with a regex
that extracts the || echo "..." fallback text from each shell command.
This ensures the prompt shows meaningful messages like
'(If empty: not a git repository or git unavailable)' even when bash is
unavailable (e.g. Windows without Git Bash), matching what Linux users see
from working shell execution.
Also make injection test assertion platform-agnostic — accept either bash
output or the static echo fallback text.
* refactor(test): extract duplicate mockContext into createMockToolContext helper
The three non-git fallback tests each had an identical ~42-line mockContext
object. Moved it to a shared createMockToolContext(cwd, commands) helper
and a FULL_GIT_COMMANDS constant. Also updated the injection test to use
the same helper. Net -89 lines.
* fix(createMovedToPluginCommand): only grant allowedTools when fallback prompt runs
The ant (USER_TYPE === 'ant') branch returns a plugin-install notice that
doesn't need Read/Glob/Grep/Bash tools, but allowedTools was statically
attached to the command object. This caused processSlashCommand to grant
turn-scoped permissions for tools that were never used.
Changed to a getter that returns undefined in the ant branch, so the
plugin-install notice runs without unnecessary tool permissions.
* fix(bughunter): simplify shell commands to single git commands, narrow catch to surface interruptions
* fix(bughunter): surface permission-denied/aborted shell preprocessing, fix Windows cleanup
* fix(dragDropPaths.test): resolve package.json relative to test file, not process.cwd()
* fix(commands.test): restore original cwd in rmRetry, guarantee env/cache cleanup on rm failure
* fix(bughunter): bound diff to 400 lines, swap HEAD~10 for git log -10
Address both P2 reviewer findings on feat/bughunter-command-v3-new.
(1) Fresh-repo HEAD~10 lookup stripped every snippet. In a one-commit
repo, `git diff --name-only HEAD~10..HEAD --diff-filter=AM` exits
128 (HEAD~10 doesn't resolve). The shell-execution catch then ran
the outer "strip all snippets" fallback, leaving git status /
diff --cached / diff HEAD empty even though those commands would
have produced useful context. Switched to `git log -10 --name-only
--diff-filter=AM`, which works at any history depth and yields the
same file list. Applied to bughunter, bughunter-security, and
bughunter-perf.
(2) Diff cap removed in
|
||
|
|
d5588ea80d |
feat(context-collapse): opt-in between-turns context collapse (span summarization) (#1619)
* feat(context-collapse): implement context collapse for proactive context management
* feat(context-collapse): add turn-boundary helpers for span selection
* feat(context-collapse): deterministic turn-anchored span selection
* feat(context-collapse): code-computed span risk score
* feat(context-collapse): ctx-agent summarization instruction
* feat(context-collapse): implement ctx-agent span summarization spawn
* fix(context-collapse): make runtime activation opt-in (CLAUDE_CONTEXT_COLLAPSE)
* fix(context-collapse): address review feedback on restore state and test rigor
- restoreContextCollapseState now resets armed/lastSpawnTokens up front so a
snapshot-less restore cannot carry stale spawn state across sessions.
- projectView reuses a stable timestamp from the replaced span instead of
new Date(), keeping the read-side projection deterministic.
- Strengthen the disabled-state and turn-boundary assertions, drop an internal
renderToolUseMessage assertion, and isolate the operations/persist/spawn tests
from shared module and CLAUDE_CONTEXT_COLLAPSE env state.
* test(context-collapse): re-init enablement in persist.test hooks
resetContextCollapse() does not re-read CLAUDE_CONTEXT_COLLAPSE, so the
afterEach env delete left enabled=true in module state, leaking to the
next test file. Call initContextCollapse() in both hooks so module
enablement stays synced to the env var.
* test(context-collapse): stop spawnCtxAgent module stubs leaking across files
spawnCtxAgent.test.ts stubs shared modules (tokens, forkedAgent, messages,
analytics, log, spanSelection) via mock.module in beforeEach. bun's
mock.restore() does not undo mock.module, so the tokens stub (() => 100000)
bled into autoCompact/microCompact/runAgent tests run later in the full serial
suite, making them see every conversation as over-threshold (4 spurious
failures in test:full, all green in isolation).
Restore each stub to its real implementation in afterEach. The reals are
snapshotted into plain objects up front because 'import * as' yields a live
namespace that mock.module mutates in place, so holding the namespace would
restore the stub. autoCompact.js is deliberately not restored here since
autoCompact.test.ts re-imports it fresh via a cache-busting nonce.
Also reset+reinit the collapse module in afterEach so enabled state stays
synced to the now-unset env var.
* test(context-collapse): also restore autoCompact stub from spawnCtxAgent
The getEffectiveContextWindowSize stub on ../compact/autoCompact.js was the one
module the previous commit left unrestored, on the assumption that restoring it
would clash with autoCompact.test.ts's nonce re-import. It doesn't: the nonce
import uses a different specifier, and the snapshot restore is keyed by the
plain specifier. compressToolHistory imports getEffectiveContextWindowSize and
sizes tool-history truncation from it, so the leaked 20000-token window made it
fully omit tool results ('chars omitted') instead of mid-truncating
('[…truncated') for large-context models, failing the openaiShim compression
tests in the full serial suite. Restore all seven mocked modules.
* fix(context-collapse): re-arm after reset and gate ctx_inspect on opt-in
resetContextCollapse() left armed=false while enabled stayed true, so the
first /compact, main-thread compaction cleanup, or rewind permanently
disabled collapse for the rest of an opted-in session. Reset now mirrors
restoreContextCollapseState and sets armed=enabled.
CtxInspectTool.isEnabled() returned true unconditionally, advertising
ctx_inspect to the model in every default session even when the runtime
opt-in was off. It now returns isContextCollapseEnabled(). The opt-in is
also exposed as the contextCollapseEnabled global config key, so it is
reachable through /config instead of only the CLAUDE_CONTEXT_COLLAPSE env
var.
* refactor(context-collapse): drop no-op ternary in drainStaged persist call
The (stagedQueue.length > 0 ? 0 : 0) subtrahend always evaluated to 0, so
this is just persistCommits(processed.length).
* fix(context-collapse): persist commits before advancing the snapshot
drainStaged removed processed spans from the staged queue and then fired
persistCommits and persistSnapshot in parallel. If the snapshot write (which
no longer lists those spans as staged) landed while the commit write failed
or the process died between them, restore would find the spans neither staged
nor committed and the collapse would disappear on resume. Chain the snapshot
write after the commit write so the commit log is durable first.
* fix(context-collapse): project committed collapses on the query path, fix opt-in reach
Three issues from review:
- Committed collapses were never re-applied to the model input. The query path
calls applyCollapsesIfNeeded but only drained staged spans; projectView (which
replays the commit log) ran only in /context. Since messagesForQuery is rebuilt
from full REPL history each turn and the commit log is repopulated on resume,
the archived spans returned to the model on the next turn, undoing the collapse.
applyCollapsesIfNeeded now runs projectView first (idempotent). Adds a
regression that a committed collapse changes the next query input.
- Cache-safe params were saved only for exact repl_main_thread/sdk sources, but
the REPL tags non-default output styles as repl_main_thread:outputStyle:*, so
those sessions left the ctx-agent without params (empty spawns). Matches
repl_main_thread:* now, via a small tested helper.
- contextCollapseEnabled had no settings control. Adds a /config toggle that
refreshes runtime state (re-runs initContextCollapse) so it applies without a
restart.
* fix(context-collapse): clear already-committed staged spans; harden config toggle
After projecting committed collapses before draining, a span present in both the
commit log and the staged snapshot (a restore whose snapshot predates the
matching commit write) could not be drained — projectView had already removed
its messages — so it lingered in stagedQueue and distorted spawn/overflow
checks. drainStaged now drops staged spans that are already committed and syncs
the snapshot. Adds a regression covering the committed+staged overlap restore.
Also wraps the /config context-collapse refresh in try/catch so a failed
require/init can't crash the settings UI, and lists the toggle in the
save-and-close change summary like the neighboring compaction settings.
* fix(context-collapse): re-sync runtime state on config cancel
The context-collapse toggle's onChange refreshes the module-level
enabled/armed cache via initContextCollapse(). The revert path restored
the config key on disk but left that cache untouched, so enabling the
toggle and then pressing Escape kept collapse active for the rest of the
session. Re-init context collapse after the global config snapshot is
restored so cancel fully reverts runtime state.
* fix(context-collapse): keep collapsed summaries visible to the model
projectView and drainStaged replaced an archived span with a system
informational placeholder, but normalizeMessagesForAPI filters out every
system message that is not a local command. So once a collapse committed,
the next model request lost both the archived messages and the
<collapsed> summary meant to stand in for them, defeating the feature.
Mark the placeholder with isCollapseSummary and let it take the same
model-input path as local-command system messages (converted to a user
message), so the summary survives normalization. Added a regression that
runs the projected view through normalizeMessagesForAPI and asserts the
summary is still present.
* fix(context-collapse): avoid competing snapshot write after drain
After an immediate post-spawn drain, drainStaged(messages, true) starts
its own persistCommits().then(persistSnapshot) chain to guarantee commit
durability before the snapshot stops listing the staged spans. The
unconditional await persistSnapshot() that followed could win that race
and persist a snapshot with no staged spans before the commits landed,
reopening the crash window that drops collapses on restore. Only persist
directly when nothing was drained.
* fix(context-collapse): fall back, keep summaries non-snippable, gate /context
Three review findings:
- Suppress autocompact and the blocking preempt only when collapse holds a
real committed/staged reduction, not on mere enablement. Adds
hasActiveReduction(); a first over-threshold turn where spawnCtxAgent cannot
produce a span (getLastCacheSafeParams() still null) now falls back to
autocompact/blocking instead of sending an oversized transcript.
- Preserve isMeta when converting a collapse-summary placeholder to a user
message in normalizeMessagesForAPI, so the HISTORY_SNIP sweep cannot tag the
only replacement for an archived span as snippable.
- Gate the two /context projectView calls on isContextCollapseEnabled(), so a
disabled session does not under-report token usage from a lingering commit
log while the API receives the full transcript.
Adds regressions for hasActiveReduction and for the summary surviving
normalization as a non-snippable meta message.
* fix(context-collapse): scope collapse to the main thread that owns the store
The collapse store (commitLog/stagedQueue) is module-level and shared by
in-process subagents (agent:*) and the ctx-agent (marble_origami), which
run in the same process but do not own the main transcript.
applyCollapsesIfNeeded only skipped marble_origami, so a subagent could
stage or commit a span, flip the global hasActiveReduction(), and make
the next main-thread turn suppress autocompact and the blocking
prompt-too-long preempt while projectView() no-ops against the main
messages, sending an oversized transcript to the API.
Add isMainThreadSource() and gate both application (applyCollapsesIfNeeded,
isWithheldPromptTooLong, recoverFromOverflow) and fallback suppression
(autoCompact shouldAutoCompact, query collapseOwnsIt) to the owning
thread. Subagents now autocompact and preempt their own oversized turns
normally and never mutate the shared store.
Also adds the staged-only hasActiveReduction regression CodeRabbit
requested.
* fix(context-collapse): persist archived count so resumed stats stay accurate
restoreContextCollapseState rebuilt each commit with an empty archived
list, and getStats summed that list, so after a resume /context, the
context visualization, the token warning, and ctx_inspect reported
'N spans summarized (0 messages)' even though projectView was actively
removing the archived spans. The persisted-entry docstring claimed
projectView lazily refills the archive, but it only splices by boundary
uuid and never does.
The archived messages are never read back (only their count fed
getStats), so replace the per-commit Message[] with a persisted
archivedCount. It is written with each commit and restored on resume;
pre-field sessions restore as 0. getStats now reports the same figure
live and after resume.
* fix(context-collapse): keep collapse summary non-snippable across user merge
Preserving isMeta on the system->user conversion was not enough: when the
collapsed span ends right before the next user turn, normalizeMessagesForAPI
merges the summary into that real user message. Under HISTORY_SNIP
mergeUserMessages clears isMeta whenever an operand is real user content and
keeps the real turn's uuid, so the combined block — which carries the only
<collapsed> replacement for the archived span — got a snip id and the model
could queue it for removal.
Carry an isCollapseSummary marker onto the converted user message and through
mergeUserMessages (either operand), strip any snip id already baked into the
real turn when the merge absorbs a summary, and skip such blocks in
appendMessageTagToUserMessage. The merged block stays non-snippable
regardless of merge direction or isMeta being cleared.
* fix(context-collapse): preserve collapse marker on split, drop empty snip blocks
normalizeMessages split path now forwards isCollapseSummary so an array-backed
collapse summary keeps its non-snippable marker across API normalization.
stripSnipTagsFromContent drops a text block whose only content was the snip
marker, so the merge recovery path no longer emits an empty text block.
|
||
|
|
650fae952d |
fix(sdk): make stub-leak detection TDZ-safe + defer to next microtask (#1287) (#1398)
* fix(sdk): make stub-leak detection TDZ-safe + defer to next microtask (#1287) `bun run scripts/start-grpc.ts` crashed at startup with: ReferenceError: Cannot access 'QueryEngine' before initialization. at detectStubLeaks (src/entrypoints/sdk/index.ts:29:33) at src/entrypoints/sdk/index.ts:47:1 The detector ran at module-load time and read each critical import directly. When the start script's circular-import chain reached the SDK barrel before `QueryEngine.js` had finished initializing its own export bindings, the QueryEngine reference at line 29 hit the temporal dead zone and threw. Stub-leak detection is meant to catch `__stub: true` markers from the esbuild plugin — TDZ is a different bug class (an uninitialized binding can't carry `__stub`), so the detector should treat the access failure as 'nothing to check here' rather than crashing the entire SDK entry. Two changes: 1. Wrap each import read in safelyAccess(() => binding) so a TDZ ReferenceError on one returns undefined and the loop continues. Real stub markers still surface as the explicit SDK init error. 2. Defer detectStubLeaks() from module-load to queueMicrotask, so every same-tick init in the circular chain (start-grpc.ts → SDK index → QueryEngine → ... → SDK index) completes before we read bindings. Microtask runs before any actual SDK usage, so a real stub leak still surfaces well before the first query() call. Tests (3): SDK barrel imports without throwing, anti-regression on real __stub: true bindings, TDZ-shaped access returns undefined. * test(sdk): exercise the real stub-leak detector with stubbed fixtures (#1287) The regression test asserted only that a local object literal had __stub === true and re-implemented safelyAccess inline, so it never ran the real detector: removing queueMicrotask(detectStubLeaks), dropping the loop, or swallowing the __stub case would all still pass. Split the detection primitives (safelyAccess + the critical-import scan) into src/entrypoints/sdk/stubLeakDetection.ts and have the SDK entry point import them. The test now feeds stub-shaped fixtures through the real checkCriticalImportsForStubs / safelyAccess and asserts: a real __stub: true binding throws the explicit SDK init error; non-stub modules pass; a TDZ ReferenceError is tolerated (skipped) without crashing; a stub behind a skipped TDZ access is still caught; and the SDK barrel import never throws on its own load. Detector runtime behavior is unchanged. |
||
|
|
c74397cd2f | chore(gitignore): ignore local worktree directories (#1681) | ||
|
|
b8c7c3bfac |
feat(memory): add memory.autoWrite alias for autoMemoryEnabled (#1326) (#1396)
* feat(memory): add memory.autoWrite alias for autoMemoryEnabled (#1326) The attribution half of #1326 was fixed via #1335 (merged 2026-05-26). The memory half — '[memory writes should be] explicit and configurable' with the exact shape `memory.autoWrite` requested by the issue — remains. Rather than parallel-tracking a new key, alias `memory.autoWrite` to the existing `autoMemoryEnabled` opt-out and document the relationship. Either key opts out; when both are set, the more restrictive (false) value wins so a parent-scope opt-out can't be silently re-enabled by a narrower memory.autoWrite: true. The new `memory` namespace is intentional — future opt-in fields (approval gates, etc.) can be added under it without claiming a new top-level key each time. - types.ts: add `memory.autoWrite` to the settings schema; cross-link to autoMemoryEnabled in the description. - paths.ts isAutoMemoryEnabled: read both keys; opt-out wins on conflict; default unchanged (enabled). - paths.test.ts (new): pins default, both opt-out paths, both opt-in paths, opt-out-wins-on-conflict in both directions, env-var still overrides settings. Tests 7/7 green. Default behavior unchanged — this is purely an additive discoverable alias for governance / regulated / client- sensitive repos that prefer the namespaced shape called out in the issue. * fix(memory): evaluate autoWrite opt-out across raw settings sources (#1326) isAutoMemoryEnabled() read the already-merged settings object, so source precedence had already collapsed same-key values before the "false wins" rule applied: a lower-priority memory.autoWrite/autoMemoryEnabled: false opt-out was silently overwritten by a higher-priority true, re-enabling auto-memory against the stated governance guarantee. Evaluate the opt-out across the raw per-source settings instead, via getEnabledSettingSources() + getSettingsForSource() (per-source cached, so the hot path stays cheap). A single false in any source now wins, so a parent-scope opt-out cannot be re-enabled by a narrower scope flipping the key to true. Test now drives per-source fixtures and covers the cross-source precedence case (lower-priority false beats higher-priority true) for both keys. * test(memory): stop the autoWrite test leaking settings mocks across files The previous test mock.module()'d both settings.js and constants.js. bun's mock.restore() does not undo mock.module(), so the constants.js stub leaked into later serial test files and broke flagSettings.test.ts (its cache-busted settings import still resolved the mocked getEnabledSettingSources). Drive the real getEnabledSettingSources() via setAllowedSettingSources() instead of mocking constants, stub only getSettingsForSource, and re-register the real settings module after each test so nothing leaks. Coverage is unchanged (per-source fixtures + cross-source precedence cases). |
||
|
|
8f88608055 |
test(file-suggestions): stop cross-spawn mock leaking into later suites (#1667)
fileSuggestions.test.ts installs a cross-spawn mock via mock.module, which bun does NOT undo on mock.restore() — it persists process-wide. The mock's interception was gated on a closure captured at install time, so after this suite the persisted mock kept returning a fake child (with no kill()) for any git command. Test files run sequentially in one process, so a later suite's real `git ls-files` (e.g. /lsp recommend's filesystem-scan fallback) hit the fake child, hung to its 5s timeout, and crashed on child.kill() — an order-dependent failure in smoke-and-tests. Gate the interception on a module-level activeSpawnScenario that is set only while one of this suite's spawn-scenario tests runs and cleared in afterEach, so the persisted mock falls through to the real spawn afterward. Also give the fake child a kill() that emits close, so any stray caller terminates cleanly instead of throwing. Verified the full src suite is green across repeated runs (was intermittently red on the /lsp filesystem-scan test). |
||
|
|
5c0e6612c2 |
chore(main): release 0.19.0 (#1596)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>v0.19.0 |
||
|
|
7be9dce8ef |
fix: vision handling for OpenAI-compatible models (#1663)
* Fix vision handling for OpenAI-compatible models Add route-aware vision capability checks for image reads so registered non-vision models get an actionable refusal before sending image content. Classify provider-side image/text errors as canonical vision_not_supported responses, preserve image-only tool results for OpenAI-compatible shims, and strip rejected images from retry messages. Add focused coverage for Xiaomi MiMo/OpenGateway route collisions, canonical errors, shim image handling, and the Read prompt. * Address vision review findings Move the Read tool vision gate before the UNC no-I/O early return so UNC image paths cannot bypass non-vision model checks. Add direct FileReadTool.validateInput coverage for non-vision denials, provider override/env precedence, and UNC image paths. Add the missing OPENAI_BASE_URL exclusion assertion for the Xiaomi MiMo canonical error path. * Isolate vision gate tests from provider env Clear OPENAI_BASE_URL and OPENAI_API_BASE before each FileReadTool vision-gate test so full-suite provider tests cannot leak route state into these cases. * Fix vision gate test full-suite isolation Import FileReadTool and prompt with a cache-busted module id so compact.test's process-global mock cannot replace validateInput during test:full. Invoke validateInput directly instead of optional chaining, matching the review finding and making missing exports fail clearly. * Lock vision prompt env mutations Acquire the shared mutation lock before mutating OPENAI_BASE_URL and OPENAI_API_BASE in the FileReadTool vision prompt tests, and release it after restoring the environment. |
||
|
|
bac74aafee |
fix: Ollama max output token override (#1659)
* Fix Ollama max output token override Allow unknown integration models without runtime maxOutputTokens metadata to honor CLAUDE_CODE_MAX_OUTPUT_TOKENS above the Anthropic 64k fallback while still capping at the provider context window or OpenAI-compatible fallback context window. Update the max-output error copy for third-party providers, add regression coverage for issue #1604, and ignore generated Python/pytest cache artifacts. * Use standard pytest cache ignore pattern Replace the non-standard pytest-cache-files pattern with pytest's default .pytest_cache directory ignore entry. |
||
|
|
0c45e16f18 |
feat(config): add compactModel option to use a separate model for compaction (#1445) (#1629)
* feat: add compactModel config option to use a different model for compaction When set and different from mainLoopModel, the forked-agent prompt-cache-sharing path is bypassed (guaranteed cache miss with different models) and the streaming fallback uses compactModel for the API call instead. Closes #1445 * feat(config): expose compactModel in /config TUI and add compactModel test coverage Addresses review feedback on #1629: - Surface compactModel as a managedEnum setting in the /config screen, following the teammateDefaultModel pattern (submenu + ModelPicker). - Add a compact.test.ts case covering the compactModel !== mainLoopModel path: cache-sharing is skipped and the streaming compaction fallback routes model/maxOutputTokensOverride to compactModel. * fix(compact): use compactModel for tool-search check and add no-op guard Addresses round-2 review feedback on #1629: - compact.ts: pass compactModel ?? mainLoopModel to isToolSearchEnabled so the tool-search capability check matches the model actually used for streaming compaction (not always mainLoopModel when a compact model is set) - Config.tsx: mirror the teammateDefaultModel no-op guard — return early when compactModel is unset and the picker confirms null (no-op selection) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(compact): normalize compactModel comparison in no-op guard Compare globalConfig.compactModel ?? null against the picker's selection so re-confirming the current value (including explicit "Default"/null when a model was previously set) is treated as a no-op instead of marking settings dirty. * fix: resolve compactModel alias to full model ID before API calls ModelPicker stores alias strings (e.g. 'sonnet') directly in globalConfig.compactModel. Compact reads that value and must call parseUserSpecifiedModel() to expand it to the canonical model ID before comparing against mainLoopModel or sending to the API. Two read-sites in compactConversation and streamCompactSummary are both fixed. Test updated so the 'skips cache-sharing' case uses the resolved model ID (legacy claude-opus-4-1 remaps to current default) and a new test verifies alias expansion end-to-end. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
c3db79832b |
fix: sandbox temp dir fallback (#1662)
* Fix sandbox temp dir fallback Probe Claude temp directories before returning them and fall back through platform temp and config-home temp paths when the primary temp base is inaccessible. Use the resolved Claude temp dir for sandboxed shell cwd tracking and TMPDIR/CLAUDE_TMPDIR propagation so the sandbox allowlist, Bash, and PowerShell providers agree on the writable temp path. Update @anthropic-ai/sandbox-runtime to 0.0.55 and refresh bun.lock. Validation: bun install passed after escalation; bun run build passed; python -m pytest -q python/tests passed; bun run typecheck:type-tests passed; git diff --check passed. bun run check still reports full-suite order/global-state failures; focused reruns of the reported failing files passed with a dummy ANTHROPIC_API_KEY. bun run typecheck has pre-existing unrelated repo-wide strictness failures; security:pr-scan fails before scanning on mergeBase.stderr. * Fix PR typecheck and read-only temp fallback Handle EROFS as an inaccessible filesystem error for sandbox temp fallback behavior. Add narrow type annotations and inference fixes so the stricter typecheck job passes. |
||
|
|
a36ef463ce |
docs(readme): add social links and clarify license line (#1660)
- Add Discord (discord.gg/k68zFR6AcB) and X (x.com/gitlawb) as shields.io badges in the top badge row and as descriptive links in the Community section. - License section now notes contributor modifications are MIT while the derived Claude Code remains Anthropic's, with a "See more" link to LICENSE. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
241d52af47 |
fix(gitDiff): keep hunk content lines beginning with -- or ++ (#1646)
The metadata-skip block in parseGitDiff matched line.startsWith('---')
and line.startsWith('+++') on every line, including lines inside a hunk.
A removed line whose content starts with '--' becomes the diff line
'---...', and an added line whose content starts with '++' becomes
'+++...'; both were treated as file-header lines and dropped, so the
rendered diff silently lost real changes.
These header markers only appear in the file preamble before the first
@@ hunk header, so the skip block is now gated on !currentHunk. Inside a
hunk, +/-/space lines are content and are kept.
Adds parseGitDiff tests covering the dropped-content case, the regression
that header lines are still skipped, and a normal hunk.
|
||
|
|
bd3ad89dd7 |
fix(security): bundle real sandbox runtime in open CLI (#1641)
* fix(security): bundle real sandbox runtime in open CLI * test(sandbox): cover fail-closed runtime diagnostics * fix(sandbox): report doctor inspection failures |
||
|
|
7c034c5a62 |
feat: add redacted diagnostic issue reports (#1647)
* feat: add redacted diagnostic issue reports * fix: address diagnostic report review feedback * fix: report Codex runtime diagnostics accurately |
||
|
|
b036e9fa7c |
fix: startup provider validation fallback (#1658)
* fix startup provider validation fallback * test startup provider behavior |
||
|
|
d8dbf274b4 |
chore(runtime): align Node.js minimum version (#1644)
* chore(runtime): align Node.js runtime requirements * test(runtime): cover prefixed Node versions * fix(runtime): check node executable in doctor |
||
|
|
00ff6de4ca |
fix(suggestions): stop slash-command dropdown freezing on a throwing getter (#1657)
Typing a multi-character slash command (e.g. /provider) left the dropdown frozen on the bare "/" list with /simplify highlighted. Root cause: the /sandbox command's `get description()` read `SandboxManager.checkDependencies().errors`, but checkDependencies() returns null at runtime, so `.errors` threw. That getter runs for every command while building the Fuse search index (only for non-empty queries — bare "/" returns before the index is built), so the throw rejected the whole updateSuggestions() call and the list never narrowed. Fixes: - sandbox-toggle: null-guard checkDependencies() so the getter can't throw. - commandSuggestions: make index building resilient — a single command whose description/isHidden/aliases getter throws degrades gracefully (safe fallback) instead of breaking suggestions for every command; a command whose name can't resolve is dropped. Broken getters now leave a one-time `warn` debug breadcrumb instead of failing silently. - useTypeahead: command results re-rank every keystroke, so the highlight snaps to the top/best match (selectedSuggestion = 0) instead of following the previously selected command by id (which made it stick to /simplify). Tests: - New sandbox-toggle getter tests (null/present/missing deps, via spyOn). - New commandSuggestions tests: narrowing, throwing description/isHidden/name getters, broken command still listed, and best-prefix-match-first ranking. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
5113e378a6 |
Feat/system prompt immediate tools (#1656)
* feat: instruct model to use tools immediately instead of waiting for user prompts * test: add coverage for immediate-tool-use directive in REPL and non-REPL modes * test(prompts): deterministically clear environment variables in prompt tests |
||
|
|
c2cf603344 |
feat(ctx): add /ctx context window visualization and token bars to /cost (#1610)
* feat(ctx): add /ctx context window visualization and token bars to /cost Adds a new /ctx slash command that surfaces exactly what the model sees on the next API call, with per-category token bars, last-response breakdown, session token usage, per-model totals, and a session summary. The command reuses the same pipeline as /context (compact boundary, optional context-collapse transform, microcompact, analyzeContextUsage) so the totals match what is actually sent, not a rough estimate. A single 'local' command with supportsNonInteractive: true is registered in the public COMMANDS array (replacing the disabled /ctx_viz stub) and added to REMOTE_SAFE_COMMANDS and BRIDGE_SAFE_COMMANDS, so /ctx works identically in interactive REPL, headless -p, remote, and bridge modes. /cost gains a Token usage section with colored bars for input, output, cache read, and cache write tokens, appended after the existing cost/duration/code-changes block without changing the per-model line. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ctx-viz: fix bar scale to use contextWindow denominator - Current Context block: use contextWindow as barMax so bars visually match percentage column - Session Token Usage: use sessionTotalTokens as sessionMax for same reason - Update test assertions for new tokens/contextWindow scale * cost-tracker: add format test for token bar display * test: add missing mocks for ctx_viz rendering test * fix(ctx_viz): restore mocks after test; filter capacity rows from model-seen breakdown * test: verify capacity rows are filtered from ctx viz output - Add 'Free space' capacity row to mocked analyzeContextUsage categories - Add assertion that rendered output does not contain 'Free space' - Verifies CAPACITY_ROWS filtering logic in ctx-noninteractive.ts * fix: isolate ctx_viz mocks and filter deferred categories - Restore real modules in afterEach to prevent mock.module() leakage into downstream tests (autoCompact, compression) - Filter deferred categories (isDeferred: true) from model-visible rows to avoid overstating context usage * fix: eliminate mock.module() for leaky modules in ctx_viz test Only analyzeContext.js is mocked (single data fixture). All other modules (autoCompact, microCompact, context, model, state) use their real implementations to avoid process-global mock.module() leakage into downstream autoCompact and compression tests. * fix: eliminate mock.module() entirely via renderCtxReport extraction Extract renderCtxReport() from call() so the rendering test can construct a hand-crafted RenderInput directly, requiring zero mock.module() calls. This avoids process-global mock leakage into downstream autoCompact/compression tests AND makes the test immune to mock pollution from any preceding test file. * chore: derive RenderInput from collectCtxData return type, use it in test - RenderInput is now Awaited<ReturnType<typeof collectCtxData>> — single source of truth, no manual property duplication. - Test imports RenderInput type and uses it in the renderCtxReport assertion instead of 'unknown', enabling compile-time fixture validation. --------- Co-authored-by: Gravirei <gravirei@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
8bce86f841 | docs(test): clarify work secret localhost regression (#1637) | ||
|
|
7448be1164 |
[codex] chore(query): add tool-pairing diagnostics (#1625)
* chore(query): add tool-pairing diagnostics Add a pure validator for tool_use/tool_result pairing issues and feed phase, query source, model, provider, and agent context into the existing pre-API repair log. Keep ensureToolResultPairing behavior intact while making future repair logs identify missing, orphaned, duplicate tool_use, and duplicate tool_result cases. * fix(query): complete pairing diagnostics coverage Address CodeRabbit review by detecting server-side tool use blocks without matching in-message results and by making pairing validation lazy so it only runs after the repair path has actually mutated messages. |
||
|
|
124788b1f3 |
Feat/fuzzy-file-edit (#1561)
* feat(FileEditTool): add whitespace-agnostic fallback matching * test(FileEditTool): add unit tests for whitespace-agnostic matcher * fix(FileEditTool): preserve boundary whitespace in fuzzy match as requested by CodeRabbit * fix: address PR feedback on token boundaries and indentation recovery * fix: recover deep indentation for nested blocks * fix: isolate trailing newline boundary from next line indentation * fix: abort fuzzy match if requested indentation map conflicts * fix: resolve typecheck error by checking adjustNewStringIndentation return value * fix: preserve exact vertical newline count and horizontal boundary spacing * fix: enforce strict inline whitespace and preserve Markdown hard breaks * fix: add missing boolean argument to normalizeIndentation in adjustNewStringIndentation |
||
|
|
9a72ecd25c |
[codex] fix(tokens): fallback when provider lacks countTokens (#1624)
* fix(tokens): fallback when provider lacks countTokens * test(tokens): isolate shim fallback coverage Avoid process-wide api client mocks in token estimation tests by exercising the count-token dispatch helper directly. Add non-empty tool coverage for the rough fallback path so the local overhead remains covered. |
||
|
|
174ebd5125 |
[codex] fix(query): abort active work on QueryGuard timeout (#1623)
* fix(query): abort active work on QueryGuard timeout * fix(query): contain timeout handler failures * test(query): harden timeout handler cleanup * test(query): centralize QueryGuard timer cleanup |
||
|
|
9fbcd755a3 | fix(mcp): demote successful stdio startup stderr (#1622) | ||
|
|
5fd4a09d01 | fix(read): improve oversized file guidance (#1626) | ||
|
|
661b5ad8cd |
docs(non-technical-setup): add Getting Help section with support link… (#1631)
* docs(non-technical-setup): add Getting Help section with support links and diagnostic check The non-technical setup guide ended abruptly after the advanced setup reference. New users — especially non-technical ones — had no clear path to follow when they encountered issues beyond the common problems listed. Add a Getting Help section at the end that includes: - GitHub Discussions link for Q&A and setup help - GitHub Issues link for bugs and feature requests - A quick diagnostic check (openclaude --version) with actionable next steps for when the command is not found This also cross-references the Windows Quick Start guide for npm Path configuration, reusing existing documentation rather than duplicating instructions. Impact: improves first-run experience for the guide's target audience without changing any code or behavior. * docs(non-technical-setup): add Getting Help section and restructure around /provider flow The guide previously directed users to set environment variables before launching the CLI. This misses the simpler onboarding path: users can install, run openclaude, and use /provider for guided setup inside the CLI without any env vars. Changes: - Remove required API key from 'Before You Start' prerequisites - Rewrite 'Fastest Path' to recommend install -> run -> /provider - Remove env variable step from the fast path - Add note under provider descriptions that /provider handles setup - Update 'Invalid API key' troubleshooting to use /provider instead of re-pasting env vars - Add 'Getting Help' section with support links and diagnostic check Impact: better aligns the non-technical guide with the current recommended onboarding flow. No code changes. * docs(non-technical-setup): restore API key prerequisite, restructure Fastest Path around /provider Re-add the API key to prerequisites. The key is still needed - the /provider flow is just the recommended way to enter it instead of env vars. Changes: - Keep API key in prerequisites - Rewrite Fastest Path to install -> run -> /provider instead of env vars - Add note under provider descriptions that /provider handles setup - Update Invalid API key troubleshooting to use /provider - Add Getting Help section with support links and diagnostic check Impact: aligns the non-technical guide with the current recommended onboarding flow. No code changes. |
||
|
|
4e56fd5921 |
[codex] perf(skills): cap skill listing budget (#1627)
* perf(skills): cap skill listing budget * chore(skills): export listing budget options |
||
|
|
de726c43e1 |
Fix custom provider context discovery (#1620)
* Fix custom provider context discovery Teach the custom OpenAI-compatible gateway to discover context windows from /v1/models metadata, including LiteLLM model_info context_length and max_input_tokens fields. Use cached discovery metadata when resolving runtime context and output limits, with sync cache reads kept memoized and partitioned by endpoint, credential, and custom headers. Add provider-profile maxContextLength env overrides and document LiteLLM context metadata plus the CLAUDE_CODE_OPENAI_CONTEXT_WINDOWS fallback. Cover startup discovery, runtime cache lookup, custom gateway parsing, profile overrides, and env custom-header cache partitioning with focused tests. * Fix discovery smoke test isolation Clear CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC during discovery service test setup so full-suite environment state cannot force startup discovery down the nonessential-traffic skip path. Verified with: - bun test ./src/integrations/discoveryService.test.ts - CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 targeted startup discovery test - bun run smoke - bun run typecheck * Partition custom discovery startup test cache Use a test-only custom header in the startup custom route discovery test so it exercises network discovery even when the full suite has pre-seeded the no-header custom discovery cache key. Verified with: - bun test ./src/integrations/discoveryService.test.ts - CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 targeted startup discovery test - bun run smoke - bun run typecheck * Fix profile context override lifecycle Add CLAUDE_CODE_OPENAI_CONTEXT_WINDOWS to managed profile cleanup so switching profiles clears stale context-window overrides, including same-model OpenAI-compatible switches. Preserve persisted context-window overrides when rebuilding OpenAI-compatible startup env after restart. Verified with: - bun test src/utils/providerProfile.test.ts src/utils/providerProfiles.test.ts - bun run typecheck - bun run smoke * Detect profile context override drift Include CLAUDE_CODE_OPENAI_CONTEXT_WINDOWS in OpenAI-compatible active-profile env alignment so managed profiles with maxContextLength are re-applied when the override is missing or stale. Verified with: - bun test src/utils/providerProfiles.test.ts - bun run typecheck - bun run smoke |
||
|
|
716c1d47f6 |
feat(compact): auto-compact prompt on /resume + determinate progress bar (#1386)
* feat(compact): auto-compact prompt on /resume + determinate progress bar On /resume, if the conversation exceeds 70% of the auto-compact threshold, a dialog appears offering to compact before continuing. Shows token count, context window usage, and effective window percentage. Also adds a determinate progress bar during compaction that advances as the summary streams in. - Add RESUME_COMPACT_PROMPT feature flag (enabled) - Add shouldPromptCompactOnResume() threshold gate - Add ResumeCompactPrompt dialog component - Emit compact_progress events during streaming in compact service - Render ProgressBar next to spinner during compaction in REPL - Add 5 tests for threshold gating logic * feat(compact): determinate progress bar via streamed text deltas Wire compact_progress events from streamed summary output (forkedAgent onStreamEvent forwards text deltas) and render a determinate ProgressBar in the REPL, replacing the indeterminate spinner during compaction. Extracts the bar into a CompactProgressBar component shared by manual /compact and the resume-triggered path. Emits coarse hooks/start ticks on the session-memory path so the bar moves immediately. * fix(compact): keep spinner visible and use bracketed progress bar Render the "Compacting conversation" spinner throughout compaction with the progress bar beneath it, instead of swapping the spinner out for the bar. Redraw the bar as a bracketed fill ([███····]) with no background rectangle, so it no longer reads as one solid block. * docs: remove resume-compact-prompt plan file Per PR review feedback — drop the planning doc from the PR. * fix(compact): prompt to compact on CLI --continue/--resume startup Startup resume paths (--continue, --resume <id>, ResumeConversation screen) install initialMessages via the initial useState rather than the resume() callback, so the threshold check never ran. Schedule the prompt from the mount-time initialMessages effect as well. * fix(compact): use MessageType alias for resumeCompactPending state main aliases the message type import as `Message as MessageType`; the rebased resumeCompactPending state still referenced the bare `Message`. * fix(compact): always close session-memory progress and fix progress-ratio units - Wrap the session-memory compaction attempt in try/finally so compact_end is always emitted, even when it returns null or throws, preventing a stuck progress bar/spinner. - Clear compactProgressRatio in resetLoadingState so an aborted or errored compaction does not leave the progress bar rendered in the idle UI. - Fix the progress denominator unit mismatch: estimatedOutputChars now uses a token-to-char converted estimate (preCompactTokenCount) instead of the token-scale preCompactTokenCount * 0.25, so progress no longer advances too fast and hits the cap prematurely. |
||
|
|
a7d6580521 |
fix(plugins): prevent ENOENT on Windows marketplace cache finalization (#1500) (#1531)
* fix(plugins): prevent ENOENT on Windows marketplace cache finalization (#1500) * test(plugins): add regression test for Windows marketplace cache finalization (#1500) Add regression tests covering the case-only path comparison bug where fs.rm would destroy source data when temp and final cache paths differ only in case on case-insensitive filesystems (Windows NTFS). Three test cases: 1. Generic mixed-case name (MyMarketplace/mymarketplace) — verifies the samePathCaseInsensitive guard skips rm+rename 2. GitHub-style naming (AgriciDaniel-claude-obsidian) — explicitly models the exact #1500 bug report scenario 3. Already-lowercase name (claude-obsidian) — verifies the string-equality fast path that GitHub sources hit post-fix Also exports loadAndCacheMarketplace from _test for testability. * test: add rename-failure fallback regression test for EXDEV Add a regression test that forces the rename-failure fallback path in loadAndCacheMarketplace. Uses a 'url' source with a mocked axios response so the temp cache path (timestamp-based) truly differs from the final cache path (marketplace.name.toLowerCase()), bypassing the samePathCaseInsensitive guard. The test stubs rename to throw EXDEV and verifies the cp+rm fallback correctly copies data to the final location, cleans up the temporary path, and returns the final cache path. * test: fix brittle temp-cleanup assertion in marketplace fallback test Replace fragile rmSpy.call filter with direct readdirSync(cacheDir) to verify the real temp directory state. Fixes CI failure at line 316. * test: use pre-call snapshot for platform-agnostic fallback assertion Replace brittle readdirSync length check with delta comparison against pre-call directory state. Works on both case-sensitive (Linux) and case-insensitive (Windows) filesystems. * test: isolate marketplace mocks and fix cross-platform temp file assertion - Move axios mock inside describe with beforeAll/afterAll lifecycle - Filter temp_*.json files from directory delta comparison - Add mock isolation to prevent leakage to other test files * test(marketplace): assert renameSpy called and no temp files linger after EXDEV fallback * test(marketplace): add cp spy and isolate axios mock to test lifecycle Address outstanding review gaps for #1531: 1. The rename-failure fallback (cp + rm) is now explicitly verified via a cpSpy on the real fs/promises.cp. The mock.module('fs/promises', ...) registration in beforeAll wraps cp with a call-through spy, so the test exercises the actual filesystem fallback while still asserting the call args (temp source, finalCachePath dest, { recursive: true } option). Previously, the test only proved the final file existed, which could be reached by a different code path or by leftover pre-test state. 2. The axios mock is now isolated to the test lifecycle: the axiosGetSpy is created in beforeEach (with a fresh implementation) instead of at module scope, and the mock.module('axios', ...) factory uses a closure indirection so the spy can be swapped per test. This prevents call counts and implementations from leaking across tests in this suite or into other suites. 3. cpSpy.mockClear() runs in beforeEach to reset .mock.calls / .mock.results between tests while preserving the call-through behavior of the persistent mock.module() registration. Skipped: the P1 about finalCachePath lowercasing the cache directory (marketplaceManager.ts:1712). The current code already preserves cacheDir and lowercases only marketplace.name, so the original review concern was already addressed by an earlier commit. Skipped: temp path detection via rmSpy filter and explicit temp_* cleanup verification. Both are already covered by the existing afterEntries/beforeEntries snapshot comparison (L334-377) and the lingeringTempFiles assertion (L380-385). * Revert "test(marketplace): add cp spy and isolate axios mock to test lifecycle" This reverts commit c57ad55db18edbd235d8af337ac755fe1cb12eaa. * test(marketplace): force real module + drop unused source.name Two pre-existing issues that blocked typecheck and caused 4 marketplace tests to fail in the full suite: 1. mock.module('./marketplaceManager.js', async (original) => ...) called original() to 'force the real module' but original() returns the previously-registered factory's output, which is a stale mock from a prior test file (e.g. officialMarketplaceStartupCheck.test.ts or lspRecommendation.test.ts). The stale mock has its own env-var behavior, so getMarketplacesCacheDir() returned the leaked /tmp/openclaude-marketplaces instead of the test's tempDir. The real module reads process.env.CLAUDE_CODE_PLUGIN_CACHE_DIR live on every call, so the beforeEach override takes effect immediately. Forcing the real module makes the env-var-driven cache path work correctly. The factory now returns undefined so Bun uses the real module regardless of any previously-registered mock. 2. The EXDEV test's url source had a 'name' field that does not exist in the MarketplaceSource.url variant of the schema (only url and optional headers are allowed). The field was unused — the test hardcodes finalCachePath from the marketplace manifest name (lowercased), not from source.name. Drop the field; typecheck goes green. * test(marketplace): use mock.restore() to clear stale marketplaceManager mock Previous attempt used mock.module(path, () => undefined) but Bun rejects that — TypeError: 'mock(module, fn) requires a function that returns an object'. mock.module() with no factory isn't a documented API, so switch to mock.restore() which clears all module mocks. After mock.restore(), the real marketplaceManager.js is used on import. The EXDEV describe's beforeAll re-registers the axios mock (which mock.restore() also cleared), so the suite-specific mock still works. Skipped: spying on cp via setFsImplementation. The FsOperations interface (src/utils/fsOperations.ts L23-72) has no cp method, so adding cp: cpSpy to setFsImplementation would fail typecheck. The rename-failure fallback is still verified via filesystem state assertions: before/after readdirSync snapshot plus the explicit lingeringTempFiles === [] check. * Revert "test(marketplace): use mock.restore() to clear stale marketplaceManager mock" This reverts commit bbbebfb305b5585f34e0a13530a5ea4ce7a9edd7. * test(marketplace): fix mock.module TypeError (factory must return object) 0c917a1 used () => undefined to force the real module, but Bun rejects factories that don't return an object ('mock(module, fn) requires a function that returns an object'). Switch to mock.restore() at the top of the file — clears all module mocks registered by prior test files so the real marketplaceManager.js is used on import. The EXDEV describe's beforeAll re-registers the axios mock (which mock.restore also cleared), so suite-specific mocks still work. The env-var-leak bug (getMarketplacesCacheDir returning the leaked /tmp/openclaude-marketplaces instead of the test's tempDir) is a deeper issue with how process.env is read in Bun's test runner. Pre-existing on origin/main and out of scope for this PR; documented in the PR body. * test(marketplace): drop mock.restore() to let natural module resolution work CodeRabbit round 8 P2: the previous implementation called mock.restore() at file-scope on the assumption it would clear module mocks registered by other test files (lspRecommendation.test.ts, officialMarketplaceStartupCheck.test.ts). Bun's docs confirm mock.restore() does NOT clear mock.module() registrations, so the partial mock from the prior file persisted and the import of _test broke ("Export named '_test' not found"). The reproducer is real: running lspRecommendation.test.ts first then this file fails. But the root cause is a pre-existing bug in lspRecommendation.test.ts (its mock for '../config.js' is missing normalizeMaxMessagesCompactionThreshold, added to config.ts in PR #1605 / commit |
||
|
|
822eff39d1 |
fix(copilot): limit sub-agent concurrency to reduce Premium Request usage (#678) (#1534)
* fix(copilot): limit sub-agent concurrency to reduce Premium Request usage (#678) * fix(copilot): enforce sub-agent concurrency cap at Agent invocation level AgentTool.isConcurrencySafe() now returns false when getCopilotMaxConcurrentSubagents() > 0, preventing the tool scheduler from batching multiple Agent calls together. This ensures at most one sub-agent runs at a time when the cap is active. Previously, AgentTool was always concurrency-safe, allowing the scheduler's runToolsConcurrently to batch multiple Agent calls from a single assistant message — bypassing the documented MAX_SUBAGENTS cap. Add comprehensive copilotOptimization unit tests. * fix(copilot): enforce cap for any positive value and honor OPTIMIZATION_DISABLED - shouldForceSyncSubagentsInCopilotMode: gate on > 0 instead of === 1 so any configured cap (2, 3, ..., 10) forces serial execution - isConcurrencySafe: early-return true when OPTIMIZATION_DISABLED is set - Update log message to reflect any-cap behavior * fix(copilot): align scheduler with launch path, fix mock leak - isConcurrencySafe now uses shouldForceSyncSubagentsInCopilotMode() instead of raw cap check, matching the launch path at line 447 - Add afterAll(mock.restore) to copilotOptimization.test.ts to prevent providers.js mock leaking to AgentTool routing tests * fix(copilot): clarify MAX_SUBAGENTS semantics and fix remediation hint log - Document that only MAX_SUBAGENTS=0 and =1 are enforced; values 2-10 have no runtime effect. - Fix the log remediation hint to depend on the actual cause: MAX=0 suppresses sub-agents entirely (not just forces sync), FORCE_SYNC=1 requires unsetting the flag, and MAX>=1 requires ALLOW_SUBAGENTS=1 to restore parallel execution. * docs(env): document GITHUB_COPILOT_* tuning vars in .env.example The Copilot Premium Request optimization introduces four env vars (GITHUB_COPILOT_MAX_SUBAGENTS, GITHUB_COPILOT_ALLOW_SUBAGENTS, GITHUB_COPILOT_FORCE_SYNC_SUBAGENTS, GITHUB_COPILOT_OPTIMIZATION_DISABLED) that change how sub-agents run for CLAUDE_CODE_USE_GITHUB=1 sessions. Previously these were documented only in source comments, which made them undiscoverable for users affected by the new default. Add them to the GitHub Models section (Option 4) of .env.example with descriptions of each var's effect and default value, addressing the reviewer ask to put the new default behavior in user-facing docs. * fix(copilot): telemetry reflects final async mode; docs in README Address outstanding review gaps for #1534: 1. Telemetry is_async/isAsync now uses the final shouldRunAsync value computed once at the top of the function (was duplicating the partial expression, omitting isCoordinator/forceAsync/assistantForceAsync/ proactiveModule signals that contribute to the launch decision). 2. The shouldSuppressSubagentsInCopilotMode() throw now happens before the event log (so a suppressed-agent error isn't followed by a misleading 'is_async: true' event). 3. isCoordinator, forceAsync, assistantForceAsync are now computed once alongside forceSyncCopilot instead of being declared inline later. 4. README: add GitHub Copilot sub-agent optimization subsection under Provider Notes, with the env var table mirroring the .env.example entry (default behavior, cap semantics, all-opt-out). The doc comment in copilotOptimization.ts L16-29 already explains MAX_SUBAGENTS=0/1 enforcement; the test at L186-191 is consistent with the current implementation (positive cap = synchronous). Skipped: getEffectiveConcurrencyCap() in toolOrchestration.ts (the function no longer exists in the current code; the bot's review was based on an earlier version). * fix(copilot): skip <BackgroundHint /> when forced sync When forceSyncCopilot is true the task can no longer be backgrounded (registerAgentForeground is skipped at L918), but the background hint UI was still rendered once the progress threshold elapsed. That advertises a non-existent affordance on every long-running Copilot sub-agent, which is confusing for users. Gate the hint on the same !forceSyncCopilot condition as the foreground registration. Address the CodeRabbit P2 on round 6. * test(copilot): use spyOn instead of mock.module to avoid partial-mock leak CodeRabbit P2 review on round 7 found the copilotOptimization test registered mock.module('./model/providers.js', () => ({ only 4 exports })) which removed all other exports of providers.ts. Downstream tests in the same CI process (e.g. withRetry, domainCheck, apiPreconnect, agent) that import symbols like isFirstPartyAnthropicBaseUrl would then fail with 'Export named ... not found in module' errors. Switch to spyOn() on the real providers module's getAPIProvider. The real module's other exports remain available, and the spy is torn down via mockRestore() in afterEach. Also drop the cache-busting dynamic-import pattern: the spy persists across the static import, so the test no longer needs a fresh module per test. Also fix README P3: the earlier PowerShell heredoc introduced a TAB (0x09) and Form Feed (0x0C) in place of 't' and 'f' in the new Copilot section, rendering 'tengu_agent_tool_selected' as 'engu_...' and 'false' as 'alse'. Rewrite the line with proper 't' and 'f' characters and add backticks for code formatting (was unformatted plain text). Skipped: P2 scheduler-boundary coverage (CodeRabbit round 6 item). That requires driving multiple Agent tool-use blocks through the scheduler in AgentTool/StreamingToolExecutor, which is a larger change than the current PR's scope. * test(copilot): add FORCE_SYNC overrides ALLOW_SUBAGENTS precedence test CodeRabbit round 9: add a test that pins the precedence between GITHUB_COPILOT_FORCE_SYNC_SUBAGENTS=1 and GITHUB_COPILOT_ALLOW_SUBAGENTS=1. The user explicitly asking for synchronous execution must win over the softer "I'm fine with the cap" opt-out. A future reordering of the checks in shouldForceSyncSubagentsInCopilotMode() would silently allow parallel Copilot sub-agent launches when the user asked for sync; this test locks the precedence. Verified locally: 23/23 pass (was 22/22 before adding this test). * fix(copilot): address jatmn round 11 P2/P3 and add scheduler-boundary coverage This commit addresses the latest human + bot review feedback on #1534 across three findings: 1. **P3: Update GitHub Copilot comment in github.ts to use billing-cycle wording.** The previous comment hard-coded "per month (300 for Copilot Free)" — a calendar quota the runtime doesn't own. Mirror the wording from src/utils/copilotOptimization.ts: "per billing cycle, with the exact quota set by the user's Copilot plan." Same docstring shape across both files now. 2. **P2: Add afterEach cleanup to copilotOptimization.test.ts.** Captured the GITHUB_COPILOT_* env vars at module top-level and restore them in afterEach. Previously only beforeEach deleted them, so the precedence test (which sets FORCE_SYNC=1 + ALLOW_SUBAGENTS=1) left those values in process.env after the file completed. Verified by `bun test src/utils/copilotOptimization.test.ts ../copilot-env-probe.test.ts`: before the fix, the probe test sees FORCE_SYNC=1 leaked. After the fix, the probe sees the original env. This is the round 11 P2 review item from jatmn. 3. **P2: Add scheduler-boundary regression test.** New file src/tools/AgentTool/AgentTool.copilotScheduling.test.ts pins the launch↔scheduler alignment by calling `AgentTool.isConcurrencySafe()` directly under each Copilot flag combination. Seven matrix rows: OPTIMIZATION_DISABLED=1, default cap=1, cap=2, ALLOW_SUBAGENTS=1, FORCE_SYNC=1 alone, FORCE_SYNC=1 + ALLOW_SUBAGENTS=1 (precedence), cap=0 (suppressed). A future reorder of the helpers in copilotOptimization.ts that breaks the precedence would fail FORCE_SYNC + ALLOW_SUBAGENTS, locking the launch/scheduling alignment. This is the round 9 / round 11 P2 review item from CodeRabbit + jatmn that has been deferred across multiple rounds. The test uses spyOn on providers.getAPIProvider to control the provider state, then imports AgentTool via cache-busting (?copilotScheduling=... query string) — the same pattern as AgentTool.routing.test.ts. Per-test timeout of 30s absorbs the ~16s one-time AgentTool module load (subsequent tests are sub-1ms because the module is cached after the first beforeAll import). All three changes are verified locally: - `bun test src/utils/copilotOptimization.test.ts` — 23/23 pass - `bun test src/tools/AgentTool/AgentTool.copilotScheduling.test.ts` — 7/7 pass - `bun test --max-concurrency=1` of both files together — 30/30 pass * test(copilot): move per-test timeout to 3rd arg (bun:test API) * fix(copilot): let FORCE_SYNC override MAX_SUBAGENTS=0 + add scheduler-boundary test Two review findings: 1. FORCE_SYNC vs suppression: shouldSuppressSubagentsInCopilotMode() returned true for MAX_SUBAGENTS=0 before FORCE_SYNC was consulted, so GITHUB_COPILOT_MAX_SUBAGENTS=0 + GITHUB_COPILOT_FORCE_SYNC_SUBAGENTS=1 threw "Sub-agents are disabled" instead of running them synchronously, contradicting the documented behavior. FORCE_SYNC (like ALLOW_SUBAGENTS) now bypasses the =0 suppression; docs clarified accordingly. 2. Scheduler-boundary coverage: the existing tests only called isConcurrencySafe() directly. Added a regression that drives multiple Agent tool-use blocks through the real batching path (partitionToolCalls, now exposed via _test): forced-sync splits them into serial single-block batches, ALLOW_SUBAGENTS coalesces them into one concurrent batch. Catches a future divergence between launch and scheduling policy for multiple Agent blocks in one assistant message. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
f4c3be850e |
chore: remove dead code and add knip gate to CI check (#1612)
Delete 32 unreferenced source files (~4,000 lines) verified dead by import-specifier grep and knip: test-only token utilities, orphaned hooks (useTaskListWatcher, useSkillImprovementSurvey + its component), the removed DevBar and ConfigTool UIs, unregistered bundled skills (stuck, verifyContent), unused analytics sinks, the benchmark command, and stale migrations/helpers. Remove unused dependencies code-excerpt, stack-utils, and tsx from package.json plus their entries in build stub/external lists. Add knip with a tuned knip.json (entrypoints, build-time stub targets, subprocess-launched fixtures, and runtime-string-imported SDKs ignored; providerAutoDetect kept intentionally as provider pre-wiring) and wire `bun run deadcode` into the `check` script so dead code stays dead. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
db2d093af3 |
fix(session): filter Anthropic-only params from 3P provider requests (#248) (#1533)
* fix(session): filter Anthropic-only params from 3P provider requests (#248)
* test: add regression tests for provider gates (PR #1533)
- betas.test.ts: add isAnthropicProvider tests for mistral, xai, minimax
- compact.test.ts: verify compactConversation skips forked-agent
cache-sharing for non-Anthropic providers and uses it for Anthropic
These tests pin the provider gates added in PR #1533:
- getMergedBetas() returns [] for non-Anthropic (betas.test.ts)
- compactConversation()/streamCompactSummary() skip cache-sharing
when isAnthropicProvider() returns false (compact.test.ts)
* test: fix leaky mock harness and add redacted_thinking coverage
- Complete all missing module exports in compact.test.ts mock harness to
prevent global mock leakage breaking other test files
- Add afterAll cleanup hook for safety
- Add regression test for redacted_thinking blocks stripped before
OpenAI-compatible replay in openaiShim
* test: eliminate mock.module leaks causing 10 CI failures
Replace broad mock.module() stubs for betas/providers/envUtils with
env-var-based provider control. Add mock.restore() safety nets to
betas.test.ts and autoCompact.test.ts. Tests now use real modules
without cross-test-file mock contamination.
* test: clear provider profile env vars and remove aggressive mock.restore
- Snapshot and clear CLAUDE_CODE_PROVIDER_PROFILE_ENV_APPLIED flags
so isAnthropicProvider() returns correct values regardless of
provider profile state from previous tests
- Remove mock.restore() from betas.test.ts and autoCompact.test.ts
that was breaking legitimate mocks in other test files
* test(betas): ensure provider env vars are cleared in afterEach, not just beforeEach
* test(3p): also clear VENICE_API_KEY and MIMO_API_KEY per test
resolveEnvOnlyProviderRouteId (src/integrations/routeMetadata.ts:428) returns
'venice' or 'xiaomi-mimo' when those env vars are set, even when the test
sets CLAUDE_CODE_USE_OPENAI=1 — its 'env-only' check runs BEFORE the
USE_OPENAI branch (L658). Without clearing these, leaked values from
earlier tests in the same process cause isAnthropicProvider() to return
the wrong value, so the new provider-gate assertions fail:
getMergedBetas returns [] for the openai provider
expected: []
received: [claude-code-20250219, interleaved-thinking-2025-05-14,
context-management-2025-06-27, prompt-caching-scope-2026-01-05]
isAnthropicProvider is false for the openai/gemini/mistral/xai/minimax
expected: false received: true
compactConversation provider gate > uses forked-agent cache-sharing
for Anthropic providers
expected: runForkedAgent called received: 0 calls
The previous 'CLAUDE_CODE_PROVIDER_PROFILE_ENV_APPLIED[_ID]' fix was
insufficient because those vars don't gate the env-only routes directly;
VENICE_API_KEY / MIMO_API_KEY do. Add them to the per-test cleanup in
both betas.test.ts (PROVIDER_ENV_KEYS) and compact.test.ts (SAVED_ENV).
Skipped: pre-existing CI failures unrelated to this PR's provider gate
(autoCompactIfNeeded circuit breaker × 7, getEffectiveContextWindowSize
on MiniMax M2, microCompact MCP, /export direct filename, getProjectMemoryPathForSelector).
These fail on origin/main with the same pre-PR failures and are out of
scope for #1533.
* test(3p): use crypto.randomUUID() for message uuids
The Message type's uuid field is now a branded UUID type from node:crypto
(strings must match the 5-segment UUID format). After rebase onto the
latest origin/main, the typecheck failed at L26 and L38 where
\\ est-\\\ no longer satisfies the type.
crypto.randomUUID() produces a valid UUID per call, so each message
gets a unique, type-correct uuid. No behavioral change for the tests
themselves — they only rely on the messages being distinct.
* test(3p): cast assistantMessage.message through 'as never'
The Message type's assistant message field is AssistantMessageContent
<BetaContentBlock>, which requires id, model, usage at the type level.
The compact code paths under test don't read these fields, so the
helper focused on the text content needs to bypass the type check.
Pre-rebase this likely passed via a wider union; after the rebase the
branded type tightened. 'as never' keeps the helper minimal without
having to fabricate realistic id/model/usage values.
* test(betas): add non-Claude GitHub regression test for provider gate
CodeRabbit P2 review on the most recent round: the provider gate tests
cover the GitHub Native Anthropic exception (Claude model) but no
sibling assertion for CLAUDE_CODE_USE_GITHUB=1 with a non-Claude model.
That untested branch is the risky half of the gate — a future broadening
of isGithubNativeAnthropicMode() (e.g. matching on the wrong substring,
or matching on 'claude' too permissively) would silently re-introduce
Anthropic-only beta headers for OpenAI-style models served via GitHub.
Add the inverse case: GitHub provider with OPENAI_MODEL='gpt-4o-mini' must
return [] (the gate strips the headers). Verified locally: betas.test.ts
runs 19/19 (with the new test) and the betas+compact pair runs 21/21
cleanly, confirming no test-isolation regression between the two files.
* docs(compact): update stale "3P default: true" comments
CodeRabbit round 10 nitpick: the comment at compact.ts L434-439 still
claims "3P default: true" and frames the GB flag as a "kill-switch",
but the code is now gated by isAnthropicProvider() so non-Anthropic
3P providers are never on this path in the first place. Update both
the compactConversation() block and the streamCompactSummary() block
(now points at the first block instead of a separate stale sentence)
to describe the actual current behavior: cache-sharing is enabled
only for Anthropic-capable providers when tengu_compact_cache_prefix
is on; non-Anthropic providers remain incompatible and would send
Anthropic-only params they reject.
Verified: 2/2 compact.test.ts still passes; no behavioral change, only
the comment text.
Skipped: rebase to current origin/main (#1533 was rebased earlier in
4c1b4c2; no new conflicts since).
* test(3p): also clear NEARAI_API_KEY per test
Reproduce locally with `bun test --max-concurrency=1` and find that
`providerValidation.test.ts` (which runs alongside the other 3800+ test
files in the same CI process) sets NEARAI_API_KEY for one of its
neearai-detect tests, and the previous cleanup list missed that env
var. So when betas.test.ts and compact.test.ts later run their
`isAnthropicProvider is true for firstParty` (and the other
`isAnthropicProvider is true for *`) tests, hasNearaiEnvOnlyProviderIntent()
returns true on the leaked value, the function classifies the provider
as 'nearai' (not Anthropic), and the gate strips all betas. The test
that sets no env var then sees `getMergedBetas()` return `[]` instead
of the Anthropic list, which is the failure pattern on CI.
resolveEnvOnlyProviderRouteId (src/integrations/routeMetadata.ts:428)
returns 'nearai' (or 'xai', 'minimax', 'venice', 'xiaomi-mimo') for
those env var leak paths, all of which were missed by the prior
profile-env-var fix. The previous CLAUDE_CODE_PROVIDER_PROFILE_ENV_APPLIED
fix was insufficient because that var doesn't gate the env-only routes
directly — only the API keys do. NEARAI_API_KEY is the latest in the
family (xai/minimax/venice/mimo all already in the list).
Add NEARAI_API_KEY to PROVIDER_ENV_KEYS in betas.test.ts and
SAVED_ENV in compact.test.ts, matching the xai/minimax/venice/mimo
additions from 768ab65.
Verified: betas.test.ts (19/19) and compact.test.ts (2/2) still
pass in isolation and as a pair. The full-suite failure should now
resolve since the leak path is covered.
Skipped: tracing the exact leak site. The 3800+ test files can't
all be enumerated; the fix is comprehensive (all known env-only
intent vars are now in the clear list).
* test(3p): pre-warm importFreshBetas in beforeAll to avoid 5s timeout
The first call to importFreshBetas() in this file triggered a 4.3s module
load (growthbook feature-flag init), which exceeded Bun's default 5s test
timeout. On the agent's local Windows machine, that race is close to
deterministic (the openai test, being first in source order, paid the
full cost and reported `(fail) this test timed out after 5000ms`). On CI
(Ubuntu) the import was usually fast enough to pass — but flaky, and
completely unrelated to actual test correctness.
Move the slow import to beforeAll so it happens once before any test
runs and is not charged to the first test's budget. After pre-warming,
every per-test import is sub-second (the 579ms / 8ms timings in the
last run confirm this). All 21 tests in the betas+compact pair now pass
deterministically.
This is the only remaining source-order flake in this file. The actual
test logic (provider-gate assertions, env-var isolation, cache-busting
imports) was already correct.
* test(3p): add FIREWORKS_API_KEY to provider env cleanup
CI smoke run on #1533 still shows the openai/gemini/bedrock tests
failing in `getAPIProvider` even after the pre-warm + clearProviderEnv
fix from
|
||
|
|
9e902db866 |
feat(agent-routing): model-only agent routes (set the verifier or any agent's model on the current provider) (#1617)
* feat(agent-routing): allow model-only agentModels entries in settings schema * feat(agent-routing): resolve model-only agent routes that reuse the current provider * feat(agent-routing): enforce org model allowlist for model-only agent routes * docs(agent-routing): document model-only routes and built-in agent keys * test(agent-routing): stub and assert partial-entry routing warnings Silence the intentional console.error noise from partial agentModels entries in CI logs by stubbing console.error per describe block and asserting the expected warning message fires, so future routing warnings or failures are not masked. * test(agent-routing): consolidate shouldEnforceModelAllowlist import Move the import to the top import block instead of mid-file. |
||
|
|
5f48ada523 |
Add dedicated continue command (#1618)
Add a first-class /continue command for issue #1613 so interrupted work can resume from the transcript instead of opening the /resume picker or merely acknowledging the request. Keep /resume focused on previous-conversation selection while sharing goal/todo continuation helpers with /continue. Continue active goals without resetting turn counters or last evaluation state, resume paused goals through the goal resume path, and fall back to a hidden continuation instruction when no goal or todos are tracked. Make goal persistence best-effort during paused-goal continuation so an on-disk save failure does not block the in-memory resume and continuation query. Thread optional /continue hints through goal/todo continuation paths as well as transcript-only continuation. Register and localize /continue separately from /resume, and add tests for active, paused, completed, todo-backed, picker fallback, transcript-only continuation, optional hints, persistence failures, and argument-search behavior. Validation run: bun run build; bun run typecheck; focused resume tests; command registry tests. Prior full-check runs still have an unrelated full-suite-only LSP timeout, and raw env also needs auth for existing export tests. |
||
|
|
a3a3c3659d |
perf(cli): restore --version fast path with dynamic provider imports (#1611)
The static imports of providerProfile.js and providerValidation.js at the top of cli.tsx side-effect-loaded the entire integrations graph (~11.7k lines of vendor/gateway/model descriptors) at module evaluation, defeating the zero-import --version fast path. Convert them to dynamic imports at their use sites, matching the file's existing convention. --version: ~0.47s median (0.35-0.60s) -> steady 0.26s. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
b9c65deabc |
feat(gemini-vertex): native Gemini Vertex client and auth helpers (1/3) (#1607)
* feat(gemini-vertex): add native Gemini Vertex client and auth helpers Standalone foundation for the native Gemini-on-Vertex provider: - geminiVertexClient.ts: Anthropic-shaped client for the Vertex AI generateContent API — message/tool conversion (functionCall / functionResponse, thought signatures), system instruction mapping, streaming and non-streaming, ToolSearch tool_reference rendering, temperature clamping for thinking models. - geminiAuth.ts: Vertex helpers (project/location/model resolution from env, GEMINI_VERTEX_* with Google Cloud fallbacks), access-token / ADC credential resolution with injectable GoogleAuth for tests, and a default Vertex model constant. No call sites yet — provider routing, selection and profiles land in the follow-up PRs. 22 targeted tests, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(gemini-vertex): pin GEMINI_VERTEX_* override precedence and blank-value handling Addresses CodeRabbit review on the env-resolution helpers: explicit overrides win over defaults, project id precedence is GEMINI_VERTEX_PROJECT > GOOGLE_CLOUD_PROJECT > GCLOUD_PROJECT > GOOGLE_PROJECT_ID, and blank values are treated as unset. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(gemini-vertex): cover whitespace trimming of explicit overrides Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
614a8d9c54 |
fix(tool-search): enable MCP tool deferral on converted-wire providers (#1608)
* feat(tool-search): enable MCP tool deferral on converted-wire providers The CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS kill switch (defaulted on since #281) disabled tool search everywhere, so OpenAI-compatible providers received every MCP tool inline. Large palettes (e.g. claude-flow exposes 293 tools, ~306KB of schemas) crash the Codex backend mid-stream with an opaque server_error surfaced as 'API Error: 500'. The kill switch exists to keep Anthropic beta shapes (defer_loading, tool_reference, beta headers) off the wire for accounts that lack the betas. Converted wires never carry those shapes, so the switch is now scoped to Anthropic-wire providers only, and the converters render ToolSearch tool_reference results as plain text: - toolSearch.ts: pure resolveToolSearchMode(env, provider); kill switch applies only to firstParty/bedrock/vertex/foundry/minimax - codexShim.ts: render tool_reference blocks in tool results; drop a dead filter that targeted a nonexistent tool name - openaiShim.ts: same rendering on the chat_completions path Verified end-to-end on a real session: 343 tools that failed deterministically with 500 on the Codex backend now complete (the model defers, searches, loads and calls MCP tools across multiple turns). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(tool-search): cover tool_reference rendering on the chat/completions path Addresses CodeRabbit review: the OpenAI shim conversion now has a focused regression test mirroring the Codex shim coverage, via a test-only __test.convertMessages export (same pattern as WebSearchTool). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(tool-search): assert plain-text tool message content directly Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
0b24b60ce9 |
feat(provider): add Fireworks AI as official OpenAI-compatible provider (#1590)
* feat(provider): add Fireworks AI as official OpenAI-compatible provider
Includes vendor descriptor, brand descriptor (276 models), model
descriptors (full + merged), routing metadata, env auto-detection,
profile support, client defaults, and docs.
* test: add focused regression tests for Fireworks AI auth and routing
- Add 7 env-only routing tests in client.test.ts (shim routing,
stale model replacement, base URL override, shim option cleanup,
non-Fireworks override ignored, priority with MiniMax, Bedrock yield)
- Add FIREWORKS_API_KEY auto-detection test in providerAutoDetect.test.ts
- Add profile apply/persistence/env-drift tests in providerProfiles.test.ts
- Fix FIREWORKS_API_KEY propagation in strictEnv early return path
* fix: address reviewer comments on Fireworks integration
- Remove OPENAI_API_KEY exclusion so Fireworks cred wins over stale OpenAI key
- Fix TS type error in test by using String() wrapper
- Add Fireworks to detection priority comment in providerAutoDetect.ts
- Add useFireworksEnvOnlyProvider to shim condition for pattern consistency
- Replace loose .includes('fireworks.ai') with isFireworksBaseUrl() exact hostname check
* fix: add explicit case 'fireworks' in applyProviderFlag for credential precedence
- Add 'fireworks' to PREFERRED_PROVIDER_ORDER
- Add case 'fireworks' with dedicated key winning pattern (mirrors atlas-cloud)
- Add FIREWORKS_API_KEY to copiedOpenAIKeyProvider detection so stale
keys are cleaned up when switching away from Fireworks
* fix: guard fireworks defaultModel assignment against 'undefined' string coercion
* fix: remove leftover conflict marker in providerProfiles.ts
* docs(fireworks): add JSDoc to Fireworks functions for coderabbit docstring coverage
Adds JSDoc annotations to isFireworksBaseUrl, getFireworksBaseUrlOverride,
hasFireworksEnvOnlyProviderIntent, isFireworksModelName, and
applyFireworksEnvOnlyDefaults.
* fix(fireworks): cross-check NEARAI_API_KEY in env-only intent functions
hasNearaiEnvOnlyProviderIntent and hasFireworksEnvOnlyProviderIntent were
missing mutual cross-checks. When both NEARAI_API_KEY and FIREWORKS_API_KEY
are set, neither excludes the other, and nearai silently wins by ordering.
Adding !hasNonEmptyEnvValue(processEnv.FIREWORKS_API_KEY) to the nearai intent
and !hasNonEmptyEnvValue(processEnv.NEARAI_API_KEY) to the fireworks intent
ensures both return false, forcing explicit provider selection.
* fix(fireworks): fix typo in JSDoc — OPENAI_API_API_BASE -> OPENAI_API_BASE
* fix(fireworks): remove merge artifact and preserve no-key auth headers
- src/utils/providerAutoDetect.ts: remove leftover ======= conflict
marker and stale duplicate priority lines
- src/utils/providerProfiles.ts: preserve apiFormat, authHeader,
authScheme, authHeaderValue in the no-key OpenAI-compatible
fallback path so saved Responses mode / custom auth config
survives restart
* fix: Fireworks env-only startup preservation and MIMO priority comment
- Add FIREWORKS_API_KEY check to hasConcreteProviderSelection() so env-only
Fireworks setup is not overwritten by Gitlawb Opengateway default
- Add regression test verifying FIREWORKS_API_KEY survives no-profile startup
- Fix providerAutoDetect.ts priority comment to include MIMO_API_KEY (position 8)
and renumber subsequent entries to match actual detection order
* fix: also preserve env-only NEAR AI startup in hasConcreteProviderSelection()
* fix: remove duplicate Fireworks model descriptor, add FIREWORKS_API_KEY to test env cleanup
* fix: move duplicate model check to generation-time, add OPENAI_AUTH_* env cleanup to test harness
---------
Co-authored-by: Gravirei <gravirei@users.noreply.github.com>
|
||
|
|
d08593de92 |
feat(web): rebuild landing as Astro static site with gitlawb theme and full docs (#1606)
* feat(web): rebuild landing as Astro static site with gitlawb theme and full docs Replaces the Vite+React SPA in web/ with a fully static Astro 6 site: - gitlawb-aligned design system: dark-default monochrome surface tokens, Geist Mono, hairline grids, [light]/[dark] toggle — keeping the orange #ff7a1a openclaude accent (#e85d00 in light mode for AA contrast) - 9-page docs section: installation, quickstart, providers, slash commands (all 69 user-facing commands with argument hints), CLI reference (every non-hidden flag), configuration, keybindings, skills — rendered from typed data files seeded from the CLI source - SEO: per-page canonicals/OG/Twitter, JSON-LD (SoftwareApplication, TechArticle, BreadcrumbList), @astrojs/sitemap, robots.txt, and generated 1200x630 OG cards, all on https://openclaude.gitlawb.com - zero framework JS: theme toggle, copy buttons, mobile nav, and TOC highlighting are small vanilla scripts - CI-compatible: same typecheck/build script names, bun.lock regenerated Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * feat(web): redesign logo as gitlawb-aligned circuit mark White terminal face (hollow node eyes, >_ prompt mouth) on a black square with an orange git-fork trace descending to two commit nodes — same stroke language as the gitlawb mark. Adds the SVG source as the favicon and regenerates all three OG cards with the new logo. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(web): address review feedback on copy button a11y and css lint - CopyCommand: generic aria-label (component is reusable, not install-specific) and a visually-hidden role="status" live region so screen readers announce the copied state - global.css: lowercase text-rendering keyword, blank line before color-scheme, kebab-case fade-up keyframe name Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
8f92346cf4 |
fix: avoid file suggestion OOM on large repos (#1074)
* fix: avoid file suggestion OOM on large repos * fix: handle ignore scope and abort semantics in file suggestions * test: stabilize rebased branch CI verification * test: make proxy env cleanup windows-safe * test: preload file suggestions module in setup * fix: keep file suggestions lazy on startup * chore: address final review nits |
||
|
|
89d05317b6 |
feat: add Vietnamese i18n for slash command descriptions (#1431)
* feat: add Vietnamese i18n support for slash command descriptions
Add a simple i18n helper that reads the `language` setting from config
to display localized skill descriptions. Currently supports English
(default) and Vietnamese.
To switch to Vietnamese, set in ~/.claude/settings.json:
{ "language": "vietnamese" }
Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
* feat(i18n): add Vietnamese translations for all 85 command descriptions
- Fix detectLocale() to read ~/.claude/settings.json directly via
readFileSync instead of broken require('../../utils/config.js')
- Add commandDescVi translation map with 85 Vietnamese descriptions
- Export translateCommandDescription() for use in command rendering
- Modify formatDescriptionWithSource() to translate descriptions
when language is set to "vietnamese"
- Bump version to 0.15.1
* fix: add prepare script for git-based installs
When installing via `npm install -g git+https://...`, npm runs the
`prepare` script automatically. This ensures the CLI is built from
source during installation.
Requires Bun to be installed globally.
Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
* fix(i18n): read locale from merged settings
* feat(i18n): translate all prompt-type commands + add env validation + node version files
## Changes
### 1. Fix prompt-type command translations (src/commands.ts)
- `formatDescriptionWithSource()` now calls `translateCommandDescription()` for ALL command types
- Previously only translated `builtin`/`mcp` source commands
- Now translates: workflow, plugin, bundled, and default cases
- Fixes: /review, /insights, and other prompt-type commands now display Vietnamese
### 2. Add missing Vietnamese translations (src/skills/bundled/i18n.ts)
Added 17 new command translations:
- /btw: "Đặt câu hỏi nhanh bên lề mà không làm gián đoạn cuộc hội thoại chính"
- /compact: "Xóa lịch sử hội thoại nhưng giữ tóm tắt trong ngữ cảnh"
- /auto-fix: "Cấu hình tự động sửa: chạy lint/test sau khi AI chỉnh sửa"
- /bridge-kick: "Chèn trạng thái lỗi bridge để kiểm thử khôi phục thủ công"
- /review: "Hoàn thành đánh giá bảo mật cho các thay đổi đang chờ trên nhánh hiện tại"
- +12 more commands
### 3. Add Zod env validation at startup (src/utils/envValidation.ts)
- New file: validates critical env vars using Zod at startup
- Crashes immediately if invalid (instead of wasting time)
- Validated vars: ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, CLAUDE_CONFIG_DIR, HTTP_PROXY, HTTPS_PROXY, NODE_EXTRA_CA_CERTS
- Integrated into src/entrypoints/init.ts
### 4. Add node version files
- .nvmrc: Node 22
- .node-version: Node 22
- Matches Dockerfile (node:22-slim) and package.json engines (>=22.0.0)
## Test Results
- 3007 pass, 11 fail (all in changeDetector.test.ts - pre-existing, unrelated to i18n)
Co-Authored-By: OpenClaude <noreply@openclaude.ai>
* fix: restore validateBoundedIntEnvVar in envValidation.ts
* Localize bundled skills descriptions at read time
* fix(i18n): localize slash command suggestions
Search rendered localized command descriptions and rebuild the Fuse index when language-sensitive text changes.
Preserve Unicode letters and numbers for Vietnamese slash queries, localize the remaining requested command descriptions, and keep exact slash command submission from following a stale highlighted suggestion.
Tests: bun test src/commands.test.ts; bun test src/utils/suggestions/commandSuggestions.test.ts; bun test src/utils/envValidation.test.ts
Thanks to @jatmn for the patient review and guidance.
* fix(i18n): tighten slash command localization scope
* fix(i18n): centralize localization and preserve external metadata
* fix(commands): scope localized descriptions to OpenClaude-owned commands
* fix(i18n): read session language before initial settings
* fix(i18n): prefer whenToUse localization keys
---------
Co-authored-by: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
Co-authored-by: OpenClaude <noreply@openclaude.ai>
Co-authored-by: lht3003-rgb <lht3003-rgb@users.noreply.github.com>
|
||
|
|
a3f144bbf2 |
fix(ollama): parse text-based tool calls as fallback (#1053) (#1076)
* fix(ollama): parse text-based tool calls as fallback for #1053 Ollama's OpenAI-compat streaming endpoint does not emit structured `delta.tool_calls` fields for many models (qwen2.5-coder, llama3.x, phi-4, gemma). Instead these models write the tool call intent as a JSON block inside the response text, so the agent loop never receives a tool_use event and tools are silently skipped. Fix: - Accumulate streamed text in `accumulatedText` during the delta loop - At `finish_reason=stop` with no API-level tool calls, and only when `isOllamaProvider()` is true, parse the accumulated text with a regex that matches both common formats: {"name":"X","arguments":{...}} {"type":"function","function":{"name":"X","arguments":{...}}} (also handles ```json``` fenced blocks) - Emit proper content_block_start/delta/stop tool_use events for each parsed call, then override finish_reason to 'tool_calls' so stopReason becomes 'tool_use' and the agentic loop continues - The fallback is gated on isOllamaProvider() — no-op for all other providers Also adds unit tests for parseTextToolCalls covering: bare JSON object, fenced block, type:function shape, string-encoded arguments, dedup by name:args, multiple distinct calls, plain text, malformed JSON. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ollama): replace non-greedy regex with brace-depth scanner for bare JSON tool calls The regex arm `(\{"(?:name|type)"\s*:[\s\S]*?\})` stopped at the first `}`, truncating any tool call whose arguments is a nested object (e.g. {"file_path":"/tmp/foo.ts"}). JSON.parse then failed on the truncated string, so parseTextToolCalls returned no calls. Fix: split into two passes. - Pass 1: fenced ``` blocks — regex is safe here because ``` bounds the non-greedy match, forcing the engine to extend past inner `}` chars. - Pass 2: bare JSON — extractBalancedJson walks character-by-character tracking string/escape/brace depth and returns the full balanced object. processedRanges prevents double-counting inner objects nested inside an already-extracted outer tool call. All 10 focused tests now pass, including the 5 that were failing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ollama): context guard (P1) + text buffer (P2) for text-based tool calls P1: add trailing-context guard in parseTextToolCalls Pass 2 — bare JSON immediately followed by non-whitespace, non-`{` text is skipped, preventing JSON examples in explanatory prose from becoming real tool calls. P2: buffer Ollama text deltas instead of yielding text_delta immediately. At finish_reason=stop the buffer is flushed with tool-call JSON ranges stripped, so raw JSON never leaks as visible text when Ollama emits text-based tool calls. Also changes parseTextToolCalls to return { calls, toolCallRanges } so callers can reconstruct stripped text; adds stripRanges() helper. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(ollama): add P1/P2 integration tests + fix TS fetch cast - Add integration tests for think-tag filtering (P1) and visible text preservation before structured tool_calls (P2) - Fix globalThis.fetch assignment cast: use `as unknown as FetchType` to satisfy TS overlap requirement (matches pattern in openaiShim.test.ts) - All 15 tests pass (10 existing parseTextToolCalls + 5 new streaming) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ollama-shim): use stripThinkTags() instead of raw regex for think-content removal The fallback path that strips tool-call JSON ranges from accumulatedText was using a hand-rolled /<think>[\s\S]*?<\/think>/gi regex to remove hidden reasoning content. stripThinkTags() (already imported from thinkTagSanitizer.ts) handles closed pairs, unterminated opens, and orphan tags — making the cleanup more robust without duplicating logic. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ollama): flush ollamaTextBuffer when real delta.tool_calls arrive before finish_reason=stop * fix(ollama): open text block on flush when hasEmittedContentStart is false + regression tests Adds a defensive guard in the no-tool-call flush path: if ollamaTextBuffer has content but hasEmittedContentStart is false, emit content_block_start before the delta so the text is never silently dropped. Also adds 3 regression tests covering plain Ollama text responses (two-chunk, single-chunk, and multi-chunk) to prevent regressions in the normal text path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ollama): preserve visible prose before text-based tool-call fallback When an Ollama model emits normal text followed by tool-call JSON at finish_reason=stop, the hasEmittedContentStart flag is false (text is buffered, never emitted). The existing fallback only handled the case where a text block was already open; prose was silently dropped. Fix: lift stripped/strippedVisible computation before the hasEmittedContentStart guard and add an else-if branch that opens a text block, emits the visible prose, and closes it before starting the synthetic tool_use blocks. Also adds a regression test: Ollama stream with prose chunk then bare JSON tool-call chunk, asserts the prose appears in a text_delta and the Read tool_use block follows. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ollama): apply explanatory-text context guard to fenced JSON blocks The context guard (skip JSON followed by non-{ trailing text) was only applied in Pass 2 (bare JSON). Pass 1 (fenced blocks) parsed all ```json``` blocks unconditionally, so explanatory fenced examples were treated as live tool calls. Fix: apply the same trailing-context guard to Pass 1 — if non-whitespace, non-{ text immediately follows the closing fence, skip the block. Also adds two regression tests: - fenced block followed by explanatory prose is skipped - fenced block at end of string (no trailing content) is still parsed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ollama): only add fenced/bare range to toolCallRanges after parseAndAdd accepts it parseAndAdd now returns bool. Ranges are pushed to processedRanges (for Pass-2 dedup) unconditionally, but only added to acceptedRanges (returned as toolCallRanges) when parseAndAdd returns true. Previously, fenced or bare JSON blocks with bad JSON, missing name, or duplicate content were added to toolCallRanges regardless, causing their text to be silently stripped from output even though no tool call was emitted. Adds four regression tests: bad-JSON fence, no-name fence, duplicate fenced blocks, and duplicate bare JSON. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ollama): address CodeRabbit review — fetch restore, isOllama threading, terminal flush, pretty JSON - Restore globalThis.fetch in afterEach across all streaming describe blocks to prevent SSE stub leakage between tests - Thread isOllama boolean from resolveProviderRequest call site into openaiStreamToAnthropic generator instead of re-reading env vars inside - Flush ollamaTextBuffer for all terminal Ollama finish reasons ('stop','length','content_filter','safety'); only remap finish_reason to 'tool_calls' for the original 'stop' case - Allow optional whitespace/newlines in BARE_TOOL_CALL_START_RE so pretty-printed bare JSON like '{\n "name":' is detected - Add regression tests for pretty-printed JSON detection and 'length' flush Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(ollama): add content_filter and safety terminal finish reason cases Regression coverage requested by CodeRabbit — the OLLAMA_TERMINAL_REASONS set includes 'content_filter' and 'safety' in addition to 'length', so add dedicated flush-buffer assertions for those two reasons and confirm stop_reason is not remapped to tool_use semantics. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ci: retrigger — flaky test re-run (sessionStorage ENOENT + autoCompactCooldown race) * fix(openaiShim): derive Ollama flag from resolved request baseUrl Replace global isOllamaProvider() (which reads OLLAMA_BASE_URL / OPENAI_BASE_URL env vars) with isLikelyOllamaEndpoint(request.baseUrl). The request has already been routed through resolveProviderRequest(), so its baseUrl is the authoritative source for provider identity; using global env state could misroute streams when a provider profile or per-agent providerOverride selects a different endpoint. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
2002e4c116 |
UI/upgraded UI (#1605)
* feat(ui): OpenClaude brand identity — orange accent, wordmark, dot-pulse spinner Give OpenClaude a real visual identity instead of reskinned upstream visuals: - theme.ts: add brand/brandShimmer keys and repoint the claude family (claude, claudeShimmer, clawd_body, briefLabelClaude) to the gitlawb orange #ff7a1a across all 6 themes — darkened variants for light backgrounds, luminance-separated variants for daltonized themes, redBright floor for 16-color ANSI themes. Values stay rgb() strings (parseRGB in Spinner/utils.ts silently fails on hex). - brand.ts (new): BRAND_NAME, BRAND_TAGLINE, accent constant, and a 2-row Unicode half-block OPENCLAUDE wordmark split for two-tone render. - LogoV2: full logo shows the wordmark + centralized tagline; CondensedLogo drops the duplicated OPEN CLAUDE header and renders a brand-colored name+version line; Clawd mascot body now clawd_body. - Startup splash: new 'ember' gradient palette (brand orange) as the default (/logo keeps sunset et al.); tagline centralized from brand.ts. - Spinner: dot-pulse glyph sweep (· ∘ ○ ◎ ◉ ●) replacing the asterisk set (drops Ghostty/darwin special-cases); defaults switched to brand/brandShimmer. Reduced-motion and stall interpolation unchanged. - figures.ts: TEARDROP_ASTERISK marker → ◎ to match the new glyph family. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * feat(ui): built-in default statusline Ship a status bar that renders when no custom /statusline command is configured: `model · ctx % · session cost · rate limit`. Previously this slot was empty unless the user wired up their own shell command. - BuiltinStatusLine.tsx (new): pure segment builders (buildBuiltinStatusSegments / fitSegments — drops lowest-priority segments right-to-left as the terminal narrows) plus a memoized component reusing the exact data pipeline the custom StatusLine feeds to user commands (getRuntimeMainLoopModel, getCurrentUsage, calculateContextPercentages, getTotalCost, getRawUtilization). Pure in-process computation — no subprocess, no debounce. - Context % colors warning ≥70 / error ≥90 (aligned with auto-compact warnings); rate limit shows the worst of the 5h/7d windows and is absent (not 0%) for API-key users; cost hidden at $0. - PromptInputFooter: custom statusline always wins; the built-in bar also suppresses the "? for shortcuts" hint like the custom one does. - defaultStatusLineEnabled global-config toggle (default on) surfaced in /config settings. - Unit tests for segment building, narrow-width fitting, threshold colors, and custom-statusline precedence. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * feat(ui): polish pass — fuzzy match highlighting, completion flash, diff gutter, dialog hints - Fuzzy pickers: new highlightFuzzyMatch (contiguous-run or greedy- subsequence, bold so the focused row's color survives) applied to Quick Open and prompt-history results. FuzzyPicker now passes the live query as a third renderItem argument so memoized render callbacks can't capture a stale query. - Select: inline option descriptions now truncate with an ellipsis on narrow terminals instead of clipping at the edge (row made shrinkable, description wrapped in truncate-end). - CompletionFlash (new): static `✓ Done · 12s` row for ~1.5s after a response completes. Keys off the isLoading transition (the spinner also hides mid-turn for streaming text); same row footprint as the spinner; suppressed for sub-second turns, brief mode, open permission/prompt queues, running teammates, and reduced motion. - Diff gutter: line numbers on +/- lines are now dimmed (previously full decoration intensity competed with content, worst on light themes); +/- sigils keep full intensity. Mirrored in the non-highlighted Fallback renderer. - Dialog: optional showNavigationHint prepends "↑/↓ navigate" to the default input guide; opted in from Select-hosting dialogs (IdleReturn, DevChannels, WorktreeExit). Dialog rewritten as plain React (was react-compiler output) to take the new prop safely. - Spinner: the ↑/↓ request-direction glyph now leads the status parens whenever any status shows — previously buried in the tokens part, which only appears after 30s; width gating reserves its space. - Replaced the stale `/`-search TODO in ScrollKeybindingHandler with a pointer to the shipped REPL transcript search. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(ui): address review — explicit statusline default, narrow-terminal thinking gate - createDefaultGlobalConfig now sets defaultStatusLineEnabled: true explicitly, matching the factory's other default-true booleans (read sites already coalesced to true; no behavior change). - SpinnerAnimationRow: the width gate reserves mode-glyph space that a thinking-only spin never uses; on narrow terminals where nothing else renders, re-try the thinking gate with that space returned so '(thinking)' shows instead of nothing. CompletionFlash NaN guard was reviewed and not applied: both refs are useRef(0) — initialized numbers by type and construction — and the active→inactive transition guard guarantees the start time was set before the elapsed math runs. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(ui): address review round 2 — hint accuracy, flash suppression, coverage - Dialog: the cancel shortcut hint now renders only when isCancelActive, matching the keybinding it advertises. - PromptInputFooter: new exported resolveFooterStatusLine(settings, guards, config?) is the single source of truth for which status line renders — custom wins over builtin, and all render guards (prompt mode, short fullscreen, exit message, pasting) force null. suppressHint and the render now share it, so '? for shortcuts' is suppressed only when a status line actually renders. - CompletionFlash: suppression now clears an already-active flash (the effect resets state and the render guard skips the commit-gap frame) instead of only preventing new ones. - REPL: flash suppression reuses the existing hasActivePrompt aggregate, which covers the sandbox, worker-sandbox, and elicitation queues the hand-rolled expression missed. - builtinStatusLineShouldDisplay takes an injectable config (defaults to getGlobalConfig()) so the config-off path is testable without module mocks; tests added for config-off, custom-wins-regardless, and the resolver's full variant x guard matrix. Validation: typecheck exit 0; full suite 3733 green; smoke + bundle guard green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(ui): drop fit-budget floor, cover reduced-motion render gap - BuiltinStatusLine: remove the artificial 10-column floor on the fit budget — fitSegments returning [] is the signal that nothing fits, and the empty branch already handles it (row-reserve in fullscreen, null otherwise). The floor forced a truncated segment onto panes narrower than the model name. - CompletionFlash: the render guard now also skips the one frame between reducedMotion flipping on and the effect clearing the flash, same as the suppressed case. Component-level mount tests for PromptInputFooter were considered and deliberately not added: the footer needs 10+ mocked contexts to mount, mock.module harnesses leak across bun test files in this repo, and the branching under review is exactly the pure resolveFooterStatusLine contract already pinned by tests. Validation: typecheck exit 0; full suite 3733 green; smoke + bundle guard green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
3752dfe6f7 |
fix(typecheck): recreate missing CLI Transport interface (#1581)
* fix(typecheck): recreate missing CLI Transport interface * fix(transports): implement async close in CLI transports * fix(transports): harden async close cleanup * fix: address async transport close review feedback * test: isolate environment-sensitive suites * fix(transports): drain uploader after close failure * fix(transports): type guard CCR stream events * test: remove unused auto-compact fixture helper |
||
|
|
94d2a6a503 |
ci: split typecheck into its own PR-checks job (#1599)
The Typecheck step lived inside the smoke-and-tests job and typecheck:type-tests ran inside `bun run check`, so type errors were buried mid-job and serialized behind the build. They now run as a dedicated parallel `typecheck` job (tsc --noEmit + the focused type tests) with its own status check, and `check` slims to smoke + test:full so nothing runs twice in CI. Local scripts (typecheck, typecheck:type-tests, hardening:strict) are unchanged. Review feedback: the new job's checkout sets persist-credentials: false (no credentials needed), and CONTRIBUTING.md now documents typecheck as a CI-enforced check instead of a recommended-local-only one. Validation: workflow YAML parses (jobs: smoke-and-tests, typecheck, web); typecheck exit 0; type-tests green; `bun run check` green. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
9c742319fb |
Fix snip metadata leaks and TUI corruption (#1600)
* Fix snip metadata leaks and TUI corruption Replace visible snip ID tags with internal snip_id metadata and update SnipTool guidance/tests so the model can request pruning without echoing user-visible IDs. Add compatibility parsing for legacy [id:...] markers, clarify synthetic OpenAI shim tool-result messages, and tighten test cleanup around env/session state. Mitigate Konsole+tmux rendering corruption by disabling the scroll fast path in that environment while preserving bottom-follow behavior and clearing culled cached output. Validation: bun run build; bun run smoke; ANTHROPIC_API_KEY=test-key bun run check; ANTHROPIC_API_KEY=test-key bun run test:full; python -m pytest -q python/tests; bun run security:pr-scan -- --base upstream/main. * Stabilize cooldown smoke test Use usage-bearing high-context fixtures in autoCompactCooldown tests so the cooldown assertions do not depend on process-global threshold overrides surviving full-suite order. Add CodeRabbit-requested SAST suppression comments for snip regex literals. * Use rule-id semgrep suppressions Update the snip regex suppressions to CodeRabbit's requested nosemgrep rule-id form, with the explanatory text kept in a separate comment. * Pin cooldown test context window Set and restore CLAUDE_CODE_AUTO_COMPACT_WINDOW in autoCompactCooldown tests so the high-context fixture lands above the auto-compact threshold but below the hard prompt limit on both local Windows and Linux CI. * Honor autocompact breaker metadata Block oversized requests when autocompact reports an active or tripped breaker, even if a later auto-compact config read is stale. This keeps cooldown protection active and stabilizes the CI smoke path. |
||
|
|
eacc7d8fac |
feat: add NEAR AI provider integration (#1594)
* feat: add NEAR AI provider integration
- Create vendor, brand, and model descriptors for NEAR AI (22 models)
- Add NEAR AI to route metadata, client, provider auto-detect, and profiles
- Update compatibility tests and ProviderManager test PRESET_ORDER
- Add README and docs entries for NEAR AI provider
- Update .env.example with NEAR AI configuration
* fix: address CodeRabbit review comments
- Fix .env.example: change 'Option N' to 'Option 11' in quick reference
- Narrow isNearaiModelName to use explicit NearAI model prefixes instead of broad includes('/')
- Add NEARAI_API_KEY propagation in strictEnv startup path
* fix: align NEAR AI validation host matching with wildcard subdomain routing
- Add *.completions.near.ai to matchBaseUrlHosts in vendor descriptor
- Add matchHostnameAgainstRouteHosts helper with wildcard (*.) prefix support
- Use helper in both resolveRouteIdFromBaseUrl and getRuntimeValidationTarget
- Add regression test for qwen35-122b.completions.near.ai TEE endpoint
- Add NEARAI_API_KEY to test env cleanup list
* fix: align Near AI integration with env-only provider best practices
- Replace loose .includes('near.ai') with isNearaiBaseUrl() in providerProfiles.ts
for exact hostname validation (all 4 instances)
- Add NEARAI_API_KEY to copiedOpenAIKeyProvider detection in providerFlag.ts
- Add case 'nearai' to applyProviderFlag switch with dedicated key precedence
- Add 'nearai' to PREFERRED_PROVIDER_ORDER
- Add useNearaiEnvOnlyProvider to OpenAI shim condition in client.ts
- Remove OPENAI_API_KEY exclusion from hasNearaiEnvOnlyProviderIntent (dedicated
key wins over stale generic key, consistent with xAI pattern)
- Update detection priority comment in providerAutoDetect.ts to include
MIMO_API_KEY, XAI_API_KEY, and NEARAI_API_KEY
* fix: add exact completions.near.ai host to isNearaiBaseUrl
* fix: add higher-precedence provider key exclusions to hasNearaiEnvOnlyProviderIntent
* fix: add OPENAI_API_KEY and MINIMAX_API_KEY exclusions to hasNearaiEnvOnlyProviderIntent
* fix(near-ai): don't let stale OPENAI_API_KEY suppress Near AI routing
---------
Co-authored-by: Gravirei <gravirei@users.noreply.github.com>
|