Releases: dorukardahan/B12
Release list
v11.83.0
Added
- Read-only session-summary identity audit that classifies every active unbound row by policy category and inventories payload-free producer, platform, project, tag-shape, and age dimensions.
- Documented the session-summary identity, retention, structured recovery, backup/rollback, collision, and project-continuity policy (docs/session-summary-identity-policy.md).
Changed
- MCP session-summary writers now explicitly mark intentionally unbound identity with stable producer and platform metadata instead of relying on a missing
session_id; the genericmemory_storepath supplies safe defaults for manual summaries. - SessionEnd no longer applies a rank-only session-summary cap; retention remains audit-, review-, and backup-gated.
- MCP server import falls back to the SDK v2
MCPServername, so the server still starts on environments where the Python MCP SDK v2 is installed (the package pin remains on v1 for now).
Fixed
- Embed daemon queue wait is now bounded: MCP client requests that cannot be admitted within the configured queue timeout fail fast instead of waiting indefinitely.
- Installer daemon restart now verifies process identity before signaling, so a stale PID file can no longer terminate unrelated Python processes.
- Release tooling now parses CHANGELOG headings correctly when versions are documented inside code spans, fenced/indented code blocks, or block HTML comments — validation no longer misreads the first real release version.
Internal
- Release dry-run is covered by an end-to-end test verifying version sync across all package touchpoints without git side effects.
v11.82.1
Fixed
- Long-running MCP and embedding daemons now detect when a package-manager Python upgrade removes or replaces their mapped interpreter, then restart at safe request boundaries instead of continuing on a stale runtime.
- MCP recovery drains already accepted JSON-RPC work before restarting, while existing proxy reconnection carries the host session onto the respawned daemon.
- Embedding recovery now works for MCP-only hosts and restarts the model service on demand only when semantic search or reranking needs it, avoiding an unnecessary model startup for keyword-only retrieval.
- Embed-daemon signal handling, singleton-lock validation, per-user process discovery, and client shutdown are hardened so restart detection cannot kill in-flight work or mistake another process for the managed daemon.
Changed
b12 healthnow inspects each live daemon's actual mapped executable on macOS and Linux, compares its Python runtime with the installed environment, and reports a launchd restart on macOS or process-termination/supervisor guidance on Linux and other Unix hosts when stale.- The stale-runtime check cadence is configurable while remaining lightweight by default.
Internal
- Added cross-platform regression coverage for deleted/replaced interpreters, safe MCP draining, MCP-only recovery, prompt signal shutdown, on-demand embedding startup, lock ownership, process scoping, and healthy/stale health output.
v11.82.0
Codex users: after upgrading, re-run ./install.sh --codex and accept the new SessionEnd hook in Codex's StartupHooksReview trust screen. Without both steps, session summaries can silently stop.
Added
- Added
scripts/b12_dedupe_session_summaries.pyto clean the pre-existing duplicate session-summary backlog. It is dry-run by default; use--executeto apply soft-deletes. Back up the database first. The command is idempotent, never touches rows without asession_id, and leaves hard deletion to the normal 90-day garbage-collection window.
Changed
- Codex summary extraction now runs from a real
SessionEndhook instead of the legacy delayed-notify adapter;Stopis now turn-scoped only. - Existing Codex installations must re-run
./install.sh --codexso the new hook is registered and the legacy adapter is retired. Retirement safely handles--previous-notifyJSON wrappers while preserving user-owned notify commands. - The installer reports hooks disabled in Codex trust state but deliberately does not override the user's trust choice.
Fixed
- Repeated session-end events now upsert one summary row per session instead of appending duplicates, while preserving the original
created_atvalue and keeping FTS, vector, and graph indexes synchronized. - Summary migration and resurrection now handle soft-deleted hash collisions safely, preserving one live canonical summary without rewriting unrelated legacy rows.
- Codex upgrades preserve symlink-managed configuration and user handlers that share a hook group with B12, and roll back notify changes if the legacy adapter cannot be retired safely.
- Release version synchronization now includes the shipped OpenCode manifest, closing a packaging drift gap.
Internal
- Modernized Python package license metadata, upgraded
actions/setup-pythonto v7, and added CI coverage for README platform-count drift.
Field verification
- Verified end-to-end on macOS with both Codex and Claude Code. On a real 112 MB database, the dedupe dry-run predicted 571 removals across 114 sessions, execution matched exactly, a second run reported the database unchanged, zero duplicate sessions remained, and FTS stayed intact.
v11.81.5
Changed
- Report Antigravity as a first-class host in
b12 health, requiring its MCP configuration, staged native plugin files, and installed plugin state before marking it loadable.
Fixed
- Keep the launchd daemon plist template strict-XML compatible so Python
plistlibhealth checks no longer warn after a successful install, with regression coverage for every shipped plist template and the deployed health-check path. - Avoid treating Antigravity's shared
.geminidirectory as evidence that the legacy Gemini CLI is installed.
Internal
- Synchronize contributor heredoc syntax-validation examples with the canonical CI probe so redirected Python heredocs are parsed correctly.
v11.81.4
Changed
- Clarify that the default BGE-M3 embedding model downloads once during setup, with practical size guidance for the default and optional GGUF backends.
- List Google Antigravity consistently in the public supported-platform summary.
Internal
- Add regression coverage for lifecycle hook adapters and setup documentation verification across supported hosts.
v11.81.3
Added
- Google Antigravity CLI integration — B12 hooks now detect and support the Antigravity coding assistant as a host platform.
- CI check for synchronized package versions across
pyproject.toml,package.json, andpackage-lock.json(both top-level and root package fields), preventing silent drift during releases.
Fixed
- MCP config template consistency validation — catches mismatches between platform config templates before they reach users.
v11.81.2
A maintenance release that hardens OpenCode permission handling, expands regression coverage across the plugin, and aligns public setup guidance with current BGE-M3 and cross-agent behavior.
Changed
- Accurate BGE-M3 and GGUF setup guidance. Documentation now identifies the default FP32 download as roughly 2.2 GB, records the published Q4_K_M (~438 MB) and Q8_0 (~635 MB) artifact sizes, and tells manual-install users to install the optional
ggufdependency set before enabling the backend. (#155) - Consistent installation and supported-tool documentation. Quick-start examples now use the same executable installer step, the documented support list matches the integrations B12 ships, and setup examples use portable paths. (#143, #153, #154)
Fixed
- OpenCode permission pattern arrays now fail closed. Automatic approval requires every supplied pattern to be a valid trusted B12 tool; mixed, blank, malformed, and runtime non-string entries are rejected instead of being blessed by one trusted value. (#156)
Internal
- OpenCode lifecycle and persistence coverage is substantially broader. Automated checks now exercise memory capture, retrieval, tagging, database updates, FTS restore transitions, embeddings, graph neighbors, session context, working memory, guardrails, and permission allow/deny boundaries. (#148, #156)
- Embedding fallback behavior is documented in source. sqlite-vec kNN remains the primary route, with bounded linear scans retained deliberately when ANN is unavailable, disabled, or below threshold; runtime behavior is unchanged. (#145)
v11.81.1
Post-release hotfixes from a retroactive GPT-5.5 + GLM-5.2 review of the v11.81.0 changes (the headless review CLIs had timed out during original development, so those PRs had landed on empirical verification plus the in-PR bot). The fresh two-model pass caught real bugs the bot missed — including a P1 regression and a NameError.
Fixed
- Bilingual deferral regression (P1). A sentence combining a Turkish-negated obligation with the English "will see" hedge — e.g.
gerek yok, we will see how it goes— scored DECISION instead of baseline. The negation and hedge subtractions ran independently on the original text, so the hedge subtraction re-introduced the Turkish-negated token (gerek) into its own residual. The subtractions now chain through a single working string, so neither restores what the other removed (a real obligation alongside a negated one still commits). - Capability-drift path crashed instead of exiting. Fix C's capability-drift branch logged via
_sys.stderr, but_syswas only bound in_run_as_proxy, not the extracted_proxy_session— so a reconnect that negotiated a different protocol/capability set raisedNameErrorinstead of exiting cleanly. The import is now in scope, and a drift-exit integration test exercises the path. - Reconnect write/clear race. The proxy's write-failure path could clear the
connectedevent after a successful reconnect had already set it, wedging stdin forwarding until timeout (and dropping the very session Fix C exists to preserve). It now clearsconnectedonly if the writer it failed on is still the current one. - Reconnect budget is now a hard cap. A daemon that accepted a connection but never answered the replayed
initializecould overshootB12_MCP_RECONNECT_BUDGET(fixed 5s readline + uncapped backoff sleeps). All waits are clamped to the remaining deadline. -32001synthesis write is guarded. If the host closed stdout while the proxy was synthesizing retryable errors for in-flight requests, the write raised an unretrieved task exception; it now degrades cleanly.- Deadline / commitment heuristic edge cases.
by end ofis no longer a bare token that fired on non-temporal objects (by end of the book) — replaced by aby (the) end of <temporal>pattern covering day/week/month/year/quarter, month names, Q1–Q4, H1/H2, and next/this, whileby end of daystill fires; the "will see" hedge no longer swallows a real commitment after an unpunctuatedbut(…goes but we must migrate) and no longer misfires when a comma or dash precedes the wh-continuation (we'll see, if we need to migrate); the Turkish possessive deadline<weekday> günü sonuna kadaris recognized; and the dead bare-adirect-dative alternative was removed.
Changed
- The reconnect capability-drift check compares full capabilities, not just top-level keys — a nested change (e.g.
tools.listChangedflipping) is now caught via canonical-JSON comparison instead of silently splicing a divergent session onto the host.
Internal
- New regression tests: bilingual TR-neg + hedge,
by end oftemporal-vs-object (incl. month/quarter), thebutcontrast clause, comma/dash before a wh-continuation,günü sonuna kadar, and proxy capability-drift clean-exit + nested-drift compatibility. Both models stress-tested the new regexes for ReDoS (clean). Full suite 343 passed.
v11.81.0
A "reaper-class" regression audit (a multi-agent sweep for hardening/perf features that quietly break a core path) surfaced 22 confirmed defects; this release ships fixes for all of them plus Fix C, the resilient MCP proxy that closes the whole "B12 drops mid-session" class. Each change went through a three-model ceremony (implementation + headless GPT-5.5 / GLM-5.2 review where available + the Codex GitHub App bot as the live review gate).
Added
- Fix C — the per-session stdio proxy now reconnects transparently. When the daemon-side socket closes while the host is still alive (launchd restart,
./install.sh --daemonredeploy, RSS self-guardos._exit,MAX_CONNeviction, or a crash),_proxy_sessionre-dials the daemon with capped backoff (total budgetB12_MCP_RECONNECT_BUDGET, default 30s ≈ one launchd respawn), replays the cachedinitializehandshake — forwarding the replayed response when startup never completed, otherwise swallowing it after a protocol/capability drift check — replaysnotifications/initialized, synthesizes a retryable-32001error for each in-flight request so the host fails fast instead of hanging, and resumes piping. The host never sees the break. It exits to the legacy "disconnected until manual/mcp" behavior only on budget exhaustion or material capability drift. Disable withB12_MCP_PROXY_RECONNECT=0. (#141) - Approximate-nearest-neighbor vector search is on by default in
embed_daemon.py(_ann_supportednow defaultsenabled=True, threshold 500), matching the documented default — previously the code default (enabled=False, threshold 10000) contradicted the docs and only a config seed enabled it. (#135)
Fixed
- Mid-session checkpoint capture was silently dead for 40+ days (P0).
hooks/memory-checkpoint.shomittedcontent_hashfrom its INSERT (the live upstream-seeded schema declares itTEXT UNIQUE NOT NULL), so every checkpoint raised anIntegrityErrorthat was swallowed asdropped_dedup— 0 of thousands of rows landed. The INSERT now writescontent_hash+memory_type+ numeric epoch timestamps viaON CONFLICT(content_hash) DO NOTHING(notINSERT OR IGNORE, which also swallows realNOT NULL/CHECKviolations), tags the project from the hook's stdin.cwdrather than$PWD, and adds buffer-retention + a circuit breaker. (#128) - The keyword-recall hook ranked best matches LAST.
hooks/memory-retrieval.shcomputed the BM25 term as0.40 * (1 / (1 + abs(rank)))— decreasing in match strength — so the strongest keyword hits scored lowest. Corrected to0.40 * min(abs(rank)/20, 1.0), matching the MCP path (the same inversion fixed server-side in v11.0.0 but never in the hook SQL). (#130) - Restoring a soft-deleted memory could corrupt the FTS index. All three external-content FTS5 indexes (
memory_fts,memory_fts_stemmed,memory_content_fts) had an UPDATE sync trigger guarded only onnew.deleted_at IS NULL, so a restore (deleted_atNOT NULL → NULL) issued an FTS5deletefor a row already absent from the index →database disk image is malformed. A new_ensure_fts_sync_triggershelper splits the update logic bydeleted_attransition (active→active re-index, active→deleted remove, deleted→active insert-only) and migrates existing DBs on the next start. (#139) - Duplicate FTS trigger sets double-indexed every write. A DB upgraded across versions could carry both the legacy upstream
fts_*triggers and B12'smemory_fts_*set on the same table;_ensure_schemanow ensures the soft-delete-aware B12 set and drops the legacy one so a dual-trigger DB self-heals. (#139) - The commitment/deadline importance detector over-fired. Bare weekdays ("we met last Monday", "Happy Friday") scored as deadlines and "we'll see" scored as a commitment. Weekdays now require a deadline context (EN
by/before/until/due[/on][/the end of][/time] <weekday>; Turkish<weekday>(dative|günü|akşamı|sonu|numeric) kadar|dek, comparative "kadar" excluded), and the "will see" deferral idiom is stripped to the clause boundary while a real commitment elsewhere still fires. (#11, #140) - Dual-scale importance corrupted ranking.
importance_scoreis written on two scales (fractional[0,0.95]and level multipliers[0.7,2.0]); the read-sideORDER BYsites compared them raw. Normalized at all ranking sites — SessionStart, MCP, the OpenCode plugin (db.ts+ rebuiltdist), and the long-session re-surface path. (#131, #132) - Embeddings were silently skipped on store. The MCP encode timeout (5s) was below the daemon's own budget, so a slow first encode dropped the embedding and the memory was never vector-searchable. The client timeout is now
B12_DAEMON_CLIENT_TIMEOUT(default 20s) with defensive parsing, andimportactually backfills the embeddings for its imported rows (scoped to the importedcontent_hashes). (#9, M1, #134) - Trigram FTS dropped short tokens. The trigram tokenizer can't match tokens under 3 chars; the recall path now keeps the
ORfallback for< 3-char (< 2for stemmed) terms. (#19, #135) statbroke hook output on Linux. Several hooks used BSD-firststat -f, which on GNU coreutils means--file-systemand poisoned the parsed value. They are now GNU-first (stat -c) with an all-digits guard (session-start, proactive-surface,b12_smoke.sh). (#8, #136)- Proactive surfacing ran unlocked on a failed lock acquire.
hooks/memory-proactive-surface.shfell through to its work even when it couldn't take the lock; it now emits{}and exits 0. (#18, #136) - The daemon leaked a SQLite connection per request and lost the session summary on an RSS exit.
embed_daemon.pynow registers each connection before setup and closes it on every path (a_close_open_connsregistry +handle_requestfinally); the RSS self-guard flushes the current session tracker (not an empty global) before exiting. (#13, #12, #137) - The marketplace catalog drifted ~62 releases behind.
.claude-plugin/marketplace.json's plugin-entry version was never synced;release.shnow syncs and stages it. Thedocs/architecture.mdnote that the OpenCode plugin "scrubs nothing" was corrected (it scrubs + caps on its native write path since the v11.80.0 work). (#15, #16, #138)
Changed
- Blocking work is off the shared MCP event loop.
memory_refine/memory_surface(embed socket round-trips, SQLite) andmemory_export(gzip) ran inline on the FastMCP loop, freezing every connected client; they now offload via a cancellation-safe_run_locked_offthread(shield + drain-loop) /to_thread. (#133) install.sh --all/--fullnow reload the launchd MCP daemon. Previously only--daemonreloaded it, so the documentedgit pull && ./install.sh --allupgrade left the old daemon serving old in-memory code and daemon-side fixes silently didn't activate. The reload preserves a customB12_DATA_DIRand propagates exit status. (#129)
Internal
- New regression suites: checkpoint INSERT contract, install daemon-reload, retrieval-correctness, surfacing trainers, MCP async offload, embedding pipeline, FTS/ANN coverage, hook reliability, daemon reliability, version/doc drift, FTS trigger dedup + restore-no-corruption, importance signals (EN+TR deadline/commitment edge cases), and the Fix C proxy (handshake observers, drift check, mock-daemon reconnect / synthesized-error / budget-exhaustion). Documentation synced across
README.md(env-var table:B12_MCP_PROXY_RECONNECT,B12_MCP_RECONNECT_BUDGET) anddocs/architecture.md(Fix C, FTS triggers, event-loop offload).
v11.80.3
Fix B12 MCP repeatedly disconnecting mid-session in Claude Code. The shared MCP daemon's idle-connection reaper cancelled any client connection idle beyond B12_MCP_IDLE_TIMEOUT (default 1800s = 30 min); during a normal coding stretch with no B12 tool call the connection legitimately sat idle and was reaped. The per-session stdio proxy exits on the resulting socket EOF, and Claude Code does not auto-respawn an exited stdio MCP server — it shows B12 "disconnected" until a manual /mcp reconnect, which only reset the 30-min clock so it dropped again. Root cause independently confirmed by GPT-5.5, GLM-5.2, and Grok review.
Fixed
- Idle-connection reaping is now DISABLED by default (
B12_MCP_IDLE_TIMEOUTdefault1800→0) inb12_mcp_daemon.pyand in theconfig/com.b12.mcp.daemon.plistlaunchd template. Reaping a live-but-idle session was never client-invisible (the "host respawns the proxy on next use" premise is false for Claude Code) and was never needed for cleanup: a closed tab, a crashed host, and a SIGKILL'd proxy all deliver socket EOF, so connections track open editors 1:1 and self-clean. SetB12_MCP_IDLE_TIMEOUT>0to re-enable (not recommended for Claude Code).
Changed
B12_MCP_MAX_CONNdefault64→256. With the reaper off, the connection-cap evictor is the remaining intentional drop path (it cancels via the same client-visible code path), so the cap is raised well above realistic concurrency and documented as an emergency backstop only.- Corrected the now-false "reaping is client-invisible / Claude Code respawns the proxy" comments in
b12_mcp_daemon.py,b12_mcp_server.py:_run_as_proxy, anddocs/architecture.md, and synced the README env-var table. The remaining socket-close triggers (cap eviction, RSS self-guardos._exit, launchd restart, daemon redeploy) are documented as still surfacing a one-time drop, to be addressed by a future reconnect-with-initialize-replay proxy (Fix C).