* feat: merge knowledge graph + conversation arc into memdir
Replace the standalone KG/ARC system (SQLite + JSON + Orama storage)
with direct integration into the existing auto-memory directory.
What changed:
- New memdir/vectorIndex.ts — Orama full-text index over all memory/ .md
files, replacing the separate knowledge.orama binary
- New memdir/autoExtractFacts.ts — auto-detects env vars, paths,
versions, URLs, IPs, backtick concepts from conversation and writes
them as structured .md files into memory/.facts/ with frontmatter
- conversationArc.ts now persists arc state (goals, decisions,
milestones, phase) to memory/.arc.json sidecar instead of the KG
- knowledgeGraph.ts gutted from 728→165 lines — now a thin
compatibility layer that reads .facts/ files from memdir and
delegates vector search to vectorIndex.ts
- build.ts: enabled CONVERSATION_ARC and MULTI_TURN_CONTEXT feature
flags (previously undefined → dead-code eliminated in production)
Removed:
- src/utils/storage/ (SQLiteProvider, JSONProvider, 3 test files) —
unused after KG migration
- src/utils/knowledgeGraph.test.ts, .stress.test.ts
- src/utils/conversationArc.test.ts, .perf.test.ts
- ~1700 lines of redundant storage code
Benefits:
- Single memory system (memdir) instead of two parallel systems
- Auto-extracted facts are plain .md files — visible to the model,
discoverable by the existing Sonnet prefetch
- Vector search indexes real memory content, not a separate DB
- Arc state survives across sessions via .arc.json
- ~700 lines removed from the production bundle
* fix: type errors, add test suites, fix resetArc() disk-write bug
- Fix vectorIndex.ts: parseFrontmatter returns nested {frontmatter, content},
Orama DB typed as 'any' matching existing code pattern
- Fix resetArc(): was overwriting .arc.json on disk — now clears only in-memory state
- Add vectorIndex.test.ts (6 tests): build/search/persist/rebuild
- Add autoExtractFacts.test.ts (10 tests): env vars, paths, versions,
URLs, backtick concepts, PascalCase, React/Redux, file signatures, frontmatter
- Add conversationArc.test.ts (14 tests): arc init, persistence, goals,
decisions, milestones, phase detection, arc summary, finalize, stats
- Update verify-kg-merge.sh: add test suite check (33 tests)
* fix: remove generic type param from restore() call
* fix: address CodeRabbit findings — YAML injection, secrets leak, cache invalidation, frontmatter parsing, test weakness
* fix: redact URL credentials/query/hash in endpoint extraction; add persistence + reindex regression test
* test: add regression for URL credential/query/hash redaction in fact extraction
* feat: wire getOrchestratedMemory into query.ts prompt; remove dead promises array in autoExtractFacts
* feat: enhance memory management by adding clearArcArtifacts function and integrating it into the clear command; implement file count tracking in vector index
* feat: enhance fact extraction by adding tests for absolute paths, backtick concepts, technical terms, project file signatures, and IP addresses; implement clearArcArtifacts function in tests
* Review-fix: scoped IP tagging, scrubbedContent, arcMemoryDir null, vector-index cleanup
* Review-fix: isAutoMemoryEnabled gate, scrubbed paths, clearIndex in cleanup
* Review-fix: URL-stripped path scan, digit-key/quoted-value env redaction, rm isAutoMemory gate
* Review-fix: quoted multi-token env values fully redacted, regression test
* Review-fix: freshness check on each search, integration test, typecheck in verifier
* Review-fix: missing-index-file reinit, full-pipeline integration test
* Review-fix: gate arc/RAG on isAutoMemoryEnabled, add integration+stale-index tests
Addresses three P1/P2 findings from code review:
1. P1: Honor auto-memory opt-out before writing arc facts
- Add isAutoMemoryEnabled() checks in query.ts before calling
updateArcPhase() and getOrchestratedMemory()
- Add same check inside conversationArc.ts extractFactsAutomatically()
- Prevents .facts file writes when auto-memory disabled via --bare,
CLAUDE_CODE_DISABLE_AUTO_MEMORY=1, or memory.autoWrite: false
2. P2: Add query-level integration test coverage
- New test in conversationArc.test.ts verifies query.ts path
- Confirms arc functions called behind feature gates and results
appended to system prompt (lines 555-575)
3. P2: Add stale-index regression tests
- 6 new tests in vectorIndex.test.ts cover:
* Searching after adding files
* Searching after editing files
* Searching after removing files
* Searching when .vector-index missing
* Searching when .vector-index-meta.json missing
* Mixed stale conditions
All tests pass (31 total, 99 assertions), typecheck clean.
* Review-fix: use >= for mtime staleness check to catch same-ms edits
The verification agent discovered a timing-dependent bug in the stale
index detection. When a file edit and index save occur within the same
millisecond, latestMtime equals indexMtime, causing the check
`latestMtime > indexMtime` to return false. The stale index is not
refreshed and searches miss the updated content.
Changed line 220 from `>` to `>=` in the mtime comparison. The file
count check catches add/remove operations, but edits that don't change
the file count rely on mtime comparison.
All 12 vector index tests now pass consistently, including the
"searching after editing a file picks up changes" test that previously
failed intermittently.
* Review-fix: move auto-memory gate to persistence layer
Addresses inline review comment: query.ts was incorrectly skipping
updateArcPhase() entirely when isAutoMemoryEnabled() returned false,
leaving the in-memory arc state stale.
Fixed by:
- Removed isAutoMemoryEnabled() gate from query.ts line 449
- Added gate inside conversationArc.ts updateArcPhase() at persistence
layer (line 223), so phase advances but only persistence is disabled
- Arc state tracking now works regardless of auto-memory setting
- Only disk writes (.arc.json, .facts files, index rebuilds) are gated
The 2ms delay in vectorIndex.test.ts is kept as a pragmatic fix for the
timing race. Content-hash detection would be ideal but adds complexity;
the mtime check works reliably in production.
All 31 tests pass, typecheck clean.
* Refactor memory handling: consolidate metadata retrieval and improve auto-memory checks
* Fix: update expectation to use toEqual for prompt comparison in conversationArc tests
* fix: detect same-size content changes via content hash; handle text-block user messages in arc query
* fix: skip symlinked dirs in vector index walk; enable arc/multi-turn flags; add production-path tests
* fix: follow symlinked dirs in vector index walk per review
* fix: skip symlinked dirs in vector index walk; add symlink-boundary regression tests
* fix: skip indexing symlinked directories and ensure only files are processed
* fix: show cmd output on failure in verifier; add multi-turn coverage; clear mempath cache; cover arc reset in knowledge clear test
* fix: clear memoized auto-mem path after teardown in knowledge + conversationArc tests
* fix: isolate vector index per memdir, filter secrets from backtick facts, clean trailing ws
* fix: extend credential filter for AWS/GitLab tokens; reject all symlinks in vector index
* fix: use repo's redactSecretSubstringsForDisplay; add npm/glpat/AKIA/ASIA/xox to shared patterns; cover NPM+JWT in tests
* fix: P1 backtick credential safety + untrusted-data boundary; P2 legacy migration + non-fatal writes; P3 type safety + build regression
* fix: B1-B5, M6, M8 — no message mutation, migration data loss, empty-file, non-fatal writes, probe, rebuildIndex resilience, dead import
* fix: address 8 reviewer findings (R1-R8)
P1:
- Keep retrieved facts in DATA ONLY block with strict system instruction
- Catch lowercase config secrets (api_key=...) in env scrubber
- Migrate SQLite working store (knowledge.db) before deleting provider
- /knowledge clear atomically archives legacy sources
P2:
- Run legacy migration on getOrchestratedMemory retrieval path
- Preserve entity attributes in migration frontmatter
- Only rebuild vector index when facts actually changed
- Fix feature-flag verifier --define syntax (declare const)
* fix: close 9 memory findings — approval gate, non-fatal writes, secret scrub, per-project migration, attribute/relation preservation, WAL cleanup, cheaper index
- Gate auto fact extraction on isMemoryWriteApprovalRequired() + isAutoMemoryEnabled() so default projects cannot silently persist conversation content
- Make ensureFactsDir/writeFactMemory degrade non-fatally (no turn-breaking throw on read-only dirs)
- Scrub token-like URL/path/hyphenated segments from durable facts via looksLikeSecret (reuses providerSecrets.looksLikeSecretValue)
- Restore passive project-rule extraction as rule facts
- Honor isAutoMemoryEnabled() before legacy migration; scope the migration guard per project (Set) instead of a single global
- Preserve legacy entity attributes and relations through migration (indented attributes + relation fact file, reconstructed in getGlobalGraph)
- Clear SQLite WAL/SHM sidecars on /knowledge clear
- Replace per-turn content hashing in vectorIndex getMdStats with size+mtime metadata; drop redundant initMemdirIndex call in getOrchestratedMemory
- Add knowledgeGraph tests covering P1#2/P1#4/P2#5/P2#8 and extend autoExtractFacts tests
* test: avoid global cwd pollution in knowledgeGraph tests
Replace process.chdir with a per-test setFsImplementation mock cwd that is
reset via setOriginalFsImplementation in afterEach, so the test no longer
leaks a changed process.cwd() into other test files. Assert the auto-memory
gate via the project-specific legacy file rather than the shared resolved
memdir dir (which bun runs concurrently across it blocks).
* fix: address 15 reviewer findings (R1-R15)
P1:
- Gate saveArcToDisk/finalizeArcTurn/saveIndex/migration on memory-write approval
- Retire legacy sources after successful migration (rmSync, backup preserved)
- Slugify entity.type and summary.id in migration filenames (path traversal)
- Fall back to JSON when SQLite has zero entities
- Scrub rule-fact extraction on scrubbedContent + looksLikeSecret/redact check
P2:
- Track skipped vs completed migration; re-enable clears skip marker
- Walk back to latest human text for tool-round vector queries
- Auto-extract goals/decisions from user messages in updateArcPhase
- /knowledge clear message says durable wipe, not session-only
- Bind arc state to projectKey (re-resolve on cwd change)
- clearIndex(memoryDir?) scopes to one memdir
- yamlQuote all migration frontmatter fields
- Real SHA-256 contentHash alongside fileFingerprint for same-size edits
- Stop claiming production-pipeline coverage in tests/verifier
* test: isolate governance mock by removing afterEach clear
Remove setGovernancePolicySettingsForSourceForTesting(null) from afterEach
in autoExtractFacts, conversationArc, and knowledgeGraph test files. The
module-level mock is set in each file's beforeEach and since there is no
afterEach cleardown, parallel test execution can no longer corrupt the
mock state across files.
This fixes 26 CI test failures caused by one file's afterEach clearing
the mock that another concurrently-running file had set in its beforeEach.
* fix: isolate governance mock per async context via executionAsyncId tracking
Replace the module-level variable in governancePolicy.ts with an
executionAsyncId-keyed Map and an async_hooks.createHook that propagates
the override from parent to child async resources. This ensures each
concurrent test's beforeEach/afterEach cannot corrupt the override set
by another test file, even when test bodies directly mutate the mock.
Also restore setGovernancePolicySettingsForSourceForTesting(null) calls
in afterEach hooks (removed in 39c68376), which are now safe because
each afterEach only clears its own async context.
Fixes 26 CI test failures across knowledgeGraph (2), conversationArc (7),
and autoExtractFacts (17/22, governance-gate tests).
* Refactor knowledge graph legacy migration, secure secrets and IP octets, optimize build-time feature flags, cap conversation arc collections, and resolve all verification check gaps
* Fix governancePolicy enablement in full test runs by checking for test runner globals
* fix: ensure auto memory is enabled in conversationArc, knowledgeGraph, autoExtractFacts tests
Delete CLAUDE_CODE_DISABLE_AUTO_MEMORY and CLAUDE_CODE_SIMPLE env vars
in beforeEach hooks so tests are not blocked when CI sets these vars.
Also revert governancePolicy.ts to the simple module-level variable
(removing the async ID tracking approach that broke with Bun v1.3.13).
Fixes 33 CI test failures where isAutoMemoryEnabled() returned false
causing all disk-persistence guards to fire.
* fix: address P1/P2 review findings — redaction, bounds, legacy backup
[P1] Redact goal/decision descriptions with redactLikelySecrets before
persisting to .arc.json, session summaries, and prompt summaries so
credentials captured by auto-extraction regexes are not durably stored.
[P1] Bound multi-turn tool input serialization to 2000 chars and apply
redactLikelySecrets, preventing oversized/credential-bearing tool inputs
from overflowing the next provider request or exposing secrets.
[P2] Archive the non-selected legacy store (JSON or SQLite) and its WAL
sidecars before retiring both sources, ensuring a recoverable snapshot
exists if generated fact files are incomplete or a migration bug surfaces.
* fix: address P1 findings — safe legacy retirement, multi-turn aggregate budget
[P1] Do not retire a legacy store unless every existing source was
successfully archived. Track archived sources in a Set and skip deletion
of any source whose backup failed (knowledgeGraph.ts).
[P1] Archive the selected SQLite WAL/SHM sidecars alongside its migration
backup, since committed state may reside only in the WAL file and the
advertised recovery backup would otherwise be incomplete (knowledgeGraph.ts).
[P1] Bound the aggregate multi-turn tool replay to 10KB total and stop
appending further turns once the budget is exceeded, preventing many
Agent/MCP calls per turn from adding unbounded text to system prompts
(conversationArc.ts).
* fix: address P1/P2 review findings — rule extraction gate, SQLite read status, atomic WAL/SHAM, KG status gate, byte budgets
* fix: tighten looksLikeOpaqueToken to avoid flagging compound model names
* fix: address P1/P2 review findings — rebase, test isolation, SQLite retry, attribute redaction, entity aliases, body content, project-scoped multiturn, skip unchanged writes, knowledge list gate
* fix: address P1/P2/P3 review findings — secret scrub on migrate, lean decision gate, git-root legacy lookup, backup retention, index rebuild chaining, single vector search, drop unreferenced fixture
* fix: scrub secret entity names on migrate, exclude summary facts from entities, reset multi-turn on /knowledge clear
* fix: address P1 review findings for legacy graph redaction, recovery-safe clear, stable change guards
- Redact embedded secrets in migrated legacy knowledge-graph entities,
summaries, and rules via shared sanitizeLegacyText() policy
- Preserve legacy artifacts (json/db/wal/shm) as migration-backup before
/knowledge clear; resetGlobalGraph returns { archived, failures }
- Skip rewrite + index rebuild on unchanged turns by stripping the volatile
detectedAt timestamp (facts and arc session summaries)
- Always recompute the authoritative content hash in getMdStats so edits of
equal size with preserved mtime are detected and served correctly
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: redact protocol-relative URL userinfo in legacy migration
new URL() throws for scheme-less URLs, so the catch branch now redacts
obvious //user:pass@host userinfo instead of persisting credentials.
* fix(memory): harden memdir migration and retrieval
---------
Co-authored-by: Gravirei <gravirei@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Kevin Codex <kevin@gitlawb.com>
* fix(release): sync web changelog entries from release please
* fix(release): provide sync push credentials
* fix(release): gate and finalize web release sync
* fix(release): make web release sync recoverable
* fix(release): target release PR commands explicitly
* fix(release): honor manifest release configuration
* fix(release): recover failed web sync retries
* ci(release): run full sync preflight
* fix(release): validate synchronized PR head
* fix(release): validate bot sync in release job
* fix(release): harden bot-owned web sync
* fix(release): validate exact bot PR head
* fix(release): isolate and bind PR synchronization
* fix(release): isolate validation from write credentials
* fix(release): reject non-regular generated inputs
* fix(release): require a valid forward version bump
* fix(release): scope sync artifacts to run attempts
* fix(release): reuse validated artifacts across retries
* fix(release): resume readiness after a completed push
* fix(release): bind retries to the validated commit
* fix(release): address synchronization review findings
* fix(release): support CRLF changelog recovery
* fix(release): keep validation transitions fail-closed
* fix(release): restore scoped web sync and marker ownership
Cut the multi-job finalize state machine back to a single draft-until-push
sync path, and fix consecutive releases leaving stacked automation markers
by stripping leftover draft ownership when inserting the next version.
* fix(release): keep web sync from blocking npm publish
Move pending Release Please web sync into its own job so a sync failure cannot skip install-verify, npm, or docker after a release tag is already created.
* fix(release): close web-sync trust and policy gaps
Remove hand-curation escape hatches, split read-only validation from
write-only push, discover bot PRs by branch identity, and run the full
local gate suite before marking the release PR ready.
* fix(release): validate gates against synchronized commit
Commit the synced releases.ts in the read-only validate job before
typecheck, security scan, and whitespace checks so those gates inspect
the content that will be marked ready, not the pre-sync HEAD.
* fix(release): harden web-sync trust boundary and draft gating
Run sync from trusted main with only changelog/manifest overlaid from
the bot PR, re-draft after release-please, serialize sync without
canceling in-flight pushes, and require an explicit sync base.
* fix(release): restore overlaid inputs before validate cleanliness gate
Fetching changelog/manifest from the bot PR dirtied tracked files on the
trusted main checkout and made the final git-diff gate fail on every
pending release. Restore those overlays after sync and fetch origin/main
for the security/whitespace checks.
* fix(release): reuse validated sync artifacts on retry
* fix(release): validate release sync inputs and retries
* fix(release): recover web sync state transitions
* fix(release): protect generated release ownership
* fix(release): repair web sync recovery gates
* fix(release): bind sync artifacts to validated base
* fix(release): verify synchronized file mode
* fix(release): paginate bot PR discovery
* perf(cli): enable Node module compile cache
Warm CLI invocations spend substantial time compiling the bundled ESM entrypoint. Enable Node's optional on-disk compile cache only in the process that imports the bundle, while preserving early Node 22 compatibility and making cache failures non-fatal.
Add deterministic launcher coverage, packaging checks, and a reproducible benchmark procedure so the startup benefit can be measured without flaky CI thresholds.
* fix(ci): isolate minimum Node launcher check
The full validation suite depends on knip and oxc-parser behavior unavailable in Node 22.0.0. Keep full CI on the active Node 22 line and exercise the declared runtime floor in a dedicated build-and-launch job.
* fix(benchmark): harden startup measurements
Keep environment setup outside the timed process window, document the API's Node 22.8 floor, and preserve completed benchmark results when git metadata is unavailable.
* test(cli): verify compile cache disable behavior
Pair NODE_DISABLE_COMPILE_CACHE with a temporary cache directory and assert that supported Node releases leave it empty while preserving normal launcher output.
* refactor(openai-shim): extract XML and response conversion
* fix(openai-shim): align facade tests with XML extraction
* fix(openai-shim): restore shared XML tool-call sequencing
Wire parseXmlToolCalls through the façade sequence counter, reuse the
shared extractBalancedJson helper, and restore production dependency
coverage in conversion and façade tests.
* fix(openai-shim): restore façade coverage and require XML sequencer
Restore non-streaming convert and HY3 JSON-fallback e2e seams so façade
dependency wiring stays exercised, require an injected XML id sequencer,
and assert consecutive shared-sequence ids.
* fix(openai-shim): restore provider coverage and array content guard
Keep relocated openaiShim suite tests in test:provider, and reject
non-object array content parts the same way the inline converter did.
* fix(openai-shim): preserve mixed XML/HY3 tool-call order
Sort HY3 and standard XML candidates by source offset before ID
assignment, isolate focused test sequencers, and restore fetch after
the Gemini non-streaming façade test.
* feat(install): enforce and guard the zero-warning npm install contract
`npm install -g @gitlawb/openclaude` is verified zero-warning today, but
nothing kept it that way: the runtime deps were caret ranges resolved
fresh on every user install (the published tarball ships no lockfile),
no CI step ever installed the package, and registry-side drift (a
transitive dep deprecated after we ship) is invisible to file-based CI.
Static contract (fast, offline, every PR via `bun run build`):
- Pin the 3 runtime deps to exact versions so the verified resolution IS
the shipped resolution.
- New validators in scripts/externalsValidation.ts (unit-tested):
dependencies must equal RUNTIME_DEPENDENCY_CONTRACT exactly (no ranges,
no unreviewed additions), no consumer-run install hooks or funding
field, engines.node pinned. Wired into validate-externals.ts.
Runtime verification (scripts/verify-clean-install.ts, `install:verify`):
- Tarball mode (release gate) and published mode (registry watch), each
running cold-install and upgrade-over-previous scenarios in throwaway
prefixes with a cold cache and normalized env/flags.
- Strict output whitelist (summary lines only) with network failures
retried and reported as infra (exit 2), never as a hygiene verdict.
- Structural authority over the installed tree: any package declaring
install scripts fails, the installed manifest must match the static
contract, tarball payload/size asserted.
- Boot must be silent: --version prints the exact packed version;
--help (which, unlike the --version zero-import fast path, loads the
real bundle) must exit 0 with empty stderr.
CI: release publishes only after the verify passes on Node 22 (npm 10,
the supported floor — warning phrasing and EBADENGINE behavior differ
from npm 11) and Node 24, plus a final gate on the publishing machine
replacing `npm pack --dry-run`. A daily install-hygiene workflow
re-verifies the published @latest on {ubuntu, macos, windows} x
{Node 22, 24} — the only defense against post-release registry drift,
and the OS matrix covers the per-platform @vscode/ripgrep packages.
Found-by-the-guard fix: a fresh machine printed "Warning: ignoring saved
provider profile. OPENGATEWAY_API_KEY is required..." on every command
(even --help) because the injected fresh-install Opengateway default
fails validation without a key (#1651 chose ignore+warn). The default
env is still ignored, but the warning now only fires for genuinely
persisted profiles; published 0.24.0 carries the old noise, so the
verify script exempts exactly that version until the next release.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(install): address CodeRabbit review on the install-hygiene guard
- release.yml: pin install-verify to least-privilege `contents: read` and
disable credential persistence on its checkout; same persist-credentials
hardening on the install-hygiene cron checkout.
- verify-clean-install: previousPublishedVersion now follows the same
retry/infra discipline as installWithRetry — transient registry failures
retry and then exit 2 (infra) instead of silently skipping the
upgrade-scenario coverage; a clean not-published answer still skips.
- providerProfile: the fresh-install warning suppression now keys on
explicit provenance (persisted profile resolved once in
applyStartupEnvFromProfile) instead of sniffing the
DEFAULT_STARTUP_PROVIDER_ENV_VAR marker, which a persisted profile's
env can inherit from a parent CLI process; regression test covers the
marker-collision case.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* test(install): cover previousPublishedVersion retry/skip/infra branches
CodeRabbit follow-up: the branches deciding whether the upgrade-install
scenario runs, skips, or aborts as infra were untested. Extract the loop
as resolvePreviousPublishedVersion with injected effects (runView,
onRetry, onInfraFailure) per the repo's dependency-injection testing
convention, guard main() behind import.meta.main so the test import does
not launch a real verification, and add regression tests: first-try
success, transient-infra retry then success, clean E404 → null skip
without retries, persistent infra → onInfraFailure (exit 2 in the real
wiring), and unparseable version output → null.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
---------
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
* feat(provider): route GPT-5.6 models to the OpenAI Responses API
GPT-5.4/5.5/5.6 (incl. gpt-5.6-sol/terra/luna) reject function tools +
reasoning_effort on /v1/chat/completions, so an agent CLI (which always
sends tools) can't use them. Add a model+base predicate
(modelRequiresResponsesApi) that auto-selects the existing /v1/responses
transport for these models on api.openai.com and Azure OpenAI hosts.
Precedence: explicit responses/responses_compat > catalog
requiredApiFormat > explicit chat_completions > predicate > default. The
gpt-5.6 catalog entries deliberately set no requiredApiFormat so the
chat_completions escape hatch works for them. Register the gpt-5.6
descriptors and openai-vendor catalog entries (with reasoning metadata so
buildResponsesBody emits nested reasoning.effort).
Also fix a latent bug: the responses branch of buildRequestUrl emitted a
bare ${base}/responses and skipped Azure handling, so a forced/auto
responses route 404'd on Azure. It now mirrors buildChatCompletionsUrl —
deployment-style bases get the deployment path + api-version, bases
already containing /deployments/ keep their path and gain api-version,
while the modern Azure v1 surface (.../openai/v1) is preserved as
${base}/responses.
* fix(provider): honor OPENAI_AZURE_STYLE in the responses gate and use the Azure v1 responses surface
CodeRabbit review on #1961: the responses auto-route gate only checked
hostnames, ignoring the OPENAI_AZURE_STYLE override the shim honors for
custom/private Azure endpoints (APIM-fronted, private link). The Azure
detection is now a single shared predicate (isAzureStyleBaseUrl) used by
both the gate and the shim: OPENAI_AZURE_STYLE truthiness first, then
hostname matching.
Per Microsoft's docs, the Responses API exists only on the Azure v1
surface ({resource}/openai/v1/responses, model in the request body, no
api-version, no deployment-scoped form), so buildResponsesUrl now
normalizes any Azure-style base to that surface instead of mirroring the
chat builder's deployment-path + api-version form, which built endpoints
that do not exist.
https://learn.microsoft.com/en-us/azure/foundry/openai/how-to/responses
* fix(provider): address maintainer review on GPT-5.6 responses routing
Narrows the responses auto-route to verified variants (gpt-5.4/5.5/5.6
minus -mini/-nano, two-digit minors deliberately unmatched), corrects the
gpt-5.6 context window to 1,050,000 per the OpenAI model pages, documents
OPENAI_AZURE_STYLE's routing effect in .env.example, and hardens
buildResponsesUrl normalization (trailing-slash strip, stacked Azure
suffixes stripped until stable). Also pins --max-concurrency=1 on the
default test script and adds direct coverage for isAzureStyleBaseUrl, the
override-driven responses URL, and the gpt-5.6 catalog metadata.
* test(provider): pin responses predicate behavior for patch and suffixed ids
Pins gpt-5.4.1 (patch of a verified family, routed), gpt-5.41 (two-digit
minor read, not routed), and gpt-5.6-mini-high (mini variant, not routed)
so the predicate's edge behavior is asserted rather than implied.
* fix(provider): responses-contract test, regional OpenAI hosts, Azure deployment docs, env allowlist
Updates the providerOverride gpt-5.4 effort test to the Responses contract
the auto-route now sends (nested reasoning.effort, /responses URL); widens
the auto-route host check to OpenAI-controlled *.api.openai.com regional
endpoints; documents and regression-tests the explicit
OPENAI_API_FORMAT=responses path for arbitrary Azure deployment names; and
allows OPENAI_AZURE_STYLE through the --provider-env-file allowlist.
* fix(provider): narrow responses auto-route to verified minors and carry GPT-5.6 reasoning metadata on Azure
Narrows the model-name auto-route predicate from gpt-5.[4-9] to gpt-5.[4-6]
so unverified future minors (5.7/5.8/5.9) are not auto-routed, and syncs the
comment plus the two remaining "5.4+" phrasings in .env.example.
Fixes GPT-5.6 reasoning metadata on Azure and regional OpenAI bases: those
hosts resolve to route 'custom' (empty catalog), so resolveCatalogReasoningMetadata
returned undefined and the request dropped its default 'high' effort and the
reasoning.encrypted_content include. It now falls back to the openai vendor
catalog by model name on route 'custom', so gpt-5.6 carries its advertised
default 'high' and xhigh instead of incidental legacy controls.
* fix(provider): gate the custom-route reasoning fallback to verified OpenAI/Azure bases
The round-3 custom-route fallback also fired for arbitrary OpenAI-compatible
gateways (which resolve to route 'custom' too), injecting a default
reasoning_effort:high on a chat_completions request those gateways may reject
— a behavior change on third-party gateways the PR promised not to make.
Gate the fallback on baseUrlSupportsResponsesAutoRoute (the same verified
OpenAI/Azure surfaces the Responses auto-route uses), threading the request
base via the reasoning context (process.env fallback for the upstream path).
* test(provider): isolate OPENAI_API_BASE/OPENAI_AZURE_STYLE in the gpt-5.6 reasoning tests
The Azure/regional/gateway reasoning tests snapshot-restored only
CLAUDE_CODE_USE_OPENAI/OPENAI_BASE_URL/OPENAI_API_KEY. A leaked
OPENAI_AZURE_STYLE from another test would make isAzureStyleBaseUrl treat
the gateway base as Azure-style, firing the fallback and flipping the
'no injected default' assertion. Snapshot both keys and delete them before
each test's setup so a leaked value cannot corrupt the result.
* fix: preserve GPT-5.6 fallback and Azure routing
* fix: cover GPT-5.6 Azure edge cases
* fix: cover GPT-5 forced chat tools
* fix(provider): narrow Azure-style responses routing
* fix(provider): isolate agent overrides from Azure mode
* fix(provider): isolate override reasoning from Azure mode
* fix(provider): isolate override API format
* fix(provider): preserve responses effort routing
* fix(provider): restore safe context and clear Azure mode
* fix(provider): preserve Azure routing state
* fix(provider): preserve Azure profile routing
* fix(provider): preserve automatic Responses routing in profiles
---------
Co-authored-by: jatmn <the@jat.mn>
* fix(deps): ship a zero-warning, minimal install
The published package declared 62 runtime `dependencies`, but `dist/cli.mjs`
is a fully-bundled esbuild output that inlines almost all of them. End users
therefore installed ~476 transitive packages — including three subtrees the
bundle never needs at install time, each emitting an install warning:
- node-domexception (deprecated) via google-auth-library
- protobufjs (allow-scripts) via @grpc/* (already bundled into dist)
- sharp (allow-scripts) native image module
The repo's `overrides`/`allowScripts` silence these locally, but those are
root-only npm settings and are ignored when the package is installed as a
dependency — so end users saw the warnings.
Core changes:
- package.json: runtime dependencies trimmed 62 -> 3 (@orama/orama,
@orama/plugin-data-persistence, @vscode/ripgrep). Bundled packages, plus
the optional sharp/google-auth-library, move to devDependencies so they
are built/tested but not shipped.
- package.json: @anthropic-ai/sdk, @modelcontextprotocol/sdk, react and
react-reconciler declared as OPTIONAL peerDependencies — externalized by
the ./sdk bundle but bundled into the CLI. Optional peers keep the CLI
install minimal and warning-free while still resolving for ./sdk consumers.
- externals.ts: sharp, google-auth-library and @anthropic-ai/bedrock-sdk
marked OPTIONAL_RUNTIME_EXTERNALS (loaded on demand, not shipped).
- validate-externals.ts: runtime deps validate against externals; bundled
deps validate against dependencies + devDependencies.
- client.ts: load @anthropic-ai/bedrock-sdk via the runtime importer so
esbuild no longer inlines it and hoists its static @aws-sdk import into
the CLI bundle (that was a startup crash for default installs).
Optional-dependency UX (consistent, actionable errors):
- New src/utils/optionalRuntimeModule.ts exports importRuntimeModule and
importOptionalRuntimeModule. The optional variant translates a missing
package (code === 'ERR_MODULE_NOT_FOUND', specifier present in message)
into "<feature> requires "<pkg>" ... Run `npm i -g <pkg>`". Generic so
typed call sites keep their module types.
- Routed ALL optional-package load sites through it (previously only one
did): google-auth-library (client.ts, auth.ts, geminiAuth.ts),
@anthropic-ai/foundry-sdk + @azure/identity (client.ts), and the
@aws-sdk/* Bedrock paths (model/bedrock.ts, tokenEstimation.ts, aws.ts).
- imageProcessor.ts: sharp-missing error now says `npm i -g sharp`.
- docs/advanced-setup.md: new "Optional provider packages" table and a
Vertex note documenting the on-demand installs.
- Unit test for the helper (friendly error, success path, specifier match,
raw passthrough).
- knip.json: ignore google-auth-library (now loaded via runtime string).
Verified on the current tree:
- tsc, build/validate-externals, knip, and tests all pass.
- npm pack + install --omit=dev adds 8 packages, zero deprecation/
allow-scripts/funding warnings; --version/--help/mcp list run.
- With packages absent, CLAUDE_CODE_USE_BEDROCK and CLAUDE_CODE_USE_VERTEX
print the friendly `npm i -g <pkg>` error (verified end-to-end).
- ./sdk imports once its optional peers are present (24 exports, no warns).
- Bundled ajv + ajv-formats validate with no ajv installed; no unguarded
native runtime requires (fsevents absent in chokidar 4; bun:sqlite Bun-only).
Trade-off: image reads, AWS Bedrock, Azure Foundry and GCP/Vertex now prompt
a one-time `npm i -g <pkg>` instead of being shipped to every user.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
Review fixes (CodeRabbit + jatmn):
- validate-externals: the INTENTIONALLY_BUNDLED exemption is now scoped per
bundle. The CLI exempts every bundled package; the SDK does NOT exempt
packages declared as peerDependencies (keyed on package.json, an independent
source of truth) so dropping react/@anthropic-ai/sdk from SDK_EXTERNALS now
fails validation instead of silently passing. Added an explicit minimal-
install contract check: bundled packages must be devDependencies-only — never
in `dependencies`, and only the SDK-external subset may be optional peers.
Validation logic extracted to scripts/externalsValidation.ts + tests.
- FileReadTool oversized-image fallback now loads via the shared
getImageProcessor() (not a raw import('sharp')) and re-throws
ImageProcessorUnavailableError, so a missing processor surfaces the
`npm i -g sharp` install hint instead of returning an over-budget image.
- optionalRuntimeModule: match the missing specifier as a QUOTED token, not a
raw substring, so a missing transitive package whose name contains the
requested one (sharp vs sharp-libvips, @aws-sdk/client-bedrock vs
@aws-sdk/client-bedrock-runtime) no longer triggers the wrong install hint.
Predicate extracted to isMissingSpecifierError() with regression tests.
- docs/advanced-setup.md: the Vertex auth section now shows both documented
paths (gcloud ADC and a GOOGLE_APPLICATION_CREDENTIALS service-account file).
Review fixes (round 2, CodeRabbit):
- validate-externals: assert the optional-peer install contract — every
peerDependency must be { optional: true } in peerDependenciesMeta
(validateOptionalPeers), so losing that flag fails the build instead of
silently reintroducing install warnings.
- validate-externals: hard-check OPTIONAL_RUNTIME_EXTERNALS placement
(validateOptionalRuntimeexternals). Anything esbuild can see statically must
stay external in BOTH bundles (dropping sharp/google-auth-library now fails);
the runtime-indirection-only subset (new RUNTIME_INDIRECTION_ONLY_EXTERNALS)
must stay OUT of externals so esbuild never re-exposes their static imports.
- Deeper-dig fix: @anthropic-ai/foundry-sdk was misclassified as
INTENTIONALLY_BUNDLED, but it is loaded only through the Function indirection
(esbuild never sees it, so it was never actually bundled) — its sole presence
in dist is the specifier string. Per the PR's own "Azure Foundry now prompts"
trade-off it is on-demand, so it now lives in OPTIONAL_RUNTIME_EXTERNALS +
RUNTIME_INDIRECTION_ONLY_EXTERNALS (mirroring bedrock-sdk). sandbox-runtime is
genuinely statically imported, so it stays bundled.
- Provider-routing coverage (scripts/optionalRuntimeSpecifiers.test.ts): a
static scan asserts every importOptionalRuntimeModule specifier is a declared
OPTIONAL_RUNTIME_EXTERNAL and never also INTENTIONALLY_BUNDLED — the
invariant that keeps a provider's optional package loadable on demand.
- All new validators extracted to scripts/externalsValidation.ts with tests.
Review fixes (round 3, CodeRabbit):
- client.ts: gate the Vertex google-auth-library import behind the non-skip
branch. CLAUDE_CODE_SKIP_VERTEX_AUTH (proxy/test) uses a mock GoogleAuth and
must not require the optional package; it was loaded unconditionally before.
- optionalRuntimeModule: drop the hard-coded `npm i -g`. The helper backs both
the global CLI and project-local ./sdk consumers, so the hint is now
context-neutral ("npm install <pkg>" / add -g for the global CLI).
- validate-externals: every SDK_ONLY_EXTERNALS entry must STAY a
peerDependency (a dropped peer leaves runtimeDeps while the SDK still
externalizes it); and OPTIONAL_RUNTIME_EXTERNALS must never be shipped (fail
on overlap with dependencies/peerDependencies). Both with tests + live-verified.
- optionalRuntimeSpecifiers.test: pin the EXACT set of optionally-loaded
specifiers instead of a >=5 count (a count passes even if a provider path
regresses).
- attachments: extract tryReadEditedImageAttachment() — background watched-file
image attachments DEGRADE to null on any failure (incl.
ImageProcessorUnavailableError) so a missing optional package never aborts a
turn, while the explicit FileReadTool path still surfaces the install hint.
Deterministic regression test (bad path -> null).
- docs: Bedrock row notes profile-based auth also needs
@aws-sdk/credential-providers; install-hint wording matches the new message.
Review fixes (round 4, CodeRabbit):
- attachments: stop sending the raw file path through the analytics
bypass-cast (tengu_watched_file_compression_failed). Send only the safe
file extension via getFileExtensionForAnalytics, matching the existing
tengu_file_read_dedup pattern, so no usernames/project paths can leak.
- externals.ts: corrected the OPTIONAL_RUNTIME_EXTERNALS header comment,
which still claimed all entries "remain in COMMON_EXTERNALS" — no longer
true since the indirection-only subset (bedrock/foundry) must stay OUT of
the externals lists.
(Other CodeRabbit comments on this push re-surface items already addressed in
prior commits: the peerDependenciesMeta-optional check (validateOptionalPeers),
the SDK-peers-present and optional-not-shipped validator rules, the
exact-specifier-set test, the attachments degrade contract + test, and the
context-neutral install hint are all present. The "assert every optional
external is a devDependency" suggestion is intentionally NOT applied: @aws-sdk/*
and @azure/identity are transitive devDeps via bedrock-sdk/foundry-sdk, so a
blanket assertion would be incorrect; source resolution is covered by the
build + tests that import these packages.)
Review fixes (round 5, CodeRabbit):
- attachments: stop leaking file paths via logError in the background-image
degrade path. readImageWithTokenBudget can throw path-bearing messages
(e.g. "Image file is empty: <path>") and logError persists message/stack, so
log only the error TYPE name now. (Analytics payload was already sanitized.)
- attachments: tryReadEditedImageAttachment takes an injectable reader so the
degrade contract is tested for the EXACT error types — ImageProcessorUnavailableError
and a path-bearing read error both degrade to null (not just ENOENT) — plus a
success case. No mocking.
- validate-externals: enforce the source-install half of the optional contract.
Non-transitive OPTIONAL_RUNTIME_EXTERNALS must be devDependencies so `bun
install` source builds resolve them. The new TRANSITIVE_OPTIONAL_EXTERNALS
documents the exemption (@aws-sdk/* via @anthropic-ai/bedrock-sdk, @azure/identity
via @anthropic-ai/foundry-sdk — provided transitively, not direct devDeps). A
blanket "all optionals are devDeps" check would have wrongly failed on those.
Tests + live-verified (dropping sharp from devDependencies now fails).
Review fixes (round 6, CodeRabbit + jatmn):
- optionalRuntimeSpecifiers.test: the call-site scan regex missed
generic-annotated calls (importOptionalRuntimeModule<...>(...)) in
model/bedrock.ts and tokenEstimation.ts, so the exact-set assertion was
incomplete. Regex now allows an optional generic; EXPECTED_SPECIFIERS adds
@aws-sdk/client-bedrock and @aws-sdk/client-bedrock-runtime (7 total).
- importOptionalRuntimeModule default generic is now <T = unknown> (was any),
so destructured imports are no longer silently any. Every call site now
supplies its module type — typeof import('<pkg>') where the package is
type-resolvable (bedrock-sdk, foundry-sdk, @aws-sdk/credential-providers,
google-auth-library), and a named minimal-shape alias for @azure/identity
(not a direct devDep, so typeof import can't resolve it). This gives
compile-time verification of each provider's module contract (export names,
shapes) — the structural answer to the "cover the provider branches" ask.
- attachments: tryReadEditedImageAttachment takes injectable {read,log,track};
a new test asserts the sanitized-telemetry contract directly — the logError
payload is path-free and the analytics payload carries only `ext`, never the
edited-image path.
* fix(deps): address optional runtime review findings
* test(deps): isolate optional runtime importer mocks
* fix(deps): clarify AWS optional auth labels
* fix(deps): close optional runtime review gaps
---------
Co-authored-by: jatmn <the@jat.mn>
* feat: add Codebase Intelligence — repo map with PageRank-ranked structural summaries
Adds a new module that builds a structural map of the repository by parsing
source files with tree-sitter, building a cross-file reference graph weighted
by IDF, ranking files with PageRank, and rendering a token-budgeted summary
of the most important files and their signatures.
Surface:
- RepoMap tool the model can call on-demand, with focus_files / focus_symbols
- /repomap slash command with --tokens, --focus, --stats, --invalidate
- Auto-injection into session system context, gated by REPO_MAP=1 env var
(compile-time feature('REPO_MAP') flag stays off in scripts/build.ts)
How it works:
git ls-files → tree-sitter WASM parse → extract defs/refs →
IDF-weighted directed graph → PageRank → render top files until token budget
Files imported by many others rank highest. Common symbol names (get, set,
map, value) are down-weighted via IDF. Results cached to disk keyed by
(path, mtime, size) — only changed files are re-parsed.
Supported languages: TypeScript, JavaScript, Python.
Tree-sitter tag queries are inlined as string constants in queries.ts so
they ship inside dist/cli.mjs and work after npm install — the .scm source
files are kept for readability/Aider attribution but are not required at
runtime. A drift-guard test (queries.test.ts) asserts byte-equality between
the inlined strings and the .scm source files.
Dependencies added: web-tree-sitter, tree-sitter-wasms, graphology,
graphology-pagerank, graphology-operators, js-tiktoken.
* fix(repomap): invalidate rendered cache on file edits + Windows test fix
- computeMapHash now folds per-file mtime+size into the cache key so a
source edit (without changing the file list) no longer returns the
prior rendered map. Adds a regression test that edits a file and
confirms the second build reflects the new symbol without manual
invalidateCache().
- queries.test.ts byte-for-byte drift guard normalizes CRLF -> LF when
reading the .scm source so Windows checkouts pass. .gitattributes
also pins *.scm to LF on future checkouts.
- Externals: declare web-tree-sitter, tree-sitter-wasms, graphology*,
and js-tiktoken in scripts/externals.ts so build validation passes.
* fix(repomap): expand directory focus paths
* fix(repomap): satisfy deadcode check
* Fix repo map review findings
* Resolve remaining repo map review findings
* fix(repomap): address review findings
* fix(repomap): address review findings
* fix(repomap): resolve smoke and review follow-ups
* fix(repomap): preserve cached tag order
* fix(repomap): resolve review follow-ups
* fix(repomap): satisfy query promise lint
* Fix repo map context timeout cleanup
* fix: address repo map review findings
* fix: cancel timed-out repo map context builds
* fix(repomap): preserve git file path whitespace
* fix(repomap): handle graph and parsing edge cases
* fix(repomap): preserve shell token positions
* fix(repomap): respect configured cache home
* fix(repomap): address review findings
- Add explicit 10000ms timeout to the feature-flag-off context test to avoid cold-import flakes.
- Add --focus-symbols flag to /repomap and forward it to buildRepoMap, matching the RepoMap tool.
- Add parsing/command tests and docs coverage for --focus-symbols.
---------
Co-authored-by: gnanam1990 <gnanasekaran.sekareee@gmail.com>
* fix(deps): remove deprecated uuid install path
* fix(api): address PR #1232 review — type the local Vertex client surface
Resolves the blocker raised by @Vasanthdev2004, @gnanam1990, and @jatmn: the
in-repo AnthropicVertex replacement compiled under bun (no type-check) but added
4 `tsc --noEmit` errors that the upstream typed SDK did not.
- Declare `messages`/`beta` as typed class fields (BaseAnthropic doesn't, but
the upstream @anthropic-ai/vertex-sdk client did), so typed consumers —
client.ts `new AnthropicVertex(...)` and the SDK calling `.messages` — keep
the resource surface. (vertexClient.ts:145/146, test:53)
- Widen the header-merge helpers to accept the base client's request header type
(HeadersLike), and handle the NullableHeaders shape it actually passes so the
merge stays correct, not just type-clean. (vertexClient.ts:182)
Also drops the now-stale `@anthropic-ai/vertex-sdk` entries left behind by the
dependency removal:
- scripts/externals.ts INTENTIONALLY_BUNDLED (P3)
- knip.json ignoreDependencies
Testing: `tsc --noEmit` clean; vertex/client/gemini tests 51 pass; smoke green
(INTENTIONALLY_BUNDLED back in sync, 57 entries); knip clean.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(api): address CodeRabbit review on PR #1232 — auth precedence + coverage
- Security (vertexClient.ts): merge resolved Google auth headers LAST so a
caller-supplied Authorization / x-goog-user-project can't override the Vertex
credential and send the wrong token upstream. Other request headers still
pass through unchanged.
- Tests (vertexClient.test.ts): add focused regression coverage for the
previously-unguarded routing/auth branches —
* streaming → :streamRawPredict path (+ model stripped, stream preserved)
* count_tokens → count-tokens:rawPredict path rewrite
* auth-header precedence: caller Authorization does NOT override the Vertex
token (guards the fix above + exercises the NullableHeaders merge branch).
Testing: tsc clean; vertexClient tests 5 pass; full src/services/api 840 pass;
smoke + knip green.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(api): validate and encode Vertex model before building the URL
CodeRabbit follow-up on PR #1232: `model` was interpolated straight into the
Vertex endpoint path, so a missing/non-string model would silently route to
`.../models/undefined:rawPredict` instead of failing fast. Now throw a clear
error on a missing/empty model and encodeURIComponent the value before building
the path. Adds a focused test for the missing-model case.
Testing: tsc clean; vertexClient tests 6 pass; smoke + knip green.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(api): address remaining review items from PR #1232
- [P2] Fix count_tokens method guard: apply method==='post' to both paths
- [P3] Remove unused accessToken option from AnthropicVertex
- [P3] Narrow batches type on messages/beta resources with Omit
* test: add count_tokens?beta=true routing regression test
---------
Co-authored-by: Kevin Codex <kevin@gitlawb.com>
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
Co-authored-by: Gravirei <gravirei@users.noreply.github.com>
* fix: type safety, defensive defaults, and unbounded retry prevention
QueryEngine.ts:
- Import PERMISSION_MODES runtime constant and validate permissionMode
before casting in submitMessage — invalid mode strings fall back to
'default' instead of crashing with ReferenceError: PERMISSION_MODES is
not defined (fixes the runtime gap from the original PR)
- Use splice(0, length, ...messages) instead of length=0 + push() for
atomic array replacement in snip replay, so concurrent readers of
getMessages() never observe an empty state
withRetry.ts:
- Cap persistent retry loop at 100 attempts via PERSISTENT_RETRY_MAX_ATTEMPTS
constant — prevents unbounded retry (~8 hours max with exponential backoff
and 6-hour reset cap) when the unattended retry path is enabled
autoCompact.ts:
- Add MIN_AUTOCOMPACT_FAILURE_COOLDOWN_MS = 10_000 floor for
OPENCLAUDE_AUTOCOMPACT_FAILURE_COOLDOWN_MS override — prevents
misconfiguration from effectively disabling the circuit breaker
autoCompact.test.ts:
- Update test override from 5000 to 15000 to respect the new 10s minimum floor
- Add test case verifying values below the floor (5000, 9999) are rejected
and that the floor value (10000) is accepted
- Update circuit breaker retry-time expectation from 111_000 to 121_000
to account for the new 15s cooldown override
* test: enable UNATTENDED_RETRY feature in bun test scripts
The new persistent retry cap test in withRetry.test.ts needs the real
UNATTENDED_RETRY feature gate to fire, which requires passing
--feature=UNATTENDED_RETRY on the bun test command line. Enable it
in the standard test, test:full, test:coverage, and test:provider
scripts so the test sees the production gate behavior.
* test: cover persistent retry cap driven through real gate
Add a regression test that proves the persistent retry path stops
after PERSISTENT_MAX_ATTEMPTS=100 retryable 429s by driving the real
isPersistentRetryEnabled() gate (no test override seam). Also:
- Switch makeError to the new APIError() constructor so the test
errors match the real wire shape and exercise the production
canRetry/shouldRetry branches
- Add CLAUDE_CODE_UNATTENDED_RETRY to the envKeys clear list so the
gate isn't poisoned by leaked state from a prior test
- Mock src/utils/sleep.js in importFreshWithRetryModule so the
exponential-backoff delays don't slow the suite down
* test: defensively clear leaked env vars in client.test.ts
The 4 failing tests in CI (first-party Anthropic fetch wrapper, env-only
MiniMax routing, OPENAI_MODEL preservation, OpenAI shim options) all sit
at the top of the file and are sensitive to leaked env vars from prior
test files in the same process. Extend the beforeEach, afterEach, and
inline cleanup to clear OPENAI_AUTH_HEADER, OPENAI_AUTH_SCHEME,
OPENAI_AUTH_HEADER_VALUE, MIMO_API_KEY, VENICE_API_KEY, and
NVIDIA_API_KEY alongside the existing vars, and add ANTHROPIC_API_KEY /
ANTHROPIC_AUTH_TOKEN / ANTHROPIC_MODEL to the first test's inline
cleanup so it does not rely solely on the global beforeEach when run
in isolation.
* test: isolate countMcpToolTokens tests from mcp.ts env-var side effect
src/entrypoints/mcp.ts sets CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=true as
a top-level side effect on import. mcp.test.ts imports that file, so the
env var is leaked into the rest of the test process. In
analyzeContext.mcp.test.ts the kill switch forces getToolSearchMode() to
'standard', which makes isToolSearchEnabled() return false, so the
'keeps deferred MCP schemas excluded' test sees isDeferred=false and
returns mcpToolTokens=1000 instead of 0.
Clear the kill switch and ENABLE_TOOL_SEARCH in beforeEach (restoring
the original values in afterEach) so the test observes the production
default of tool search enabled regardless of test ordering.
* fix: validate against EXTERNAL_PERMISSION_MODES; confine persistent retry override to test-only module var
QueryEngine.ts:
- Switch SDK init permissionMode validation from the internal
PERMISSION_MODES set to EXTERNAL_PERMISSION_MODES. The internal
set can include the classifier-only 'auto' mode that is not a
valid wire value for the SDK system/init payload, so emitting
it would produce an unsupported configuration.
withRetry.ts:
- Rename PERSISTENT_RETRY_MAX_ATTEMPTS to PERSISTENT_MAX_ATTEMPTS
to match the convention of the other PERSISTENT_* constants and
re-export it as _PERSISTENT_MAX_ATTEMPTS_FOR_TEST for unit-test
assertion of the cap value (no runtime override seam).
- Hoist isPersistentRetryEnabled() into a local
persistentRetryEnabled const at the top of withRetry() so all
call sites see a consistent snapshot within a single retry
chain.
- Thread persistentRetryEnabled through shouldRetry() so its
branch is decided once per chain rather than re-reading the
feature flag and env var on every attempt.
* feat: emit telemetry when persistent retry cap is reached
* fix: surface changelog cache-write failures in migration
- Only swallow EEXIST (file already exists) errors in migrateChangelogFromConfig
- Rethrow all other write failures (permissions, disk full, etc.)
- Log migration errors instead of silently ignoring them
- This ensures migration failures are surfaced and will retry on next startup
* fix: split mkdir and writeFile in changelog migration
- Ensure mkdir runs before writeFile try/catch
- Only suppress EEXIST from writeFile, not mkdir
- Prevents EEXIST from mkdir incorrectly counting as successful write
* fix: stop overriding getGlobalConfig in user.test.ts mock
* Fix leftover conflict marker in QueryEngine.ts
* fix: remove stale retry guard and handle mkdir EEXIST
- Remove duplicate shouldRetry() call without persistentRetryEnabled arg in withRetry.ts
- Wrap mkdir in try-catch to handle EEXIST on Windows/Bun readonly folders in releaseNotes.ts
* test: don't restore OPENAI auth header env vars in afterEach
These vars were being restored from originalEnv which captures polluted values
from prior test files in the full suite. Removing the restoreEnv calls keeps
them cleared between tests, fixing 'Could not resolve authentication method'
failures in CI smoke-and-tests.
* fix: remove duplicate OPENAI_AUTH_* keys in originalEnv (TS1117)
* fix: restore OPENAI auth header snapshot and move MCP lock to top-level
- client.test.ts: restore OPENAI_AUTH_HEADER/SCHEME/HEADER_VALUE in afterEach
so the file doesn't permanently clear those globals in its worker
- analyzeContext.mcp.test.ts: move acquireSharedMutationLock/release to the
top-level beforeEach/afterEach so all env mutations in this file happen
while the shared mutation lock is held
* fix: use splice for atomic array replacement in snip replay
Restores the atomic array replacement using splice(0, length, ...messages)
instead of length=0 + push(...) that was claimed in commit 4d54d5d but
lost during merge. This ensures concurrent readers of getMessages() never
observe an empty mutableMessages array during snip replay, matching the
compact_boundary behavior.
* ci: add UNATTENDED_RETRY feature flag to release workflow test command
The persistent retry cap test requires the UNATTENDED_RETRY feature flag
to be enabled. The release workflow was running 'bun test --max-concurrency=1'
without the feature flag, causing the test to fail on the release path.
This aligns the release workflow with the package.json test scripts which
all include --feature=UNATTENDED_RETRY.
* fix: clear auth env vars in shared setup; add telemetry at persistent retry cap
- Remove duplicate OPENAI_AUTH_HEADER/SCHEME/VALUE deletes from
clearEnvForMiniMaxOnlyTest() (shared beforeEach already clears them)
- Clarify persistent retry cap comment: the ~8h estimate only applies to
the exponential-backoff path; the reset-delay path (up to
PERSISTENT_RESET_CAP_MS / 6h per attempt) can take far longer
- Telemetry event at retry cap already present from prior commit
* fix: make persistent retry cap test pass without --feature=UNATTENDED_RETRY
Updated test to account for feature flag behavior in retry logic.
* fix: normalize REPL bridge permissionMode against EXTERNAL_PERMISSION_MODES
* fix: export isPersistentRetryEnabled for test-side feature-gate assertion
* fix: use isPersistentRetryEnabled() as real feature gate in retry cap test
Refactor withRetry test to include isPersistentRetryEnabled check and update expected calls logic.
* fix: restore missing retryableRateLimit declaration in persistent retry test
Refactor runRetries function for clarity.
* 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.
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>
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>
* Enhance OpenClaude VS Code extension with Microsoft Foundry / Azure OpenAI support. Added configuration options for Azure API key, endpoint, and deployment settings. Updated README and documentation for new features, including a setup wizard for Azure integration. Improved terminal launch environment handling for Azure compatibility.
* Fix packaged Windows helper runtime references
* Use installed CLI from Windows helper aliases
* Scope Windows helper env overrides to invocation
* Align Windows alias docs with shipped helper
* Harden release publish checks and remove vulnerable Firecrawl SDK
Add a post-publish npm verification step to the release workflow so GitHub releases fail if the npm latest tag does not resolve to the expected version within the retry window.
Update dependency pins to remediate the audit findings by moving axios to 1.16.0, upgrading the Anthropic SDK to 0.94.0, and bumping the Bedrock and Vertex wrapper packages so Bun installs dedupe onto the patched SDK.
Replace the @mendable/firecrawl-js dependency with a small in-repo fetch-based Firecrawl client used by WebFetchTool and the Firecrawl web-search provider. Preserve self-hosted support, add transient 502 retry/backoff behavior, and cover the new client with focused tests.
Validation:
- bun test src/tools/firecrawl/client.test.ts src/tools/WebSearchTool/providers/firecrawl.test.ts
- bun run build
- bun run smoke
- packed-install npm audit --omit dev --json returned 0 vulnerabilities
* Harden Bun test isolation for release validation
Fix shared-module test leaks that were breaking providerProfile in the full serialized Bun suite.
- preserve full module surfaces when mocking env/provider modules
- remove unnecessary env/envUtils mocks from user/install surface tests
- use a fresh providerProfile module import for the Codex OAuth cleanup regression
- relax the Windows-only permission assertion in providerProfile tests
Validation:
- bun install --frozen-lockfile
- bun test --max-concurrency=1
- bun run smoke
- bun run build
- npm pack
* Complete execa mock coverage in user test
Fix the remaining cross-file Bun mock leak reported in review by expanding the persisted execa mock in src/utils/user.test.ts to include execaSync.
This keeps later imports that touch secure-storage and exec helpers from failing or hanging when bun test runs files serially after user.test.ts.
Validation:
- bun test src/utils/user.test.ts src/utils/effort.codex.test.ts
- bun test --max-concurrency=1
- bun run build
- bun run smoke
- npm pack
* Preserve full module surfaces in user test mocks
Convert the auth, config, cwd, and execa mocks in src/utils/user.test.ts into pass-through mocks with targeted overrides.
This fixes the remaining Bun process-global mock leakage where later suites could fail or hang after user.test.ts because leaked partial mocks were missing exports such as auth/config helpers or execaSync.
Validation:
- bun test src/utils/user.test.ts src/utils/effort.codex.test.ts
- bun test src/utils/user.test.ts src/utils/openclaudeInstallSurfaces.test.ts
- bun test --max-concurrency=1
* Override ip-address to 10.2.0
Add a top-level override for ip-address and refresh bun.lock so the MCP SDK -> express-rate-limit path resolves to ip-address@10.2.0 instead of 10.1.0.
This keeps the branch's audit-remediation scope aligned with the remaining transitive advisory path without changing the direct MCP SDK pin.
Validation:
- bun pm why ip-address
- bun audit
* fix: use cleanup-safe Firecrawl timeouts
* Isolate attribution settings tests
* test: remove stale provider profile import
---------
Co-authored-by: JATMN <12479882+jatmn@users.noreply.github.com>
The use-input.test.ts added in #1198 broke the full `bun test` run two ways:
1. It imported `@testing-library/react-hooks`, which was never installed and
is React 16/17/18-only (incompatible with this repo's React 19), so the
file errored on load.
2. Its top-level `vi.mock('./use-stdin.js', …)` registered a module mock that
leaks across every later file in the same `bun test` process. The fake
eventEmitter's `.on` was a no-op, so `useInput` silently registered no
listener and dropped all keystrokes — surfacing as timeouts in
MonitorPermissionRequest and the agent-menu/wizard TextInput tests (which
passed in isolation but failed in the full suite).
Rewrite the test to inject the stdin handle via StdinContext.Provider (no
leaking global mock) and render through the real ink root (no
@testing-library/react-hooks). Drop the now-dead react-hooks and
react-test-renderer devDependencies and reconcile the lockfile.
Full suite: 3429 pass, 0 fail (was 9 fail + 1 error).
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
* fix: preserve raw mode across component re-renders (issue #843)
* fix(input): only reset raw mode on explicit isActive=false, not on MCP re-render churn (issue #843)
* fix: balance raw mode for isActive false transitions + add regression test
Fixes the issue where cleanup closes over stale isActive=true and returns
early without calling setRawMode(false), leaving rawModeEnabledCount
incremented after UI no longer has active useInput.
Changes:
- Use a ref to track whether raw mode was actually enabled
- Check the ref in cleanup instead of stale isActive closure value
- Add 6 regression tests covering the true->false/unmount paths
Addresses jatmn's review feedback: 'fix raw mode balance for isActive: false transitions'
* fix(input): debounce raw-mode reset to survive MCP re-render churn (issue #843)
* fix: add react-test-renderer dep and fix use-input test for CI
- Add react-test-renderer devDependency (required by @testing-library/react-hooks)
- Add @testing-library/react-hooks to INTENTIONALLY_BUNDLED in externals.ts
- Fix use-input.test.ts 'MCP re-render churn' test to use isActive rerender
instead of separate renderHook calls (refs don't persist across instances)
* fix: address P1 raw-mode counter imbalance and P2 test-dep scope (PR #1196)
P1 (use-input.ts:64-68): skip setRawMode(true) on isActive false->true
when a deferred reset is pending, preventing counter over-increment
that leaked raw mode on final unmount. Test updated to assert
balanced 1-then-1 call pattern (no redundant setRawMode(true)).
P2 (package.json, externals.ts): move @testing-library/react-hooks
from dependencies to devDependencies; remove from INTENTIONALLY_BUNDLED.
* fix(test): isolate provider-related attribution and preconnect tests
Remove process-global provider mocks from apiPreconnect tests and exercise real env-based provider resolution with hermetic first-party setup.
Reset bootstrap/settings state around attribution tests and reload the attribution module per test so provider and client state cannot leak across suites.
Verification: bun test --max-concurrency=1 src/utils/apiPreconnect.test.ts src/utils/attribution.test.ts
* Fix full local check failures
Add a check script that runs smoke plus the full single-concurrency Bun test suite, and wire it into CONTRIBUTING, the PR template, and PR checks.
Fix Windows/full-suite failures by preferring Git Bash over the WSL bash launcher, normalizing settings paths before source matching, making path and warning-glyph tests platform-aware, and restoring persistent Bun module mocks for AgentTool and hook-chain tests.
Verified with bun test src\tools\BashTool\BashTool.errorOutput.test.ts --max-concurrency=1 and bun run check.
* fix(test): eliminate mock.module() leaks and platform-specific test failures
## Problem
The full test suite (bun test --max-concurrency=1) had 10 failing tests on
Windows. Investigation revealed 4 distinct root causes, all stemming from
bun's mock.module() not being fully reversible by mock.restore(). When a
test file replaces a shared module via mock.module(), stale bindings persist
in already-imported modules even after mock.restore() is called. This is a
known bun limitation.
The CI (Ubuntu) only showed 1 consistent failure (the attribution test),
but the Windows-local failures exposed real bugs that could surface in CI
under different test ordering.
## Changes
### src/utils/hookChains.integration.test.ts (root polluter)
This file was the biggest source of test pollution with 9 mock.module()
calls replacing shared modules (analytics, growthbook, policyLimits,
teammateMailbox, teammate, AgentTool, replBridge, etc.) with partial
surfaces. For example, the teammateMailbox mock only exported writeToMailbox
but the real module has 20+ exports including isIdleNotification,
createIdleNotification, readMailbox, etc. When mock.restore() didn't fully
undo these mocks, downstream tests got undefined for missing exports.
Fix: Import real modules via cache-busted dynamic imports before setting up
mocks, then spread the real module surface into each mock.module() call.
This way even if the mock leaks, downstream tests see the full module
surface with only the intended overrides. All 9 mock.module calls now
spread their real module counterparts.
Also fixed: the test was failing in isolation with SyntaxError because
attachments.ts transitively imports isIdleNotification from
teammateMailbox.js, which was missing from the partial mock.
### src/utils/settings/changeDetector.test.ts (Windows path normalization)
4 tests failed because getSourceForPath() normalizes paths using
path.normalize() which converts forward slashes to backslashes on Windows.
The test hardcoded Unix-style paths (/tmp/openclaude/user/settings.json)
but path.normalize produces \tmp\openclaude\user\settings.json on
Windows. The path comparison always failed, so handleChange() returned
early without triggering any callbacks or debounce timers.
Fix: Import normalize from 'path' and apply it to all test path constants
(pathsBySource, getManagedSettingsDropInDir). This matches what the
production code does.
### src/utils/exportFormats.test.ts (Windows path separator)
resolveExportFilepath() uses path.join() which produces backslash-separated
paths on Windows. The test expected forward-slash paths.
Fix: Import join from 'path' and use it in the expected value so the
assertion is platform-agnostic.
### src/utils/file.test.ts (growthbook mock leak)
importFileModuleWithKillswitchEnabled() mocked growthbook.js with only
getFeatureValue_CACHED_MAY_BE_STALE: () => killswitchEnabled. When
killswitchEnabled was false, this poisoned isAgentSwarmsEnabled() for all
downstream tests because agentSwarmsEnabled.ts has a static import of
getFeatureValue_CACHED_MAY_BE_STALE that captured the mock binding.
Fix: Import the real growthbook module and spread it into the mock, so
all exports remain available even if the mock leaks.
### src/utils/plugins/officialMarketplaceStartupCheck.test.ts (same pattern)
Same growthbook mock leak pattern. Top-level mock.module with only
getFeatureValue_CACHED_MAY_BE_STALE: () => true.
Fix: Import real growthbook module and spread into mock.
### src/tools/AgentTool/AgentTool.teammateModel.test.ts (transitive mock binding)
4 tests failed with 'Agent Teams is not yet available on your plan' because
isAgentSwarmsEnabled() returned false. The function checks
getFeatureValue_CACHED_MAY_BE_STALE('tengu_amber_flint', true) from
growthbook.js, but the static import binding in agentSwarmsEnabled.ts was
captured from a leaked mock that returned false.
Cache-busting the AgentTool.js import doesn't help because
agentSwarmsEnabled.ts is a transitive dependency that keeps its
already-loaded (mocked) growthbook binding.
Fix: Add mock.module for agentSwarmsEnabled.js in importAgentToolWithSpawnMock()
to pin isAgentSwarmsEnabled to true, matching the test's intent (it sets
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1).
## Verification
- bun run smoke: passes
- bun test --max-concurrency=1: 3019 pass, 0 fail (verified twice)
- No skipped tests (test.skip/it.skip/describe.skip), no test.todo,
no flaky markers, no test exclusions in config
## Known remaining risks
6 test files still have partial mock.module() calls on providers.js
(withRetry, officialRegistry, domainCheck, conversationRecovery, fastMode)
that don't spread the real module. These don't cause failures under current
test ordering but are latent risks if bun changes file execution order.
* Fix remaining provider mock leak risks
Address the known remaining risks from 7583157 by making provider mocks in withRetry, officialRegistry, and fastMode tests spread and restore the real providers module surface.
Verified with the targeted provider-mock test group and bun run check.
* Harden smoke test coverage
Remove the CI-only skip and unrelated error swallowing from the SDK query lifecycle tests so fork/resume behavior is asserted in CI and local runs.
Isolate test-suite global state by disabling built-in SDK agents for the lifecycle test, restoring MACRO presence exactly, clearing the agent cache, restoring axios mocks, and protecting xAI loopback tests from proxy/fetch leakage.
Make the provider API test script run serially to match the shared env/proxy mutation surface.
Validation: bun run check; CI=1 bun test tests\\sdk\\query-lifecycle.test.ts --max-concurrency=1; bun run test:provider; npm run test:provider-recommendation; bun run security:pr-scan -- --base upstream/main; bun run web:typecheck; bun run web:build; python -m pytest -q -p no:cacheprovider python/tests.
* Expose hidden SDK test failures
Tighten SDK test drains so they only suppress expected lifecycle abort errors instead of swallowing arbitrary init and bootstrap failures.
Replace no-op test assertions with real checks and add V2 lifecycle isolation for MACRO, built-in agents, and agent cache state.
Fix SDK V2 sendMessage to fast-exit when the caller-provided AbortController is already aborted, preventing aborted sessions from submitting work and producing result messages.
Validation: bun test scripts\\feature-flags-source-guard.test.ts tests\\sdk\\query-concurrency.test.ts tests\\sdk\\sdk-v2-lifecycle.test.ts --max-concurrency=1; bun test tests\\sdk\\query-concurrency.test.ts tests\\sdk\\query-lifecycle.test.ts tests\\sdk\\sdk-v2-lifecycle.test.ts --max-concurrency=1; bun run check.
* Fix CI smoke test failures
Respect SDK context null session project directories so regenerated SDK sessions do not fall back to global project state.
Isolate attribution tests from CI provider/model environment and replace nondeterministic live query permission checks with direct assertions against the SDK permission machinery.
Validation: bun test src\\utils\\attribution.test.ts tests\\sdk\\query-lifecycle.test.ts tests\\sdk\\permissions.test.ts --max-concurrency=1; bun test tests\\sdk\\sdk-context-isolation.test.ts tests\\sdk\\query-concurrency.test.ts --max-concurrency=1; bun run check.
* Stabilize attribution contract test
Assert that includeCoAuthoredBy emits the default co-author trailer without pinning the active provider's model label, which can legitimately differ in CI provider environments.
Validation: bun test src\\utils\\attribution.test.ts --max-concurrency=1; ANTHROPIC_MODEL=claude-sonnet-4-5-20250929 CLAUDE_CODE_USE_BEDROCK=1 bun test src\\utils\\attribution.test.ts --max-concurrency=1; bun run check.
Ensures package.json scripts (dev, start), scripts/provider-launch.ts,
and Dockerfile route node executions through the bin/openclaude launcher
rather than calling node directly on dist/cli.mjs.
This resolves PR feedback:
1. Preserves the robust launcher relaunch guard, GC exposure, and test
coverage already merged on main (from #1242).
2. Prevents hardcoded heap caps (--max-old-space-size=8192) from overriding
user-provided NODE_OPTIONS or OPENCLAUDE_NODE_MAX_OLD_SPACE_SIZE_MB
settings during development, start, or containerized runs.
Co-authored-by: daltoncoder <daltoncoder@example.com>