Releases: TSchonleber/brainctl
v2.8.0 — 16 brain subsystems + v2 MCP tool surface
This release lands the issue #116 brain-architecture work and consolidates the MCP tool surface to fit harness caps. Supersedes overnight PRs #120–#137 as a single artifact (PR #138).
Added — 16 brain-region / nucleus subsystems (Phase 1)
Migrations 067–082 introduce schemas + dispatch for:
- 067 locus coeruleus — phasic NE surprise gain (Aston-Jones–Cohen)
- 068 nucleus basalis — phasic ACh attention gating; adds
bg_modulators.acetylcholine - 069 ARAS — arousal / sleep-wake transitions
- 070 habenula — negative-prediction "no-go" signal
- 071 hippocampus CA1 + subiculum — mismatch detection / output
- 072 workspace bandwidth — global-workspace throttling
- 073 connectome — explicit inter-region graph
- 074 sleep architecture — REM / NREM cycle tracking
- 075 VTA / SNc — dopamine pathway log
- 076 septum theta — hippocampal theta pacing
- 077 raphe — serotonin
- 078 memory aging — synaptic tagging-and-capture (Frey & Morris)
- 079 claustrum — multimodal binding
- 080 colliculi — orienting response
- 081 mammillary — Papez-circuit transit log
- 082 olfactory — single-trial valence imprinting
Each subsystem ships an mcp_tools_*.py module, design proposal in docs/proposals/, and matching pytest module.
Changed — MCP tool surface v2 (hard cutover, 370 → 100 visible)
Consolidated the public MCP tool surface from 370 named tools to 100 visible via 35 action-discriminated dispatchers. v1 tool names remain callable internally; rollback is a one-line filter removal. Full v1→v2 mapping in docs/TOOL_MIGRATION_V2.md.
Call pattern:
Why: many MCP harnesses cap at ~100 visible tools, and ~370 tool descriptions in every system prompt was burning ~50k tokens before any agent work began. v2 cuts that to ~12k tokens.
Added — Windows CI smoke
test-windows (3.12) job verifies brainctl init + core test subset on windows-latest. Currently continue-on-error: true; promotion to required after 2–3 green PRs.
Fixed — UTF-8 read for SQL files (Windows hardening)
Path.read_text(encoding="utf-8") explicit on every SQL-ingest site (_impl.py, brain.py, migrate.py ×3). Previously the system locale encoding was used, which crashed on em-dashes / arrows / γ in init_schema.sql on Windows cp1252 locales.
Fixed — fresh brainctl init includes every migration
init_schema.sql now contains migrations 067–082 inlined, and cmd_init calls migrate.run() after executescript() as defense in depth. acetylcholine is declared inline in the bg_modulators CREATE TABLE to avoid a SQLite-version-dependent backfill quirk that left the column NULL on CI Linux SQLite 3.31.
Install
pip install brainctl==2.8.0
pip install brainctl[mcp]==2.8.0 # MCP server (stdio + HTTP)
pip install brainctl[all]==2.8.0 # everythingBundle credit
This release supersedes 17 overnight PRs: #120 through #137 plus #138. PRs #139 onward continue against main.
v2.7.0 — Procedural memory layer (Velamj, PR #94)
[2.7.0] — 2026-05-13 — Procedural memory layer (Velamj, PR #94)
The third memory type. brainctl now treats Tulving's 1972 tripartite
typology as a first-class data model: episodic, semantic, and
new in 2.7.0, procedural. Procedures are reusable workflow patterns
(tool sequences, decision recipes, recovery playbooks) with their own
canonical table, FTS5-backed search, and execution-feedback loop.
Provenance note: this primitive was contributed by Velamj as
PR #94 on 2026-04-24, 19 days before any of the public competing
implementations in the agent-memory space. Shipping it as the headline
v2.7.0 feature.
Added
-
Procedural memory layer (
src/agentmemory/procedural.py,
+1679 LOC). Canonical procedure tables (procedures,
procedure_steps,procedure_sources) added via migration 052,
plus an FTS5 virtual table forprocedure_search. Bridge synopsis
rows are kept inmemoriesso legacy memory search still surfaces
procedures by their text content. -
brainctl procedure ...CLI subcommand surface —add,get,
list,search,update,feedback,backfill,stats. The
feedbacksubcommand records execution outcomes and updates a
procedure's quality counters + status so successful patterns
accumulate reuse signal. -
8 new MCP tools (now 209 total, was 201) for the same surface:
procedure_add,procedure_get,procedure_list,
procedure_search,procedure_update,procedure_feedback,
procedure_backfill,procedure_stats. -
docs/PROCEDURAL_MEMORY_MIGRATION.mddocuments the migration
envelope (PRAGMA foreign_keys=OFF; BEGIN; ... COMMIT;), the
schema_versions-only-on-success contract, backward-compat
expectations for legacy episodic/semantic readers, and the
rollout discipline for mixed-version operators.
Tests
- 70 new procedural-targeted tests (
test_procedural.py,
test_mcp_tools_procedural.py, additions totest_migrate.py
andtest_validation.py). Full suite green at 2225 passed,
13 skipped, 2 xfailed (fresh-venv[all]install).
Doc counts updated
MCP_SERVER.md+scripts/check_docs.pyasserts: 201 → 209 MCP
tools.brainctl-mcp --list-toolsreflects the new surface.
v2.6.4 — Fix #114: BRAINCTL_ALLOWED_TOOLS for tool-capped MCP clients
[2.6.4] — 2026-05-13 — Fix #114: BRAINCTL_ALLOWED_TOOLS for tool-capped MCP clients
Added
-
BRAINCTL_ALLOWED_TOOLSenv var on the stdio MCP server. When set
to a comma-separated list of tool names,tools/listreturns only
the named tools andtools/callrejects everything else. When
unset (the default), the full 201-tool surface is exposed —
backward compatible for clients without tool caps.Required for clients that enforce a hard cap on total MCP tools
(Google's Antigravity IDE caps at 100; with brainctl's 201 surface,
the IDE refused to load the server at all). Example:"env": { "BRAINCTL_ALLOWED_TOOLS": "memory_add,memory_search,event_add,event_search,entity_create, entity_get,entity_observe,entity_relate,entity_search, decision_add,handoff_add,handoff_latest,handoff_consume, trigger_create,trigger_list,trigger_check,stats,agent_orient, agent_wrap_up,validate,lint" }
-
Hard-fail on unknown tool names at process start. A typo like
memory-add(hyphen) prints a clear error pointing at
memory_add(underscore) viadifflib.get_close_matches—
prevents silent misconfigurations.
Design notes
- The HTTP transport's
BRAINCTL_HTTP_ALLOWED_TOOLS(v2.5.0) is
REQUIRED + must be non-empty (security default: no allowlist → no
remote service). The stdio variant is OPTIONAL by default for
backward compatibility — stdio clients spawn the server locally
and are trusted; we don't want to break every existing brainctl
user's MCP config by suddenly making the env var mandatory. - Both variants now hard-fail on unknown tool names (stdio: at module
import; HTTP: already raised at startup in v2.5.0). No silent
warnings.
Tests
- 18 round-trip tests covering: unset / empty / whitespace-only env
→ no filter; valid names → exact filter applied; unknown name →
SystemExit with helpful "did you mean X?" suggestions; whitespace
trimming; allowed call passes the gate; disallowed call raises
ValueError mentioning the env var. - Full suite: 2112 passed, 28 skipped, 2 xfailed.
v2.6.3 — wallet export-key (Phantom-compatible base58 private key)
[2.6.3] — 2026-05-13 — Wallet export-key: Phantom-compatible private-key export
Added
-
brainctl wallet export-key— print or save the wallet's secret as
a base58 private key in the format every standard Solana wallet
UI accepts under "Import private key" (Phantom, Backpack, Solflare,
Glow). Required becausewallet exportwrites the Solana CLI
64-int JSON format which those wallets do not accept.Two modes:
- default: secret printed to stdout (with stderr SAFETY warnings)
-o <path>: secret written to the file at mode 0600; JSON
payload elides the secret to prevent piping leaks
brainctl wallets are NOT BIP39-mnemonic-derived (they're ed25519
keypairs generated directly), so the base58 secret IS the recovery —
there is no separate seed phrase. The CLI prints prominent SAFETY
warnings explaining this and the wallet-UI import paths for each
supported wallet.
Documentation
- README + CLAUDE.md updated to reference
wallet export-key.
Tests
- 5 new round-trip tests confirming the base58 secret decodes to a
valid solders Keypair, matches the wallet address, writes to disk
at 0600 with secret elided from the JSON payload, and handles
existing files / force / missing-wallet edge cases correctly.
v2.6.2 — Fix #108: brainctl-mcp idle-timeout no longer kills stdio servers
[2.6.2] — 2026-05-13 — Fix #108: brainctl-mcp idle-timeout no longer kills stdio servers
Fixed
brainctl-mcpno longer self-terminates after 1h idle on stdio
(closes #108). Stdio MCP clients (Claude Desktop, Codex, Cursor)
own the process lifecycle, so killing the server out from under
them left the agent without memory tools until manual restart, with
no upstream warning surfaced to the user. TheBRAINCTL_MCP_IDLE_TIMEOUT_SEC
default is now0(disabled) instead of3600(1h). The
parent-death watchdog stays active — that's the actually load-bearing
safety net for orphaned children when Claude Desktop crashes.
Changed
BRAINCTL_MCP_IDLE_TIMEOUT_SECsemantics:0, empty, garbage, or negative → disabled (no idle check at all)1..59→ clamps up to60(sub-minute idle timeouts can kill the
server mid-LLM-thought)>= 60→ honored verbatim
- Loop now skips the idle check entirely when the timeout is
0.
Documentation
- New section in
docs/AGENT_ONBOARDING.mdcovering the MCP server
lifecycle watchdog (parent-death detection, idle-timeout reaping,
the three relevant env vars).
Tests
- Six lifecycle tests updated/added for the new defaults:
disabled-by-default, explicit-zero, sub-minute clamp, explicit
honored value, garbage → disabled, negative → disabled. New test
test_watchdog_skips_idle_check_when_timeout_is_zeroconfirms the
loop runs cleanly through an "idle forever" state.
v2.6.1 — Onboarding from other providers + local bundle decrypt
[2.6.1] — 2026-05-13 — Onboarding from other providers + local bundle decrypt
Two launch-readiness additions on top of v2.6.0.
Added
-
brainctl import <provider> <source>— onboard memories from
other providers into brain.db. Quarantine-by-default into scope
imported:<provider>so the agent's primary scope stays clean
until the user explicitly promotes specific records via
brainctl memory promote.Shipped providers:
brainctl import mem0 <export.json>— parses every mem0 export
shape (SDK{"results":[...]},{"memories":[...]}, legacy
top-level list). mem0-only fields (score, app_id, run_id,
categories, labels) round-trip via the memory's source_metadata.brainctl import json <records.json>— generic JSON / JSONL.
Accepts list root,{"memories":[...]},{"data":[...]},
{"records":[...]}. Required record field iscontent;
everything else is optional.
CLI flags:
--scope,--category,--no-quarantine,--dry-run,
--json. Uses the canonicalcmd_memory_addpath so FTS5 indexes,
vec embeddings, and scope/tag persistence stay consistent.
Provider registry undersrc/agentmemory/importers/; adding a new
provider is a three-step BaseImporter subclass + register_importer
call (zep / cognee / letta / langchain are the natural next ones). -
brainctl bundle decrypt <mint> --ciphertext-uri ar://...—
local-side AES decryption of a bundle you minted yourself. Reads
the per-bundle key from~/.brainctl/keys/<mint>.key, fetches
ciphertext from Arweave, AES-256-GCM decrypts, streams the
plaintext bundle JSON to stdout (or-o <path>).Closes the "I minted but can't read my own bundle without writing
Python" gap. The companionsend-key/receive-keycommands for
gifting decryption capability to another wallet are roadmap
(v2.6.2+); the marketplace settle flow remains the only turnkey
key-handoff path for now.
Tests
- 17 new round-trip tests for the importers (mem0 SDK shape, mem0
legacy shape, JSON list, JSON wrapped, JSONL, missing-content
skips, malformed-input rejections, tag-extraction edge cases).
Full suite: 2093 passed, 28 skipped, 2 xfailed.
v2.6.0 — Marketplace negotiation, protocol rename, protocol fees
[2.6.0] — 2026-05-12 — Marketplace negotiation, protocol rename, and protocol fees
Three coordinated changes:
-
The negotiation half of the chain-canonical agent memory
marketplace. The REST API at brainctl.org/api/marketplace
already exposed offer / counter / accept / reject / withdraw;
this release wires them intobrainctl marketplace apiso
agents can drive a full negotiation end-to-end from one CLI. -
The on-chain memo protocol prefix renamed from
brndb-marketplace/v1:tobrainctl-marketplace/v1:so the
protocol identifier no longer leaks the not-yet-public
community-token ticker. Pre-launch cutover; no production
volume was on chain at the old prefix. -
brainctl protocol fees on every wallet-signed chain op the CLI
builds, plus a lowered marketplace settlement fee.
Protocol fees (new)
Every CLI-initiated chain transaction includes a small protocol-fee
transfer to a public treasury wallet. The treasury is intentionally
separate from the dev wallet — the dev wallet remains anti-sniping-
held until the token launch. Defaults (calibrated to SOL ≈ $200):
--sign --pin-onchain0.0005 SOL (~$0.10)marketplace api list0.0005 SOL (~$0.10)marketplace api offer0.0005 SOL (~$0.10)marketplace api counter0.0005 SOL (~$0.10)marketplace api accept0.0005 SOL (~$0.10)marketplace api reject0.0005 SOL (~$0.10)marketplace api withdraw0.0005 SOL (~$0.10)marketplace api cancel0.0005 SOL (~$0.10)export --sign --mint0.0025 SOL (~$0.50)marketplace api settle2.5% of trade (replaces 3.5%)- marketplace JIT mint at settle free (seller already paid 2.5%)
- pure offline
export --signfree (no chain interaction) - devnet free (all ops)
Treasury wallet (hardcoded default):
AYyx94RdL4LpBozqZahQ37Q3ziKEoiGZnypp8h9WwW4D
Env overrides for fees and treasury are in
agentmemory.protocol_fees. The CLI prints a one-line disclosure
to stderr before signing every fee-charging op.
Marketplace settlement fee lowered
MARKETPLACE_FEE_BPS reduced from 350 (3.5%) to 250 (2.5%) on both
the Python client and the brainctl-launch TS indexer. No production
volume was on chain at 350 bps, so the cutover is safe.
Added
brainctl marketplace api offer <listing> --price-usd N— buyer
submits an offer. Builds + signs the offer manifest, uploads to
Arweave, posts the on-chain offer memo, registers with the API. TTL
capped at 24h per the protocol.brainctl marketplace api counter <offer> --price-usd N— either
side counters; the chain audit trail captures the full thread.brainctl marketplace api accept|reject <offer>— seller-side.brainctl marketplace api withdraw <offer>— offerer-side.brainctl marketplace api offers <listing>— list visible offers
(auction-mode public, private-mode visible only to seller/offerer).- New primitives in
agentmemory.marketplace:
build_offer_manifest,build_counter_manifest,manifest_hash,
MAX_OFFER_TTL_HOURS, plus five newMEMO_*_PREFIXconstants and
formatters.parse_memoextended to parse all five new actions.
Changed
- Protocol memo prefix renamed from
brndb-marketplace/v1:to
brainctl-marketplace/v1:across both the Python client and the
brainctl-launch indexer/server. The previous prefix was an
early-design choice that coupled the protocol identifier to the
not-yet-launched community token ticker; the new name decouples
them permanently. Touchesagentmemory.marketplace.MARKETPLACE_SCHEMA,
every memo formatter,marketplace_listen.pyruntime checks,
commands/marketplace_cli.pyschema strings, andtools/zk_mint.js.
Since the marketplace just opened (no production volume yet), this
is a clean cutover — no back-compat parser branch needed. - Marketplace JIT-mint cNFT branding: name
"BRNDB #<hash>"→
"brainctl memory #<hash>", symbolBRNDB→MEM. Pre-launch
test mints no longer engrave the ticker into permanent chain
history. tools/README.mdsmoke-test JSON example: neutralized name/symbol.
Tests
- 11 new round-trip tests for the negotiation memo formatters +
manifest builders. 67/67 marketplace tests pass.
v2.5.1 — beta-audit follow-up
v2.5.1 — Beta-audit follow-up: MCP dispatcher + retrieval correctness
Closes the full beta-tester audit (#97) plus the Hermes service-env docs ask (#89). No new features — every change is a correctness fix for an MCP tool that was returning wrong results or no results at all on the 2.5.0 surface.
Fixed
- MCP dispatcher (#103). Several extension tools (
health,lint,validate,backup,budget_status,decay_report,lifecycle_summary,write_gate_stats,consolidation_events,retirement_analysis) crashed with_call_X() got an unexpected keyword argument 'agent_id'. New_invoke_dispatch_fnintrospects each callable's signature and adapts the call shape. memory_searchcold-start FTS rebuild (#97-2). FTS5'integrity-check'now runs on first call per process; rebuild is automatic if the inverted index is inconsistent.handoff_latestreturns pinned packets (#97-3). Whenstatusis omitted, bothpendingandpinnedare eligible; pinned wins ties.inferincludes events in evidence (#97-4). No more self-contradicting"No evidence found"withl1_results > 0.thinkuses OR-rewrite for seed selection (#97-5). Multi-word natural-language queries no longer return empty seeds.gaps_scancoverage check (#97-6). An agent is a coverage hole iff it has zero active memories. Active agents with memories are no longer flagged at severity 1.0.multi_passcontinuity gate (#97-7). Pass-2 hits must share at least one original-query token; surface-level word overlap with enrichment terms no longer leaks unrelated memories.brainctl migrate --backup/--backup-retain(#102). Forwarded kwargs no longer crashmigrate.run().
Documentation
- Service-launched env propagation (#104, closes #89). New Environment Variables section in
docs/AGENT_ONBOARDING.mdcoveringBRAINCTL_HOME,BRAIN_DB,BRAIN_DB_FEDERATION, and the systemd / launchd / agent-runtime gotcha.
Dependencies
openai2.32.0 → 2.34.0 (dev),cognee1.0.1 → 1.0.5 (dev),mem0ai2.0.0 → 2.0.1 (dev). Runtime users unaffected.
Testing
22 new unit tests across 7 files. Local sweep + CI matrix (3.11 / 3.12 / 3.13) green.
Install:
pip install --upgrade brainctl
v2.5.0 — Streamable HTTP transport
Remote MCP shipped. brainctl now speaks HTTP in addition to stdio —
xAI Grok remote-MCP, Strand, and any network-boundary agent can drive
the same tool surface via a bearer-token + allowlisted Streamable HTTP
endpoint.
Added
brainctl-mcp-httpconsole script — sibling ofbrainctl-mcp.
Exposes the existingapp: Server(single source of truth; no tool
re-registration) overPOST /mcpper the MCP 2025-06-18 Streamable
HTTP spec.- Auth. Static bearer token (
BRAINCTL_HTTP_TOKEN, ≥32 chars,
constant-time compare viahmac.compare_digest). 401 on missing or
wrong token. - Allowlist.
BRAINCTL_HTTP_ALLOWED_TOOLS(comma-separated)
filterstools/listand short-circuitstools/callon
non-allowlisted names with JSON-RPC-32601— the check wraps the
existing dispatch, no tool behavior duplicated. GET /health— public liveness probe.- Transport hardening. 1 MiB body cap · 100/min per-IP sliding
rate limit · 10s gracefulSIGTERMdrain · structured JSON logs
(request id / method / tool name / duration / status — never
tool args or results). - Dockerfile note. Stdio remains the default
CMD; run with
--entrypoint brainctl-mcp-http+ publish port 8080 for the HTTP
image. Seedocs/MCP_HTTP.md.
Packaging
brainctl-mcp-http = "agentmemory.mcp_http:main"registered as a
console script.- The
mcpextra (and theallextra) now pullstarlette>=0.37,
uvicorn[standard]>=0.30, andpython-json-logger>=2.0alongside
the MCP SDK. Stdio-only users don't pay extra — the imports are
only loaded onbrainctl-mcp-httpboot.
Stdio is unchanged
brainctl-mcp boots the original stdio server with identical tool
registration and dispatch. Existing Claude Desktop / local-subprocess
users do nothing.
Testing
11 new tests in tests/test_mcp_http.py (Starlette TestClient —
no extra test-deps required) cover config validation, auth, allowlist
filtering, allowlisted tool dispatch, malformed JSON, and the 1 MiB
body cap. Pyright-strict clean; ruff check + format --check
clean. 1900 passed, 28 skipped, 2 xfailed locally; CI matrix
(3.11 / 3.12 / 3.13) green.
Docs
docs/MCP_HTTP.md — env reference, local run, curl probes,
Fly.io-style deploy note, security caveats (bearer-only auth; run
behind TLS).
v2.4.12
Fifth slice of the 2026-04-19 audit fix wave. Single-focus release
closing I23 — the CE cold-start issue that caused the recurring
latency-gate flake in CI.
Fixed
- Cross-encoder warmup no longer poisons the rolling p95 latency
window. The first_ce_rerank_timedcall includes model loading
(typically 15–40s forbge-reranker-v2-m3). Before 2.4.12 that
first sample went straight into_CE_LATENCY_SAMPLES_MSand stayed
there for the full 64-call deque rotation — the strict latency
fallback at the call site then silently skipped CE for the next
minute+ of queries without a clear operator signal beyond the
missingcross_encoder_appliedtrace. Now the first
_CE_WARMUP_SAMPLEScalls (default 1; envBRAINCTL_CE_WARMUP_SAMPLES
to override) are tracked separately, excluded from the p95 window,
and surfaced ascross_encoder_warmup_msin_debug_skipsso the
cost is still observable. This is what was causing the recurring
latency-gateflake in CI over the prior release chain.
Testing
1878 passed, 28 skipped, 2 xfailed locally. Two new regression
tests at tests/test_ce_warmup_burn_in.py lock the warmup-exclusion
behavior and the BRAINCTL_CE_WARMUP_SAMPLES=0 opt-out path.