e4ec654355cfb85164a6449b9954a290386282a0
732 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
789efe4234 |
feat: disable subagent summaries, label subagent observations (#2073)
* feat: disable subagent summaries and label subagent observations Detect Claude Code subagent hook context via `agent_id`/`agent_type` on stdin, short-circuit the Stop-hook summary path when present, and thread the subagent identity end-to-end onto observation rows (new `agent_type` and `agent_id` columns, migration 010 at version 27). Main-session rows remain NULL; content-hash dedup is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: address PR #2073 review feedback - Narrow summarize subagent guard to agentId only so --agent-started main sessions still own their summary (agentType alone is main-session). - Remove now-dead agentId/agentType spreads from the summarize POST body. - Always overwrite pendingAgentId/pendingAgentType in SDK/Gemini/OpenRouter agents (clears stale subagent identity on main-session messages after a subagent message in the same batch). - Add idx_observations_agent_id index in migration 010 + the mirror migration in SessionStore + the runner. - Replace console.log in migration010 with logger.debug. - Update summarize test: agentType alone no longer short-circuits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: address CodeRabbit + claude-review iteration 4 feedback - SessionRoutes.handleSummarizeByClaudeId: narrow worker-side guard to agentId only (matches hook-side). agentType alone = --agent main session, which still owns its summary. - ResponseProcessor: wrap storeObservations in try/finally so pendingAgentId/Type clear even if storage throws. Prevents stale subagent identity from leaking into the next batch on error. - SessionStore.importObservation + bulk.importObservation: persist agent_type/agent_id so backup/import round-trips preserve subagent attribution. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * polish: claude-review iteration 5 cleanup - Use ?? not || for nullable subagent fields in PendingMessageStore (prevents treating empty string as null). - Simplify observation.ts body spread — include fields unconditionally; JSON.stringify drops undefined anyway. - Narrow any[] to Array<{ name: string }> in migration010 column checks. - Add trailing newline to migrations.ts. - Document in observations/store.ts why the dedup hash intentionally excludes agent fields. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * polish: claude-review iteration 7 feedback - claude-code adapter: add 128-char safety cap on agent_id/agent_type so a malformed Claude Code payload cannot balloon DB rows. Empty strings now also treated as absent. - migration010: state-aware debug log lists only columns actually added; idempotent re-runs log "already present; ensured indexes". - Add 3 adapter tests covering the length cap boundary and empty-string rejection. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * perf: skip subagent summary before worker bootstrap Move the agentId short-circuit above ensureWorkerRunning() so a Stop hook fired inside a subagent does not trigger worker startup just to return early. Addresses CodeRabbit nit on summarize.ts:36-47. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
8ec91e7ffa |
fix: break infinite summary-retry loop (#1633) (#2072)
* Initial plan * fix: break infinite summary-retry loop (#1633) Three-part fix: 1. Parser coercion: When LLM returns <observation> tags instead of <summary>, coerce observation content into summary fields (root cause fix) 2. Stronger summary prompt: Add clearer tag requirements with warnings 3. Circuit breaker: Track consecutive summary failures per session, skip further attempts after 3 failures to prevent unbounded prompt growth Agent-Logs-Url: https://github.com/thedotmack/claude-mem/sessions/e345e8ec-bc97-4eaa-94bd-6e951fda8f77 Co-authored-by: thedotmack <683968+thedotmack@users.noreply.github.com> * refactor: extract shared constants for summary mode marker and failure threshold Addresses code review feedback: SUMMARY_MODE_MARKER and MAX_CONSECUTIVE_SUMMARY_FAILURES are now defined once in sdk/prompts.ts and imported by ResponseProcessor and SessionManager. Agent-Logs-Url: https://github.com/thedotmack/claude-mem/sessions/e345e8ec-bc97-4eaa-94bd-6e951fda8f77 Co-authored-by: thedotmack <683968+thedotmack@users.noreply.github.com> * fix: guard summary failure counter on summaryExpected (Greptile P1) The circuit breaker counter previously incremented on any response containing <observation> or <summary> tags — which matches virtually every normal observation response. After 3 observations the breaker would open and permanently block summarization, reproducing the data-loss scenario #1633 was meant to prevent. Gate the increment block on summaryExpected (already computed for parseSummary coercion) so the counter only tracks actual summary attempts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test: cover circuit-breaker + apply review polish - Use findLast / at(-1) for last-user-message lookup instead of filter + index (O(1) common case). - Drop redundant `|| 0` fallback — field is required and initialized. - Add comment noting counter is ephemeral by design. - Add ResponseProcessor tests covering: * counter NOT incrementing on normal observation responses (regression guard for the Greptile P1) * counter incrementing when a summary was expected but missing * counter resetting to 0 on successful summary storage Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: iterate all observation blocks; don't count skip_summary as failure Addresses CodeRabbit review on #2072: - coerceObservationToSummary now iterates all <observation> blocks with a global regex and returns the first block that has title, narrative, or facts. Previously, an empty leading observation would short-circuit and discard populated follow-ups. - Circuit-breaker counter now treats explicit <skip_summary/> as neutral — neither a failure nor a success — so a run that happens to end on a skip doesn't punish the session or mask a prior bad streak. Real failures (no summary, no skip) still increment. - Tests added for both cases. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test: reference SUMMARY_MODE_MARKER constant instead of hardcoded string Addresses CodeRabbit nitpick: tests should pull the marker from the canonical source so they don't silently drift when the constant is renamed or edited. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: also coerce observations when <summary> has empty sub-tags When the LLM wraps an empty <summary></summary> around real observation content, the #1360 empty-subtag guard rejects the summary and returns null — which would lose the observation content and resurrect the #1633 retry loop. Fall back to coerceObservationToSummary in that branch too, mirroring the unmatched-<summary> path. Adds a test covering the empty-summary-wraps-observation case and a guard test for empty summary with no observation content. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: thedotmack <683968+thedotmack@users.noreply.github.com> Co-authored-by: Alex Newman <thedotmack@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
b8d63d949f |
fix(worktree): self-heal Chroma metadata on re-run
Addresses unresolved CodeRabbit finding on WorktreeAdoption.ts:296. Previously, Chroma patch failures stranded rows permanently: adoptedSqliteIds was built only from rows where merged_into_project IS NULL, so once SQL committed, reruns couldn't rediscover them for retry. The Chroma id set is now built from ALL observations whose project matches a merged worktree — including rows already stamped to this parent. Combined with the idempotent updateMergedIntoProject, transient Chroma failures self-heal on the next adoption pass. SQL writes remain idempotent (UPDATE still guards on merged_into_project IS NULL), so adoptedObservations / adoptedSummaries continue to count only newly-adopted rows. chromaUpdates now counts total Chroma writes per pass (may exceed adoptedObservations when retrying). |
||
|
|
7a66cb310f |
fix(worktree): address PR review — schema guard, startup adoption, query parity
Addresses six CodeRabbit/Greptile findings on PR #2052: - Schema guard in adoptMergedWorktrees probes for merged_into_project columns before preparing statements; returns early when absent so first boot after upgrade (pre-migration) doesn't silently fail. - Startup adoption now iterates distinct cwds from pending_messages and dedupes via resolveMainRepoPath — the worker daemon runs with cwd=plugin scripts dir, so process.cwd() fallback was a no-op. - ObservationCompiler single-project queries (queryObservations / querySummaries) OR merged_into_project into WHERE so injected context surfaces adopted worktree rows, matching the Multi variants. - SessionStore constructor now calls ensureMergedIntoProjectColumns so bundled artifacts (context-generator.cjs) that embed SessionStore get the merged_into_project column on DBs that only went through the bundled migration chain. - OBSERVER_SESSIONS_PROJECT constant is now derived from basename(OBSERVER_SESSIONS_DIR) and used across PaginationHelper, SessionStore, and timeline queries instead of hardcoded strings. - Corrected misleading Chroma retry docstring in WorktreeAdoption to match actual behavior (no auto-retry once SQL commits). |
||
|
|
d1601123fd |
feat(ui): hide observer-sessions project from UI lists
Observer sessions (internal SDK-driven worker queries) run under a synthetic project name 'observer-sessions' to keep them out of claude --resume. They were still surfacing in the viewer project picker and unfiltered observation/summary/prompt feeds. Filter them out at every UI-facing query: - SessionStore.getAllProjects and getProjectCatalog - timeline/queries.ts getAllProjects - PaginationHelper observations/summaries/prompts when no project is selected When a caller explicitly requests project='observer-sessions', results are still returned (not a hard ban, just hidden by default). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f6fda8fff4 |
fix(worktree): address CodeRabbit PR review feedback
- Document --branch override in npx-cli help text - Guard ContextBuilder against empty projects[] override; fall back to cwd-derived primary - Ensure merged_into_project indexes are created even if ALTER ran in a prior partial migration - Reject adopt --branch/--cwd flags with missing or flag-like values - Use defined --color-border-primary token for merged badge border Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
d24f3a7019 |
fix(worktree): address PR review — test assertion, dry-run sentinel, git timeouts
- Update allProjects test expectation to match [parent, composite] (matches JSDoc + callers in ContextBuilder/context handlers). - Replace string-matched __DRY_RUN_ROLLBACK__ sentinel with dedicated DryRunRollback class to avoid swallowing unrelated errors. - Add 5000ms timeout to spawnSync git calls in WorktreeAdoption and ProcessManager so worker startup can't hang on a stuck git process. - Drop unreachable break after process.exit(0) in adopt case. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
bce4ce32ec |
feat(ui): show merged-into-parent badge on adopted observations
ObservationCard renders a secondary "merged → <parent>" chip when merged_into_project is set, next to the existing project label. Both are meaningful: project is origin provenance, merged_into_project is the current home. Extends PaginationHelper's observations and summaries queries with OR merged_into_project = ? so the single-project viewer fetch pulls in adopted rows — the plan's Phase 3 covered multi-project context injection; this is the single-project UI read path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
5664fabce4 |
feat(cli): npx claude-mem adopt [--dry-run] [--branch X]
Adds a manual escape hatch for the worktree adoption engine. Covers squash-merges where git branch --merged HEAD returns nothing, and lets users re-run adoption on demand. Wired through worker-service.cjs (same pattern as generate/clean) so the command runs under Bun with bun:sqlite, keeping npx-cli/ pure Node. --cwd flag passes the user's working directory through the spawn so the engine resolves the correct parent repo. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
0b90495391 |
feat(worktree): auto-adopt merged worktrees on worker startup
Invokes adoptMergedWorktrees() right after runOneTimeCwdRemap() and before dbManager.initialize(), wrapped in try/catch so adoption failures never block startup. Idempotent, so running every startup is cheap — the SQL UPDATE only touches rows where merged_into_project IS NULL. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
3e770df332 |
feat(worktree): query plumbing surfaces merged rows under parent project
ObservationCompiler.queryObservationsMulti and querySummariesMulti WHERE clause extended with OR merged_into_project IN (...), so a parent-project read pulls in rows originally written under any child worktree's composite name once merged. SearchManager wraps the Chroma project filter in \$or so semantic search behaves identically. ChromaSync baseMetadata now carries merged_into_project on new embeddings; existing rows are patched retroactively by the adoption engine. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
a7c3c4af2d |
feat(worktree): adoption engine for merged worktrees
Detects merged worktrees via git (worktree list --porcelain + branch --merged HEAD), then stamps merged_into_project on SQLite observations/summaries and propagates the same metadata to Chroma in lockstep. `project` stays immutable; adoption is a virtual pointer. Idempotent via IS NULL guard on UPDATE and by idempotent Chroma metadata writes. SQL is source of truth — Chroma failures are logged but don't roll back SQL. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
3d1dfcc26a |
feat(migration): add merged_into_project column for worktree adoption
Nullable pointer on observations and session_summaries that lets a worktree's rows surface under the parent project's observation list without data movement. Self-idempotent via PRAGMA table_info guard; does not bump schema_versions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
dc198d5677 |
feat(worktree): include parent repo observations in worktree read scope
Worktrees are branches off main; the parent holds the architecture, decisions, and long-tail history the worktree inherits. Scoping reads to the worktree alone meant every new worktree started cold on any question that required prior context. Expand `allProjects` in a worktree to `[parent, composite]` so reads pull both. Writes still go through `.primary` (the composite), so sibling worktrees don't leak into each other. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
193e7e0719 |
feat(worktree): auto-apply cwd-based project remap on worker startup
Ports scripts/cwd-remap.ts into ProcessManager.runOneTimeCwdRemap() and invokes it in initializeBackground() alongside the existing chroma migration. Uses pending_messages.cwd as the source of truth to rewrite pre-worktree bare project names into the parent/worktree composite format so search and context are consistent. - Backs up the DB to .bak-cwd-remap-<ts> before any writes. - Idempotent: marker file .cwd-remap-applied-v1 short-circuits reruns. - No-ops on fresh installs (no DB, or no pending_messages table). - On failure, logs and skips the marker so the next restart retries. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
9d695f53ed |
chore: remove auto-generated per-directory CLAUDE.md files
Leftover artifacts from an abandoned context-injection feature. The project-level CLAUDE.md stays; the directory-level ones were generated timeline scaffolding that never panned out. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
a65ab055ca |
fix(worktree): audit observation fetch/display for composite project names
Three sites didn't account for parent/worktree composite naming: - PaginationHelper.stripProjectPath: marker used full composite, breaking path sanitization for worktrees checked out outside a parent/leaf layout. Now extracts the leaf segment. - observations/store.ts: fallback imported getCurrentProjectName from shared/paths.ts (a duplicate impl without worktree detection). Switched to getProjectContext().primary so writes key into the same project as reads. - SearchManager.getRecentContext: fallback used basename(cwd) and lost the parent prefix, making the MCP tool find nothing in worktrees. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
3869b083d0 |
fix(context): derive project from explicit projects array, not cwd
When a caller (e.g. worker context-inject route) passes a `projects` array without a matching cwd, the cwd-derived `context.primary` drifted from the projects being queried — producing an empty-state header for one project while querying another. Use the last entry of `projects` so header and query target stay in sync. |
||
|
|
040729beef |
fix(project-name): use parent/worktree composite so observations don't cross worktrees
Revert of #1820 behavior. Each worktree now gets its own bucket: - In a worktree, primary = `parent/worktree` (e.g. `claude-mem/dar-es-salaam`) - In a main repo, primary = basename (unchanged) - allProjects is always `[primary]` — strict isolation at query time Includes a one-off maintenance script (scripts/worktree-remap.ts) that retroactively reattributes past sessions to their worktree using path signals in observations and user prompts. Two-rule inference keeps the remap high-confidence: 1. The worktree basename in the path matches the session's current plain project name (pre-#1820 era; trusted). 2. Or all worktree path signals converge on a single (parent, worktree) across the session. Ambiguous sessions are skipped. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
c76a439491 |
fix: drop orphan flag when filtering empty-string spawn args (#2049)
Observations were 100% failing on Claude Code 2.1.109+ because the Agent SDK emits ["--setting-sources", ""] when settingSources defaults to []. The existing Bun-workaround filter stripped the empty string but left the orphan --setting-sources flag, which then consumed --permission-mode as its value, crashing the subprocess with: Error processing --setting-sources: Invalid setting source: --permission-mode. Make the filter pair-aware: when an empty arg follows a --flag, drop both so the SDK default (no setting sources) is preserved by omission. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
aa7cdb6d9f |
fix: revert unauthorized $CMEM branding in context header
A prior Claude instance snuck in a `$CMEM` token branding header
during a context compression refactor (
|
||
|
|
d0fc68c630 |
revert: remove overengineered summary salvage logic (#1718) (#1850)
The synthetic summary salvage feature created fake summaries from observation data when the AI returned <observation> instead of <summary> tags. This was overengineered — missing a summary is preferable to fabricating one from observation fields that don't map cleanly to summary semantics. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
05232ff091 |
fix: reap stuck generators in reapStaleSessions (fixes #1652) (#1698)
* fix: reap stuck generators in reapStaleSessions (fixes #1652) Sessions whose SDK subprocess hung would stay in the active sessions map forever because `reapStaleSessions()` unconditionally skipped any session with a non-null `generatorPromise`. The generator was blocked on `for await (const msg of queryResult)` inside SDKAgent and could never unblock itself — the idle-timeout only fires when the generator is in `waitForMessage()`, and the orphan reaper skips processes whose session is still in the map. Add `MAX_GENERATOR_IDLE_MS` (5 min). When `reapStaleSessions()` sees a session whose `generatorPromise` is set but `lastGeneratorActivity` has not advanced in over 5 minutes, it now: 1. SIGKILLs the tracked subprocess to unblock the stuck `for await` 2. Calls `session.abortController.abort()` so the generator loop exits 3. Calls `deleteSession()` which waits up to 30 s for the generator to finish, then cleans up supervisor-tracked children Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: freeze time in stale-generator test and import constants from production source - Export MAX_GENERATOR_IDLE_MS, MAX_SESSION_IDLE_MS, StaleGeneratorCandidate, StaleGeneratorProcess, and detectStaleGenerator from SessionManager.ts so tests no longer duplicate production constants or detection logic. - Use setSystemTime() from bun:test to freeze Date.now() in the "exactly at threshold" test, eliminating the flaky double-Date.now() race. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
f97c50bfb9 |
fix: session lifecycle guards to prevent runaway API spend (#1590) (#1693)
* fix: add session lifecycle guards to prevent runaway API spend (#1590) Three root causes allowed 30+ subprocess accumulation over 36 hours: 1. SIGTERM-killed processes (code 143) triggered crash recovery and immediately respawned — now detected and treated as intentional termination (aborts controller so wasAborted=true in .finally). 2. No wall-clock limit: sessions ran for 13+ hours continuously spending tokens — now refuses new generators after 4 hours and drains the pending queue to prevent further spawning. 3. Duplicate --resume processes for the same session UUID — now killed and unregistered before a new spawn is registered. Generated by Claude Code Vibe coded by ousamabenyounes Co-Authored-By: Claude <noreply@anthropic.com> * fix: use normalized errorMsg in logger.error payload and annotate SIGTERM override Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: use persisted createdAt for wall-clock guard and bind abortController locally to prevent stale abort Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: re-trigger CodeRabbit review after rate limit reset * fix: defer process unregistration until exit and align boundary test with strict > (#1693) - ProcessRegistry: don't unregister PID immediately after SIGTERM — let the existing 'exit' handler clean up when the process actually exits, preventing tracking loss for still-live processes. - Test: align wall-clock boundary test with production's strict `>` operator (exactly 4h is NOT terminated, only >4h is). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
983be42998 |
fix: resolve Gemini CLI 0.37.0 session capture failures (#1664) (#1692)
Three root causes prevented Gemini sessions from persisting prompts, observations, and summaries: 1. BeforeAgent was mapped to user-message (display-only) instead of session-init (which initialises the session and starts the SDK agent). 2. The transcript parser expected Claude Code JSONL (type: "assistant") but Gemini CLI 0.37.0 writes a JSON document with a messages array where assistant entries carry type: "gemini". extractLastMessage now detects the format and routes to the correct parser, preserving full backward compatibility with Claude Code JSONL transcripts. 3. The summarize handler omitted platformSource from the /api/sessions/summarize request body, causing sessions to be recorded without the gemini-cli source tag. Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
16a0737dfc |
fix: use parent project name for worktree observation writes (#1820)
* fix: use parent project name for worktree observation writes (#1819) Observations and sessions from git worktrees were stored under basename(cwd) instead of the parent repo name because write paths called getProjectName() (not worktree-aware) instead of getProjectContext() (worktree-aware). This is the same bug as #1081, #1317, and #1500 — it regressed because the two functions coexist and new code reached for the simpler one. Fix: getProjectContext() now returns parentProjectName as primary when in a worktree, and all four write-path call sites now use getProjectContext().primary instead of getProjectName(). Includes regression test that creates a real worktree directory structure and asserts primary === parentProjectName. * fix: address review nitpicks — allProjects fallback, JSDoc, write-path test - ContextBuilder: default projects to context.allProjects for legacy worktree-labeled record compatibility - ProjectContext: clarify JSDoc that primary is canonical (parent repo in worktrees) - Tests: add write-path regression test mirroring session-init/SessionRoutes pattern; refactor worktree fixture into beforeAll/afterAll * refactor(project-name): rename local to cwdProjectName and dedupe allProjects Addresses final CodeRabbit nitpick: disambiguates the local variable from the returned `primary` field, and dedupes allProjects via Set in case parent and cwd resolve to the same name. --------- Co-authored-by: Ethan Hurst <ethan.hurst@outlook.com.au> |
||
|
|
3d92684e04 |
fix: filter empty string args before Bun spawn() to prevent CLI parsing errors (#1781)
Bun's child_process.spawn() silently drops empty string arguments from
argv, unlike Node which preserves them. When the Agent SDK defaults
settingSources to [] (empty array), [].join(",") produces "" which gets
pushed as ["--setting-sources", ""]. Bun drops the "", causing
--permission-mode to be consumed as the value for --setting-sources:
Error processing --setting-sources: Invalid setting source: --permission-mode
This caused 100% observation failure (exit code 1 on every SDK subprocess
spawn), resulting in 0 observations stored across all sessions.
The fix filters empty string args before passing to spawn(), making the
behavior consistent between Node and Bun runtimes.
Fixes #1779
Related: #1660
Co-authored-by: bswnth48 <69203760+bswnth48@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
||
|
|
471e1f62f9 |
Fix npx search and default Codex context to workspace-local AGENTS (#1780)
* Fix npx search query parameter mismatch * Use workspace-local Codex AGENTS context by default --------- Co-authored-by: bnb <bnb> |
||
|
|
eeb6841033 |
fix: coerce corpus route filters (#1776)
* fix: coerce corpus route filters * test: cover unsupported corpus type filters |
||
|
|
2a2008bac2 |
fix(file-context): preserve targeted reads + invalidate on mtime (#1719) (#1729)
* fix(file-context): preserve targeted reads + invalidate on mtime (#1719) The PreToolUse:Read hook unconditionally rewrote tool input to {file_path, limit:1}, which interacted with two failure modes: 1. Subagent edits a file → parent's next Read still gets truncated because the observation snapshot predates the change. 2. Claude requests a different section with offset/limit → the hook strips them, so the Claude Code harness's read-dedup cache returns "File unchanged" against the prior 1-line read. The file becomes unreadable for the rest of the conversation, even though the hook's own recovery hint says "Read again with offset/limit for the section you need." Two complementary fixes: - **mtime invalidation**: stat the file (we already stat for the size gate) and compare mtimeMs to the newest observation's created_at_epoch. If the file is newer, pass the read through unchanged so fresh content reaches Claude. - **Targeted-read pass-through**: when toolInput already specifies offset and/or limit, preserve them in updatedInput instead of collapsing to {limit:1}. The harness's dedup cache then sees a distinct input and lets the read proceed. The unconstrained-read path (no offset, no limit) is unchanged: still truncated to 1 line plus the observation timeline, so token economics are preserved for the common case. Tests cover all three branches: existing truncation, targeted-read pass-through (offset+limit, limit-only), and mtime-driven bypass. Fixes #1719 * refactor(file-context): address review findings on #1719 fix - Add offset-only test case for full targeted-read branch coverage - Use >= for mtime comparison to handle same-millisecond edge case - Add Number.isFinite() + bounds guards on offset/limit pass-through - Trim over-verbose comments to concise single-line summaries - Remove redundant `as number` casts after typeof narrowing - Add comment explaining fileMtimeMs=0 sentinel invariant |
||
|
|
59ce0fc553 |
fix: exclude primary key index from unique constraint check in migration 7 (#1771)
* fix: exclude primary key index from unique constraint check in migration 7 PRAGMA index_list returns all indexes including those backing PRIMARY KEY columns (origin='pk'), which always have unique=1. The check was therefore always true, causing migration 7 to run the full DROP/CREATE/RENAME table sequence on every worker startup instead of short-circuiting once the UNIQUE constraint had already been removed. Fix: filter to non-PK indexes by requiring idx.origin !== 'pk'. The origin field is already present on the existing IndexInfo interface. Fixes #1749 * fix: apply pk-origin guard to all three migration code paths CodeRabbit correctly identified that the origin !== 'pk' fix was only applied to MigrationRunner.ts but not to the two other active code paths that run the same removeSessionSummariesUniqueConstraint logic: - SessionStore.ts:220 — used by DatabaseManager and worker-service - plugin/scripts/context-generator.cjs — bundled artifact (minified) All three paths now consistently exclude primary-key indexes when detecting unique constraints on session_summaries. |
||
|
|
31ee1024c5 |
fix: restrict ~/.claude-mem/.env permissions to owner-only (0600) (#1770)
* fix: restrict .env file permissions to owner-only (0600) API keys stored in ~/.claude-mem/.env were created without explicit permissions, defaulting to umask-dependent mode. On systems with a permissive umask (e.g. 0022), the file would be world-readable. - Set directory permissions to 0700 on creation - Set file permissions to 0600 via writeFileSync mode option - Call chmodSync after write to fix permissions on pre-existing files Signed-off-by: Jochen Meyer * fix: also restrict pre-existing directory permissions to 0700 The initial fix only set directory mode on creation. Pre-existing ~/.claude-mem/ directories from earlier installs remained world-readable. Add chmodSync for the directory alongside the existing file chmod, and document the Windows limitation (ACLs, not POSIX permissions). --------- Signed-off-by: Jochen Meyer |
||
|
|
a390a537c9 |
fix: broadcast uses summaryForStore to support salvaged summaries (#1718)
syncAndBroadcastSummary was using the raw ParsedSummary (null when salvaged) instead of summaryForStore for the SSE broadcast, causing a crash when the LLM returns <observation> without <summary> tags. Also removes misplaced tree-sitter docs from mem-search/SKILL.md (belongs in smart-explore). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
2357835942 |
Merge pull request #1686 from ousamabenyounes/fix/issue-1633
fix: expose summaryStored in session status to detect silent summary loss (#1633) |
||
|
|
77a22d30b2 |
Merge pull request #1555 from ousamabenyounes/fix/issue-1384-mcp-inputschema
fix: declare inputSchema properties for search and timeline MCP tools (#1384 #1413) |
||
|
|
40a25e0225 |
Merge pull request #1676 from ousamabenyounes/fix/issue-1625
fix: filter ghost observations with no content fields (#1625) |
||
|
|
4c2ab98d90 |
Merge pull request #1679 from ousamabenyounes/fix/issue-1297
fix: set cwd to homedir when spawning chroma-mcp to prevent pydantic .env.local crash (#1297) |
||
|
|
216d17879d |
Merge pull request #1680 from ousamabenyounes/fix/issue-1447
fix: suppress false ERROR when duplicate daemon loses port bind race (#1447) |
||
|
|
9a91a1be2b |
Merge pull request #1701 from ck0park/fix/list-corpora-mcp-result-shape
fix: list_corpora MCP tool — wrap response in CallToolResult shape (fixes #1700) |
||
|
|
b7c23ca232 |
fix(ResponseProcessor): salvage synthetic summary when AI returns <observation> instead of <summary>
Fixes Issue #1312: AI sometimes returns <observation> XML tags instead of <summary> tags during the summarize phase, despite clear instructions in buildSummaryPrompt() requiring <summary> ONLY output. When this occurs, parseSummary() returns null and the entire session summary is lost. This fix detects the condition (summary missing + observations present) and synthesizes a summary from the observation data, ensuring session summaries are not completely lost. The salvage mapping: - request: observation title - investigated: observation narrative or facts - learned: observation facts joined - completed: title if type is feature/bugfix - notes: indicates this is a synthetic salvage summary Observations are stored normally regardless of this fallback. Co-authored-by: Sisyphus <sisyphus@openclaw> |
||
|
|
edc8535ac1 | fix: skip queueLength===0 completion branch when session returns 404 | ||
|
|
ad127bec40 |
fix: wrap list_corpora response in MCP CallToolResult shape (fixes #1700)
GET /api/corpus returned a bare array, which the MCP server wrapper
(callWorkerAPI) forwards directly. MCP's tools/call validation rejects
non-object results with "expected object, received array", so the
list_corpora MCP tool was completely unusable.
Every other corpus endpoint is a POST that already returns the
{content:[...]} shape, so this is a targeted one-file fix.
|
||
|
|
2f19eab9c2 |
fix: expose summaryStored in session status to detect silent summary loss (#1633)
Stop hook polled queueLength===0 as a proxy for summary success, but the queue empties regardless of whether the LLM produced valid <summary> tags. Added lastSummaryStored tracking on ActiveSession, surfaced via the /api/sessions/status endpoint, and emit a logger.warn in the Stop hook when summaryStored===false. Generated by Claude Code Vibe coded by ousamabenyounes Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
08cf2ba3bd |
fix: suppress false ERROR when duplicate daemon loses port bind race (#1447)
When the MCP server and SessionStart hook both spawn a worker daemon concurrently, one loses the bind race (EADDRINUSE / Bun's port-in-use error). The loser now checks if the winner is healthy; if so, it logs INFO and exits cleanly instead of logging a misleading ERROR on every first session start. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
c7c4fd54d6 |
fix: set cwd to homedir when spawning chroma-mcp to prevent pydantic .env.local crash (#1297)
chroma-mcp uses pydantic-settings which auto-reads .env/.env.local from the CWD. When the project directory contains non-chroma variables (e.g. CELERY_TASK_ALWAYS_EAGER), pydantic rejects them with "Extra inputs are not permitted", crashing the subprocess and triggering a permanent backoff loop. Passing cwd: os.homedir() to StdioClientTransport ensures pydantic never reads project env files. Generated by Claude Code Vibe coded by ousamabenyounes Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
e398212983 |
fix: filter ghost observations with no content fields (#1625)
When the LLM is overwhelmed by large context it can emit bare <observation/> blocks (or ones containing only <type>). These are stored as rows where title, narrative, facts and concepts are all null/empty, appearing as meaningless "Untitled" entries in the context window. Add a guard in parseObservations() that skips any observation where every content field is null/empty before pushing it to the result array. Generated by Claude Code Vibe coded by ousamabenyounes Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
c648d5d8d2 |
feat: Knowledge Agents — queryable corpora from claude-mem (#1653)
* feat: add knowledge agent types, store, builder, and renderer Phase 1 of Knowledge Agents feature. Introduces corpus compilation pipeline that filters observations from the database into portable corpus files stored at ~/.claude-mem/corpora/. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add corpus CRUD HTTP endpoints and wire into worker service Phase 2 of Knowledge Agents. Adds CorpusRoutes with 5 endpoints (build, list, get, delete, rebuild) and registers them during worker background initialization alongside SearchRoutes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add KnowledgeAgent with V1 SDK prime/query/reprime Phase 3 of Knowledge Agents. Uses Agent SDK V1 query() with resume and disallowedTools for Q&A-only knowledge sessions. Auto-reprimes on session expiry. Adds prime, query, and reprime HTTP endpoints to CorpusRoutes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add MCP tools and skill for knowledge agents Phase 4 of Knowledge Agents. Adds build_corpus, list_corpora, prime_corpus, and query_corpus MCP tools delegating to worker HTTP endpoints. Includes /knowledge-agent skill with workflow docs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: handle SDK process exit in KnowledgeAgent, add e2e test The Agent SDK may throw after yielding all messages when the Claude process exits with a non-zero code. Now tolerates this if session_id/answer were already captured. Adds comprehensive e2e test script (31 assertions) orchestrated via tmux-cli. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use settings model ID instead of hardcoded model in KnowledgeAgent Reads CLAUDE_MEM_MODEL from user settings via getModelId(), matching the existing SDKAgent pattern. No more hardcoded model assumptions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: improve knowledge agents developer experience Add public documentation page, rebuild/reprime MCP tools, and actionable error messages. DX review scored knowledge agents 4/10 — core engineering works (31/31 e2e) but the feature was invisible. This addresses discoverability (docs, cross-links), API completeness (missing MCP tools), and error quality (fix/example fields in error responses). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add quick start guide to knowledge agents page Covers the three main use cases upfront: creating an agent, asking a single question, and starting a fresh conversation with reprime. Includes keeping-it-current section for rebuild + reprime workflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address code review issues — path traversal, session safety, prompt injection - Block path traversal in CorpusStore with alphanumeric name validation and resolved path check - Harden system prompt against instruction injection from untrusted corpus content - Validate question field as non-empty string in query endpoint - Only persist session_id after successful prime (not null on failure) - Persist refreshed session_id after query execution - Only auto-reprime on session resume errors, not all query failures - Add fenced code block language tags to SKILL.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address remaining code review issues — e2e robustness, MCP validation, docs - Harden e2e curl wrappers with connect-timeout, fallback to HTTP 000 on transport failure - Use curl_post wrapper consistently for all long-running POST calls - Add runtime name validation to all corpus MCP tool handlers - Fix docs: soften hallucination guarantee to probabilistic claim - Fix architecture diagram: add missing rebuild_corpus and reprime_corpus tools Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: enforce string[] type in safeParseJsonArray for corpus data integrity Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add blank line before fenced code blocks in SKILL.md maintenance section Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
07be61cf91 |
feat: support ANTHROPIC_BASE_URL in EnvManager (#1627)
* feat: add custom OpenRouter base URL support Allow users to configure a custom base URL for OpenRouter API calls through settings UI and environment management. Generated with AI Co-Authored-By: AI Partner * refactor: remove OpenRouter base URL customization, keep Claude URL changes Only retain ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN support in EnvManager for custom Claude API endpoint configuration. Generated with AI Co-Authored-By: AI Partner * chore: revert build artifacts to match main Generated with AI Co-Authored-By: AI Partner * fix: remove ANTHROPIC_AUTH_TOKEN, add ANTHROPIC_BASE_URL persistence - Remove unnecessary ANTHROPIC_AUTH_TOKEN (inherited from parent process) - Add ANTHROPIC_BASE_URL to saveClaudeMemEnv() to fix config persistence - Keep only ANTHROPIC_BASE_URL support for custom API endpoint Generated with AI Co-Authored-By: AI Partner |
||
|
|
f7fd2221c8 |
fix: rebuild FTS5 index after bulk observation import (fixes #1631) (#1632)
Imported observations were invisible to the MCP search tool because the FTS5 content table was not reliably updated during bulk import. The import handler now calls rebuildObservationsFTSIndex() after inserting new observations, ensuring the full-text search index is consistent. A new SessionStore.rebuildObservationsFTSIndex() method encapsulates the FTS5 rebuild command and is a no-op when the observations_fts table does not exist (e.g. FTS5 unavailable on Windows). |
||
|
|
abd55977ca |
fix(mcp): MCP server crashes with Cannot find module 'bun:sqlite' under Node (#1645)
* fix(mcp): MCP server crashes with Cannot find module 'bun:sqlite' under Node
The MCP server bundle (mcp-server.cjs) ships with `#!/usr/bin/env node` so
it must run under Node, but commit
|