Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"name": "b12-memory",
"source": "./",
"description": "Persistent cross-session memory with automatic extraction, semantic search, spaced repetition, and contradiction detection. Supports Claude Code, Codex, Gemini, VS Code, Cursor, and 5+ more platforms.",
"version": "11.83.0",
"version": "11.83.1",
"category": "memory",
"tags": [
"memory",
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "b12-memory",
"version": "11.83.0",
"version": "11.83.1",
"description": "Persistent cross-session memory system with automatic extraction, semantic search, and spaced repetition. Works across 10+ AI coding platforms.",
"author": {
"name": "Doruk Ardahan",
Expand Down
38 changes: 19 additions & 19 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,31 @@

All notable changes to B12 are documented in this file.

## [Unreleased]
## [v11.83.1] — 2026-10-21

### Changed
Hook output reliability, untyped memories shown as general, and an installer that keeps other tools' hooks.

- Config / install help form now lists Google Antigravity alongside the other documented hosts.
### Added
- `.b12-ignore`: an empty file in a Claude Code config folder makes every install mode skip that folder.

### Fixed
### Changed
- Graph expansion no longer pulls session summaries or progress rows in as related memories, matching the primary retrieval paths.
- The stdio proxy's default reconnect budget (`B12_MCP_RECONNECT_BUDGET`) is now 45 seconds, so open sessions survive a daemon restart that needs a second launchd spawn.
- The config and install help form lists Google Antigravity alongside the other documented hosts.

- Running the test suite on a machine with B12 installed no longer touches the live install. Installer tests used to rebind the live `com.b12.mcp.daemon` launchd job to a temporary plist and stop the live embed daemon, and a test session wrote a summary into the live database. A root `conftest.py` now isolates `HOME`, the daemon sockets and `launchctl`/`crontab` before any test module loads.
- `B12_EMBED_RUNTIME_DIR` and `B12_MCP_DAEMON_SOCK` are now honored by every hook and script. About 30 call sites, plus the installer's socket wait and uninstall, still used hardcoded `/tmp/b12-*` paths, so a custom runtime dir split clients from the daemon. The installer now also writes custom values into the launchd job. Defaults are unchanged.
- `install.sh` no longer deletes hooks that other tools added to a setup's `settings.json`. It replaced the whole `hooks` block with the B12 template; it now swaps only B12's own handlers (current `~/.B12/hooks/` and legacy `~/.claude*/hooks/memory-*` paths) and keeps the rest.
- `install.sh --all` and the skill install no longer write into `~/.claude-*` folders that are not Claude Code setups (for example a transcript archive). A folder counts as a setup when it holds something Claude Code creates there (`settings.json`, `.claude.json`, `CLAUDE.md`, `projects/`, `skills/` and similar); `~/.claude` always does, and a `.b12-ignore` file opts any folder out. The `.b12-ignore` opt-out applies to every install mode, including the default `~/.claude` and an explicit target directory.
- Daemon runtime self-heal now works on macOS. When Homebrew deleted the versioned Python keg a daemon was running from, `proc_pidpath` failed with ENOENT and the check fell back to the venv launcher, which still exists, so the daemons kept running on the deleted runtime and `b12_health` reported them as fine. A deleted binary is now reported as missing.
- Open sessions no longer drop B12 when a daemon restart needs a second launchd spawn. The stdio proxy's default reconnect budget (`B12_MCP_RECONNECT_BUDGET`) is now 45 seconds; the old 30 seconds equalled launchd's respawn throttle, so if the first spawn exited at once the budget ran out just before the daemon came back and every session needed a manual `/mcp` reconnect.
- Memories stored without a `memory_type` no longer surface as a bare `null`. The display line was built by concatenating the type in SQL, and concatenating NULL yields NULL, so proactive surfacing, recall and the session-start and subagent context blocks lost those memories' content. They now show as `[general]`, the schema default, and the proactive-surface hook drops any result without a display before it counts, injects or records the results, so a malformed daemon response no longer spends the token budget on an empty block or marks unseen memories as already surfaced. The type filters had the same NULL trap: `memory_type NOT IN (...)` and `memory_type != '...'` are NULL for an untyped row, so the keyword, cold-semantic, session-start and subagent queries dropped those memories entirely, and the feedback digest never decayed or archived them. Every type exclusion now keeps untyped rows, treating them like `general`; in the hooks an untyped row that carries the `session-summary` tag is still kept out, as a summary. Graph expansion (in `memory-retrieval.sh` and the OpenCode plugin) now applies the same summary exclusion as the primary retrieval paths, so session summaries and progress rows, typed or tagged, are no longer pulled in as graph neighbours. `memory-browse.sh` had the same concatenation: its delete preview came back empty, so an untyped memory could not be deleted ("No active memory found"), and its stats printed a blank line for the untyped group, now labelled `none`. The OpenCode plugin's graph expansion and its session-start project and universal blocks (which printed `[null]`) are fixed the same way. Python display paths had the Python version of the bug: the recall daemon and the resurface, topic-shift and cross-session pickers sent an untyped memory's type as `''`, which the retrieval hook rendered as `[|src:...]`, and the MCP session-context, quality, stats and surfacing outputs printed `[None]`; `memory_refine` turned an explicit JSON `null` type into the string `"None"`. They now send and print `general` (`none` for stats groups), and the retrieval hook also maps an empty type to `general` for a daemon that has not been restarted yet.
- The Gemini CLI SessionEnd adapter's background worker no longer inherits the adapter's stdout, stderr or the fd 3 copy of stderr, so it cannot hold the host's pipe open while the B12 hook runs. A test now checks every backgrounded job in `hooks/` (multi-line blocks, one-line groups, plain commands, pipelines and and-or lists, at the end of a line or before another command), applies its redirections in order (including `exec` redirections such as `exec 3>&2`, the bodies of foreground groups, and `$( )` and backtick substitutions, quoted or not, process substitutions, and commands continued over several lines), and fails if any command, including an earlier pipeline stage, is left with stdout or stderr on the hook's own pipes. The Gemini tool-call adapter's checkpoint job is wrapped in one redirected group and now also closes the fd 3 copy of stderr from `exec 3>&2`, which kept the host's stderr open until the checkpoint script finished. The `printf` feeding the input parser in the working-context and proactive-surface hooks now discards its stderr so the process substitution meets the same rule.
- Hook watchdogs no longer hold the host's stdout pipe open after the hook exits. The orphaned watchdog `sleep` delayed EOF until the timer expired, and Claude Code then refused hook output that injected context, so proactively surfaced memories and tag corrections were dropped.
- The MCP server and daemon no longer flood stderr with SDK validation dumps for newer-protocol client probes (`server/discover`, pre-initialization `roots/list_changed`) or with one INFO line per request. Each known probe is logged once as a single line; all other warnings are unchanged. In daemon mode this stderr is an unrotated launchd log that had grown by megabytes per day.
- Installer now migrates launchd jobs that still run B12 Python scripts with `/usr/bin/python3` (Python 3.9 on macOS) to the B12 venv interpreter. Older consolidation jobs failed on every run because the scripts require Python 3.11+. Jobs that were unloaded or disabled stay off, and `/bin/bash` jobs are untouched.
- `memory-consolidate.py` exits with a clear message under Python older than 3.11 instead of a traceback.
### Fixed
- Hook output reaches Claude Code again. Hook watchdogs and backgrounded workers no longer keep the host's stdout or stderr pipe open after the hook exits, which had caused proactively surfaced memories and tag corrections to be refused. This covers the Gemini CLI SessionEnd worker and the Gemini tool-call checkpoint job, which also kept the `exec 3>&2` copy of stderr.
- Memories without a `memory_type` show as `[general]` everywhere (recall, proactive surfacing, the session-start and subagent blocks, MCP outputs, `memory-browse.sh` and the OpenCode plugin) instead of `null`, `[]` or `[None]`, and type filters no longer drop them. `memory-browse.sh delete` can delete them again, and proactive surfacing no longer spends its token budget on results without a display.
- `install.sh` keeps hooks that other tools added to `settings.json` and no longer writes into `~/.claude-*` folders that are not Claude Code setups.
- The installer moves launchd jobs that still run B12 scripts with `/usr/bin/python3` to the B12 venv interpreter, and `memory-consolidate.py` exits with a clear message below Python 3.11.
- Daemon runtime self-heal works on macOS when Homebrew removes the Python version a daemon was started from.
- The MCP server and daemon log each newer-protocol client probe once instead of writing SDK validation dumps to stderr.
- `B12_EMBED_RUNTIME_DIR` and `B12_MCP_DAEMON_SOCK` are honored by every hook and script.

### Internal

- `scripts/release.sh` now replaces the `## [Unreleased]` block, as the repo writes it, with the curated release section. It only matched `## Unreleased`, so a release would have landed above the pending entries and left them under it. The CHANGELOG also gains the missing v10.5 and v11.21.0 entries.
- The test suite passes on macOS. Socket-based tests now use a short temp dir (`tmp_path` there exceeds the 104-byte AF_UNIX limit), the SessionEnd retention tests use the platform's DB path instead of the Linux one (on macOS they were checking an unused file), and two tests that simulate a deleted runtime through a symlinked launcher, which only works on Linux, are skipped on macOS.
- The test suite no longer touches a live install (temporary HOME, runtime dir and sockets, `launchctl` and `crontab` stubs) and passes on macOS.
- A repo-wide test fails if a backgrounded hook job leaves an fd on the hook's own pipes; shapes it does not model are tracked in #220.
- `scripts/release.sh` replaces the `## [Unreleased]` block, and the CHANGELOG gains the missing v10.5 and v11.21.0 entries.

## [v11.83.0] — 2026-08-17

Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,14 @@ Dependency update PRs are review-gated too: Dependabot can suggest updates, but

## Changelog (recent)

### v11.83.1 (2026-10-21)

- Hook output reaches Claude Code again: watchdogs and background workers no longer hold the host's pipes open.
- Memories without a type show as `[general]` everywhere and are no longer dropped by type filters.
- `install.sh` keeps other tools' hooks and honors a `.b12-ignore` opt-out.

See [CHANGELOG.md](CHANGELOG.md) for the full notes.

### v11.83.0 (2026-08-17)

See [CHANGELOG.md](CHANGELOG.md) for the full notes.
Expand Down
2 changes: 1 addition & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3697,7 +3697,7 @@ restart_embed_daemon_if_running() {
# Main
# ═════════════════════════════════════════════

echo "B12 Memory System Installer (v11.83.0 — multi-platform)"
echo "B12 Memory System Installer (v11.83.1 — multi-platform)"
echo "─────────────────────────────────"

# Full setup: create venv first
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "b12-memory-system",
"version": "11.83.0",
"version": "11.83.1",
"private": true,
"description": "Persistent memory system for AI coding assistants",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion plugins/opencode/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "b12-opencode-plugin",
"version": "11.83.0",
"version": "11.83.1",
"description": "B12 Memory System - OpenCode Plugin",
"type": "module",
"main": "./dist/index.js",
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "b12-memory"
version = "11.83.0"
version = "11.83.1"
description = "Persistent cross-session memory system for AI coding assistants"
readme = "README.md"
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion scripts/b12_health.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

# ── Constants ────────────────────────────────────────────────────────

VERSION = "11.83.0" # keep in sync with package.json / plugin.json / B12_VERSION (see docs/releasing.md)
VERSION = "11.83.1" # keep in sync with package.json / plugin.json / B12_VERSION (see docs/releasing.md)

_HOME = Path.home()
_B12_DIR = Path(os.environ.get("B12_DATA_DIR", _HOME / ".B12"))
Expand Down
2 changes: 1 addition & 1 deletion scripts/b12_mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
# falls back to legacy in-process stdio mode below — non-Claude-Code consumers
# (Codex, Gemini, Kimi, OpenCode, Grok) see zero behaviour change either way.
MCP_DAEMON_SOCK = os.environ.get("B12_MCP_DAEMON_SOCK", f"/tmp/b12-mcp-{_UID}.sock")
B12_VERSION = "v11.83.0"
B12_VERSION = "v11.83.1"

# Fix C — resilient proxy reconnect. When the daemon-side socket closes while the
# host CLI's stdin is still open (daemon restart by launchd/RSS-guard, redeploy,
Expand Down
Loading