831 Commits

Author SHA1 Message Date
Alex Newman adce0fdfaf chore: bump version to 13.25.2 (#4128)
PATCH 13.25.2 — ships two merged fixes:
- #4125 CLAUDE_MEM_LLM_TIMEOUT_MS honored from settings.json; deadline expiry keeps buffered observer work
- #4124 context filter falls back to the mode's types when the configured filter matches nothing

Bundles rebuilt with `npm run build`; #4124 had not been rebuilt into plugin/scripts on main.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 22:48:14 -07:00
Alex Newman 4d323afe72 fix(observer): honor CLAUDE_MEM_LLM_TIMEOUT_MS from settings and keep work on deadline expiry (#4125)
* fix(observer): honor CLAUDE_MEM_LLM_TIMEOUT_MS from settings and keep work on deadline expiry

The per-attempt LLM deadline was read from process.env once at module load,
so a value in ~/.claude-mem/settings.json had no effect. It now resolves
env -> settings.json -> 30000 on every withRetry call, and the key is
registered in SettingsDefaultsManager.

A deadline expiry threw a plain Error, so preservingAbortReason returned
null and the session finalized with reason=null, dropping buffered work.
It now throws ClassifiedProviderError kind 'transient', and the
OpenAI-compatible provider maps 'transient' to a transport: pause, which
handleGeneratorExit already preserves.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8tzshoEirtFhFwGi4didL

* fix(observer): accept a numeric CLAUDE_MEM_LLM_TIMEOUT_MS in settings.json

SettingsDefaultsManager.loadFromFile returns JSON values as-is, so a bare
number (e.g. 90000) reached raw.trim() and threw a TypeError before the
retry loop started. Coerce with String(raw) so a number behaves like its
string form; strict whole-integer validation and bounds are unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8tzshoEirtFhFwGi4didL

* fix(observer): reject non-string, non-number CLAUDE_MEM_LLM_TIMEOUT_MS values

String([90000]) reads as "90000" and passed the integer check. Only a
string or a number is accepted now; any other JSON type warns and falls
back to the 30s default.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8tzshoEirtFhFwGi4didL

* build: rebuild plugin bundles for the LLM timeout changes

Rebuilt with npm run build only (no marketplace sync, no worker restart).
worker-service.cjs now carries the settings-based CLAUDE_MEM_LLM_TIMEOUT_MS
resolution, the string-or-number type check, and the 'transient'
classification on deadline expiry.

Most of the diff is minifier identifier renaming across every bundle
(including viewer-bundle.js, which this PR does not touch in src); the
committed bundles were last rebuilt at 96f04a9fe. Generated .map files are
left out, matching main.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8tzshoEirtFhFwGi4didL

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 13:14:34 -07:00
Alex Newman dcfc44221d chore: bump to 13.25.1 (npm stage stuck on 13.25.0) (#4096)
PATCH 13.25.1 — npm stage stuck on 13.25.0; republish path.
2026-09-15 18:00:28 -07:00
Alex Newman 96f04a9fe2 feat(grok-bot): live-write growing Memory INDEX from the worker (#4095)
Live Grok Bot Memory INDEX writer (13.25.0). Worker writes zz-claude-mem-inject.md as observations land; house fallback for thin seat diaries. CCS not required. Windows chroma lifecycle flake known-unrelated (Linux green).
2026-09-15 17:45:28 -07:00
Alex Newman 40be934a84 fix(telegram): wrap-ups on real session end from the final summary, one short bulleted list under 255 chars (#4074)
* fix(telegram): shorten SessionEnd summary wrap-ups

Keep SessionEnd-only delivery and format the entire stored summary through the existing provider and summary-model settings. Replace the template with the exact short-list prompt and cap MarkdownV2 output at complete bullet boundaries.

Verification: npm run build and npx tsc --noEmit -p . passed. All 56 focused tests passed across 5 files, run one file at a time. The orchestrator will run the full suite.

Generated plugin output is byte-identical across consecutive builds.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017DdeSScwheHym2T4xQ8cLW

* fix(telegram): handle wrap-up provider text responses

Request plain-text output without reasoning through the existing OpenRouter summary model path. Accept assistant text blocks and log and reject unusable completions instead of silently producing empty wrap-ups. Preserve ordinary summary request options, the prompt, trigger, and 255-character bullet cap.

Verification: npm run build and npx tsc --noEmit -p . passed. All 72 scoped wrap-up tests passed across 6 files, run one file at a time. The worker bundle is byte-identical across consecutive builds. No full suite or live Telegram send was run.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017DdeSScwheHym2T4xQ8cLW

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-13 02:42:19 -07:00
Alex Newman c61f8be53d feat(telegram): session wrap-up notifications from the Stop summary (plan 26) (#4073)
* docs(plans): plan-26 Telegram session wrap-ups from the Stop summary

Alex, 2026-09-12: "the 'telegram session wrap-ups' are not derived of the
stop hook summary, they're coming at the end of all turns. ALSO NOT scoped
that way".

Verified against this checkout: notifyTelegram fires per observation batch
from the PostToolUse path (ResponseProcessor.ts:623-628), never from the
summary path (:828-890); the plugin registers Stop (per turn) and no
SessionEnd (plugin/hooks/hooks.json); delivery is one global chat with no
route, no ledger. Claude Code docs give SessionEnd a 1.5 s budget that
plugin timeouts cannot raise, so the hook must POST and exit.

Plan: SessionEnd hook + /api/sessions/session-end; wrap-up text is the
latest stored summary (getSummaryForSession, no new model call); SQLite
ledger telegram_wrapups (schema v52) with atomic claim; JSON route map
CLAUDE_MEM_TELEGRAM_WRAPUP_ROUTES with exact/parent match and reject on
unknown; observation alerts behind a new default-off switch. Four Codex
phases, each with exact files, bun tests and a stop line.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017DdeSScwheHym2T4xQ8cLW

* feat(telegram): wrap-up ledger, routes and notifier (plan-26 phase 1)

Baseline failures (pre-existing):
- tests/worker/field-deadline-wire.test.ts — mandatory first-baseline failure
- tests/infrastructure/plugin-distribution.test.ts — mandatory first-baseline failure

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017DdeSScwheHym2T4xQ8cLW

* docs(telegram): mark plan-26 phase 1 complete

* feat(telegram): SessionEnd hook, session-end route and wrap-up request (plan-26 phase 2)

Verification: focused 2/2 + 2/2 + 9/9; CLI+HTTP 176/176; build/tsc green; full npm test 3758 pass, 28 skip, sole pre-existing field-deadline failure. Cold Node probe 0.197/0.172/0.170s failed pre-dispatch due inherited bun:sqlite Node incompatibility.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017DdeSScwheHym2T4xQ8cLW

* docs(telegram): mark plan-26 phase 2 complete

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017DdeSScwheHym2T4xQ8cLW

* feat(telegram): deliver wrap-up after the session-end summary lands (plan-26 phase 3)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017DdeSScwheHym2T4xQ8cLW

* docs(plan): mark telegram wrap-up Phase 3 complete

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017DdeSScwheHym2T4xQ8cLW

* docs(telegram): wrap-up routes, alerts default off; plan-26 verification

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017DdeSScwheHym2T4xQ8cLW

* docs(plan): mark telegram wrap-up Phase 4 complete

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017DdeSScwheHym2T4xQ8cLW

* fix(telegram): recover SessionEnd wrap-up review findings

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

Claude-Session: https://claude.ai/code/session_017DdeSScwheHym2T4xQ8cLW

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-13 01:04:32 -07:00
Mamadou Wane ed2b39b410 harden(observer): deny SendMessage and ListAgents to Observer sessions (#3709)
Every other entry in OBSERVER_DISALLOWED_TOOLS stops the Observer from acting
on the user's machine. SendMessage and ListAgents let a toolless Observer ask
a session that still can — borrowing that session's authority while "you do
not have access to tools" stays literally true. ListAgents is how a session
finds peers to address, so denying the send without denying discovery is half
a boundary.

Both tools are added to the deny-list, the shipped plugin bundle is synced to
the same 14-tool array, and the docblocks now say plainly that the layered
"no single option is load-bearing" guarantee holds on the SDK path only: on
the CLI spawn path (claude --disallowedTools ...) this list is the sole
enforcement, because tools, allowedTools and canUseTool have no command-line
equivalent.

Author: @mamadou-wane
Refs: #3607 (plan-19 Observer Subprocess Isolation)

Verified against origin/main (1ce59d484), which the branch is already rebased
onto: bun test tests -> 3722 pass, 28 skip, 2 fail (both pre-existing on main:
field-deadline-wire real-network test and plugin-distribution npm-tarball test
that needs a build). tsc --noEmit clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015w89Sfxy7rZK9xDWixDPv7
2026-09-12 10:16:34 -07:00
Alex Newman ed57a511f5 chore: bump version to 13.24.23 (#4056)
Mechanical package-facing bump so 13.24.23 ships the supervisor cwd jail and hook project-dir fixes now on main after v13.24.22. Rebuilds plugin bundles so the injected version matches. Does not npm publish.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>
2026-09-11 10:33:46 -07:00
Alex Newman 51a3969fa7 chore: bump version to 13.24.22 (#4053)
Mechanical package-facing bump so 13.24.22 ships the empty-title observation capture fix now on main after v13.24.21. Rebuilds plugin bundles so the injected version matches. Does not npm publish.
2026-09-11 10:19:37 -07:00
Alex Newman 7186eb5608 chore: bump version to 13.24.21 (#4052)
Mechanical package-facing bump so 13.24.21 ships the session rehydration fix now on main after v13.24.20. Rebuilds plugin bundles so the injected version matches. Does not npm publish.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>
2026-09-11 09:56:13 -07:00
Alex Newman 85fc991c65 chore: bump version to 13.24.20 (#4048)
Package-facing patch bump so 13.24.20 ships the commits now on main after v13.24.19. Rebuilds plugin bundles so the injected version matches.

Does not npm publish.
2026-09-11 03:05:54 -07:00
Alex Newman fe4908b1c8 chore: bump version to 13.24.19 (#4043)
Package-facing patch bump so 13.24.19 ships Batch II leftovers now on main after v13.24.18. Rebuilds plugin bundles so the injected version matches.

Does not npm publish.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>
2026-09-11 02:44:51 -07:00
Alex Newman d796d9c620 chore: release 13.24.18 (#4035)
Package-facing patch bump so 13.24.18 ships the commits now on main after v13.24.17. Rebuilds plugin bundles so the injected version matches.

Does not npm publish — Prioritizer publishes from tag.
2026-09-11 02:16:53 -07:00
Alex Newman 50c9d977eb chore: bump version to 13.24.17 (#4028)
Package-facing patch bump so 13.24.17 ships #4026/#3575 (health probe deadline cap), #3445 (desktop-bundled Codex CLI on macOS), and #4027 (register memory_session_id, do not re-register). Rebuilds plugin bundles so the injected version matches.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>
2026-09-11 01:41:03 -07:00
Alex Newman 033667ed9a chore: bump version to 13.24.16 (#4023)
Package-facing patch bump so 13.24.16 ships #3629 (NULL memory_session_id), #3640 (runtime-only marketplace package.json), and #3631 (dead-transport Chroma handshake). Rebuilds plugin bundles so the injected version matches.
2026-09-11 01:02:12 -07:00
Alex Newman 2f4de2ba25 chore: bump version to 13.24.15 (#4022)
Package-facing patch bump so 13.24.15 ships #3727 / #3706 (daemon no longer inherits user project cwd). Rebuilds plugin bundles so the injected version matches.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>
2026-09-11 00:30:03 -07:00
Alex Newman dc36ee7d45 chore: bump version to 13.24.14 (#4020)
Package-facing patch bump so 13.24.14 ships the commits now on main after v13.24.13. Rebuilds plugin bundles so the injected version matches.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>
2026-09-11 00:10:44 -07:00
Alex Newman c51d62a4db chore: bump version to 13.24.13 (#4013)
Package-facing patch bump so 13.24.13 ships the commits now on main after v13.24.12. Rebuilds plugin bundles so the injected version matches.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>
2026-09-10 23:35:17 -07:00
Alex Newman ec9e3e32d8 chore: bump version to 13.24.12 (#4007)
Package-facing patch so 13.24.12 ships #4003, #4005, #4006, #3826, and #3773, which landed on main after v13.24.11 was tagged.
2026-09-10 23:12:13 -07:00
Alex Newman 9841dd4120 chore: bump version to 13.24.11 (#4004)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>
2026-09-10 22:25:56 -07:00
Alex Newman 49d4925d2d fix(hooks): opt-in CLAUDE_MEM_FETCH_VERBOSE for worker IPC fetch (#3994)
Pass Node/undici { verbose: true } from the existing worker-utils fetch wrappers when CLAUDE_MEM_FETCH_VERBOSE is 1/true/on/yes. On failure, log the serialized error cause chain. Default fetch behavior is unchanged.

Closes #3957
2026-09-10 21:40:40 -07:00
Alex Newman 6f9a73c8ca chore: bump version to 13.24.10 (#3993)
Package-facing patch so 13.24.10 ships #3991 (content flush batch 200→40 + requestTimeout settings), which landed on main after v13.24.9 was tagged.
2026-09-10 21:20:08 -07:00
Alex Newman 50b3efb4fa chore: bump version to 13.24.9 (#3990)
Package-facing security patch for the #3861 / #3985 credential-leak fixes
(unauthenticated /api/settings redaction, MCP workspace containment, host
bind tightening). Publish-ready; do not npm publish from this PR.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>
2026-09-10 20:57:20 -07:00
Alex Newman 47c06ff9a1 fix(observer): surface quota cooldown on observer-health and session-start (#3986)
Quota cooldown is an intentional pause, not a failure, so observer-health
stayed green and the session-start banner stayed silent while the worker
queued work. Mirror the breaker into observer-health.json and show a
pause notice on the existing health/banner path without changing
queue-accept or drain-on-clear.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>
2026-09-10 20:49:31 -07:00
Alex Newman d6e4e845fa security: close credential leak paths from #3861 / #3680 (#3985)
Rebase the three leak-path fixes from #3680 onto current main without the
stale plugin-bundle merge or a global requireLocalhost (that would break
Observation TV's remoteReadOnly guard).

- Mask known secrets on unauthenticated GET /api/settings; skip POST writes
  that are exactly the stored secret's mask so a viewer save cannot clobber
  a key, while still accepting a replacement that happens to start with '*'.
- Confine smart_search / smart_unfold / smart_outline to the workspace,
  realpathing both sides so an in-workspace symlink cannot escape.
- Reject arbitrary IPv4 CLAUDE_MEM_WORKER_HOST values. Loopback and the
  documented 0.0.0.0 / :: bind-all addresses stay valid for Docker and
  Observation TV.

Closes #3861

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>
2026-09-10 20:31:51 -07:00
Alex Newman 35642e94e3 chore: bump version to 13.24.8
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ah5a7Ygdm3uzBxSFZ8kqx6
2026-09-11 02:11:35 +00:00
Alex Newman 75faf9ac17 fix: add error listener to child.stdin in bun-runner.js to prevent EPIPE crash (#3895) (#3976)
A write to the spawned bun child's stdin can race the child exiting,
producing an EPIPE. With no 'error' listener on the Socket, Node
re-throws it as an unhandled exception and crashes the hook wrapper.

Add a no-op error listener before the write block so the EPIPE is
silently absorbed — consistent with the script's existing exit-0-on-error
contract for hook failures.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>
2026-09-10 18:54:34 -07:00
Alex Newman 69a12b2f96 Merge branch 'main' into cursor/version-bump-13-24-7-db81 2026-09-10 18:08:57 -07:00
Alex Newman d959572bde fix(setup): guard plugin deps on completeness, not node_modules existence (#3972)
* fix(setup): guard plugin deps on completeness, not node_modules existence

`ensurePluginDependencies()` decided whether to run `bun install` by asking
whether `node_modules/` existed. A tree that is merely present — but short
of the declared closure — satisfied that check and permanently skipped
repair on every subsequent Setup run.

Two trigger paths, and the second needs no corruption at all:

1. An install interrupted mid-fetch (network timeout, OOM, registry 5xx)
   leaves `node_modules/` behind incomplete.
2. A tree that was complete *for the version that created it*. `zod` was
   added to plugin deps after some users had already installed; their
   node_modules has been incomplete ever since, and no upgrade heals it
   because the stale tree is gitignored and gets re-seeded into each new
   cache version.

The worker then dies at boot on `Cannot find module 'zod/v3'` while memory
search keeps working — `mcp-server.cjs` bundles zod (build-hooks.js:519
hard-fails if it ever externalizes it) while `worker-service.cjs` has 19
external zod requires. So the plugin looks alive while capture is dead.
One reporter lost ~4 months of capture with no visible symptom.

Guard on completeness instead: every key of `package.json` `dependencies`
must resolve, with a `<dep>/package.json` fallback for bin-only packages
like tree-sitter-cli (gh #2730), plus the zod subpaths the worker requires.
This mirrors `verifyCriticalModules` (src/npx-cli/install/setup-runtime.ts:245),
which already applies exactly this contract on the npx install path but was
never reachable from the Setup hook. It cannot be imported here — this
script is standalone and dependency-free — so the probe is inlined and the
two are cross-referenced.

Two consequences fall out:

- The post-failure `rmSync` of node_modules is removed. It existed only
  because the existence guard would otherwise block retry forever; the
  completeness guard re-detects the gap on the next run, so deleting bought
  nothing while actively destroying a partial tree that still powers search.
- A zero exit from `bun install` is no longer trusted. It can exit 0 with a
  failed integrity check, so the closure is re-probed afterwards and the
  diagnostic reports what actually resolves.

The install diagnostic now names the unresolvable modules, which is what
stops this failure mode from being silent.

Fixes #3755. Refs #3604 (plan-16), #2730.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NT5K64VU4a7Kbc36oTVjyc

* fix(setup): keep the completeness probe inside the plugin's own node_modules

Greptile P1 on #3872, and it was right.

`require.resolve(dep, { paths: [nodeModulesPath] })` reads as tree-scoped
but is not. `paths` only seeds Node's lookup; resolution then walks every
ancestor directory and always consults the global folders
($HOME/.node_modules, $PREFIX/lib/node). Plugin roots live at
~/.claude/plugins/cache/thedotmack/claude-mem/<version>/, so a copy of a
dependency anywhere above them — or installed globally — answered for the
plugin's own.

Verified against the committed probe: a plugin whose node_modules is
completely EMPTY, with a valid zod one directory up, exits 0 with no output
and never runs the install. That is the #3755 bug reintroduced by the fix
for it, and it would have shipped silently.

Presence is now checked by statting `<node_modules>/<dep>/package.json`
directly, which cannot escape the tree. This is also the signal
scripts/check-postinstall-allowlist.js:75-78 already uses, and it handles
scoped names (split into path segments) and bin-only packages like
tree-sitter-cli (package.json present, no entry point — gh #2730) without
the bare-name/fallback dance.

zod's subpaths still need real resolution, since they are `exports` entries
that a present directory does not guarantee. Those are now accepted only
when the resolved file lands inside the plugin's own zod directory. Both
sides are realpath'd before comparison: bun can materialise node_modules
entries as links into a shared store, and Node returns the real path of
what it resolved, so a literal comparison would report a healthy linked
install as missing and loop the install forever. Containment uses
path.relative rather than string prefixing so a sibling like zod-extra is
not mistaken for being inside zod.

Regression test added; it fails against the previous commit's probe.

Note for follow-up: verifyCriticalModules (setup-runtime.ts:245) has the
same ancestor/global escape. It is far less dangerous there — a post-install
assertion that fails loud, not the gate deciding whether repair runs at all
— but worth tightening.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NT5K64VU4a7Kbc36oTVjyc

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 18:06:09 -07:00
Cursor Agent 4890e14c4b chore: bump version to 13.24.7
Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>
2026-09-11 01:05:17 +00:00
Alex Newman 22879ef523 fix(supervisor): reap chroma-mcp trees no worker owns at boot (refs #3905)
Co-authored-by: Stefan Nesiu Bedreag <stefan.nesiu-bedreag@gmail.com>
Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>
2026-09-10 17:52:30 -07:00
Alex Newman a51affa9ed fix(smart-file-read): build each grammar once instead of recompiling per query (fixes #3926)
`runBatchQuery()` shells out with `tree-sitter query -p <grammar-dir>`, and
`-p/--grammar-path` is documented as implying `--rebuild`: the CLI recompiled the
grammar from C on every invocation. Measured with tree-sitter-cli 0.26.9 on Linux
x86_64, one `smart_outline` over a 933-line TypeScript file cost 734/711/704 ms
across three runs, nearly all of it the compile. The artifact the CLI wrote to
its own cache was rewritten each time and never read back.

Grammars are now built once into <data-dir>/tree-sitter-libs and queried with
`-l <lib> --lang-name <language>`. The same three runs become 733/16/12 ms and
return the identical 30 symbols; the first call still pays one build.

Because `parseFilesBatch()` issues one CLI call per language group, the old cost
was a fixed ~0.7s per language per tool call regardless of batch size, paid alike
by smart_outline, smart_search and smart_unfold, and repeatedly by anything that
walks a repository.

The artifact lives in the data dir rather than in node_modules: an update
replaces node_modules wholesale, and those directories belong to the installer.
Staleness is an mtime comparison against src/parser.c and the optional scanner —
npm and bun both stamp installed files with the install time, so a plugin update
invalidates the previous artifact on its own. The check is re-run per call rather
than memoized, since four stats cost nothing next to the spawn they guard and a
memo would pin a long-lived MCP server to the grammar that was current at boot.

Every failure path falls back to the previous `-p` invocation: a build that does
not compile, or a grammar whose language function is not named after our language
key. The opt-out is remembered per language, so a mismatch costs one extra spawn
once instead of two per batch forever. All 24 shipped grammars were verified to
bind under their existing GRAMMAR_PACKAGES key, including the irregular ones
(typescript, tsx, kotlin, php, scss, sql, markdown).

Fixes #3926

Co-authored-by: rorar <rorar@users.noreply.github.com>
Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>
2026-09-10 17:50:43 -07:00
Cursor Agent 9811b0eb78 chore: bump version to 13.24.6
Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>
2026-09-11 00:27:49 +00:00
Antonio Coppe 29279d9306 fix: validate all shipped runtime bundle versions 2026-09-11 00:08:30 +00:00
Antonio Coppe 2bb0a7487a fix: stop stale worker bundle recycle storms
Persist unsuccessful version recycles across hooks, retry after the bundle changes, and rebuild shipped workers. Check committed bundle versions before CI rebuilds them.

Refs #3940
2026-09-11 00:08:30 +00:00
Alex Newman fd0ecf0233 feat(sqlite): durable tool_uses backup index + get_tool_uses disclosure (#3898)
Raw tool I/O had no durable home. `pending_messages` is the generation
queue -- rows are claimed, summarized, and deleted -- so once an
observation existed the original tool_input/tool_response were gone.

Adds `tool_uses` (schema v51) as a by-reference side index for those
bodies, written from the one ingest choke point both the PostToolUse hook
route and the transcript-watch processor already share. The JSONL
transcripts and `src/services/transcripts/*` remain the spine and are
untouched.

Schema (Receipt freeze 2026-09-06): UNIQUE(content_session_id,
tool_use_id), nullable `or_generation_id` / `or_session_id` as join keys
back to an OpenRouter spend line, and deliberately no cost_usd/micros --
this table carries tool identity, dollars stay on the OR stamp. No FK on
session_db_id/observation_id: a FK there can abort the constructor
migration chain (#3378), and observation_id is linked late by design.

Write path dual-writes alongside -- never instead of -- the
pending_messages enqueue, and swallows its own failures so an observation
is never lost to a backup-index error. `toolUseId` now actually reaches
the worker from hooks: it was missing from NormalizedHookInput and every
hook adapter, so only the transcript path supplied it. ResponseProcessor
links the batch's claimed ids to the first stored observation.

Read path is progressive-disclosure layer 4: POST /api/tool-uses/batch
requires explicit ids (never a full-table scan), GET /api/tool-uses
returns a cheap index shape with size hints and never the payloads, and
the `get_tool_uses` MCP tool is described as a last resort. claude-mem's
own read tools are skipped by the writer -- without that, every call to
get_tool_uses would store the bodies it just returned.

Payloads are stored in-row with a 64 KB soft cap and a UTF-8-safe
truncation marker; content_hash is computed over the original.

Join contract for Receipt: RECEIPT-JOIN.md


Claude-Session: https://claude.ai/code/session_01QgdJ6gExgBirDoAxknt94m

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 18:25:26 -07:00
Alex Newman f6f72747e1 chore: bump version to 13.24.1
Patch release so marketplace and npm installs actually pick up the rebuilt
plugin bundles from #3878. 13.24.0 shipped manifests that claimed 13.24.0
while plugin/scripts/*.cjs still carried 13.23.1 bytes; the artifacts were
corrected in place, but existing 13.24.0 installs have no version change to
trigger an upgrade. 13.24.1 gives them one.

Version bumped across all 11 manifests and the README badge, then
regenerated via `npm run build-and-sync`. All four .cjs bundles are
byte-identical to 13.24.0 after normalizing the version string -- no source
changed since 4da9ffc6, so this is a pure version delta.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fu9bpdG4AWZNq3m8zkUkYg
2026-09-05 05:22:25 +00:00
Alex Newman 4da9ffc68c fix: rebuild plugin bundles to match manifest 13.24.0 (#3857)
* fix: rebuild plugin bundles so committed artifacts match manifest 13.24.0

The 13.24.0 release commit (85ccd626) bumped the manifests and CHANGELOG
but never re-ran the build, so plugin/scripts/*.cjs kept the 13.23.1 bytes
last produced by 89ca057a. The Claude Code marketplace installs straight
from this repo (.claude-plugin/marketplace.json -> "source": "./plugin"),
so every marketplace user on 13.24.0 has been executing 13.23.1 code.

ensureWorkerRunning() compares the resolved plugin version (13.24.0, taken
from the plugin cache directory name) against the worker's baked-in
__DEFAULT_PACKAGE_VERSION__ (13.23.1, reported by /api/health). The
mismatch SIGKILLs the worker and respawns the same stale file on every
hook event, with no state that survives the hook process to bound it --
an unbounded kill/respawn loop that takes the in-flight observer
generator down with it, so no observations get written.

This is a genuine `npm run build`, not a version-string patch. The
bundles were stale in code, not merely in the constant: src/ moved 704
insertions across 12 files since 13.23.1, including the observer's
<skip_summary reason="noise" /> protocol change, the new manual-session
module, and the platform_source plumbing in SessionStore and MemoryRoutes.

No version bump: the manifests were already correct at 13.24.0. It is the
artifacts that were wrong.

Fixes #3857

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bshprs1vjut2XmaGTRESqX

* fix(tests): restore module mocks so worker-spawner stubs stop leaking

bun runs the whole suite in one process and mock.module is process-global
and sticky, so stubs installed by one test file stay installed for every
file loaded after it.

tests/services/worker-spawner.test.ts mocked
src/services/infrastructure/{ProcessManager,HealthMonitor}.js and never
restored them. tests/infrastructure/{health-monitor,process-manager}.test.ts
import the same symbols through the src/services/infrastructure/index.js
barrel, so they silently exercised those stubs instead of the real code:
isPortInUse returned false without touching net.createServer, waitForHealth
returned false without fetching, getPlatformTimeout skipped the Windows
doubling, cleanStalePidFile always reported 'dead', and spawnDaemon never
returned undefined. waitForPortFree stayed real but resolved through the
stubbed isPortInUse binding, inverting its timeout case.

This is order-dependent, not new: bun walks test files in filesystem order,
and a fresh CI checkout loads worker-spawner (59) well before health-monitor
(169) and process-manager (172), while many local checkouts load
tests/infrastructure first and pass. That ordering is what surfaced 19
failures in Actions run 33939805959.

Snapshot the real namespaces eagerly, before the mock.module calls, and
reinstall them in afterAll. The snapshot must be eager: `import * as x`
yields a live namespace object that bun re-points when the module is mocked,
so spreading it inside afterAll copies the stubs back in. That is exactly
the latent bug in tests/cli/handlers/context-session-start.test.ts, whose
restore was re-installing its own hook-settings, oauth-token, project-name
and worker-utils stubs; fixed here the same way.

Tests only. No source, plugin bundle, or version changes — the committed
artifacts still match manifest 13.24.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcjsK6QqeBcYXbJ8gqBsea

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 21:26:08 -07:00
Alex Newman 89ca057a46 chore: bump version to 13.23.1
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019NoBPqpo7YaR3HKipExjAT
2026-09-01 14:00:59 -07:00
Alex Newman 263d8c0083 chore: bump version to 13.23.0
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019NoBPqpo7YaR3HKipExjAT
2026-09-01 13:27:31 -07:00
Alex Newman 76088262c3 chore: bump version to 13.22.0
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019NoBPqpo7YaR3HKipExjAT
2026-09-01 12:11:29 -07:00
Alex Newman 2aeedb42ac chore: bump version to 13.21.2
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DD5YMaAiXXeY3zaitbGC6n
2026-08-31 14:37:27 -07:00
Alex Newman e9c52a3537 chore: bump version to 13.21.1
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DD5YMaAiXXeY3zaitbGC6n
2026-08-31 14:28:16 -07:00
Alex Newman fab75dc3b9 chore: bump version to 13.21.0
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DD5YMaAiXXeY3zaitbGC6n
2026-08-31 14:05:20 -07:00
Alex Newman 8545e52207 chore: bump version to 13.20.0
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DD5YMaAiXXeY3zaitbGC6n
2026-08-31 13:15:42 -07:00
Alex Newman 679a14e555 chore: rebuild worker bundle after merging main
The merge resolved plugin/scripts/*.cjs by regenerating them from the
merged source rather than hand-merging minified output.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DD5YMaAiXXeY3zaitbGC6n
2026-08-31 13:14:21 -07:00
Alex Newman 60310f86bf fix(quota): claim the cmem gateway re-probe instead of reading the clock
The gateway fallback said "allow one normal gateway request as a probe", but
nothing made it one. shouldUseCmemFallback is a subtraction and a less-than,
and ensureGeneratorRunning gates per session, so 28-69 live sessions are 28-69
independent callers.

It is worse than the in-memory case. The marker lives on disk
(CLAUDE_MEM_PRO_FALLBACK_AT in settings.json, loaded uncached), so every
process parses the same ISO string and computes the same expiry instant -- the
herd was handed a shared clock to synchronize on. Each member's failure then
does a read-modify-write of the user's whole settings.json to re-arm the
marker; the write is atomic but the sequence is not, so concurrent settings
edits can be clobbered.

The re-probe is now claimed through the breaker's existing machinery under a
DISTINCT key, 'cmem-gateway'. Distinct because tryAdmitQuotaProbe takes the
cooldown per call: this path's 15 minutes against the provider breaker's 30
would otherwise let two callers reach contradictory answers about whether the
same breaker is armed, and provider switches (which this branch makes routine)
make the collision reachable in both directions -- a fresh funded key born into
an armed breaker and withheld for a refusal it never earned.

getSelectedProvider stays read-only for diagnostics and status; only the new
selectProviderForGenerator claims. Every path out of ensureGeneratorRunning
that does not actually send releases the claim, including the one where a
generator is already running, so a claim cannot outlive the call that took it.

Also regenerates the plugin bundles rather than hand-merging them, which is
what the five .cjs conflicts in this consolidation always needed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWmJDjFM6BNmGaLJ7Xbrsy
2026-08-31 12:18:30 -07:00
Alex Newman 5a87186191 Merge origin/main into OAuth installer fix 2026-08-31 11:22:33 -07:00
Claude 456e369817 chore: bump version to 13.18.0
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0123bYqzHGw2dtgN8D8GE3UW
2026-08-29 23:35:26 +00:00
Alex Newman 64ca5a2571 chore: bump version to 13.17.2 2026-08-29 03:03:47 -07:00