Skip to content

Releases: dorukardahan/B12

v11.83.0

Choose a tag to compare

@dorukardahan dorukardahan released this 16 Aug 23:16

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 generic memory_store path 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 MCPServer name, 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

Choose a tag to compare

@dorukardahan dorukardahan released this 14 Aug 08:53

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 health now 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

Choose a tag to compare

@dorukardahan dorukardahan released this 09 Aug 18:25
0103811

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.py to clean the pre-existing duplicate session-summary backlog. It is dry-run by default; use --execute to apply soft-deletes. Back up the database first. The command is idempotent, never touches rows without a session_id, and leaves hard deletion to the normal 90-day garbage-collection window.

Changed

  • Codex summary extraction now runs from a real SessionEnd hook instead of the legacy delayed-notify adapter; Stop is now turn-scoped only.
  • Existing Codex installations must re-run ./install.sh --codex so the new hook is registered and the legacy adapter is retired. Retirement safely handles --previous-notify JSON 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_at value 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-python to 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

Choose a tag to compare

@dorukardahan dorukardahan released this 20 Jul 16:45

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 plistlib health 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 .gemini directory 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

Choose a tag to compare

@dorukardahan dorukardahan released this 20 Jul 13:29
052b6d6

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

Choose a tag to compare

@dorukardahan dorukardahan released this 13 Jul 12:23

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, and package-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

Choose a tag to compare

@dorukardahan dorukardahan released this 10 Jul 05:36

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 gguf dependency 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

Choose a tag to compare

@dorukardahan dorukardahan released this 27 Jun 16:31

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 _sys was only bound in _run_as_proxy, not the extracted _proxy_session — so a reconnect that negotiated a different protocol/capability set raised NameError instead 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 connected event 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 clears connected only 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 initialize could overshoot B12_MCP_RECONNECT_BUDGET (fixed 5s readline + uncapped backoff sleeps). All waits are clamped to the remaining deadline.
  • -32001 synthesis 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 of is no longer a bare token that fired on non-temporal objects (by end of the book) — replaced by a by (the) end of <temporal> pattern covering day/week/month/year/quarter, month names, Q1–Q4, H1/H2, and next/this, while by end of day still fires; the "will see" hedge no longer swallows a real commitment after an unpunctuated but (…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 kadar is recognized; and the dead bare-a direct-dative alternative was removed.

Changed

  • The reconnect capability-drift check compares full capabilities, not just top-level keys — a nested change (e.g. tools.listChanged flipping) 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 of temporal-vs-object (incl. month/quarter), the but contrast 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

Choose a tag to compare

@dorukardahan dorukardahan released this 27 Jun 10:12

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 --daemon redeploy, RSS self-guard os._exit, MAX_CONN eviction, or a crash), _proxy_session re-dials the daemon with capped backoff (total budget B12_MCP_RECONNECT_BUDGET, default 30s ≈ one launchd respawn), replays the cached initialize handshake — forwarding the replayed response when startup never completed, otherwise swallowing it after a protocol/capability drift check — replays notifications/initialized, synthesizes a retryable -32001 error 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 with B12_MCP_PROXY_RECONNECT=0. (#141)
  • Approximate-nearest-neighbor vector search is on by default in embed_daemon.py (_ann_supported now defaults enabled=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.sh omitted content_hash from its INSERT (the live upstream-seeded schema declares it TEXT UNIQUE NOT NULL), so every checkpoint raised an IntegrityError that was swallowed as dropped_dedup — 0 of thousands of rows landed. The INSERT now writes content_hash + memory_type + numeric epoch timestamps via ON CONFLICT(content_hash) DO NOTHING (not INSERT OR IGNORE, which also swallows real NOT NULL/CHECK violations), tags the project from the hook's stdin .cwd rather than $PWD, and adds buffer-retention + a circuit breaker. (#128)
  • The keyword-recall hook ranked best matches LAST. hooks/memory-retrieval.sh computed the BM25 term as 0.40 * (1 / (1 + abs(rank))) — decreasing in match strength — so the strongest keyword hits scored lowest. Corrected to 0.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 on new.deleted_at IS NULL, so a restore (deleted_at NOT NULL → NULL) issued an FTS5 delete for a row already absent from the index → database disk image is malformed. A new _ensure_fts_sync_triggers helper splits the update logic by deleted_at transition (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's memory_fts_* set on the same table; _ensure_schema now 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_score is written on two scales (fractional [0,0.95] and level multipliers [0.7,2.0]); the read-side ORDER BY sites compared them raw. Normalized at all ranking sites — SessionStart, MCP, the OpenCode plugin (db.ts + rebuilt dist), 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, and import actually backfills the embeddings for its imported rows (scoped to the imported content_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 OR fallback for < 3-char (< 2 for stemmed) terms. (#19, #135)
  • stat broke hook output on Linux. Several hooks used BSD-first stat -f, which on GNU coreutils means --file-system and 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.sh fell 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.py now registers each connection before setup and closes it on every path (a _close_open_conns registry + handle_request finally); 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.sh now syncs and stages it. The docs/architecture.md note 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) and memory_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 / --full now reload the launchd MCP daemon. Previously only --daemon reloaded it, so the documented git pull && ./install.sh --all upgrade left the old daemon serving old in-memory code and daemon-side fixes silently didn't activate. The reload preserves a custom B12_DATA_DIR and 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) and docs/architecture.md (Fix C, FTS triggers, event-loop offload).

v11.80.3

Choose a tag to compare

@dorukardahan dorukardahan released this 26 Jun 22:16

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_TIMEOUT default 18000) in b12_mcp_daemon.py and in the config/com.b12.mcp.daemon.plist launchd 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. Set B12_MCP_IDLE_TIMEOUT>0 to re-enable (not recommended for Claude Code).

Changed

  • B12_MCP_MAX_CONN default 64256. 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, and docs/architecture.md, and synced the README env-var table. The remaining socket-close triggers (cap eviction, RSS self-guard os._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).