Commit Graph

1761 Commits

Author SHA1 Message Date
Alex Newman
de6139660b chore: gitignore runtime state files (#2075)
* chore: gitignore runtime state files

.claude/scheduled_tasks.lock is a PID+sessionId lock written by Claude
Code's cron scheduler every session. It got accidentally checked in during
the v12.0.0 bump and has been churning phantom diffs in every PR since.
Untrack it and ignore.

plugin/.cli-installed is a timestamp marker the claude-mem installer drops
to record when the plugin was installed. Never belonged in version control.
Ignore it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: add trailing newline to .gitignore

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 16:50:54 -07:00
Alex Newman
dc906e5c00 docs: update CHANGELOG.md for v12.2.3 2026-04-19 16:35:01 -07:00
Alex Newman
bbfb65668f chore: bump version to 12.2.3 v12.2.3 2026-04-19 16:30:52 -07:00
Alex Newman
2337997c48 fix(parser): stop warning on normal observation responses (#2074)
parseSummary runs on every agent response, not just summary turns. When the
turn is a normal observation, the LLM correctly emits <observation> and no
<summary> — but the fallthrough branch from #1345 treated this as prompt
misbehavior and logged "prompt conditioning may need strengthening" every
time. That assumption stopped holding after #1633 refactored the caller to
always invoke parseSummary with a coerceFromObservation flag.

Gate the whole observation-on-summary path on coerceFromObservation. On a
real summary turn, coercion still runs and logs the legitimate "coercion
failed" warning when the response has no usable content. On an observation
turn, parseSummary returns null silently, which is the correct behavior.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 16:30:05 -07:00
Alex Newman
e6e5751ef5 docs: update CHANGELOG.md for v12.2.2 2026-04-19 15:03:41 -07:00
Alex Newman
b1dfec0f43 chore: bump version to 12.2.2 v12.2.2 2026-04-19 14:58:55 -07:00
Alex Newman
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>
2026-04-19 14:58:01 -07:00
Alex Newman
306a0b1de9 docs: update CHANGELOG.md for v12.2.1
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 12:02:16 -07:00
Alex Newman
be730a09ac chore: bump version to 12.2.1
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v12.2.1
2026-04-19 12:01:43 -07:00
Copilot
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>
2026-04-19 12:00:38 -07:00
Alex Newman
beea7899b9 chore: remove conductor.json shim
Conductor workspace setup is no longer needed - plugins handle hook
registration directly via plugin/hooks/hooks.json. The shim was copying
a stale settings.local.json into every worktree, registering dead hook
paths (save-hook.js, new-hook.js, summary-hook.js) that no longer exist.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 23:56:22 -07:00
Alex Newman
a1741f4322 docs: update CHANGELOG.md for v12.2.0 + make generator incremental
Script now reads existing CHANGELOG.md, skips releases already documented,
only fetches bodies for new releases, and prepends them. Pass --full to
force complete regeneration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 20:21:51 -07:00
Alex Newman
11666e9ffb chore: bump version to 12.2.0 v12.2.0 2026-04-17 20:14:22 -07:00
Alex Newman
fb8e526c55 Merge pull request #2052 from thedotmack/thedotmack/worktree-remap
feat(worktree): scope per worktree, cwd backfill, and merged-worktree adoption
2026-04-17 20:10:54 -07:00
Alex Newman
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).
2026-04-16 22:01:21 -07:00
Alex Newman
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).
2026-04-16 21:31:30 -07:00
Alex Newman
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>
2026-04-16 20:05:37 -07:00
Alex Newman
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>
2026-04-16 20:03:27 -07:00
Alex Newman
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>
2026-04-16 19:50:01 -07:00
Alex Newman
0a5f072aaf build(worktree): rebuild plugin artifacts for worktree adoption feature
Regenerated worker-service.cjs, context-generator.cjs, viewer.html, and
viewer-bundle.js to reflect all six implementation phases of the merged-
worktree adoption feature.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 19:36:00 -07:00
Alex Newman
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>
2026-04-16 19:33:47 -07:00
Alex Newman
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>
2026-04-16 19:28:17 -07:00
Alex Newman
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>
2026-04-16 19:24:43 -07:00
Alex Newman
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>
2026-04-16 19:24:10 -07:00
Alex Newman
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>
2026-04-16 19:19:02 -07:00
Alex Newman
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>
2026-04-16 19:12:38 -07:00
Alex Newman
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>
2026-04-16 17:55:17 -07:00
Alex Newman
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>
2026-04-16 17:51:33 -07:00
Alex Newman
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>
2026-04-16 17:51:24 -07:00
Alex Newman
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>
2026-04-16 17:38:49 -07:00
Alex Newman
d589bc5f25 fix(cwd-remap): address PR review feedback
- Handle bare repo common-dir (strip trailing .git) instead of an
  identical-branch ternary
- Surface unexpected git stderr while keeping "not a git repository"
  silent
- Explicitly close the sqlite handle in both dry-run and apply paths
  so WAL checkpoints complete

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 17:21:49 -07:00
Alex Newman
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.
2026-04-16 17:16:51 -07:00
Alex Newman
148e1892df chore(scripts): replace worktree-remap with cwd-based remap using pending_messages.cwd
The old worktree-remap.ts tried to reconstruct per-session cwd by regex-
matching absolute paths that incidentally leaked into observation free-text
(files_read, source_input_summary, metadata, user_prompt). That source is
derived and lossy: it only hit 1/3498 plain-project sessions in practice.

pending_messages.cwd is the structured, authoritative cwd captured from
every hook payload — 7,935 of 8,473 rows are populated. cwd-remap.ts uses
that column as the source of truth:

  1. Pull every distinct cwd from pending_messages.cwd
  2. For each cwd, classify with git:
       - rev-parse --absolute-git-dir vs --git-common-dir → main vs worktree
       - rev-parse --show-toplevel for the correct leaf (handles cwds that
         are subdirs of the worktree root)
     Parent project name = basename(dirname(common-dir)); composite is
     parent/worktree for worktrees, basename(toplevel) for main repos.
  3. For each session, take the EARLIEST pending_messages.cwd (not the
     dominant one — claude-mem's own hooks run from nested .context/
     claude-mem/ directories and would otherwise poison the count).
  4. Apply UPDATEs in a single transaction across sdk_sessions,
     observations, and session_summaries. Auto-backs-up the DB first.

Result on a real DB: 41 sessions remapped (vs 1 previously),
1,694 observations and 3,091 session_summaries updated to match.
43 cwds skipped (deleted worktrees / non-repos) are left untouched —
no inference when the data isn't there.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-16 16:40:37 -07:00
Alex Newman
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>
2026-04-16 15:40:44 -07:00
Alex Newman
53622b59e9 docs: update CHANGELOG.md for v12.1.6
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 14:35:40 -07:00
Alex Newman
69080dc291 chore: bump version to 12.1.6
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v12.1.6
2026-04-16 14:31:43 -07:00
Alex Newman
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>
2026-04-16 14:30:54 -07:00
Alex Newman
70a150db74 docs: update CHANGELOG.md for v12.1.5 2026-04-15 14:41:31 -07:00
Alex Newman
d7b4610e27 chore: bump version to 12.1.5 v12.1.5 2026-04-15 14:40:44 -07:00
Alex Newman
88bb4e589e docs: update CHANGELOG.md for v12.1.4
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 12:10:29 -07:00
Alex Newman
ebefae864e chore: bump version to 12.1.4
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
v12.1.4
2026-04-15 12:06:52 -07:00
Alex Newman
0cd931bb06 Merge pull request #1865 from thedotmack/thedotmack/find-cmem-refs
fix: revert unauthorized $CMEM branding in context header
2026-04-15 12:06:10 -07:00
Alex Newman
4c792f026d build: rebuild plugin artifacts after $CMEM header revert
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 12:05:49 -07:00
Alex Newman
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 (7e072106). Reverts back to
the original descriptive format: `# [project] recent context, datetime`

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 12:04:27 -07:00
Alex Newman
5db90f2ea0 docs: update CHANGELOG.md for v12.1.3 2026-04-15 11:43:49 -07:00
Alex Newman
4ddf57610a chore: bump version to 12.1.3 v12.1.3 2026-04-15 04:26:29 -07:00
Alex Newman
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>
2026-04-15 04:22:41 -07:00
Alex Newman
1d7500604f chore: bump version to 12.1.2 v12.1.2 2026-04-15 01:00:38 -07:00
Ben Younes
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>
2026-04-15 00:58:35 -07:00
Ben Younes
b411d91885 fix: add circuit breaker to OpenClaw worker client (#1636) (#1697)
* fix: add circuit breaker to OpenClaw worker client (#1636)

When the claude-mem worker is unreachable, every plugin event (before_agent_start,
before_prompt_build, tool_result_persist, agent_end) triggered a new fetch that
failed and logged a warning, causing CPU-spinning and continuous log spam.

Add a CLOSED/OPEN/HALF_OPEN circuit breaker: after 3 consecutive network errors
the circuit opens, silently drops all worker calls for 30 s, then sends one probe.
Individual failures are only logged while the circuit is still CLOSED; once open
it logs once ("disabling requests for 30s") and goes quiet until recovery.

Generated by Claude Code
Vibe coded by Ousama Ben Younes

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: limit HALF_OPEN to single probe and move circuitOnSuccess after response.ok check

- Add _halfOpenProbeInFlight flag so only one probe is allowed in HALF_OPEN state;
  concurrent callers are silently dropped until the probe completes (success or failure)
- Move circuitOnSuccess() to after the response.ok check in workerPost, workerPostFireAndForget,
  and workerGetText so non-2xx HTTP responses no longer close the circuit
- Clear _halfOpenProbeInFlight in both circuitOnSuccess and circuitOnFailure, and in circuitReset
- Add regression test covering HALF_OPEN one-probe behavior: non-2xx keeps circuit open,
  2xx closes it

* chore: trigger CodeRabbit re-review

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-15 00:58:32 -07:00