v13.29.0

thedotmack/claude-memv13.29.0Oct 3, 2026by thedotmack

Upgrading?

AI Summary

The agent now uses `work_state_write` and `work_state_read` tools as its canonical to-do list. New OpenAI-compatible and Codex subscription providers were added, along with Kimi Code and Oh My Pi support. Several defaults were changed.

Key Highlights

  • Work state system with two MCP tools for to-do list management
  • New OpenAI-compatible provider with presets for various backends
  • Codex subscription provider integration
  • Opt-in quota fallback provider
  • Multiple upgrade notes regarding timeouts, models, and hooks

Breaking Changes

  • Observer deadline raised to 3 minutes
  • New OpenRouter default model (retired `xiaomi/mimo-v2-flash:free`)
  • Subagent observations are excluded from SessionStart by default
  • Hooks no longer block sessions
  • UserPromptSubmit timeout reduced to 15 seconds
  • Worker refuses unknown host names (DNS-rebinding guard)
  • Antigravity users must re-run install to fix hooks.json

New Features

  • MCP tools for work state management
  • OpenAI-compatible provider with presets
  • Codex subscription provider
  • Quota fallback provider
  • Kimi Code and Oh My Pi support

Full Release Notes

## The agent keeps its to-do list in claude-mem

Every session now opens with a work-state section ahead of memory: a rule telling the agent to use claude-mem's `work_state_write` and `work_state_read` tools as its canonical to-do list, then whatever is still open in the project. Claude Code gives Claude 5 models no native to-do tool, so until now nothing recorded what was in progress or what was left when a session ended. The rest of the release adds an `openai-compatible` provider with presets, a Codex subscription provider, Kimi Code and Oh My Pi support, an opt-in quota fallback, and fixes across capture, search, the worker and Windows. Several defaults changed; see **Upgrade notes**.

### Work state

- **Two MCP tools.** `work_state_write` appends one entry to a named list (fields capped at 2,000 characters as JSON) and replies with what is still open in that list. `work_state_read` returns every open item, or one list, and adds done and dropped items when `includeClosed` is true. (#4340)
- **Lists, items and state.** An entry with a `task` field is a to-do item with a `status` of `todo`, `doing`, `done` or `dropped`; any other field is state on the list. The latest value of each key wins, `null` clears a key, and `"status": "done"` closes the list. (#4340)
- **SessionStart leads with it.** Context opens with the rule and every open item, each with how long ago it last changed, capped at 3,000 characters (806 when nothing is open); memory is fitted into what remains of the 10,000-character limit. The welcome hint for a project with no observations gets the same lead; the colored terminal preview does not. (#4340)
- **Scoped like memory.** Writes land under the checkout's primary project key and reads cover every key the checkout reads, so a list started in the main checkout also shows in its worktrees. Writes from projects matched by `CLAUDE_MEM_EXCLUDED_PROJECTS` are not saved. (#4340)
- **Kept in the database, not the repo.** Entries go to a new append-only `work_state_entries` table (schema v61), so lists never conflict on merge but are not in git, and reads and writes need the worker. The server runtime's SessionStart does not read this table yet. (#4340)

### Upgrade notes

- **Observer deadline raised to 3 minutes.** `CLAUDE_MEM_LLM_TIMEOUT_MS` now defaults to `180000` (was `30000`), and the new `CLAUDE_MEM_FIELD_OPTIMIZE_TIMEOUT_MS` for oversized-field condensation (a fixed 30 seconds before) defaults to the same. A `settings.json` holding exactly the old `30000` is moved once; any other value is kept. (#4278, #4287, #4136)
- **New OpenRouter default model.** OpenRouter retired `xiaomi/mimo-v2-flash:free`, so the default is now `cohere/north-mini-code:free`, and a stored `CLAUDE_MEM_OPENROUTER_MODEL` equal to the retired id is rewritten once when the base URL is blank or openrouter.ai. A retired or unknown model now fails with a `model_unavailable` error that names the setting. (#3662)
- **Subagent observations stay out of SessionStart.** `CLAUDE_MEM_CONTEXT_MAIN_AGENT_ONLY` (default `true`) leaves rows that carry both an agent id and an agent type out of the SessionStart window, in the worker and the server runtime. Search, timeline and `get_observations` still return them; set it to `false` for the old behavior. (#3310, #4290)
- **Hooks never block a session.** No claude-mem hook exits 2 any more, so an unexpected error or a worker outage no longer drops your prompt, denies a tool call or re-wakes Stop; the session continues without memory. After `CLAUDE_MEM_HOOK_FAIL_LOUD_THRESHOLD` (default `3`) unreachable-worker hooks in a row you get one notice per session to run `npx claude-mem restart`, and Claude Code hooks other than Setup exit 0 even when the plugin scripts are missing. (#2892, #3168, #3431)
- **UserPromptSubmit gets 15 seconds in Claude Code.** The hook timeout drops from 60 to 15 seconds, and session init spends one budget, `CLAUDE_MEM_SESSION_INIT_TIMEOUT_MS` (10 seconds; 7 on Windows, where longer saved values are cut to 7), across the whole worker round-trip. (#3440, #4299)
- **The worker refuses unknown host names.** As a DNS-rebinding guard, a request whose `Host` is a DNS name other than `localhost`, `*.localhost`, `host.docker.internal`, `CLAUDE_MEM_WORKER_HOST`, the `CLAUDE_MEM_PUBLIC_URL` host or an allowed origin gets a 403; IP addresses still work. If you open the viewer through another host name (a `.local` name, a reverse proxy), add its origin to the new `CLAUDE_MEM_ALLOWED_ORIGINS` (comma-separated) and restart the worker. (#3514)
- **Antigravity users: re-run install.** Earlier installs wrote `~/.gemini/config/hooks.json` in a shape `agy` ignores, so nothing was captured. Run `npx claude-mem install --ide antigravity` to rewrite it in `agy`'s named-hook schema; `npx claude-mem antigravity-cli status` reports entries left to migrate. (#4274)

### Providers and gateways

- **New `openai-compatible` provider.** `CLAUDE_MEM_PROVIDER=openai-compatible` runs the observer against any OpenAI `/chat/completions` endpoint, configured with `CLAUDE_MEM_OPENAI_COMPAT_PRESET`, `CLAUDE_MEM_OPENAI_COMPAT_BASE_URL`, `CLAUDE_MEM_OPENAI_COMPAT_MODEL` and `CLAUDE_MEM_OPENAI_COMPAT_API_KEY`. Endpoints on localhost or a private network need no key, and a selected but half-configured provider falls back to Claude. (#3942)
- **Presets.** `nvidia-nim`, `deepseek`, `groq`, `together`, `ollama`, `lmstudio` and `vllm` (#3942), `orcarouter` (#3581), `opencode-go` and `opencode-zen` (#3623), `minimax` and `minimax-cn` (#3376), `api-route` (#4325) and `opper` (#4333). Model ids are passed verbatim, and a leading `<think>…</think>` block is stripped from replies so inline reasoning is not stored (#3376).
- **Endpoint settings in the viewer.** A new "OpenAI-compatible endpoint (BYOK)" provider option edits the preset, base URL and model (the key stays in `settings.json` or `.env`), and the OpenRouter block shows `CLAUDE_MEM_OPENROUTER_BASE_URL`, read-only while it points at the claude-mem observer. The settings API rejects base URLs that are not http(s). (#3433)
- **Codex subscription provider.** `CLAUDE_MEM_PROVIDER=codex`, or `npx claude-mem install --provider codex [--model <id>]`, runs the observer through the Codex CLI app-server on your ChatGPT subscription; run `codex login` first. `CLAUDE_MEM_CODEX_MODEL` (empty uses Codex's default) and `CLAUDE_MEM_CODEX_REASONING_EFFORT` (default `low`) are optional, and `CLAUDE_MEM_CODEX_PATH` (default `codex`) can be set only in `settings.json` or the environment. (#3882, #4216, #4318)
- **Codex throughput.** Codex runs up to `CLAUDE_MEM_CODEX_MAX_CONCURRENT_AGENTS` requests at once (default `2`) and sends consecutive queued observations in one request, up to `CLAUDE_MEM_CODEX_OBSERVATION_BATCH_SIZE` items (default `8`) and `CLAUDE_MEM_CODEX_OBSERVATION_BATCH_MAX_CHARS` characters (default `32000`). The viewer does not expose these three; set them in `settings.json`. (#4260, #4261)
- **Codex failures are classified.** A request Codex refuses the same way every time becomes a setup error with a remedy instead of an endless retry, app-server internal errors (-32603, -32700) are transient, and an empty reply goes through the normal skip handling. On Windows the default `codex` command launches the `codex.cmd` shim. (#4222, #4316, #4323)
- **Several keys per provider.** `CLAUDE_MEM_GEMINI_API_KEYS`, `CLAUDE_MEM_OPENROUTER_API_KEYS` and `CLAUDE_MEM_OPENAI_COMPAT_API_KEYS` take extra keys, separated by commas or newlines. A key that hits a rate limit, a spent quota or an auth failure is set aside for 1 minute, 30 minutes or 6 hours and the next key is used; the cmem.ai gateway never uses a pool. (#3941)
- **Quota fallback provider (opt-in).** `CLAUDE_MEM_QUOTA_FALLBACK_PROVIDER` (`claude`, `gemini`, `openrouter` or `openai-compatible`; empty, meaning off, by default) takes observer work while the selected provider is in a quota cooldown and hands it back once the primary answers its probe. `CLAUDE_MEM_QUOTA_FALLBACK_MODEL` picks the model for a Claude fallback, both are in the viewer, and authentication failures are never routed around. (#4268, #4306)
- **cmem.ai trial-expiry fallback works again.** Since v13.24.11 a terminal gateway rejection never moved memory to your Anthropic plan. Now `allowance_exhausted`, `key_invalid`, `subscription_inactive` or any gateway 401/403 switches to Claude at once and resumes the buffered work there, the SessionStart notice relays the gateway's own message and link, and unattended resumes on the gateway are capped at 3 in a row. (#4276, #4273, #4306)
- **OpenRouter request options.** `CLAUDE_MEM_OPENROUTER_REASONING_EFFORT` (`none`, `minimal`, `low`, `medium` or `high`; empty sends nothing) sets reasoning for requests to openrouter.ai only (#3001). `CLAUDE_MEM_OPENROUTER_EXTRA_BODY` (empty by default) merges a JSON object into OpenRouter-provider requests, never to the cmem.ai gateway, and cannot override `model`, `messages`, `stream` or the token caps (#3040).
- **Gateways that stream by default.** Requests to openrouter.ai and custom base URLs send `stream: false` in the worker and the server runtime, so a gateway that streams unless told otherwise no longer fails every observation (#3668, #4317). A litellm "Unable to get json response" reply is retried (#3263), and an OpenRouter reply cut at the output cap is logged with the cap and request id (#3620).
- **Observer output cap and system prompt.** The new `CLAUDE_MEM_OBSERVER_MAX_OUTPUT_TOKENS` (default `4096`) sets the output cap on OpenRouter, openai-compatible, cmem.ai gateway and Gemini observer requests, which now carry the observation schema in the system message (`systemInstruction` on Gemini). (#3868)
- **Gemini replies and throttles.** The Gemini provider reads the answer parts of a reply instead of a leading reasoning part, which had stored nothing (#4090). A 429 that names only a per-minute limit is retried after Google's delay instead of pausing capture for 30 minutes, read from the structured error details (#4089, #4243).
- **Daily caps and region refusals.** A per-day 429, such as OpenRouter's `free-models-per-day`, counts as a spent daily allowance in the worker and the server runtime (#4089, #4308). Gemini's "User location is not supported" pauses capture as `location_unsupported` with advice to choose another `CLAUDE_MEM_PROVIDER`, without rotating through the key pool (#4147, #4308).
- **HTTP provider errors.** On openai-compatible endpoints a context-length refusal recycles the observer generation instead of dropping the batch, a per-minute 429 that links to a billing page stays a rate limit, and a key pool waits for a briefly throttled key instead of arming the 30-minute breaker (#4317). An OpenRouter or openai-compatible request that gets no response names the error code and host, with a Local Network permission hint for hosts on your network (#4115, #4320).

### New hosts and harnesses

- **Kimi Code CLI.** `npx claude-mem install --ide kimi`, or `npx claude-mem kimi install|status|uninstall`, writes hooks to `$KIMI_CODE_HOME/config.toml` and the MCP server to `$KIMI_CODE_HOME/mcp.json` (default `~/.kimi-code`). Memory is injected on a session's first prompt and after compaction, tool output and failed calls are captured, and Kimi's bookkeeping tools (to-do, task and cron) are skipped. (#3676, #3547)
- **OMP (Oh My Pi).** `npx claude-mem install --ide omp` installs a hook at `~/.omp/agent/hooks/pre/claude-mem.ts` (under `$PI_CODING_AGENT_DIR` when set) that records sessions, every prompt, tool results and summaries and injects memory before model calls. Its worker requests time out after 5 seconds. (#3556, #4315)
- **DeepSeek Harness transcripts (opt-in).** The transcript watcher can tail zstd-compressed `*.jsonl.zstd` session logs, and the repository's `transcript-watch.example.json` has a `dsh` schema and watch entry for `~/.dsh/sessions` to copy into `~/.claude-mem/transcript-watch.json`. (#3691)
- **OpenCode plugin loads and files sessions correctly.** On OpenCode 1.18 the plugin failed with "Plugin export is not a function"; it now loads, tags sessions `opencode`, keys them by checkout like other hosts, and records the real prompt (#3803). Memory context is added to each request's system prompt, and captures refused while the worker boots are retried for about 10 seconds (#3208, #4091).
- **OpenCode configuration.** Install registers the claude-mem MCP server in `opencode.json` (#3621), stops writing memory into the global `~/.config/opencode/AGENTS.md` and removes a block an older install left there, and a failed context fetch is retried after a minute instead of on every system prompt (#4314).
- **Codex hooks.** Codex hook events keep `tool_use_id`, `agent_id` and `agent_type`, so dedup and the subagent settings apply (#4329). Codex's own helper prompts (task titles, memory consolidation, onboarding) no longer become sessions (#2810), and worktrees at `~/.codex/worktrees/<id>/<repo>` file memory under `<repo>` instead of `<repo>/<repo>` (#3643).
- **Codex subagent capture (opt-in).** Codex hooks do not fire for subagent threads, so set `CLAUDE_MEM_CODEX_SUBAGENT_INGESTION=true` (default `false`) for a transcript watch scoped to subagent rollouts. `CLAUDE_MEM_SKIP_SUBAGENT_OBSERVATIONS=true` still wins. (#3655, #4321)
- **Antigravity, Qwen Code and the `claude` alias.** Sources named `agy`, `antigravity` or `antigravity-*` are recorded as `antigravity-cli` with their own viewer badge (#4147), and `--ide claude` and `hook claude <event>` behave like `claude-code` (#2835). A host-sent `submitted_prompt` is stored as the prompt, and an empty one no longer stores a `[media prompt]` row for every tool round in Qwen Code (#4224).
- **Grok Bot brainbeat webhook (opt-in).** Set `CLAUDE_MEM_GROK_BOT_WEBHOOK_URL` and claude-mem POSTs each observation that matches the Grok Bot awareness triggers to it, sending `CLAUDE_MEM_GROK_BOT_WEBHOOK_SECRET` as a header when set. It is off while the URL is empty. (#3846)

### Memory, context and search

- **Named environments.** `CLAUDE_MEM_PROJECT_ENVIRONMENTS` (default `[]`, off) takes a JSON array of `{"name", "patterns"}`, and folders matching an environment's globs share one project named after it. `npx claude-mem project merge <from> <into> [--dry-run]` folds one project's memory into another without rewriting rows, runs inside the worker when one is up, and reaches other devices through cloud sync. (#2737, #4297)
- **Stable project names.** `CLAUDE_MEM_PROJECT_NAME_SOURCE=git-remote` (default `path`) names projects by the `org/repo` slug of the git `origin` remote (#2827), and outside git an empty `.claude-mem-project` file (or `.claude-mem.json`) at the project root gives every subfolder the root's name (#3311). Search covers every key a checkout has used, so memory filed under an older name is still found (#4303).
- **Case-insensitive project keys.** Checkouts whose folder names differ only in case share one memory bucket in context, search and folder `CLAUDE.md`, including every Chroma path. (#3536, #4301)
- **Worktree and submodule memory.** The startup sweep that folds merged worktrees into their parent project works again and adopts observations from deleted worktree checkouts. A session inside a git submodule uses a `<superproject>/<path>` key and reads the superproject's memory. (#3525, #4291)
- **Opt-in ACT-R ranking.** Setting `CLAUDE_MEM_REINFORCE_ALPHA` (default `0`, off) above 0 lets older observations re-confirmed on later days climb into the SessionStart window; the newest quarter of the window always stays by recency. (#3414)
- **Opt-in near-duplicate dedup.** With `CLAUDE_MEM_DEDUP_ENABLED=true` (default `false`), an observation whose normalized title matches one in the same project, platform and agent scope is merged into it instead of stored again. Near matches are only listed at `GET /api/dedup/candidates`, and `POST /api/dedup/scan` backfills. (#3063, #4294)
- **Import Claude Code auto-memory.** `npx claude-mem memory ingest [--source <dir> | --all] [--dry-run]` stores Claude Code's auto-memory files (`~/.claude/projects/<encoded-cwd>/memory/*.md`) as observations with no model call, from the checkout you run it in. It skips `MEMORY.md`, symlinked notes and notes over 64 KiB, and dedupes re-runs. (#2829, #4322, #4307)
- **Keyword matching.** Multi-word queries now require every word anywhere instead of one exact phrase, punctuation-only tokens are dropped (#4180), and mixed Latin and CJK queries match term by term (#4148). Searches with only `obs_type` or `concepts` return rows instead of a 400 (#3259).
- **SQLite fills gaps in semantic search.** A category Chroma returns nothing for (common with CJK queries or a tight date window) is refilled from SQLite FTS5 (#3173, #3285), and a query-filtered `build_corpus` with `CLAUDE_MEM_CHROMA_ENABLED=false` falls back to full-text search (#4285).
- **Results and ordering.** Observation results are grouped under day headers (#3693), date-ordered searches merge keyword matches before cutting to the limit (#4174), `timeline` counts depth correctly when rows share the anchor's timestamp (#4104), and broad project filters on large Chroma collections are applied client-side (#3675).
- **Folder and file lookups.** Folder lookups match files stored project-relative and keep exact file matches Chroma did not rank, so opt-in folder `CLAUDE.md` files (`CLAUDE_MEM_FOLDER_CLAUDEMD_ENABLED`) are no longer empty (#3116). Search routes take `projects` as a comma-separated list or a repeated key and answer other shapes with 400 `INVALID_PROJECTS` (#4304).
- **Knowledge corpora.** `build_corpus` keeps the `dateStart` / `dateEnd` you pass and is no longer limited to 90 days of Chroma matches, and `rebuild_corpus` refuses a rebuild that would keep half or fewer of a corpus's observations unless called with `force: true`. (#4168)
- **SessionStart from every harness (opt-in).** `CLAUDE_MEM_SESSION_START_INCLUDE_ALL_SOURCES=true` (default `false`), or **Include all sources at session start** in the viewer, loads recent observations from every harness for the project, not only the one starting. (#4251)
- **SessionStart fixes.** The last session summary shows again when it shares a timestamp with the newest observation (#4103), the terminal preview shows the observations the model received (#4254), and a resumed session such as `claude --resume` is set back to active (#4084).
- **New `/handoff` skill.** It writes a HANDOFF.md with the goal, current state, files in play, failed attempts, next steps and memory pointers, so a fresh session can continue. (#3748)

### Capture

- **Failed tool calls are observations.** Claude Code sends a failed tool call (a non-zero Bash exit, or a call you interrupted) to `PostToolUseFailure`, which claude-mem now registers; the stored tool use records the error and whether it was interrupted. (#4339)
- **Skip shell commands by pattern.** `CLAUDE_MEM_SKIP_BASH_PATTERNS` (empty by default) is a regular expression tested against each Claude Code `Bash` or Codex `exec_command` command, for example `^(ls|cat|pwd)\b`; a match skips the observation. (#3562)
- **Skip subagent observations (opt-in).** `CLAUDE_MEM_SKIP_SUBAGENT_OBSERVATIONS=true` skips every subagent tool call and `CLAUDE_MEM_SKIP_AGENT_TYPES` skips listed agent types (for example `workflow-subagent,Explore`), in the hook before any provider request. Both are off by default. (#2741)
- **Turn off the tool hooks from the environment.** `CLAUDE_MEM_DISABLE_TOOL_HOOKS=1` makes the PreToolUse and PostToolUse hooks exit at once, and `CLAUDE_MEM_DISABLE_OBSERVATION=1` or `CLAUDE_MEM_DISABLE_FILE_CONTEXT=1` turns off one of them. They are read from the hook's environment, not from `~/.claude-mem/settings.json`. (#3316)
- **Record Claude Code advisor calls (opt-in).** With `CLAUDE_MEM_CAPTURE_ADVISOR_CALLS=true` (default `false`), the Stop hook stores each `advisor` call's advice, model and prompt locally at no token cost, readable at `GET /api/advisor-calls` and never synced. Worker runtime only. (#3165)
- **Summaries survive a session that ends mid-tool-call.** An empty last assistant message no longer skips the summary: the hook reads the transcript, or names the last tools used. (#3184)
- **Less observer noise.** The default `code` mode tells the observer to record a fact once instead of re-confirming it during reviews, retries and polling (#3352), and a prompt that queues no work no longer starts an observer request (#3467).
- **Faster hooks.** The SessionStart worker start runs async while the context hook stays synchronous (#3401), hooks use a valid `CLAUDE_PLUGIN_ROOT` instead of scanning the plugin cache (#3470), and hook processes no longer read viewer assets at import (#3667).

### Viewer and settings

- **Sessions view.** The viewer has Timeline and Sessions tabs. Each session opens a page you can filter by observation category, and you can delete a whole session unless it is still running or holds memories synced from another device. (#3458)
- **Delete memories from the viewer.** Observation and summary cards have a delete button with a confirmation, deletes are cloud-sync safe, and other open tabs drop the row live. Browser DELETEs are accepted only from the viewer's own page or an origin in `CLAUDE_MEM_ALLOWED_ORIGINS`. (#2925, #4288)
- **Settings saves.** A save is validated only on what it changes (#4320), never writes a value that only echoes an environment variable into `settings.json` (#4324), and shows the server's reason when it fails (#3474); a body-less `POST /api/settings` gets a 400 (#3632).
- **settings.json handling.** The installer moves a corrupt `~/.claude-mem/settings.json` to `settings.json.corrupt-<epoch-ms>` instead of wiping it, and the worker and viewer refuse to write over one (#3472). When the file's `env` block holds `CLAUDE_MEM_*` keys, they win over stale root copies, which the next save removes (#4286).
- **Links behind a port-forward.** `CLAUDE_MEM_PUBLIC_URL` (empty by default) sets the browser-reachable base for viewer links in the welcome hint, SessionStart, the per-prompt banner and health notices, for remote sandboxes and dev containers. (#3323)
- **Theme colors before the theme loads.** The fallback colors match the dark theme, and source badges use theme tokens in both themes. (#4328)

### Installer and CLI

- **Fresh installs load in Claude Code.** The npm package ships `.claude-plugin/marketplace.json`, so a clean install no longer ends with "Marketplace thedotmack failed to load: cache-miss". (#3441)
- **Installs that used to abort.** npm 11.16+ no longer aborts with EALLOWSCRIPTS (#3258). Worker liveness comes from the PID file, so slow refusals (WSL2) and a port held by another process no longer stop an install before sign-in (#4119), and a port taken again mid-install warns instead of aborting (#4289).
- **tree-sitter CLI is provisioned.** The installer runs tree-sitter-cli's install step when the binary is missing, so `smart_search` and `smart_outline` work on installs that suppress lifecycle scripts, and `doctor` gains a "tree-sitter CLI" row. (#3254)
- **Old plugin versions are pruned.** Install and update remove superseded plugin cache versions, keeping the newest 2, the running worker's and the one Claude Code has registered; `npx claude-mem prune [--dry-run] [--keep <n>]` runs it on demand. (#4106)
- **`update` keeps your provider setup.** A non-interactive update keeps `openai-compatible` setups, and Gemini or OpenRouter keys that live only in a key pool or `~/.claude-mem/.env`. (#4312)
- **`doctor` and `status` find cache installs.** The CLI resolves the plugin root the way the worker does and prefers the newest complete copy, so working cache installs are no longer reported missing. (#3535)
- **Externally managed workers.** `CLAUDE_MEM_WORKER_AUTOSTART=false` (default `true`) makes hooks, the MCP server, `start` and the installer use a running worker but never launch, stop or recycle one. (#2828, #4289)
- **Smaller fixes.** Marketplace installs record the install marker instead of printing "runtime not yet set up" at every Setup (#3113), `/api/import` reports rejected rows instead of aborting the batch (#4051), and CMEM Pro trial copy says "up to 14 days" (#4265).

### Server runtime

- **SessionStart reads the shared server.** With `CLAUDE_MEM_RUNTIME=server`, SessionStart renders context from the server's rows in one `/v1/context` read sized to the host's hook limit, with short display refs that point to `observation_search`, and `start` no longer probes or spawns a local worker. (#3227, #4112, #4290, #2867)
- **Server API.** `/v1/context` no longer needs a `query`: without one it returns the newest observations (50 by default, at most 200) (#4079). The `observation_add` MCP tool now sends `content`, which `/v1/memories` validates; every call had failed with a 400 (#4143).
- **Generation.** The `CLAUDE_MEM_SERVER_GENERATION_CONCURRENCY` environment variable (unset means 1, clamped at 64) sets parallel jobs per lane (#4078), summaries read the session's first and last 500 events (#4137), provider calls time out after 10 minutes (#4123), and empty replies fail at once instead of being retried (#3368).
- **Custom generation provider.** `CLAUDE_MEM_SERVER_PROVIDER=custom` with `CLAUDE_MEM_CUSTOM_PROVIDER_MODULE=<absolute path>` loads your module's `createProvider(helpers)` factory at server start. (#3228)

### Reliability: worker and hooks

- **A wedged worker is reclaimed, on evidence only.** A claude-mem worker that holds its port and PID file but stops answering health is replaced once it is older than `CLAUDE_MEM_WEDGED_WORKER_UPTIME_S` (default 300) and its identity checks pass; a foreign process on the port is never touched. (#4129)
- **Port handling.** Hooks prove the port is free before spawning (#3219) and wait until a killed worker's port can be bound (#3416). An unbindable port is reported as a boot failure (#4299), and a worker that dies before binding exits 78 and logs its last boot step (#3558).
- **PID files.** A `worker.pid` whose process is alive but never healthy is removed once the port is free, Bun is also found through `BUN_INSTALL` (#3306), and PID namespaces such as `bwrap --unshare-pid` no longer delete a healthy worker's PID file (#4158).
- **Self-healing.** When Claude Code's auto-updater leaves the `claude` CLI unspawnable, the worker restarts itself, at most 3 times an hour (#3291). The daemon moves its working directory to the data directory, ending `EPERM` spawns from a launch folder it cannot re-enter (#3252), and an unwritable process registry degrades to memory with one warning instead of crashing (#4142).
- **Hook output is flushed.** Hooks wait for stdout to finish before exiting, so a large SessionStart context read through a pipe is no longer cut off. (#4331)
- **Full-text index bloat.** Index triggers fire only when indexed columns change, the unused `user_prompts_fts` table is dropped, and existing bloat is merged away once, in small background steps starting a minute after the worker starts. (#3284)
- **Buffered work resumes.** Work paused by an observer deadline resumes with backoff without waiting for another prompt (#4273), and a summary that arrives during an idle shutdown goes to a new observer (#3421).
- **Diagnostics.** `/api/health` fills `ai.lastInteraction` (#3197), stopped workers are recognized under Bun and Node (#3447), malformed input and circular log data no longer overflow the stack (#3264), and object log context prints as JSON (#4157).

### Reliability: observer and quota

- **Budgets follow the model's context window.** The observer resolves its model's window (or `CLAUDE_MEM_OBSERVER_CONTEXT_WINDOW`), retires a conversation at half of it using measured tokens, caps each tool field at a tenth, and stubs out old tool payloads on HTTP providers (#3625, #2957, #3151). A context-length refusal recycles the conversation and keeps the batch, and retry loops no longer grow history (#3625, #3479).
- **Replies that are not answers keep their batch.** Prose or empty replies get one retry in a fresh conversation (#3624), Claude CLI transport-error text is retried (#3460), drifted XML tags are corrected instead of copied (#3475), and capitalized tags from local models parse (#4113).
- **Field condensation.** Screenshots (MCP image blocks and `data:image` URLs) are replaced with a marker before condensation, a field over half the context window is truncated without a call (#4327), and a condense reply cut at the token limit is not stored (#4332).
- **Claude observer.** The empty tool list reaches the Claude CLI as `--tools=` (#3615), `CLAUDE_CODE_TMPDIR` is kept (#4162), the default macOS profile can refresh its token (#4153), a signed-out CLI shows in `/api/health` and the SessionStart banner (#4154), and the working directory is created before spawn (#4117).
- **Quota state per Claude account.** Rate-limit readings and cooldowns are tagged with the account that recorded them, so switching with `CLAUDE_MEM_CLAUDE_CONFIG_DIR` no longer inherits a pause (#4272, #4296). Queues held by a cooldown resume when it ends (#4110).
- **Quota windows.** The `seven_day_overage_included` window is tracked and pauses work only when a request is refused on it (#4132, #4293), the five-hour and seven-day windows refresh from the CLI's live figures (#4226), and `/api/health` marks expired windows and stores reset times in one unit (#4072, #4155, #4263).
- **Accurate outage banners.** A quota outage older than 30 minutes shows as a last-known-state note (#4085), expired cooldowns are not shown as active (#4116), and the banner stays out of the observer's own briefing (#4225).
- **No lost or misfiled observations.** The stall timer pauses while a reply is stored, so a slow store no longer stores a batch twice (#4077), and stored IDs stay paired with their observations when a batch holds an untitled one (#4294).

### Reliability: Chroma, transcripts and cloud sync

- **Corrupt collections are rebuilt.** Only a confirmed HNSW segment error on 2 batches drops a collection, which is then rebuilt from SQLite; a failed drop is retried and a chroma-mcp hung on a read is restarted (#3203, #4295). `doctor` reports chroma-mcp exits, the prewarm breaker and dropped collections (#3393).
- **Slow writes no longer kill chroma-mcp.** A timed-out request fails only that call and leaves the row pending, writes get `CLAUDE_MEM_CHROMA_MUTATION_TIMEOUT_MS` (default `600000`), and NUL bytes are replaced before writes. (#3571)
- **Chroma versions and settings.** chromadb is pinned to 1.5.9 (#3384), and the local store now records its writer epoch, so a future downgrade reports vector search unavailable instead of crashing on a newer store (#3466). `CLAUDE_MEM_CHROMA_EMBEDDING_FUNCTION` (default `default`) picks the embedding function for new collections; the API-backed ones send memory text to their vendor (#3422).
- **Backfill.** Title-only observations are indexed (#4264), partial failures finish as `rows_pending` and retry only the failed rows (#4279), and shutdown stops a backfill instead of reporting it complete (#4120).
- **Prewarm and locks.** A failing uvx prewarm backs off after 5 failures and stops after 20, with `UV_LINK_MODE=copy` on Windows (#4111), and a writer lock left by a reused PID is reaped (#4239).
- **Transcript turns are kept.** Transcript-watched sessions such as Codex rollouts record each user turn, so their observations are no longer lost (#3654). A turn the worker did not record is retried from its own line or zstd frame, and records caught mid-write are neither dropped nor duplicated (#4313, #4192, #4193).
- **Transcript watcher.** A turn with no known working directory waits for one instead of being filed under `~/.claude-mem` (#4319), a vanished session file no longer stops the watcher (#3642), and match rules gain `all`, `any`, `starts_with` and `not_starts_with` (#4223).
- **Cloud sync of large prompts.** Prompt text is cut to 200,000 bytes before it is read, which ended `SQLiteError: out of memory` crashes, and a prompt too large for one sync body uploads truncated with a marker (#3537). Prompts dead-lettered for size before this are re-queued once (#4311).
- **Cloud sync pull and status.** One pulled operation that can never apply is set aside in `sync_pull_quarantine` instead of stopping the pull, and `/api/sync/status` reports `pullQuarantine` (#4346). Status probes share one request with a 20-second limit, and background pulls wait out the retry delay after a failure (#4330).

### Security and privacy

- **Opt-in secret redaction.** With `CLAUDE_MEM_REDACT_ENABLED=true` (default `false`), 11 built-in patterns (AWS, GitHub, OpenAI, Anthropic, Slack, Stripe and Google keys, JWTs, PEM private keys and claude-mem's own keys) are replaced with markers before tool data, prompts and the last assistant message are stored. `CLAUDE_MEM_REDACT_DISABLED_BUILTINS` and `CLAUDE_MEM_REDACT_CUSTOM_PATTERNS` adjust the set, and server-runtime event bodies and tool log lines are covered too. (#2616, #4298)
- **Sign-in sends a usage summary.** The installer's cmem.ai sign-in request now also carries `install_state` (`fresh` or `update`) and a summary of the last 28 UTC days of local memory: per-day observation counts and discovery-token sums only, with no prompts, observation text, paths or project names. The installer prints the same figures as "Your memory so far". (#4266)
- **cmem account keys stay on the gateway.** A `cm_pro_` key is sent only to the cmem.ai gateway, from every key pool and the server runtime; before, one placed in a Gemini key setting was sent to Google (#4309, #4276). `GET /api/settings` also masks key pools stored as JSON arrays (#4309).
- **settings.json is owner-only.** Every writer creates `~/.claude-mem/settings.json` with mode 0600, and the cloud-sync skill no longer asks you to paste a sync token into chat. (#3498)
- **Excluded projects stay excluded.** Summaries skip unknown sessions and excluded checkouts, so an excluded OpenCode checkout no longer gets a session and an observer call every turn (#4310), and UserPromptSubmit honors `CLAUDE_MEM_EXCLUDED_PROJECTS` (#3311).
- **Less text leaves the machine.** Telegram session wrap-ups give file counts instead of paths (#4145), and recalled titles written into Grok Bot memory are stripped of control characters and fenced as untrusted text (#4146).
- **Telemetry.** New `worker_start_failed` and `supervisor_registry_degraded` events, a `top_abort_reason` field, `rate_limit`, `auth`, `deadline_exceeded`, `output_retry` and `drift` in `abort_reason`, and `chroma_zero_results` in `fallback_reason`. Telemetry stays opt-out with `npx claude-mem telemetry disable`.

### Windows

- **Worker starts.** Workers start through `Start-Process -WindowStyle Hidden` instead of Node's detached spawn, so no console window opens (#3529). The 2-minute spawn cooldown now starts only when a worker crashed during boot or could not be launched, not after any failed start (#3408).
- **Ports and locks.** The worker's listening socket is no longer inherited by child processes, a `spawn.lock` held by a dead PID is broken (#3309), and a start-token probe that times out no longer removes the PID-reuse guard (#4165).
- **Launching tools.** PATH lookups run `where.exe` directly (#3321), and an observer executable that cannot be spawned (a missing binary, or a `.cmd` shim Node refuses) is reported once as a setup error, with native `.exe` files preferred (#4122).
- **Dates.** When Bun cannot resolve the system time zone, dates fall back to a fixed UTC or ISO form instead of dropping SessionStart context or failing search. (#4126, #4302)

**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v13.28.0...v13.29.0

Related Repositories