Skip to content

Releases: TSchonleber/brainctl

v2.8.0 — 16 brain subsystems + v2 MCP tool surface

20 May 20:35
789b473

Choose a tag to compare

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:

subsystem_list()                                    // discover
subsystem_list_actions(name="lc")                   // discover actions for LC
subsystem_emit(name="lc", action="fire",
               payload={"trigger_name":"x", "surprise_magnitude":0.7})
belief(action="collapse", payload={...})
trust(action="show", payload={"agent_id":"me"})

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      # everything

Bundle 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)

13 May 08:38
9bcc5a4

Choose a tag to compare

[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 for procedure_search. Bridge synopsis
    rows are kept in memories so legacy memory search still surfaces
    procedures by their text content.

  • brainctl procedure ... CLI subcommand surface — add, get,
    list, search, update, feedback, backfill, stats. The
    feedback subcommand 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.md documents 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 to test_migrate.py
    and test_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.py asserts: 201 → 209 MCP
    tools. brainctl-mcp --list-tools reflects the new surface.

v2.6.4 — Fix #114: BRAINCTL_ALLOWED_TOOLS for tool-capped MCP clients

13 May 06:36

Choose a tag to compare

[2.6.4] — 2026-05-13 — Fix #114: BRAINCTL_ALLOWED_TOOLS for tool-capped MCP clients

Added

  • BRAINCTL_ALLOWED_TOOLS env var on the stdio MCP server. When set
    to a comma-separated list of tool names, tools/list returns only
    the named tools and tools/call rejects 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) via difflib.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)

13 May 04:55

Choose a tag to compare

[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 because wallet export writes 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

13 May 04:22

Choose a tag to compare

[2.6.2] — 2026-05-13 — Fix #108: brainctl-mcp idle-timeout no longer kills stdio servers

Fixed

  • brainctl-mcp no 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. The BRAINCTL_MCP_IDLE_TIMEOUT_SEC
    default is now 0 (disabled) instead of 3600 (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_SEC semantics:
    • 0, empty, garbage, or negative → disabled (no idle check at all)
    • 1..59 → clamps up to 60 (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.md covering 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_zero confirms the
    loop runs cleanly through an "idle forever" state.

v2.6.1 — Onboarding from other providers + local bundle decrypt

13 May 04:22

Choose a tag to compare

[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 is content;
      everything else is optional.

    CLI flags: --scope, --category, --no-quarantine, --dry-run,
    --json. Uses the canonical cmd_memory_add path so FTS5 indexes,
    vec embeddings, and scope/tag persistence stay consistent.
    Provider registry under src/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 companion send-key / receive-key commands 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

13 May 04:22

Choose a tag to compare

[2.6.0] — 2026-05-12 — Marketplace negotiation, protocol rename, and protocol fees

Three coordinated changes:

  1. 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 into brainctl marketplace api so
    agents can drive a full negotiation end-to-end from one CLI.

  2. The on-chain memo protocol prefix renamed from
    brndb-marketplace/v1: to brainctl-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.

  3. 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-onchain 0.0005 SOL (~$0.10)
  • marketplace api list 0.0005 SOL (~$0.10)
  • marketplace api offer 0.0005 SOL (~$0.10)
  • marketplace api counter 0.0005 SOL (~$0.10)
  • marketplace api accept 0.0005 SOL (~$0.10)
  • marketplace api reject 0.0005 SOL (~$0.10)
  • marketplace api withdraw 0.0005 SOL (~$0.10)
  • marketplace api cancel 0.0005 SOL (~$0.10)
  • export --sign --mint 0.0025 SOL (~$0.50)
  • marketplace api settle 2.5% of trade (replaces 3.5%)
  • marketplace JIT mint at settle free (seller already paid 2.5%)
  • pure offline export --sign free (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 new MEMO_*_PREFIX constants and
    formatters. parse_memo extended 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. Touches agentmemory.marketplace.MARKETPLACE_SCHEMA,
    every memo formatter, marketplace_listen.py runtime checks,
    commands/marketplace_cli.py schema strings, and tools/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>", symbol BRNDBMEM. Pre-launch
    test mints no longer engrave the ticker into permanent chain
    history.
  • tools/README.md smoke-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

06 May 05:54

Choose a tag to compare

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_fn introspects each callable's signature and adapts the call shape.
  • memory_search cold-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_latest returns pinned packets (#97-3). When status is omitted, both pending and pinned are eligible; pinned wins ties.
  • infer includes events in evidence (#97-4). No more self-contradicting "No evidence found" with l1_results > 0.
  • think uses OR-rewrite for seed selection (#97-5). Multi-word natural-language queries no longer return empty seeds.
  • gaps_scan coverage 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_pass continuity 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 crash migrate.run().

Documentation

  • Service-launched env propagation (#104, closes #89). New Environment Variables section in docs/AGENT_ONBOARDING.md covering BRAINCTL_HOME, BRAIN_DB, BRAIN_DB_FEDERATION, and the systemd / launchd / agent-runtime gotcha.

Dependencies

  • openai 2.32.0 → 2.34.0 (dev), cognee 1.0.1 → 1.0.5 (dev), mem0ai 2.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

21 Apr 03:39

Choose a tag to compare

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-http console script — sibling of brainctl-mcp.
    Exposes the existing app: Server (single source of truth; no tool
    re-registration) over POST /mcp per the MCP 2025-06-18 Streamable
    HTTP spec.
  • Auth. Static bearer token (BRAINCTL_HTTP_TOKEN, ≥32 chars,
    constant-time compare via hmac.compare_digest). 401 on missing or
    wrong token.
  • Allowlist. BRAINCTL_HTTP_ALLOWED_TOOLS (comma-separated)
    filters tools/list and short-circuits tools/call on
    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 graceful SIGTERM drain · 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. See docs/MCP_HTTP.md.

Packaging

  • brainctl-mcp-http = "agentmemory.mcp_http:main" registered as a
    console script.
  • The mcp extra (and the all extra) now pull starlette>=0.37,
    uvicorn[standard]>=0.30, and python-json-logger>=2.0 alongside
    the MCP SDK. Stdio-only users don't pay extra — the imports are
    only loaded on brainctl-mcp-http boot.

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

21 Apr 02:54
1f3bb5c

Choose a tag to compare

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_timed call includes model loading
    (typically 15–40s for bge-reranker-v2-m3). Before 2.4.12 that
    first sample went straight into _CE_LATENCY_SAMPLES_MS and 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
    missing cross_encoder_applied trace. Now the first
    _CE_WARMUP_SAMPLES calls (default 1; env BRAINCTL_CE_WARMUP_SAMPLES
    to override) are tracked separately, excluded from the p95 window,
    and surfaced as cross_encoder_warmup_ms in _debug_skips so the
    cost is still observable. This is what was causing the recurring
    latency-gate flake 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.