mirror of
https://github.com/Gitlawb/openclaude.git
synced 2026-11-06 20:58:14 -04:00
v0.31.0
41
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c461a0363d |
feat: merge knowledge graph + conversation arc into memdir (#1811)
* 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>
|
||
|
|
fb40d49e68 |
feat: add repo map codebase intelligence (#1867)
* feat: add Codebase Intelligence — repo map with PageRank-ranked structural summaries
Adds a new module that builds a structural map of the repository by parsing
source files with tree-sitter, building a cross-file reference graph weighted
by IDF, ranking files with PageRank, and rendering a token-budgeted summary
of the most important files and their signatures.
Surface:
- RepoMap tool the model can call on-demand, with focus_files / focus_symbols
- /repomap slash command with --tokens, --focus, --stats, --invalidate
- Auto-injection into session system context, gated by REPO_MAP=1 env var
(compile-time feature('REPO_MAP') flag stays off in scripts/build.ts)
How it works:
git ls-files → tree-sitter WASM parse → extract defs/refs →
IDF-weighted directed graph → PageRank → render top files until token budget
Files imported by many others rank highest. Common symbol names (get, set,
map, value) are down-weighted via IDF. Results cached to disk keyed by
(path, mtime, size) — only changed files are re-parsed.
Supported languages: TypeScript, JavaScript, Python.
Tree-sitter tag queries are inlined as string constants in queries.ts so
they ship inside dist/cli.mjs and work after npm install — the .scm source
files are kept for readability/Aider attribution but are not required at
runtime. A drift-guard test (queries.test.ts) asserts byte-equality between
the inlined strings and the .scm source files.
Dependencies added: web-tree-sitter, tree-sitter-wasms, graphology,
graphology-pagerank, graphology-operators, js-tiktoken.
* fix(repomap): invalidate rendered cache on file edits + Windows test fix
- computeMapHash now folds per-file mtime+size into the cache key so a
source edit (without changing the file list) no longer returns the
prior rendered map. Adds a regression test that edits a file and
confirms the second build reflects the new symbol without manual
invalidateCache().
- queries.test.ts byte-for-byte drift guard normalizes CRLF -> LF when
reading the .scm source so Windows checkouts pass. .gitattributes
also pins *.scm to LF on future checkouts.
- Externals: declare web-tree-sitter, tree-sitter-wasms, graphology*,
and js-tiktoken in scripts/externals.ts so build validation passes.
* fix(repomap): expand directory focus paths
* fix(repomap): satisfy deadcode check
* Fix repo map review findings
* Resolve remaining repo map review findings
* fix(repomap): address review findings
* fix(repomap): address review findings
* fix(repomap): resolve smoke and review follow-ups
* fix(repomap): preserve cached tag order
* fix(repomap): resolve review follow-ups
* fix(repomap): satisfy query promise lint
* Fix repo map context timeout cleanup
* fix: address repo map review findings
* fix: cancel timed-out repo map context builds
* fix(repomap): preserve git file path whitespace
* fix(repomap): handle graph and parsing edge cases
* fix(repomap): preserve shell token positions
* fix(repomap): respect configured cache home
* fix(repomap): address review findings
- Add explicit 10000ms timeout to the feature-flag-off context test to avoid cold-import flakes.
- Add --focus-symbols flag to /repomap and forward it to buildRepoMap, matching the RepoMap tool.
- Add parsing/command tests and docs coverage for --focus-symbols.
---------
Co-authored-by: gnanam1990 <gnanasekaran.sekareee@gmail.com>
|
||
|
|
203f05538e |
fix(build): shim jsxDEV when bundling production React — TUI rendered nothing (#1863)
Since
|
||
|
|
354feb483c |
fix(memory): prevent reported idle retention paths (#1856)
* fix(build): bundle production React in CLI * fix(memory): bound reported idle retention paths * fix(memory): address review feedback * fix(memory): keep fps average stable after sample cap * test(memory): cover heap dump filenames |
||
|
|
a723540163 |
perf(build): minify the CLI bundle (whitespace + syntax, keep identifiers) (#1743)
dist/cli.mjs shipped unminified at 21.7MB; whitespace+syntax minification cuts it to ~16MB (-26%) and shaves V8 parse time on every invocation. Identifier mangling stays off because the codebase matches constructor.name (errors.ts, toolExecution.ts, useCanUseTool). The SDK bundle stays unminified — its React/Ink leak check greps import syntax that minification would rewrite. The bundle guard's missing-module tripwire relied on Bun's `// missing-module-stub:<path>` module-boundary comments, which minification strips. The stub loader now also emits the marker as a side-effecting string push (survives treeshaking and syntax-minify), and the guard parses both forms. Review fix (CodeRabbit + jatmn): the marker parser previously truncated paths at the first backslash or space, so a JSON-escaped Windows marker like "missing-module-stub:C:\\Users\\Jane Doe\\...\\src\\...\\foo.js" was captured as a useless `C:` (or `C:\\Users\\Jane`) fragment and canonicalized to the wrong key — letting a newly stubbed module slip past the tripwire on Windows/spaced build hosts. Parse each marker form to its correct terminator instead: the string literal runs to its matching (back-ref) closing quote consuming escaped pairs, and Bun's comment runs to end of line. Extract canonicalStub() + the parser into scripts/stubMarkerGuard.ts so the logic is unit-testable, and add regression tests for Windows, spaced, comment-form, and multi-marker-per-line cases. Verified: build green, bundle ~16MB minified, guard passes against the real bundle, stub-guard tests pass, --version works through the minified bundle. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
a1b3346f65 |
feat(cli): add local background sessions (#1642)
* feat(cli): add local background sessions Add local detached background sessions backed by an OpenClaude-owned registry under the resolved config directory. - implement --bg spawning plus ps, logs, logs -f, kill, and an explicit attach limitation - harden registry metadata validation, atomic writes, ID/name collision handling, and terminal-name reuse - precreate child log files with precise ownership cleanup and register metadata only after spawn succeeds - verify live PIDs against the session command before treating registry entries as running - wait for process-tree termination and escalate to SIGKILL before marking sessions killed - skip live local background sessions during --continue transcript selection - preserve Node heap flags for detached children while avoiding stale launcher relaunch state - handle -- separators so dash-prefixed prompts remain positional - document storage, safety model, name reuse, and the current attach limitation Validation: - bun test - bun run typecheck - bun run smoke - isolated built-CLI --bg/ps/logs/kill smoke - CodeRabbit review findings addressed * test(utils): prevent bg registry mock leakage Restore complete bg registry and UDS module mocks after conversation recovery tests so Bun's process-global mock.module registry cannot leak partial module exports into later CLI tests. CI exposed this under Bun 1.3.13 when conversationRecovery.test ran before the bgRegistry and bg CLI test files. * test(utils): exercise bg registry without global mock Replace the conversation recovery bgRegistry module mock with real registry metadata backed by a short-lived live child process. This keeps UDS as the only mocked boundary and avoids leaking a mocked registry module into later CLI registry tests under Bun 1.3.13. * test(utils): isolate background registry state Stop the conversation recovery test from using process-wide bgRegistry mocks or real child processes by injecting the live-session dependencies directly. Pin and serialize the bg registry test config directory through the shared env mutation lock so path/cache state cannot leak from neighboring tests under Bun CI ordering. * test(utils): document Bun mock restoration Explain why conversation recovery tests re-register full module exports after mock.restore(), matching the CodeRabbit-requested Bun 1.3.13 isolation workaround. * test(cli): isolate background registry root Avoid relying on process-wide CLAUDE_CONFIG_DIR state in bgRegistry tests. Use a registry-local test root override so CI file ordering and mocked path modules cannot redirect background session metadata into another test's temp directory. * test(utils): cover live session fallback paths Add focused coverage for collectLiveBackgroundSessionIds when UDS discovery fails but registry data remains available, and when registry refresh fails but UDS data remains available. * fix(cli): harden background session management Validate persisted and newly-created background session PIDs before exposing them to management commands. Reserve named live sessions with an atomic registry write, release reservations when sessions become terminal, and cover concurrent duplicate-name attempts. Split local session management dispatch from background spawning so ps/logs/attach/kill avoid provider startup while --bg still inherits profile routing. * fix(cli): address background session review findings Preserve positional prompts when --bg is combined with optional-value flags such as --debug. Recover stale name reservations whose owner metadata is missing or terminal while preserving in-flight reservations from live creators. Cover both reviewer findings with focused parser and registry regression tests. * fix(cli): respect delimiter for background flags Limit background and print-mode flag detection to arguments before the -- delimiter so flag-shaped prompts remain positional. Keep optional resume/from-pr flags out of the required-value table and add regressions for delimiter and optional-flag prompt handling. * refactor(cli): share delimiter argument helper Move args-before-delimiter handling into the existing dependency-free CLI args utility. Use a dynamic import from the entrypoint so background flag routing shares the helper without adding top-level module load to version and management fast paths. * test(cli): cover background entrypoint routing Export the CLI entrypoint for controlled tests and add isolated importer injection so runtime routing tests do not leak global module mocks. Replace the delimiter source-layout assertion with execution-level coverage for management commands, real background flags, and flag-shaped prompt text after --. * fix(cli): preserve background resume selectors Keep space-separated --resume, -r, and --from-pr values attached when building background child args. Mark live background sessions stale when PID command identity cannot be read, avoiding termination of reused unrelated PIDs. * fix(cli): track unknown background session identity Represent unreadable live PID identity as a non-terminal unknown state so active sessions stay excluded from resume selection. Refuse to terminate unknown live PIDs because the process command cannot be positively matched to the background session. * fix(cli): honor background resume selectors Avoid adding a generated --session-id to non-forked background resume launches so the spawned print-mode child satisfies the existing resume/session-id contract. Pass --from-pr through headless print mode and resolve PR-linked sessions through the shared conversation recovery path. Add regression coverage for background resume launch args and PR selector matching. * fix(cli): treat PR resume as headless resume source Include --from-pr in print-mode resume guards so PR-linked headless resumes can run without a prompt and share resume-only options. Skip eager startup hooks for headless PR resumes and add explicit --session-id launch coverage. * fix(cli): keep background PR resumes live Resolve non-forked --from-pr background launches to the selected transcript id before writing registry metadata. Preserve PID identity refresh for PR-resume children by matching the stored invocation when argv does not carry the transcript id. Add regressions for launch registration and registry refresh. * test(cli): cover PR resume lookup failures Add regression coverage for non-forked background --from-pr launches when the selector cannot be resolved. Verify the launch planner returns the same clear error used by handleBgFlag(). |
||
|
|
d5588ea80d |
feat(context-collapse): opt-in between-turns context collapse (span summarization) (#1619)
* feat(context-collapse): implement context collapse for proactive context management
* feat(context-collapse): add turn-boundary helpers for span selection
* feat(context-collapse): deterministic turn-anchored span selection
* feat(context-collapse): code-computed span risk score
* feat(context-collapse): ctx-agent summarization instruction
* feat(context-collapse): implement ctx-agent span summarization spawn
* fix(context-collapse): make runtime activation opt-in (CLAUDE_CONTEXT_COLLAPSE)
* fix(context-collapse): address review feedback on restore state and test rigor
- restoreContextCollapseState now resets armed/lastSpawnTokens up front so a
snapshot-less restore cannot carry stale spawn state across sessions.
- projectView reuses a stable timestamp from the replaced span instead of
new Date(), keeping the read-side projection deterministic.
- Strengthen the disabled-state and turn-boundary assertions, drop an internal
renderToolUseMessage assertion, and isolate the operations/persist/spawn tests
from shared module and CLAUDE_CONTEXT_COLLAPSE env state.
* test(context-collapse): re-init enablement in persist.test hooks
resetContextCollapse() does not re-read CLAUDE_CONTEXT_COLLAPSE, so the
afterEach env delete left enabled=true in module state, leaking to the
next test file. Call initContextCollapse() in both hooks so module
enablement stays synced to the env var.
* test(context-collapse): stop spawnCtxAgent module stubs leaking across files
spawnCtxAgent.test.ts stubs shared modules (tokens, forkedAgent, messages,
analytics, log, spanSelection) via mock.module in beforeEach. bun's
mock.restore() does not undo mock.module, so the tokens stub (() => 100000)
bled into autoCompact/microCompact/runAgent tests run later in the full serial
suite, making them see every conversation as over-threshold (4 spurious
failures in test:full, all green in isolation).
Restore each stub to its real implementation in afterEach. The reals are
snapshotted into plain objects up front because 'import * as' yields a live
namespace that mock.module mutates in place, so holding the namespace would
restore the stub. autoCompact.js is deliberately not restored here since
autoCompact.test.ts re-imports it fresh via a cache-busting nonce.
Also reset+reinit the collapse module in afterEach so enabled state stays
synced to the now-unset env var.
* test(context-collapse): also restore autoCompact stub from spawnCtxAgent
The getEffectiveContextWindowSize stub on ../compact/autoCompact.js was the one
module the previous commit left unrestored, on the assumption that restoring it
would clash with autoCompact.test.ts's nonce re-import. It doesn't: the nonce
import uses a different specifier, and the snapshot restore is keyed by the
plain specifier. compressToolHistory imports getEffectiveContextWindowSize and
sizes tool-history truncation from it, so the leaked 20000-token window made it
fully omit tool results ('chars omitted') instead of mid-truncating
('[…truncated') for large-context models, failing the openaiShim compression
tests in the full serial suite. Restore all seven mocked modules.
* fix(context-collapse): re-arm after reset and gate ctx_inspect on opt-in
resetContextCollapse() left armed=false while enabled stayed true, so the
first /compact, main-thread compaction cleanup, or rewind permanently
disabled collapse for the rest of an opted-in session. Reset now mirrors
restoreContextCollapseState and sets armed=enabled.
CtxInspectTool.isEnabled() returned true unconditionally, advertising
ctx_inspect to the model in every default session even when the runtime
opt-in was off. It now returns isContextCollapseEnabled(). The opt-in is
also exposed as the contextCollapseEnabled global config key, so it is
reachable through /config instead of only the CLAUDE_CONTEXT_COLLAPSE env
var.
* refactor(context-collapse): drop no-op ternary in drainStaged persist call
The (stagedQueue.length > 0 ? 0 : 0) subtrahend always evaluated to 0, so
this is just persistCommits(processed.length).
* fix(context-collapse): persist commits before advancing the snapshot
drainStaged removed processed spans from the staged queue and then fired
persistCommits and persistSnapshot in parallel. If the snapshot write (which
no longer lists those spans as staged) landed while the commit write failed
or the process died between them, restore would find the spans neither staged
nor committed and the collapse would disappear on resume. Chain the snapshot
write after the commit write so the commit log is durable first.
* fix(context-collapse): project committed collapses on the query path, fix opt-in reach
Three issues from review:
- Committed collapses were never re-applied to the model input. The query path
calls applyCollapsesIfNeeded but only drained staged spans; projectView (which
replays the commit log) ran only in /context. Since messagesForQuery is rebuilt
from full REPL history each turn and the commit log is repopulated on resume,
the archived spans returned to the model on the next turn, undoing the collapse.
applyCollapsesIfNeeded now runs projectView first (idempotent). Adds a
regression that a committed collapse changes the next query input.
- Cache-safe params were saved only for exact repl_main_thread/sdk sources, but
the REPL tags non-default output styles as repl_main_thread:outputStyle:*, so
those sessions left the ctx-agent without params (empty spawns). Matches
repl_main_thread:* now, via a small tested helper.
- contextCollapseEnabled had no settings control. Adds a /config toggle that
refreshes runtime state (re-runs initContextCollapse) so it applies without a
restart.
* fix(context-collapse): clear already-committed staged spans; harden config toggle
After projecting committed collapses before draining, a span present in both the
commit log and the staged snapshot (a restore whose snapshot predates the
matching commit write) could not be drained — projectView had already removed
its messages — so it lingered in stagedQueue and distorted spawn/overflow
checks. drainStaged now drops staged spans that are already committed and syncs
the snapshot. Adds a regression covering the committed+staged overlap restore.
Also wraps the /config context-collapse refresh in try/catch so a failed
require/init can't crash the settings UI, and lists the toggle in the
save-and-close change summary like the neighboring compaction settings.
* fix(context-collapse): re-sync runtime state on config cancel
The context-collapse toggle's onChange refreshes the module-level
enabled/armed cache via initContextCollapse(). The revert path restored
the config key on disk but left that cache untouched, so enabling the
toggle and then pressing Escape kept collapse active for the rest of the
session. Re-init context collapse after the global config snapshot is
restored so cancel fully reverts runtime state.
* fix(context-collapse): keep collapsed summaries visible to the model
projectView and drainStaged replaced an archived span with a system
informational placeholder, but normalizeMessagesForAPI filters out every
system message that is not a local command. So once a collapse committed,
the next model request lost both the archived messages and the
<collapsed> summary meant to stand in for them, defeating the feature.
Mark the placeholder with isCollapseSummary and let it take the same
model-input path as local-command system messages (converted to a user
message), so the summary survives normalization. Added a regression that
runs the projected view through normalizeMessagesForAPI and asserts the
summary is still present.
* fix(context-collapse): avoid competing snapshot write after drain
After an immediate post-spawn drain, drainStaged(messages, true) starts
its own persistCommits().then(persistSnapshot) chain to guarantee commit
durability before the snapshot stops listing the staged spans. The
unconditional await persistSnapshot() that followed could win that race
and persist a snapshot with no staged spans before the commits landed,
reopening the crash window that drops collapses on restore. Only persist
directly when nothing was drained.
* fix(context-collapse): fall back, keep summaries non-snippable, gate /context
Three review findings:
- Suppress autocompact and the blocking preempt only when collapse holds a
real committed/staged reduction, not on mere enablement. Adds
hasActiveReduction(); a first over-threshold turn where spawnCtxAgent cannot
produce a span (getLastCacheSafeParams() still null) now falls back to
autocompact/blocking instead of sending an oversized transcript.
- Preserve isMeta when converting a collapse-summary placeholder to a user
message in normalizeMessagesForAPI, so the HISTORY_SNIP sweep cannot tag the
only replacement for an archived span as snippable.
- Gate the two /context projectView calls on isContextCollapseEnabled(), so a
disabled session does not under-report token usage from a lingering commit
log while the API receives the full transcript.
Adds regressions for hasActiveReduction and for the summary surviving
normalization as a non-snippable meta message.
* fix(context-collapse): scope collapse to the main thread that owns the store
The collapse store (commitLog/stagedQueue) is module-level and shared by
in-process subagents (agent:*) and the ctx-agent (marble_origami), which
run in the same process but do not own the main transcript.
applyCollapsesIfNeeded only skipped marble_origami, so a subagent could
stage or commit a span, flip the global hasActiveReduction(), and make
the next main-thread turn suppress autocompact and the blocking
prompt-too-long preempt while projectView() no-ops against the main
messages, sending an oversized transcript to the API.
Add isMainThreadSource() and gate both application (applyCollapsesIfNeeded,
isWithheldPromptTooLong, recoverFromOverflow) and fallback suppression
(autoCompact shouldAutoCompact, query collapseOwnsIt) to the owning
thread. Subagents now autocompact and preempt their own oversized turns
normally and never mutate the shared store.
Also adds the staged-only hasActiveReduction regression CodeRabbit
requested.
* fix(context-collapse): persist archived count so resumed stats stay accurate
restoreContextCollapseState rebuilt each commit with an empty archived
list, and getStats summed that list, so after a resume /context, the
context visualization, the token warning, and ctx_inspect reported
'N spans summarized (0 messages)' even though projectView was actively
removing the archived spans. The persisted-entry docstring claimed
projectView lazily refills the archive, but it only splices by boundary
uuid and never does.
The archived messages are never read back (only their count fed
getStats), so replace the per-commit Message[] with a persisted
archivedCount. It is written with each commit and restored on resume;
pre-field sessions restore as 0. getStats now reports the same figure
live and after resume.
* fix(context-collapse): keep collapse summary non-snippable across user merge
Preserving isMeta on the system->user conversion was not enough: when the
collapsed span ends right before the next user turn, normalizeMessagesForAPI
merges the summary into that real user message. Under HISTORY_SNIP
mergeUserMessages clears isMeta whenever an operand is real user content and
keeps the real turn's uuid, so the combined block — which carries the only
<collapsed> replacement for the archived span — got a snip id and the model
could queue it for removal.
Carry an isCollapseSummary marker onto the converted user message and through
mergeUserMessages (either operand), strip any snip id already baked into the
real turn when the merge absorbs a summary, and skip such blocks in
appendMessageTagToUserMessage. The merged block stays non-snippable
regardless of merge direction or isMeta being cleared.
* fix(context-collapse): preserve collapse marker on split, drop empty snip blocks
normalizeMessages split path now forwards isCollapseSummary so an array-backed
collapse summary keeps its non-snippable marker across API normalization.
stripSnipTagsFromContent drops a text block whose only content was the snip
marker, so the merge recovery path no longer emits an empty text block.
|
||
|
|
bd3ad89dd7 |
fix(security): bundle real sandbox runtime in open CLI (#1641)
* fix(security): bundle real sandbox runtime in open CLI * test(sandbox): cover fail-closed runtime diagnostics * fix(sandbox): report doctor inspection failures |
||
|
|
716c1d47f6 |
feat(compact): auto-compact prompt on /resume + determinate progress bar (#1386)
* feat(compact): auto-compact prompt on /resume + determinate progress bar On /resume, if the conversation exceeds 70% of the auto-compact threshold, a dialog appears offering to compact before continuing. Shows token count, context window usage, and effective window percentage. Also adds a determinate progress bar during compaction that advances as the summary streams in. - Add RESUME_COMPACT_PROMPT feature flag (enabled) - Add shouldPromptCompactOnResume() threshold gate - Add ResumeCompactPrompt dialog component - Emit compact_progress events during streaming in compact service - Render ProgressBar next to spinner during compaction in REPL - Add 5 tests for threshold gating logic * feat(compact): determinate progress bar via streamed text deltas Wire compact_progress events from streamed summary output (forkedAgent onStreamEvent forwards text deltas) and render a determinate ProgressBar in the REPL, replacing the indeterminate spinner during compaction. Extracts the bar into a CompactProgressBar component shared by manual /compact and the resume-triggered path. Emits coarse hooks/start ticks on the session-memory path so the bar moves immediately. * fix(compact): keep spinner visible and use bracketed progress bar Render the "Compacting conversation" spinner throughout compaction with the progress bar beneath it, instead of swapping the spinner out for the bar. Redraw the bar as a bracketed fill ([███····]) with no background rectangle, so it no longer reads as one solid block. * docs: remove resume-compact-prompt plan file Per PR review feedback — drop the planning doc from the PR. * fix(compact): prompt to compact on CLI --continue/--resume startup Startup resume paths (--continue, --resume <id>, ResumeConversation screen) install initialMessages via the initial useState rather than the resume() callback, so the threshold check never ran. Schedule the prompt from the mount-time initialMessages effect as well. * fix(compact): use MessageType alias for resumeCompactPending state main aliases the message type import as `Message as MessageType`; the rebased resumeCompactPending state still referenced the bare `Message`. * fix(compact): always close session-memory progress and fix progress-ratio units - Wrap the session-memory compaction attempt in try/finally so compact_end is always emitted, even when it returns null or throws, preventing a stuck progress bar/spinner. - Clear compactProgressRatio in resetLoadingState so an aborted or errored compaction does not leave the progress bar rendered in the idle UI. - Fix the progress denominator unit mismatch: estimatedOutputChars now uses a token-to-char converted estimate (preCompactTokenCount) instead of the token-scale preCompactTokenCount * 0.25, so progress no longer advances too fast and hits the cap prematurely. |
||
|
|
f4c3be850e |
chore: remove dead code and add knip gate to CI check (#1612)
Delete 32 unreferenced source files (~4,000 lines) verified dead by import-specifier grep and knip: test-only token utilities, orphaned hooks (useTaskListWatcher, useSkillImprovementSurvey + its component), the removed DevBar and ConfigTool UIs, unregistered bundled skills (stuck, verifyContent), unused analytics sinks, the benchmark command, and stale migrations/helpers. Remove unused dependencies code-excerpt, stack-utils, and tsx from package.json plus their entries in build stub/external lists. Add knip with a tuned knip.json (entrypoints, build-time stub targets, subprocess-launched fixtures, and runtime-string-imported SDKs ignored; providerAutoDetect kept intentionally as provider pre-wiring) and wire `bun run deadcode` into the `check` script so dead code stays dead. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
9755550137 |
Typecheck/zero tsc errors (#1597)
* ci(typecheck): add error-count ratchet toward zero tsc errors tsc --noEmit currently reports 697 pre-existing errors (issue #473), so PRs cannot be gated on a clean typecheck yet. This adds scripts/typecheck-ratchet.ts and a per-file baseline: CI fails when the count rises above the baseline (listing exactly which files regressed), passes at or below it, and --update lowers the baseline to lock in gains. Wired into pr-checks as its own step; once the baseline reaches zero the step becomes a plain `bun run typecheck`. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(typecheck): mechanical sweep — 697 → 624 tsc errors Type-only fixes with no runtime behavior change, except the deliberate NODE_ENV restorations: - Restore process.env.NODE_ENV comparisons that the source snapshot had baked into the literal "production", making the conditions constant (AutoUpdater dev/test skip, useTypeahead, ink devtools injection, interactiveHelpers onboarding skip, TestingPermissionTool.isEnabled — the last now correctly enables under bun test, +3 tests run green) - Type stream read helpers in openaiShim/codexShim as Bun.ReadableStreamDefaultReadResult<Uint8Array<ArrayBuffer>> and annotate throwClassifiedTransportError as never-returning, clearing the reader/response undefined cascades (29 errors) - Delete 14 stale @ts-expect-error directives - Widen useState/useRef/array generics inferred from null/[] literals - as-const notification priority/color literals to match Priority - Accept readonly Tool[] in checkLocalModelContextLoad/getCombinedTools Baseline lowered via typecheck:ratchet --update; full suite green (3690 tests), smoke + bundle guard green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(typecheck): recreate missing modules — 624 → 415 tsc errors The open snapshot never mirrored ~60 modules; the bundler noop-stubs them at build time (() => null named exports), so every recreated module here is runtime-inert by construction: no import-time side effects, gated features stay off (isAssistantMode/isSkillSearchEnabled → false, tools isEnabled → false, dialogs render null), lookups return empty, telemetry no-ops. Types are honest and derived from importer usage — no any. Highlights: - sdk: runtimeTypes re-exports/aliases, sdkUtilityTypes (NonNullableUsage), settingsTypes.generated; coreTypes.generated usage fields regenerated as a self-contained structural type (the consumer package ships without sdkUtilityTypes/@anthropic-ai/sdk, so the generated file must stay dependency-free — generator override updated to match, package-consumer-types tests green) - services: contextCollapse operations/persist/stats, compact cachedMicrocompact state/types + reactiveCompact, skillSearch (7 modules), oauth/types, lsp/types, sessionTranscript - cli/server/daemon: Transport interface, parseConnectUrl, server/* (7), daemon/*, bg/templateJobs/runners; assistant/* (KAIROS), ssh/* - tools/components: WorkflowTool trio, ReviewArtifact pair, OverflowTest/TerminalCapture/VerifyPlanExecution/DiscoverSkills, WebBrowserPanel, task dialogs, message variants, ink events/cursor - types: statusLine, fileSuggestion, notebook, messageQueueTypes; SerializedMessage rebuilt as distributed Omit-union so transcript guards narrow again; vitest-compat.d.ts mirrors Bun's runtime 'vitest' → 'bun:test' aliasing - TS2304 names: ant-model helpers imported from existing antModels.ts, inert Ultraplan/Gates/LogoV2 stubs, PromiseWithResolvers local type - build.ts: ACCEPTABLE_RUNTIME_STUBS emptied — both grandfathered bundle-reaching stubs (MonitorMcpDetailDialog, VerifyPlanExecutionTool/constants) are now real typed modules, so the degrade-on-use debt the guard tracked is retired Validation: full suite 3690 green, smoke + bundle guard green, typecheck:type-tests green, sdk package-consumer tests green; baseline lowered via typecheck:ratchet --update. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(typecheck): reconstruct Message discriminated union — 415 → 342 tsc errors src/types/message.ts was a stub where all ~40 message type aliases were 'export type X = any'. Bare-any aliases break the one thing the union is for: narrowing. Type predicates like isHookAttachmentMessage collapsed to 'never' in guard chains, cascading TS2339/TS2345 through utils/messages.ts, messageFilters.ts, groupToolUses.ts, collapseReadSearch.ts, REPL.tsx, compact.ts, stopHooks.ts and the message components. Envelope design (permissive-body discriminated union): - Each variant declares its literal discriminant(s) — message.type for the envelope union (user/assistant/attachment/progress/system), subtype for the 17-variant System family — plus the properties constructor functions in utils/messages.ts actually populate, with '[key: string]: any' as an escape hatch so unreconstructed properties never error. - UserMessage<C> / AssistantMessage<T> are generic over content shape so NormalizedUserMessage / NormalizedAssistantMessage<T> reuse the envelope without Omit (Omit over an index-signature type collapses keyof to string and silently drops the discriminant, breaking narrowing). - AssistantMessage.message is a structural AssistantMessageContent<T>, not the SDK's BetaMessage: synthetic constructors don't populate every SDK-required field (stop_details), and SDK-facing consumers need assignability to Record<string, unknown>-style bodies. - AttachmentMessage<T = Attachment> / ProgressMessage<T = Progress> stay generic over their payloads (utils/attachments.ts and Tool.ts types). - UI wrappers (GroupedToolUseMessage, CollapsedReadSearchGroup, CollapsibleMessage, RenderableMessage) and stream/control envelopes (StreamEvent over BetaRawMessageStreamEvent, RequestStartEvent, TombstoneMessage, ToolUseSummaryMessage) reconstructed from call sites. - logs.ts SerializedMessage switched from the Omit<Message, never> trick (only sound against an any stub) to an Extract-based distributed union, keeping TranscriptMessage assignable to Message. All other touched files are type-level-only adjustments (annotations on evolving arrays that inferred never[], predicate types, casts in SDK wire adapters and test fixtures) — no runtime logic changed anywhere; the full bun test suite passes 3690/0 before and after. Result: 415 → 342 tsc errors, every never-cascade in the message pipeline resolved, no file above its per-file baseline (ratchet updated). Part of issue #473. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(typecheck): narrow unknowns and fix signature drift — 342 → 94 tsc errors Clears every remaining non-test error. Honest fixes dominate: evolving array/let/useState/useRef annotations (the repo's noImplicitAny:false disables evolving types), real type guards over unknown wire payloads, hoisted react-compiler-style params annotated with their components' real Props, and callee signature corrections (useRegisterOverlay optional param, generic useVoiceState<T>, growthbook shim's accepted refresh-interval param) that each cleared several call sites. Targeted reason-commented casts only at SDK/stub/wire boundaries; no any, no new suppressions. Runtime deviations are confined to already-broken paths: benchmark.ts imported a function name that never existed (module-load crash), caches.ts called stub methods unguarded (TypeError for ant-gated users), messageActions returned undefined from a string function; CACHE_EDITING_BETA_HEADER is a best-effort reconstruction of a squash-lost constant, reachable only behind feature-gated first-party paths (flagged for review). Also: ConnectorTextBlock gains its wire-proven optional signature field; MCP server factory ambient types gain close(); ink render-node-to-output's nodeType cast fixed (intersection was collapsing the intended widening); upstreamproxy relay normalizes the socket data union. Validation: full suite 3690 green, smoke + bundle guard green; remaining 94 errors are all in test files (PR 5). Baseline lowered via typecheck:ratchet --update. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(typecheck): clean test typing, gate CI on zero tsc errors — 94 → 0 Closes the typecheck burn-down (issue #473): bun run typecheck now exits 0 across the whole repo and CI fails on any new error. Test typing: new src/test/typedMocks.ts centralizes the two bun:test gaps (asMockFetch — Mock<T> lacks fetch.preconnect; callArgs — argless-signature mocks collapse mock.calls to []). Beyond the helpers, fixes are honest: discriminated-union narrowing before member access, fixture typing with boundary casts, assertion-type corrections, and two tests realigned to production signatures they had drifted from (requestLogging logApiCallEnd args, incrementalTokenCounter tokenBudget rename) with identical assert outcomes. No assertion semantics changed; all touched suites pass. CI: the ratchet served its purpose and is retired — pr-checks now runs a plain `bun run typecheck` step; ratchet script and baseline deleted. Burn-down summary across the series: 697 → 624 (mechanical sweep) → 415 (recreate ~60 missing modules) → 342 (Message discriminated union) → 94 (narrowing + signature drift) → 0 (this PR). Validation: tsc --noEmit exit 0, full suite 3690 green, smoke + bundle guard green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(typecheck): reconcile with upstream parallel typecheck fixes Upstream landed #1591/#1592/#1595 while this series was in flight, fixing some of the same errors differently. Rebase resolutions prefer upstream where it is authoritative: their CACHE_EDITING_BETA_HEADER value ('cache-editing-2025-12-01', unconditional) replaces this series' feature-gated reconstruction; their cachedMicrocompact stub shapes (with their new test file) replace ours, with boundary casts in claude.ts where the stub's unknown[] edits meet the local pinned delete-edit shape; their reader/ReadResult stream typing in openaiShim replaces ours. MessageWithoutProgress now matches its name (Exclude<NormalizedMessage, ProgressMessage>), reconciling upstream's RenderableMessage GroupingResult with this series' message union; the @ts-expect-error upstream added for settingsTypes.generated is removed since the module now exists. tsc exit 0; full suite 3697 green (incl. upstream's new cachedMicrocompact tests); smoke + bundle guard green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(sdk): keep result usage counters required, fix assistant stub exports Addresses jatmn's and chioarub's review on the typecheck PR: 1. SDK usage contract restored: the generated result types' usage now keeps input_tokens, output_tokens, cache_creation_input_tokens, and cache_read_input_tokens as REQUIRED numbers — result messages are populated from QueryEngine.totalUsage (initialized from EMPTY_USAGE), so they are always present at runtime and strict consumers may sum them without undefined guards. The richer nested metadata (cache_creation, server_tool_use, service_tier) is modeled explicitly instead of hiding behind the index signature; the nested objects carry no index signature so the SDK's interface types stay assignable. Generator override updated and artifacts regenerated; a new package-consumer type test sums the counters and reads the nested fields so this contract cannot silently regress. The sessionHistory test fixture now carries all four counters, matching runtime shape. 2. Assistant install wizard stub mismatch fixed: dialogLaunchers imported NewInstallWizard/computeDefaultInstallDir through a module shape cast, but the assistant stub only exported default — a guaranteed runtime crash if the gated path lit up. The stub now provides real typed exports: a wizard that cancels immediately (so the launcher resolves null/user-cancelled instead of hanging on an empty dialog) and an inert computeDefaultInstallDir; the unsafe cast in dialogLaunchers is gone. Validation: tsc exit 0; full suite 3698 green (incl. the new consumer counters test); smoke + bundle guard green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
e6ce1037fe |
refactor(open-build): remove Ant employee gates (#1576)
* refactor(open-build): remove Ant employee gates * fix(open-build): address gate-removal review feedback * fix(open-build): address follow-up review findings * fix(hooks): remove stale remote fallback status * fix(open-build): keep pending background tasks visible * test(open-build): cover task footer hiding |
||
|
|
5c239eb601 |
fix(typecheck): declare bundled markdown and macro fields (#1562)
* fix(typecheck): declare bundled markdown and macro fields * fix(build): define version changelog macro |
||
|
|
7078853ea8 |
fix(typecheck): replace dead-code literal comparisons with isAntEmployee() (#1512)
* fix(typecheck): replace 'external' === 'ant' dead-code literals with isAntEmployee() The build system replaces process.env.USER_TYPE with the string literal 'external' at build time. Dead-code elimination then removes branches where 'external' === 'ant'. But TypeScript sees these as impossible comparisons (TS2367) because the narrowed literal type 'external' never equals 'ant', producing 90 type errors across 27 files. Replace all 'external === 'ant'' with isAntEmployee() and 'external !== 'ant'' with !isAntEmployee(). The function already exists in src/utils/buildConfig.ts and always returns false, so this is a behavioral no-op that makes the intent explicit and type-safe. The process.env.USER_TYPE === 'ant' pattern in other files is not touched; it will be addressed in a follow-up. Refs: #1486 * fix(build): replace isAntEmployee() calls with false at build time for DCE The bundler cannot dead-code-eliminate branches guarded by isAntEmployee() because it's an opaque function call. Extend the feature-flag preprocess plugin to also replace isAntEmployee() with false during bundling, so dynamic import() and require() calls gated behind ant-employee checks are eliminated from the external build. Also export IS_ANT_EMPLOYEE as a named constant for call-site readability and documentation, with the function kept as a convenience wrapper. * fix(build): use IS_ANT_EMPLOYEE constant for ant-only import/require guards CodeRabbit review identified that isAntEmployee() is a runtime function call that bundlers cannot evaluate for DCE. Replace all isAntEmployee() guards on dynamic import()/require() calls of ant-internal modules with the IS_ANT_EMPLOYEE boolean constant (exported as `false as const`), which the build-time source transform can replace with a literal `false` for DCE. Also extend the featureFlagPreprocessPlugin to replace IS_ANT_EMPLOYEE with false during bundling, and clean up the resulting dead imports/exports (`import { false, isAntEmployee }` → `import { isAntEmployee }`, `export const false = false as const` → removed). Affected ant-only modules (all missing from OpenClaude, must be DCE'd): - sessionDataUploader.js, eventLoopStallDetector.js, sdkHeapDumpMonitor.js - ccshareResume.js, cli/up.js, cli/rollback.js, cli/handlers/ant.js - useFrustrationDetection.js, useAntOrgWarningNotification.js - AntModelSwitchCallout.js, UndercoverAutoCallout.js |
||
|
|
cdc8057496 |
feat: enable HISTORY_SNIP — model-callable snip tool for context management (#1407)
* feat(snip): implement HISTORY_SNIP — model-callable snip tool for context management
- snipProjection.ts: boundary detection + view filter (isSnipBoundaryMessage, projectSnippedView)
- snipCompact.ts: pending registry, snipCompactIfNeeded, shouldNudgeForSnips, SNIP_NUDGE_TEXT
- SnipTool/: model-callable snip tool with Zod schema (prompt.ts + SnipTool.ts)
- types/message.ts: add SystemCompactBoundaryMessage export
- scripts/build.ts: enable HISTORY_SNIP: true
- QueryEngine.ts: fix snipReplay return type
* docs: add MCP_SKILLS implementation plan
* docs: add HISTORY_SNIP implementation plan
* fix(snip): prune headless store on snip-boundary replay
The snipReplay path called snipCompactIfNeeded with a {force:true} option
that the function never read, and the pending-snip set was already cleared
when the boundary was produced in query.ts — so the replay always reported
nothing removed and mutableMessages never shrank in long SDK sessions.
Prune the store by the boundary's own removedUuids via projectSnippedView
instead. Also drop two planning docs that were committed to the branch.
* fix(snip): persist snip boundary in SDK/headless transcripts
When a snip boundary was yielded in the SDK/headless path, snipReplay pruned
the in-memory mutableMessages store but the branch broke before adding the
boundary to the local messages array or calling recordTranscript. Later
transcript writes used the pre-snip messages copy, so the on-disk transcript
kept the removed messages and no snipMetadata boundary. After a restart or
--resume, loadTranscriptFile reconstructed the un-snipped history and the
context reduction was lost.
Mirror the boundary into the local messages copy and record it when the snip
executes, matching the compact_boundary path. recordTranscript is append-only
by UUID, so the pre-snip messages already on disk remain and the appended
boundary (carrying snipMetadata.removedUuids) lets applySnipRemovals prune
them on load.
Add a loadTranscriptFile round-trip test covering the previously-untested
snip replay: a persisted boundary prunes its removedUuids and relinks
survivors whose parentUuid pointed into the removed gap.
* fix(history-snip): record paired tool-result removals and scope pending snips per conversation
Two issues in the snip path:
1. Persist every removed message. snipCompactIfNeeded drops the paired
tool-result user messages of a snipped assistant tool-use message from the
live context, but the boundary only recorded the explicitly-marked UUIDs.
projectSnippedView / loadTranscriptFile replay solely from
snipMetadata.removedUuids, so on --resume the tool results came back orphaned
(their assistant message stayed removed) and part of the reduction was lost.
Record the paired tool-result UUIDs in removedUuids so replay drops the same
set the live snip dropped.
2. Scope pending snips per conversation. The pending registry was module-global
and stored model-facing short IDs, then cleared unconditionally on every
snipCompactIfNeeded pass. With concurrent in-process sessions, session B could
clear A's pending IDs (losing A's snip) or, on a short-ID collision, prune the
wrong message. Resolve short IDs to full UUIDs at mark time against the
snipping conversation's own messages, and consume only the UUIDs present in
the current message array. UUIDs are globally unique, so the registry
self-scopes: one session can no longer consume or mis-target another's.
* fix(history-snip): drop paired assistant tool-use when snipping a tool-result message
[id:] tags are appended to user messages only, so the model snips a
tool-result user message, not the assistant tool_use. The previous
pairing only ran assistant->user; snipping a tool-result left the
preceding assistant tool_use orphaned, so the next API-prep pass
synthesized a placeholder result and the tool interaction was never
actually removed from live context or from replay.
Pair in both directions: when a snipped user message's tool_results all
belong to an assistant turn, drop that assistant tool_use too (mirroring
the existing .every() guard so partially-snipped turns are kept), and
record its UUID in the boundary's removedUuids so replay drops the same
set.
* fix(history-snip): add SnipBoundaryMessage render component
HISTORY_SNIP ships enabled, so Message.tsx reaches the snip_boundary
render branch after the first snip. That branch requires
./messages/SnipBoundaryMessage.js and renders its named
SnipBoundaryMessage export, but no source file existed — the build
emitted a missing-module-stub exporting only a default noop, so the
named component was undefined and the render crashed right after a
successful snip.
Add the component, mirroring CompactBoundaryMessage: a single dimmed
line marking the snip with the removed-message count and the transcript
shortcut. The build now resolves the import (no stub) and the named
export is present in the bundle.
* build: guard against enabled-feature imports resolving to missing-module stubs
The missing-import scanner stubs any unresolved relative import to a noop
default export. For a require behind a DISABLED feature flag that is correct
(dead-code-eliminated, never bundled). But when a flag is ENABLED the gated
require becomes live and the stub silently degrades a real module to
() => null, so a named export resolves to undefined and crashes the first
time that path runs. SnipBoundaryMessage shipped exactly this way: build,
smoke, and unit tests all passed while the UI crashed on the first snip.
Feature-flag DCE removes disabled branches before bundling, so every
missing-module-stub marker left in dist/cli.mjs is reachable in the shipped
build. After the CLI bundle, fail the build on any stub marker not explicitly
grandfathered in ACCEPTABLE_RUNTIME_STUBS (seeded with the pre-existing
stubs), and warn on stale allowlist entries. Verified: removing the
SnipBoundaryMessage source makes the guard fail and name the module.
* fix(history-snip): drop unmirrored force-snip command registration
force-snip is gated on HISTORY_SNIP but its source (./commands/force-snip.js)
was never mirrored into this build, so require(...).default resolved to the
missing-module stub's noop. That truthy noop was spread into the command list
(commands.ts:252), registering a bare () => null as a command with no name,
description, or call — broken the moment anything enumerates commands. Enabling
HISTORY_SNIP turned this live, same class as the SnipBoundaryMessage crash.
Remove the registration rather than ship a phantom command: the implementation
is not present in this tree, so the honest behavior is to not register it. Drop
the matching ACCEPTABLE_RUNTIME_STUBS entry so the bundle guard stays strict.
* fix(history-snip): don't snip a tool result that would orphan a surviving tool_use
The result-side pairing dropped an explicitly-snipped tool-result user
message even when its paired assistant turn had other, un-snipped tool
calls. That left the assistant holding a tool_use with no matching result,
which the next API-prep pass repairs with a synthetic placeholder
(src/utils/messages.ts), so the snip never actually took effect and the
restored context still carried the stale interaction.
Block-level surgery on the surviving assistant is not an option: replay
(projectSnippedView / loadTranscriptFile) drops whole UUIDs, not blocks, so
the live store and a --resume would diverge. Instead, treat an unclean snip
as a no-op: a tool_use is safely removable only if its whole assistant turn
goes with it (the assistant is explicitly snipped, or every tool_use in it
has its result snipped). A tool-result user message whose results don't all
pair to a removable tool_use is kept, and no boundary is emitted when nothing
was cleanly removable, keeping live context and replay identical.
* docs(build): document the bundle-stub guard as a coarse tripwire
The guard rationale claimed every missing-module-stub marker left in
dist/cli.mjs is reachable in the shipped build and that each allowlisted
entry is latent runtime debt behind an enabled flag. That overstates it: the
scanner keys missing modules by specifier string, so a same-named specifier
missing in one importer (including a test file) can leave a marker even when
another importer resolves the real module, and a marker can sit on a path
that never runs. Reword the comment and error message so a flagged stub reads
as "inspect this", not "confirmed runtime crash"; the guard reliably catches
a NEW stub appearing where none was expected, which is its actual value.
* fix(build): canonicalize bundle stub markers before diffing the allowlist
The bundle guard compared raw `missing-module-stub:` marker text against
ACCEPTABLE_RUNTIME_STUBS, but the marker format is not stable across build
hosts: locally Bun emits the relative import specifier
(`./commands/fork/index.js`), while on the Linux CI merge run it emitted the
same grandfathered stubs as absolute source paths
(`/home/runner/work/openclaude/openclaude/src/commands/fork/index.ts`). The raw
diff therefore failed `bun run smoke` on CI for already-allowlisted stubs and
also reported them as stale.
Canonicalize both the bundle markers and the allowlist to a stable key (the
basename without extension) before diffing, so a stub matches in either form.
Basename is the only reduction that unifies a relative specifier of unknown
depth with an absolute path (a fixed path-segment count breaks single-segment
specifiers like `./dream.js`). The allowlist keeps the readable full specifiers;
diagnostics still print the raw marker. Guard against two allowlist entries
sharing a basename (which would let one silently cover an unrelated stub) by
failing the build if the canonical set is smaller than the allowlist.
* chore(build): drop allowlist stubs resolved by current main
Rebasing onto current main brings in the per-importer scanner (#1399)
and the real sources for four previously-stubbed modules, so they no
longer emit missing-module markers:
- ../../utils/hooks/ssrfGuard.js (per-importer keying, #1399/#1450)
- ./dream.js (/dream restored, #1399)
- ./UserForkBoilerplateMessage.js (source mirrored, #1451)
- ./commands/fork/index.js (unmirrored /fork dropped, #1451)
The bundle guard flagged all four as stale allowlist entries. Remove
them and refresh the guard rationale comment, which described the
pre-#1399 specifier-string scanner; the scanner now keys per importer.
* fix(history-snip): expose snip id on pure tool-result messages
appendMessageTagToUserMessage() only appended the [id:...] tag to a
string body or an existing text block. A user message that is purely
tool_result blocks (the normal shape for large Read/Bash outputs) has
no text block, so it returned unchanged and carried no visible id. Those
are exactly the highest-value snip targets the feature prompts the model
to remove, yet the model had no id to reference them by.
Append a dedicated text block holding the tag when a tool-result-only
message has no text block. The tool_result block is left intact, so snip
pairing is unaffected, and the tag lands on the API-bound copy only.
Export the function and add colocated tests covering string body, text
block, the pure tool_result case, and meta passthrough.
* fix(build): key bundle-stub guard on repo-relative path, not basename
The guard canonicalized every missing-module-stub marker to its basename
before checking the allowlist, so a future stub named constants.ts (or
cachedMCConfig.ts, MonitorMcpDetailDialog.ts) from any other directory
would be treated as allowlisted and slip past the guard — the exact
regression class the guard exists to catch.
Post-#1399 the per-importer scanner records each stub as the resolved
absolute source path, which differs across build hosts only by the
repo-root prefix. So key on the repo-relative path from src/ onward
(without extension): stable across hosts yet path-specific, so a stub
cannot mask a same-named file elsewhere. Drop the now-moot basename
collision guard and store the allowlist as repo-relative keys.
* fix(history-snip): describe snip as a queued, refusable request
SnipTool's tool result said "Marked N message(s) for removal. They will
be removed from context before the next model call" based only on the
count of input IDs. But snipCompactIfNeeded() can refuse the exact
request on the next turn: it keeps a tool_result whose paired tool_use
would survive (snipping it would orphan the tool call), freeing 0 tokens
and emitting no boundary. The model was told the output would be removed,
then saw it still in context with no failure signal, so it treated a
structural no-op as a successful context reduction.
Reword the tool result to describe the snip as a queued request that may
be refused, name the one refusal condition (would orphan a paired tool
call, e.g. one result from a parallel-tool turn), and give the model the
observable signal and repair: a kept message re-shows its [id:...] tag
next turn (tags are re-applied every API-prep pass), and snipping all of
that turn's tool results together removes them cleanly.
Add SnipTool.test.ts pinning the queued/refusable wording.
* test(history-snip): import UserMessage from its canonical module
messages.snipTag.test.ts imported UserMessage from ../query.js, which
imports the type but does not re-export it (TS2459). Import it from
../types/message.js, the canonical source messages.ts itself uses, so
the snip test files typecheck cleanly.
* fix(history-snip): make snip id tag injection idempotent
appendMessageTagToUserMessage() documents that it only mutates the
API-bound copy, but query.ts builds the next loop state's toolResults
from normalizeMessagesForAPI([update.message]) (query.ts:1589) and stores
that normalized, already-tagged output into state.messages
(query.ts:1976). With HISTORY_SNIP enabled the tag is carried forward as
conversation state, so the next turn re-normalizes it and appends the
same [id:...] a second time. In multi-tool agent loops every prior tool
result accumulates another duplicate tag each iteration, bloating context
and showing the model repeated IDs that are meant to be an API-projection
affordance only.
Guard the append: if the message already carries its own [id:<id>] token
(string body, last text block, or the dedicated tool_result text block),
return it unchanged. The token is derived from the message's own uuid, so
its presence means it was already tagged. Adds 3 idempotency tests.
* fix(history-snip): expose every parallel-tool sibling id before merge
normalizeMessagesForAPI tagged snip [id:] markers only after merging
consecutive user messages. A parallel-tool assistant turn yields several
adjacent tool_result user messages; the merge keeps just the first
operand's uuid, so on the resume/reload path (where the persisted
transcript is the untagged original) only the first sibling's id reached
the model. snipCompactIfNeeded refuses to drop one result of such a turn
(it would orphan the surviving tool_use), so the model needed every
sibling's id to request the whole-turn removal the snip prompt instructs,
and could never form it: a permanent no-op.
Inject the tag per user message before the merge instead, so each
sibling carries its own [id:] and joinTextAtSeam preserves them all,
matching the live path where each result is tagged at push time. The
post-merge sweep stays (idempotent) to tag user messages synthesized
during normalization (local_command, attachments).
Test: merging tagged parallel siblings keeps every sibling id and both
tool_result blocks.
* test(history-snip): type snip-replay test ids as UUID
loadTranscriptFile() returns Map<UUID, TranscriptMessage>, but the test
id() helper returned plain string, so every messages.has/get/
buildConversationChain call in the persisted-snip replay test raised a
TS2345 against the UUID-keyed map. Type id() as UUID (casting the literal
once at the source) so the new replay coverage does not add touched-path
typecheck debt. Also clears the same error cluster in the pre-existing
compact-boundary tests that share the helper.
* docs(history-snip): drop removed /force-snip from setMessages comment
The QueryEngine setMessages comment cited /force-snip as its example of a
message-mutating slash command, but that command was removed. Point the
example at /clear (src/commands/clear/conversation.ts), which still mutates
the message array via setMessages, so the comment stays accurate.
* refactor(history-snip): type SnipBoundaryMessage removedUuids as string[]
removedUuids holds message UUID strings throughout the snip feature, but
the SnipBoundaryMessage prop typed it as unknown[]. Narrow it to string[]
so the type carries intent and the test fixture no longer needs an
`as never` cast to satisfy the prop (the cast bypassed type checking and
could have hidden a real fixture/prop mismatch).
* fix(history-snip): drop stale cachedMCConfig stub-allowlist entry
cachedMCConfig.ts now exists in the tree and bundles as real code, so it
is no longer emitted as a missing-module stub. The grandfathered baseline
listed it among acceptable stubs, which made the new guard print a stale
warning and, worse, would silently accept a future reintroduced
cachedMCConfig stub as known debt instead of flagging it. Drop the entry so
the allowlist matches the actual bundle (VerifyPlanExecutionTool/constants
and MonitorMcpDetailDialog).
* fix(history-snip): guard paired snip drops and report queued count
Two CodeRabbit findings on the snip compaction path:
- Mixed-content turns: the inferred paired-drop ran its .every() check over
filtered tool blocks only, so an assistant turn like [text, tool_use] (or a
user [tool_result, text]) was treated as fully droppable and its text was
silently removed when the paired half was snipped. Require the whole message
to be tool blocks before an inferred drop; otherwise treat the snip as a
no-op (the explicit-snip path, where the model deliberately targets a message,
is unchanged and still removes wholesale).
- Queued count: markForSnip only enqueues short IDs it can resolve against the
conversation, but SnipTool reported sniped = input.message_ids.length, which
overstated the result when IDs were stale or unresolvable. markForSnip now
returns the distinct resolved UUIDs and SnipTool reports that length.
* fix(history-snip): align snip prompt with queued-not-guaranteed contract
The tool description told the model snipped IDs are "permanently remove[d]
... before the next model call", but snipCompactIfNeeded queues the request
and keeps a message when removing it would orphan a paired tool_use (the
tool_result already says so). Match the description to that contract so the
model does not treat a structural no-op as a guaranteed removal.
|
||
|
|
479b0e8226 |
fix(sandbox): guard annotateStderrWithSandboxFailures against missing runtime method (fixes Bash on builds without sandbox-runtime) (#1452)
* fix(sandbox): guard annotateStderrWithSandboxFailures against missing runtime method
Fall back to a passthrough when BaseSandboxManager.annotateStderrWithSandboxFailures
is absent, so BashTool no longer throws "is not a function" on every command when the
underlying sandbox-runtime build doesn't provide the method. No behavior change when it
is present.
* fix(sandbox): complete the SDK SandboxManager stubs so they match the CLI's Proxy-noop
The SDK build stubs @anthropic-ai/sandbox-runtime two ways: the native-stub
namespace uses `new Proxy({}, { get: () => noop })` (every access is safe), but
defaultExportOverrides replaces SandboxManager/BaseSandboxManager with hollow
classes that omit annotateStderrWithSandboxFailures. The class form wins in the
SDK bundle, so SDK embedders crash on every Bash command
(`SandboxManager.annotateStderrWithSandboxFailures is not a function`) while the
CLI build — which keeps the Proxy-noop and ships the real native runtime — is
unaffected.
Add a passthrough `annotateStderrWithSandboxFailures` to both stub classes so
they behave like the Proxy form (return stderr unchanged when no real runtime is
present). Combined with the call-site `?? passthrough` guard, the SDK now
degrades gracefully on builds without sandbox-runtime instead of throwing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
f111eaa1b3 |
feat: enable MCP_SKILLS — discover skill:// resources as invocable skills (#1408)
* feat(mcp-skills): implement MCP skill discovery via skill:// resources
- mcpSkills.ts: fetchMcpSkillsForClient — lists MCP resources, filters skill://
URIs, reads each via resources/read, parses frontmatter, builds skill commands
with loadedFrom/source: 'mcp'. Memoized per server name (LRU, size 20).
- isSkillResource: pure helper to detect skill:// URI scheme
- deriveMcpSkillName: namespaced name builder (mcp__<server>__<name>)
- Enable MCP_SKILLS: true in scripts/build.ts
All call sites, cache-invalidation paths, and consumers were already wired
behind feature('MCP_SKILLS'). Only the module itself was missing. Fixes the
"fetchMcpSkillsForClient is not a function" crash (#856) when the flag was
force-enabled without the module present.
* fix(mcp-skills): discard hooks frontmatter from remote MCP skills
A skill:// resource's hooks frontmatter was carried through the
parseSkillFrontmatterFields spread into the Command, and the slash-command
path registered command.hooks as session hooks on invocation. This let any
connected MCP server install local command hooks that later run shell in the
user's workspace, bypassing the loadedFrom === 'mcp' inline-shell guard by
moving the payload into frontmatter hooks instead of the markdown body.
Discard hooks at the MCP construction site so untrusted remote skills can
never become registrable session hooks.
* fix(mcp-skills): discard allowed-tools frontmatter from remote MCP skills
Like hooks, a skill:// resource's allowed-tools frontmatter flowed through
the parseSkillFrontmatterFields spread into the Command. On the user-typed
slash path (/mcp__server__skill) those tools are written into
alwaysAllowRules.command, so a remote MCP server could auto-approve tool
calls (e.g. Bash) that its own skill body then drives the model to make —
with no permission prompt. The inline-shell guard for loadedFrom === 'mcp'
does not cover this.
Discard allowed-tools at the MCP construction site so remote skills can't
auto-grant tools; the model still prompts on each tool use. The model-invoked
SkillTool path already gates non-empty allowedTools via
skillHasOnlySafeProperties, but the slash path bypasses checkPermissions.
* fix(mcp-skills): skip @-mention attachment scanning for remote MCP skill bodies
A skill:// resource's markdown body flows through getMessagesForPromptSlashCommand
into getAttachmentMessages, which scans for @-mentions and MCP resource refs and
reads them before the model continues. skipSkillDiscovery only gates skill
discovery, not @-mention file reads, so a remote skill could embed @~/.ssh/config
or @.env and exfiltrate local file contents into the conversation with no tool
permission prompt — the same class as the already-stripped hooks/allowed-tools.
Gate the scan input on loadedFrom === 'mcp' (new attachmentScanInputForCommand
helper): the body still reaches the model verbatim, but its @-mentions are no
longer auto-read. Thread-level attachments are unaffected (input=null only gates
the user-input branch in getAttachments).
|
||
|
|
132539ff79 |
fix(build): restore /dream slash command in bundled CLI (#1399)
Scope missing-module stubs for relative imports to the importer file so the unmirrored KAIROS dream skill stub no longer replaces the real /dream command module during bundling. |
||
|
|
f12eb1c9e8 |
Harden test isolation for smoke stability (#1192)
* Fix flaky smoke build checks Replace feature-flag build preprocessing with a Bun onLoad transform so smoke/build no longer rewrites tracked src files while tests may be reading them. Keep telemetry stubs ahead of the feature transform in both CLI and SDK builds, preserve non-empty text token counts in hybrid context splitting, and make the corrupted Orama stress test assert against the actual project directory used by the test. Verified with bun run smoke, bun test src/utils/hybridContextStrategy.test.ts, bun test src/utils/knowledgeGraph.stress.test.ts --rerun-each 3, and bun test --max-concurrency=1. * Harden KnowledgeGraph smoke stress isolation Give each KnowledgeGraph stress test its own temporary config directory and remove it during teardown so Orama, SQLite, and corrupted-file state cannot bleed between stress cases or later PR test runs. Reviewed at least 20 open PRs and found the recurring smoke-and-tests failure cluster is the full unit suite, especially KnowledgeGraph corrupted Orama recovery. Verified with bun test src/utils/knowledgeGraph.stress.test.ts --rerun-each 5, bun test --max-concurrency=1, and bun run smoke. * Harden smoke test isolation Audit and harden broad smoke-adjacent test suites for process-global leaks, including env/config restoration, shared registry/module mock cleanup, fetch/axios/mock restoration, and global MACRO/platform/sandbox mutations. Replace fragile render sleeps in interactive tests with output-driven waits, and isolate provider/model/profile tests behind the shared mutation lock so unrelated PRs do not inherit stale process state. Make SQLite knowledge graph cleanup clear closed on-disk databases before best-effort file cleanup, with coverage for the stale database reset path. Verified: bun run smoke; bun test --max-concurrency=1; python -m pytest -q python/tests; bun run security:pr-scan -- --base origin/main; bun run test:provider; npm run test:provider-recommendation. * Harden test isolation across smoke suite Guard process-global test mutations with the shared mutation lock across env, module mock, config cache, and storage tests.\n\nDeep-copy global config snapshots, restore transient globals precisely, and make plugin/LSP mocks expose compatible export surfaces so concurrent test loading does not poison unrelated suites.\n\nReplace fixed SDK cleanup sleeps with call polling to remove timing sensitivity.\n\nVerification:\n- bun test --max-concurrency=1\n- bun run smoke\n- python -m pytest -q python/tests\n- bun run test:provider\n- npm run test:provider-recommendation\n- bun run security:pr-scan -- --base origin/main\n- git diff --check * Close remaining test global-state leaks Guard remaining cache, plugin, console, and VS Code module-mock tests with the shared mutation lock.\n\nThis follow-up audit covers non-env process-global state that can leak across test files: tool schema cache, cache stats tracker state, plugin loader caches, console.error replacement, and VS Code mock.module usage.\n\nVerification:\n- leak-surface scans for env/global/mock.module/cache outliers\n- duplicate top-level mock collision cluster\n- affected tests cluster\n- bun test --max-concurrency=1\n- bun run smoke\n- git diff --check * Guard remaining mock restore cleanup Lock tests that call bun:test mock.restore without installing module mocks themselves.\n\nmock.restore is process-global, so these cleanup hooks can still tear down another test file's active module mocks when files run concurrently.\n\nVerification:\n- expanded leak scans for env, globals, module mocks, mock.restore, timers, argv, and caches\n- bun test src/components/useCodexOAuthFlow.test.tsx src/services/github/deviceFlow.test.ts\n- bun test --max-concurrency=1\n- bun run smoke\n- git diff --check * Harden test isolation for smoke stability Serialize tests that mutate process-global state behind the shared mutation lock, including process.env, transient globals, global config/cache state, storage mocks, and Bun module mocks. Add isolated env mutex instances for SDK mutex tests so timeout coverage no longer manipulates the live process-global mutex. Move top-level mock.module setup behind lock acquisition and restore mocks before releasing locks to prevent cross-file leakage under parallel smoke runs. Verified with: bun test --max-concurrency=1; bun test; bun run smoke. |
||
|
|
2c71e09394 |
chore(build): clean up external dependency validation warnings (#1124)
* chore(build): clean up external dependency validation warnings Remove 2 unused externals (@opentelemetry/sdk-trace-node, ink) and add 12 missing packages to package.json that are dynamically imported at runtime but weren't declared as dependencies. Also remove the unused @opentelemetry/sdk-trace-node dependency. This eliminates all 13 build validation warnings: - 8 missing OTel exporter deps (http, proto, grpc variants + prometheus) - 4 missing AWS SDK deps (bedrock, bedrock-runtime, sts, credential-providers) - 1 missing Azure dep (@azure/identity) - ink external pointed to local reimplementation, not npm package - sdk-trace-node was declared external but never imported Build validation now passes cleanly with 0 warnings. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * chore(build): eliminate external validation warnings Remove unused @opentelemetry/sdk-trace-node from externals and package.json (it's not imported anywhere in src/). Remove ink from SDK_ONLY_EXTERNALS (the project reimplements ink locally at src/ink/). Add OPTIONAL_RUNTIME_EXTERNALS list for packages that are dynamically imported but intentionally not direct deps — OTel protocol exporters and cloud provider SDKs are resolved from transitive deps or installed by users who need them. Validation now passes with 0 warnings instead of 13. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * feat(telemetry): full OpenTelemetry purge — remove all tracking dependencies Replace all @opentelemetry/* runtime dependencies with no-op stubs, delete OTel-only source modules, and remove 10 @opentelemetry packages from package.json plus @growthbook/growthbook. Key changes: - Delete 5 OTel-only modules (instrumentation, betaSessionTracing, bigqueryExporter, logger, firstPartyEventLoggingExporter) - Replace 9 modules with no-op stubs (sessionTracing, events, telemetryAttributes, firstPartyEventLogger, growthbook, index, sink, datadog, sinkKillswitch, perfettoTracing) - Remove all @opentelemetry/* imports from bootstrap/state.ts, entrypoints/init.ts, and ~20 caller files - Remove all OTel counter types, meter/provider state from state.ts - Clean externals.ts: remove 27 @opentelemetry/* entries - Clean build.ts: remove OTel native-stub namespace exports - Simplify no-telemetry-plugin.ts: remove redundant source-level stubs - Remove 10 @opentelemetry/* + @growthbook/growthbook from package.json - GrowthBook stub reads local ~/.claude/feature-flags.json for overrides Co-Authored-By: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com> * fix format * chore: regenerate lockfile after OTel dependency removal Co-Authored-By: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com> * fix(growthbook): route gate helpers through local flag overrides checkStatsigFeatureGate_CACHED_MAY_BE_STALE() and checkGate_CACHED_OR_BLOCKING() now resolve from ~/.claude/feature-flags.json like getFeatureValue_* does, so gates like tengu_thinkback, tengu_ccr_bridge, and VS Code upsells can be flipped on locally. Security gates (checkSecurityRestrictionGate) remain hard-false. Also adds 5 tests covering gate helper override behavior and unifies JSDoc wording for _getFlagValue-routed functions. Co-Authored-By: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
60c76b6599 |
feat: SDK Runtime — Query Engine, Sessions, and Build Pipeline (#984)
* feat(sdk): add SDK foundation — type declarations, errors, and utilities Adds standalone SDK building blocks with no SDK source dependencies: - sdk.d.ts: ambient type declarations for SDK bundle - coreSchemas.ts + coreTypes.generated.ts: Zod schemas and generated types - errors.ts: SDK-specific error classes - validation.ts: input validation utilities - messageFilters.ts: extracted message filter logic - handlePromptSubmit.ts: imports from messageFilters - 16 generated-types tests * fix(sdk): narrow assertFunction type from broad Function to callable signature Code review finding: assertFunction used `asserts value is Function` which accepts any function-like value without narrowing. Changed to `(...args: any[]) => any` for better type safety. * fix(sdk): update sdk.d.ts header — manually maintained, not generated Reviewer noted the header said "Generated from index.ts" but no generator produces this file. Updated to "Manually maintained — keep in sync with index.ts". Drift detection added in validate-externals.ts (PR 3). * fix(sdk): align sdk.d.ts types with canonical coreTypes.generated.ts Tighten SDK public type contract to resolve reviewer blockers: - PermissionResult: unknown[] → precise 6-shape discriminated union (addRules/replaceRules/removeRules/setMode/addDirectories/removeDirectories) - SDKSessionInfo: snake_case → camelCase (sessionId, lastModified, etc.) - ForkSessionResult: session_id → sessionId - SDKPermissionRequestMessage: uuid + session_id now required - SDKPermissionTimeoutMessage: added uuid + session_id - SessionMessage: parent_uuid → parentUuid - SDKMessage/SDKUserMessage/SDKResultMessage: replaced loose inline definitions with re-exports from coreTypes.generated.ts * feat(sdk): wire existing code modules + SDK shared utilities Modifies core modules for SDK integration: - QueryEngine, tools, state, commands: SDK type hooks - SDK shared utilities (shared.ts, permissions.ts) - 21 SDK tests (shared-utils, permissions) Stack: main ← pr1-foundation ← pr2-sdk-core * feat(sdk): add snake_case ↔ camelCase key mapping utilities casing.ts provides recursive key transformation for the SDK boundary layer. Internal runtime uses snake_case; public API exposes camelCase. Will be used by shared.ts, sessions.ts, query.ts at export boundaries. * test(sdk): add tests for snake_case ↔ camelCase mapping utilities Covers snakeToCamel, camelToSnake, mapKeysToCamel, mapKeysToSnake including nested objects, arrays, null/undefined, and round-trips. * feat(sdk): add SDK runtime — query engine, sessions, build pipeline Completes the SDK implementation: - SDK build target (dist/sdk.mjs) with TUI dependency stubbing - External dependency lists (scripts/externals.ts) - SDK type generation from Zod schemas (scripts/generate-sdk-types.ts) - External validation (scripts/validate-externals.ts) - SDK source: index, query, v2, sessions modules - agentSdkTypes: re-exports SDK functions (query, createSession, etc.) - 136 SDK tests + 7 build scanner tests Stack: main ← pr1-foundation ← pr2-sdk-core ← pr3-sdk-runtime * fix(sdk): align internal SDK types with camelCase public contract shared.ts: SDKSessionInfo, ForkSessionResult, SessionMessage fields now use camelCase matching sdk.d.ts. SDKPermissionRequestMessage and SDKPermissionTimeoutMessage gain required uuid + session_id fields. permissions.ts: onPermissionRequest/onTimeout callbacks now include uuid and session_id in emitted messages. * fix(sdk): update runtime modules to use camelCase field names sessions.ts: toSDKSessionInfo outputs camelCase keys, entryToSessionMessage uses parentUuid, forkSession returns sessionId. query.ts: reads sessionId from listSessions/forkSession results instead of snake_case session_id. * fix(test): update session tests to use camelCase field names session_id → sessionId in forkSession result assertions and getSessionMessages calls. * fix(sdk): prevent permission timeout race condition with once-only resolve wrapper Add createOnceOnlyResolve utility to prevent double-resolution of promises when timeout and host response happen simultaneously. This ensures deterministic behavior in the permission handling flow. * fix(sdk): improve race condition test robustness * fix(sdk): handle consecutive underscores in snakeToCamel conversion Changes: - Use _+([a-z]) regex to match multiple consecutive underscores before letters - Add lookahead (?=. ) to preserve underscore-letter pairs at string end - Handle dunder names (__proto__, __typename) by stripping wrapper and capitalizing - Add tests for consecutive underscores and trailing underscore preservation * fix(sdk): include original error message in permission callback denial When a canUseTool callback throws an error, the catch block now includes the original error message in the denial message, making debugging easier for SDK consumers. * feat(sdk): add optional timeout to env mutex for deadlock prevention Add timeout parameter to acquireEnvMutex() to prevent infinite waits in deadlock scenarios. The timeout is optional and defaults to no timeout (wait forever) for backward compatibility. Returns a MutexAcquireResult object with acquired status and optional timeout reason for failed acquisitions. * fix(sdk): remove timed-out callback from mutex queue to prevent deadlock * test(sdk): add missing error path and timeout scenario tests Add tests for timeout scenarios when host doesn't respond to permission requests, fallback behavior when no onPermissionRequest callback, and MCP connection edge cases for undefined/empty config. * fix(sdk): address code review issues - race conditions, validation, error handling - Add createPermissionTarget() factory that applies onceOnlyResolve at registration time, fixing race condition where timeout and host response could both try to resolve the same promise - Add try-catch to releaseEnvMutex() to prevent permanent lock if callback throws - Extract DEFAULT_PERMISSION_TIMEOUT_MS constant (30 seconds) - Add MCP config validation rejecting null, non-objects, and arrays - Preserve error stack traces in MCP connection failures - Add runtime validation to mapMessageToSDK for null/non-object/invalid type - Update tests to use createPermissionTarget and add validation tests * fix(sdk): syntax fixes and MCP connection error handling - Remove extra closing parenthesis in permissions.ts - Remove extra closing braces in shared.ts type definitions - Wrap MCP connection in try/catch to continue without MCP tools on failure * fix(sdk): syntax fixes, MCP error handling, and logic clarity - Remove extra closing parenthesis in permissions.ts - Remove extra closing braces in shared.ts type definitions - Wrap MCP connection in try/catch to continue without MCP tools on failure - Clarify thinkingConfig logic: use ?? true instead of !== false - Add explanatory comment about thinkingEnabled default behavior - Apply createOnceOnlyResolve wrapper in QueryImpl.registerPendingPermission * fix(sdk): comprehensive error handling and resource cleanup - Add try-catch around injectAgents() to gracefully handle plugin agent tool validation failures (prevents test crashes from unknown 'LS' tool) - Add console.warn logging to agent loading/injection catch blocks for debugging visibility (matches v2.ts pattern) - Add pendingPermissionPrompts.clear() to close() and interrupt() methods in both query.ts and v2.ts to prevent memory accumulation - Add close() method to SDKSession interface and SDKSessionImpl - Wrap MCP connection in query.ts with try-catch (matches v2.ts behavior) - Add timeoutQueue cleanup in finally blocks (query.ts + v2.ts) - Remove error.stack from MCP error messages to prevent internal path leak All 208 SDK tests pass. TypeScript errors are pre-existing. * fix(sdk): address code review non-blocking issues - Add SDKAgentLoadFailureMessage type for agent load failure events - Emit agent definition/injection failures to SDK message stream - Add tool name to permission timeout denial message - Replace 'as any' casts with proper typed state access - Fix supportedCommands to use correct mcp.commands/plugins.commands paths - Update test for correct AppState structure * fix(sdk): address code review blocking and non-blocking issues Blocking Issues Fixed: - MCP cleanup missing on session/query close - now disconnects MCP clients to prevent resource leaks in long-running processes with multiple sessions - Engine reference not cleared on close - now sets _engine = null to prevent memory leaks - Added MCP cleanup tests (9 new tests covering cleanup scenarios) Non-Blocking Issues Fixed: - Removed redundant catch block that just rethrew errors (query.ts) - Fixed inconsistent timeout denial message format (permissions.ts) - Fixed hardcoded tool name 'Bash' in test (permissions.test.ts) - Exported PermissionResolveDecision type for SDK consumers (index.ts) All 217 SDK tests pass. * fix(sdk): address code review type consistency issues - Add close() method to SDKSession interface (documented but missing from type) - Fix SDKSessionInfo, ForkSessionResult, SessionMessage field naming: snake_case → camelCase to match sdk.d.ts public contract and implementation - Add uuid and session_id to SDKPermissionTimeoutMessage for correlation - Fix JSDoc comment in forkSession to use sessionId (not session_id) These changes align internal types (shared.ts) with the public SDK contract (sdk.d.ts) and actual implementation output. The merge from origin/main introduced snake_case types that mismatched camelCase implementation and tests. * fix: restore openclaude.json comment in REPL.tsx Merge |
||
|
|
3d791bf07f |
Disable feedback/mobile commands and refresh OpenClaude branding (#980)
- disable /feedback and /mobile from command availability while keeping implementation code in place - remove or rewrite lingering user guidance that pointed to /feedback or /mobile - switch HelpV2 to a public build version helper and fix the help dialog wrapper regression - update OpenClaude-facing links and prompt copy for issue reporting and branding consistency |
||
|
|
ee0d930093 |
fix(ripgrep): use @vscode/ripgrep package as the builtin source (#911) (#932)
The vendored-binary lookup at vendor/ripgrep/<arch>-<platform>/rg never resolved in this fork — that directory does not ship — so users without a system rg had no working fallback. Switch to the @vscode/ripgrep package so Microsoft maintains the platform/arch matrix and the binary is delivered via npm. - src/utils/ripgrep.ts: replace hand-rolled vendor-path resolution with rgPath from @vscode/ripgrep. Lazy require so a missing package falls through to the system rg branch instead of throwing at import. Drop builtinExists from the config args; builtinCommand is now a string-or-null. The system override (USE_BUILTIN_RIPGREP=0), the Bun-compiled standalone embedded mode, the macOS codesign hook, and all retry/timeout/error logic are preserved untouched. - scripts/build.ts: mark @vscode/ripgrep as external. The package resolves rgPath via __dirname at runtime, so bundling would freeze the build host's absolute path into dist/cli.mjs. - src/utils/ripgrep.test.ts: update for the new config shape and add tests covering USE_BUILTIN_RIPGREP=0, embedded mode, last-resort fallback, and null builtin path. Tested locally on Linux (Bun 1.3.13). macOS (codesign hook) and Windows (rg.exe extension) need contributor verification. |
||
|
|
dcbe29558a |
fix(mcp): disable MCP_SKILLS feature flag — source not mirrored (#872)
Closes #856. MCP servers that expose resources (e.g. RepoPrompt) failed to load their tools in the open build with: Error fetching tools/commands/resources: fetchMcpSkillsForClient is not a function Root cause: scripts/build.ts set MCP_SKILLS: true, which made feature('MCP_SKILLS') evaluate to true at build time. The guards around the dynamic skill discovery path therefore stayed live. The underlying source file src/skills/mcpSkills.ts is not mirrored into the open tree, so the bundler fell back to its generic missing-module stub — which only exports `default` for require()-style imports, not the named `fetchMcpSkillsForClient` binding. At runtime the require returned an object without that property, and calling it threw. `openclaude mcp doctor` reported RepoPrompt as healthy because doctor does not exercise the skills-fetch path. Fix: flip MCP_SKILLS to false and move it into the "Disabled: missing source" group. With the flag off, every `if (feature('MCP_SKILLS'))` guard becomes a no-op at build time, the require() branch is dead code, and MCP servers with resources load normally via the existing `Promise.resolve([])` fallbacks already present at each call site. Also adds scripts/feature-flags-source-guard.test.ts to fail fast if MCP_SKILLS (or any future flag in the same category) is re-enabled without the corresponding source file being mirrored first. Verification: - Test fails on main, passes with this fix - `bun run build` produces a bundle with no `missing-module-stub:../../skills/mcpSkills.js` reference - Full `bun test` — 1222 pass / 12 fail (same pre-existing 12 as main; new test adds the +1 pass) |
||
|
|
6a62e3ff76 |
feat: enable 15 additional feature flags in open build (#667)
* feat: enable 16 additional feature flags in open build
Activate features whose source is fully available in the mirror and
that have no Anthropic-internal infrastructure dependencies:
UI/UX: MESSAGE_ACTIONS, HISTORY_PICKER, QUICK_SEARCH, HOOK_PROMPTS
Reasoning: ULTRATHINK, TOKEN_BUDGET, SHOT_STATS
Agents: FORK_SUBAGENT, VERIFICATION_AGENT, MCP_SKILLS
Memory: EXTRACT_MEMORIES, AWAY_SUMMARY
Optimization: CACHED_MICROCOMPACT, PROMPT_CACHE_BREAK_DETECTION
Safety: TRANSCRIPT_CLASSIFIER
Debug: DUMP_SYSTEM_PROMPT
Also reorganize featureFlags into documented sections (disabled/upstream/new)
with inline comments explaining each flag's purpose.
* feat: add centralized GrowthBook defaults map for open build
Add _openBuildDefaults in the GrowthBook stub (no-telemetry-plugin.ts)
with all 66 runtime feature keys, organized by category with inline
comments describing each flag's purpose.
Override tengu_sedge_lantern (AWAY_SUMMARY) and tengu_hive_evidence
(VERIFICATION_AGENT) to true so these features work out of the box
without requiring manual ~/.claude/feature-flags.json setup.
Priority: feature-flags.json > _openBuildDefaults > upstream default
* feat: replace refusal language with positive security guidance
Remove refusal instructions from CYBER_RISK_INSTRUCTION since they are
redundant for Anthropic models (applied server-side) and useless for
uncensored models in multi-provider setups. Keep positive guidance for
security testing contexts and add red teaming support.
* Revert "feat: replace refusal language with positive security guidance"
This reverts commit
|
||
|
|
a00b7928de |
fix: strip comments before scanning for missing imports (#676)
* fix: strip comments before scanning for missing imports
The scanForMissingImports regex matched require() and import() patterns
inside JSDoc comments, causing false-positive missing module detection.
A documented path like `require('./commands/proactive.js')` in a comment
was resolved from the wrong directory, marked as missing, then the global
onResolve handler intercepted ALL imports of that specifier — including
valid ones — replacing them with truthy noop stubs that broke runtime.
Strip block (/* */) and line (//) comments from source before scanning.
* fix: repair 10 pre-existing test failures
- promptIdentity.test.ts: define MACRO global (ISSUES_EXPLAINER etc.)
for test mode where Bun.define build-time replacements aren't active
- context.test.ts: clear OPENAI_MODEL env var in each test — the user's
environment (e.g. OPENAI_MODEL=github_copilot/gpt-5.4) polluted the
provider-qualified lookup, returning wrong context windows
- openclaudePaths.test.ts: set CLAUDE_CONFIG_DIR to force .openclaude
path when ~/.openclaude doesn't exist on the test machine
|
||
|
|
252808bbd0 |
feat: activate message actions in open build (#632)
Enable the MESSAGE_ACTIONS feature flag so open-build users get the shift+up keybinding for the message actions panel. Gate sites: src/keybindings/defaultBindings.ts, src/screens/REPL.tsx (5 total). Pure UI/keybinding feature with zero external dependencies. |
||
|
|
b818dd5958 |
feat: implement Monitor tool for streaming shell output (#649)
* feat: implement Monitor tool for streaming shell output Add the Monitor tool that executes shell commands in the background and streams stdout line-by-line as notifications to the model. This enables real-time monitoring of logs, builds, and long-running processes. Implementation: - MonitorTool (src/tools/MonitorTool/) — spawns LocalShellTask with kind='monitor', returns immediately with task ID - MonitorMcpTask (src/tasks/MonitorMcpTask/) — task lifecycle management and agent cleanup via killMonitorMcpTasksForAgent() - MonitorPermissionRequest — permission dialog component The codebase already had all integration points wired (tools.ts, tasks.ts, PermissionRequest.tsx, LocalShellTask kind='monitor', BashTool prompt). This PR provides the missing implementations. * fix: command-specific permission rule + architecture docs - MonitorPermissionRequest: "don't ask again" now creates a command-prefix rule (like BashTool) instead of a blanket tool-name-only rule that would auto-allow all Monitor commands - MonitorMcpTask: clarify architecture comments explaining why monitor_mcp type exists as a registry stub while actual tasks are local_bash with kind='monitor' * fix: address Copilot review feedback - Fix permission rule field: expression → ruleContent (Copilot #1) - Handle empty command prefix: skip rule creation (Copilot #2) - Remove unused useTheme() import (Copilot #3) - Save permission rules under 'Bash' toolName so bashToolHasPermission can match them — Monitor delegates to Bash permission system (Copilot #4) - Remove unused logError import from MonitorMcpTask (Copilot #6) - Copilot #5 (getAppState throws): same pattern as BashTool:915, not a bug |
||
|
|
24d485f42f |
feat: activate local-only team memory in open build (#648)
* feat: activate local-only team memory in open build Enable the TEAMMEM feature flag and the isTeamMemoryEnabled() gate so team memory works in local-only mode for all open-build users. Team memory is a shared memory system scoped per-project, stored at ~/.claude/projects/<project>/memory/team/. The implementation is already almost entirely local — extraction, UI, prompts, file detection, and path validation all work on local files. The cloud sync overlay (OAuth + API) is cleanly separated: the watcher does an early return when OAuth is unavailable, so the feature degrades gracefully to local-only storage with no crashes. What works locally: - Memory extraction (auto + team, combined prompts) - Team MEMORY.md loaded into conversation context - File selector with team memory folder option - Collapse tracking (read/search/write counts) - Secret scanning before persistence - Path validation + symlink protection What requires OAuth (not available in open build): - Cloud sync between team members - Automatic push/pull via file watcher * fix: preserve opt-out gate for team memory via feature flag Change isTeamMemoryEnabled() to read tengu_herring_clock with default true instead of unconditional return true. This enables team memory by default while preserving user opt-out via ~/.claude/feature-flags.json. |
||
|
|
99a17144ee |
feat: activate coordinator mode in open build (#647)
* feat: activate coordinator mode in open build
Enable the COORDINATOR_MODE feature flag and create the missing
src/coordinator/workerAgent.ts module that provides worker agent
definitions for the coordinator.
Coordinator mode is a multi-agent system where a coordinator agent
orchestrates independent workers via AgentTool, SendMessageTool,
and TaskStopTool. The implementation was already 99% complete
(19KB coordinatorMode.ts, 26 gate sites across 15 files) — only
the workerAgent module was missing from the source snapshot.
Workers get the standard built-in agents (general-purpose, explore,
plan). The coordinator system prompt (252 lines) handles all
orchestration logic.
Activate at runtime: CLAUDE_CODE_COORDINATOR_MODE=1
Optional scratchpad: set {"tengu_scratch": true} in
~/.claude/feature-flags.json (#639)
* fix: add worker agent type for coordinator mode
The coordinator system prompt instructs the model to spawn workers with
subagent_type: "worker", but no agent had agentType === 'worker'.
This caused AgentTool to throw "Agent type 'worker' not found" on
every coordinator spawn attempt.
Add a WORKER_AGENT definition that spreads GENERAL_PURPOSE_AGENT with
agentType: 'worker'. Also use the narrower BuiltInAgentDefinition type.
* feat: activate built-in explore and plan agents in open build
Enable BUILTIN_EXPLORE_PLAN_AGENTS so Explore (fast, haiku, read-only)
and Plan (architect, read-only) agents are available to all users in
both normal and coordinator modes.
This resolves the inconsistency flagged in code review: coordinator
workers had access to Explore/Plan agents while normal sessions did not.
The GrowthBook A/B test gate (tengu_amber_stoat) defaults to true via
the no-telemetry stub. Users can disable via feature-flags.json (#639).
|
||
|
|
adbe391e63 |
fix: replace broken bun:bundle shim with source pre-processing (#657)
* fix: replace broken bun:bundle shim with source pre-processing
The `onResolve`/`onLoad` plugin shim for `bun:bundle` was silently
ineffective in Bun v1.3.9+ — the `bun:` namespace is resolved by
Bun's native C++ resolver before the JS plugin phase runs. This meant
ALL `feature()` flags evaluated to `false` regardless of the
`featureFlags` map in build.ts (including `MONITOR_TOOL: true`).
Replace the shim with a source pre-processing step that:
1. Strips `import { feature } from 'bun:bundle'` from .ts/.tsx files
2. Replaces `feature('FLAG')` calls with boolean literals
3. Restores original files in a `finally` block after Bun.build()
Also extend the missing-module scanner to detect `require()` and
dynamic `import()` calls — not just static `import ... from` — since
modules behind feature() gates become resolvable when flags are enabled.
* fix: ensure source files are always restored after build
- Add SIGINT/SIGTERM handlers to restore pre-processed source files
on abrupt termination (Ctrl+C, kill)
- Replace process.exit(1) with process.exitCode = 1 so the finally
block runs on build failure
|
||
|
|
d1a2df2f69 | feat: activate buddy system in open build (#346) | ||
|
|
280c9732f5 |
feat: fix open-source build and add Ollama model picker (#302)
* feat: fix open-source build and add Ollama model picker - Fix build failures by stubbing 62+ missing Anthropic-internal modules with a catch-all plugin in scripts/build.ts - Add runtime shim exports (isReplBridgeActive, getReplBridgeHandle) in bootstrap/state.ts for feature-gated code references - Add /model picker support for Ollama: fetches available models from Ollama server at startup and displays them in the model selection menu - Add Ollama model validation against cached server model list Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review feedback for Ollama integration - Move Ollama validation before enterprise allowlist check in validateModel - Truncate model list in error messages to first 5 entries - Fix isOllamaProvider() to detect OLLAMA_BASE_URL-only configurations - Reuse getOllamaApiBaseUrl() from providerDiscovery instead of duplicating - Reset fetchPromise on failure to allow retry in prefetchOllamaModels - Include Default option in Ollama model picker, prevent Claude model fallthrough - Add file existence check for src/tasks/ stubs in build script Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use pre-scanned exact-match resolvers to avoid Bun bundler corruption Bun's onResolve plugin corrupts the module graph even when returning null for non-matching imports. This caused lodash-es memoize and zod's util namespace to be incorrectly tree-shaken, producing runtime ReferenceErrors. Replace all pattern-based onResolve hooks with a pre-build scan that identifies missing modules upfront, then registers exact-match resolvers only for confirmed missing imports. This avoids touching any valid module resolution paths. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: move Ollama model prefetch outside startup throttle gate prefetchOllamaModels() was inside the skipStartupPrefetches condition, so it would be skipped on subsequent launches due to the bgRefresh throttle timestamp. Ollama model fetch targets a local/remote server and is fast & cheap, so it should always run at startup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
c52245fc0a | fix: restore image paste and image tool-result handling (#308) | ||
|
|
0746802b6a |
security: kill GrowthBook phone-home and auto-updater at build time
Adds a Bun build plugin that replaces analytics/telemetry modules with no-op stubs at compile time. Primary targets (NOT killed by PR #94 or the feature() shim): - GrowthBook: phones home to api.anthropic.com on every launch, sending account UUID, org UUID, email, device ID, subscription type. Refreshes every 6 hours. Now returns defaults without making any network call. - Auto-updater: contacts storage.googleapis.com and npm registry on launch to check for new versions. Now returns null/no-op. Defense-in-depth (already gated by PR #94 or feature flags, but now the code itself is replaced with empty functions): - Datadog, 1P event logging, BigQuery metrics, Perfetto tracing, session tracing, plugin fetch telemetry, transcript sharing. Deliberately NOT stubbed: - Plugin marketplace (downloads.claude.ai) — needed for /plugin - User-configurable OTel (CLAUDE_CODE_ENABLE_TELEMETRY) — opt-in Implementation: separate plugin file (scripts/no-telemetry-plugin.ts) with a 2-line hook in build.ts. The plugin file does not exist upstream so it cannot cause merge conflicts. |
||
|
|
dda553e281 |
fix: define MACRO.PACKAGE_URL and MACRO.NATIVE_PACKAGE_URL in build
These macros are used in ~10 files (autoUpdater, localInstaller, nativeInstaller, update CLI) but were not defined in the build script's `define` block. At runtime, they resolve to `undefined`, causing commands like `npm install undefined` and `npm view undefined` to fail silently during auto-update checks. Sets MACRO.PACKAGE_URL to the published npm package name and MACRO.NATIVE_PACKAGE_URL to undefined (no native binary distribution). Relates to #29 Co-Authored-By: Juan Camilo <juancamilo.auriti@gmail.com> |
||
|
|
5175dba6da | fix: stub internal-only modules in open build | ||
|
|
2d7aa9c841 | feat: rebrand as Open Claude and harden OpenAI REPL | ||
|
|
747be9c2f3 | fix: restore interactive OpenAI REPL startup | ||
|
|
db04c3e0c0 |
fix: bypass version gate by setting MACRO.VERSION to 99.0.0
The original code checks GrowthBook for a minimum version and blocks startup if the build version is too low. Setting to 99.0.0 ensures OpenClaude always passes the check. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
3e652cafdf |
feat: add build system, stubs, and npm packaging — openclaude is now runnable
- package.json with all 70+ dependencies - Bun build script with feature flag shims, native module stubs, otel externals - Stubs for ~15 missing source files (snapshot gaps) - tsconfig.json for TypeScript - bin/openclaude entry point - Builds to single 19MB dist/cli.mjs - Verified: --version and --help work Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |