mirror of
https://github.com/garrytan/gbrain.git
synced 2026-08-14 08:53:22 +00:00
v0.45.7.0 feat(mcp,context): ambient recall — context_pack + delta frozen verbs + boundary runtime (#1) (#4028)
* feat(mcp,context): ambient recall — context_pack + delta frozen verbs + boundary runtime (#1) Two new frozen MEMORY_VERBS (context_pack, delta) on the pull surface + a Claude Code hook boundary runtime on the push surface, sharing one stateless assembler core (assembleTurnContext mode: turn|pack|delta) and a keyset session cursor (migration v126). World-only by default; include_private gated fail-closed to trusted-local. protocol_version stays 1 (additive 5→7 verbs). Survived three adversarial review waves. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * v0.45.7.0 feat(mcp,context): ambient recall — context_pack + delta frozen verbs + boundary runtime (#1) Two new frozen MEMORY_VERBS (context_pack, delta) grow the frozen set 5→7 without a wire bump — all seven stamp protocol_version: 1. context_pack assembles a deterministic, zero-LLM, budget-packed bundle (entity cards + open threads + hot facts) for a set of standing entities; delta returns only what changed since a timestamp for cheap heartbeats, with a per-session keyset cursor for at-least-once delivery. A boundary runtime wires these into Claude Code lifecycle hooks (SessionStart warm pack, PreCompact entity banking for post-compaction rehydration); Codex and any MCP host pull the same verbs at their own boundaries. World-only by default on all arms; include_private widens only for local trusted callers. Migration v126 adds session_context_state (additive). Includes the coverage close-out wave (~55 tests): real-serve compact→ session-start round trip over the live socket, --surface verbs stdio session pinning exactly 7 tools fail-closed, HTTP-transport verb calls with per-token cursor isolation, Postgres engine-parity for keyset pagination + the session-cursor table, migration v126 shape + rewind test, sub-second latency gates, CLI-level invocations, rendered-protocol boundary assertions, and a live-Codex boundary-call check. The wave caught and fixed three real bugs: the delta CLI wedging on first wake (floating GC promise racing engine teardown), the compact hook probing the PGLite socket on a Postgres config with a leftover database_path, and the verbs-surface banner hardcoding a stale verb count. Also the /document-release sweep: stale "five verbs" → seven across the protocol doc, README, INSTALL, DEPLOY, the Claude Code MCP guide, and the query skill; deferred scope filed in TODOS. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(release): bump openclaw.plugin.json to 0.45.7.0 — the sixth version location The #4033 merge auto-resolved the OpenClaw plugin manifest at master's version while the trio moved to 0.45.7.0, failing the manifest drift test on CI shard 4. Register the file in CLAUDE.md's version-locations table (five → six) so every future ship and merge re-bumps it with the trio. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
3f22f51e5d
commit
15ecc65b24
+1
-1
@@ -77,7 +77,7 @@ The agent spawns `gbrain serve` as a stdio subprocess against your local brain.
|
||||
|
||||
```bash
|
||||
gbrain serve # stdio MCP (Claude Desktop / Code / Cursor)
|
||||
gbrain serve --surface verbs # stdio MCP, just the 5 memory verbs (quickstart)
|
||||
gbrain serve --surface verbs # stdio MCP, just the 7 memory verbs (quickstart)
|
||||
gbrain serve --http # HTTP MCP with OAuth 2.1 + admin dashboard
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
# Ambient recall — placing retrieval at session boundaries
|
||||
|
||||
Long-lived agent harnesses (your OpenClaw, Hermes, Codex, Claude Code) get the
|
||||
most value from the brain not on every message, but at the moments where a fresh
|
||||
question rarely fires on its own: **session start, right after compaction, and
|
||||
on heartbeats.** This guide is the Pareto frontier of where to place each verb.
|
||||
|
||||
The bottleneck for a long-lived agent is not retrieval quality — the corpus
|
||||
answers well when asked. It is **placement**: the misses come from moments when
|
||||
no question fires. Two frozen verbs close that gap with 2-3 deterministic calls
|
||||
per session instead of per-message overhead.
|
||||
|
||||
## The frontier — which verb goes where
|
||||
|
||||
| Moment | Call | Why | Cost |
|
||||
|---|---|---|---|
|
||||
| Any entity-bearing message | `entity(name)` | Zero-LLM, p99 < 100ms. Safe to run synchronously almost anywhere. | negligible |
|
||||
| **Session start** | `context_pack(entities, budget_tokens)` | Warm the thread's 1-3 standing entities before the first message. | zero-LLM, sub-second |
|
||||
| **After compaction** | `context_pack(entities, budget_tokens)` | Rehydrate the verbatim detail the summary dropped. | zero-LLM, sub-second |
|
||||
| **Heartbeat / periodic wake** | `delta(session_id, budget_tokens)` | "What changed since my last wake" in O(changes), deduped. | zero-LLM, sub-second |
|
||||
| Explicit memory question | `recall(query \| entity, budget_tokens)` | The budget-packed read for "what do we know that we SAVED about X". | sub-second (+1 embedding if `query`) |
|
||||
| Answer needs cross-page reasoning | `synthesize(question)` | LLM-backed. **Never** on a hot or ambient path. | seconds-to-minutes, $$ |
|
||||
|
||||
Observed shape: per-message retrieval beyond `entity` cards adds latency faster
|
||||
than insight; session-start packs and post-compaction rehydration are nearly
|
||||
pure win. See the per-verb latency table in
|
||||
[`docs/protocol/MEMORY_VERBS_v1.md`](../protocol/MEMORY_VERBS_v1.md#latency-classes-per-verb).
|
||||
|
||||
## Two integration surfaces
|
||||
|
||||
- **Pull (works everywhere, including Codex + Postgres/Supabase):** the harness
|
||||
calls `context_pack` / `delta` over MCP (they are on `--surface verbs`) or the
|
||||
CLI (`gbrain context-pack`, `gbrain delta`) at the boundary and injects the
|
||||
returned `text` (or renders the structured arms). This is the portable path —
|
||||
no hooks required. It is the primary path for Codex (which has no hooks) and
|
||||
for Postgres brains (which have no local IPC socket).
|
||||
- **Push (PGLite + Claude Code):** the bundled hook framework fires
|
||||
automatically at `SessionStart` (injects a warm pack — including the
|
||||
post-compaction re-entry, `source=compact`) and `PreCompact` (banks the
|
||||
window's standing entities for that rehydration pack). Heartbeat deltas are
|
||||
the PULL path — there is deliberately no push heartbeat; call `delta` per
|
||||
the HEARTBEAT cadence table.
|
||||
|
||||
## Visibility — world-only by default
|
||||
|
||||
A pack is injected into an agent context window that may be logged or synced to a
|
||||
cloud model, so **every arm is world-visibility by default.** To pull private
|
||||
facts in, pass `include_private` — and it is honored ONLY for trusted-local
|
||||
callers (`remote === false`, i.e. the CLI/hook path). A remote MCP caller never
|
||||
widens, even if it asks (fail-closed). When it does widen, all arms widen
|
||||
together, so a pack is never a mix of private facts beside world-stripped
|
||||
synopses.
|
||||
|
||||
## Budgets
|
||||
|
||||
Every pack/delta call takes `budget_tokens`. The server packs highest-priority
|
||||
arms first (cards → facts for packs; pages → facts for deltas) and reports
|
||||
`budget_used` + `dropped_count`; the injectable `text` field is rendered from
|
||||
the packed sets, so it honors the same budget the structured arrays report. It
|
||||
never trims client-side — you always know what was left out (`dropped_count`,
|
||||
and `has_more` on deltas). Pick a budget to fit the boundary: a session-start
|
||||
pack can afford more than a heartbeat delta.
|
||||
|
||||
## Heartbeat cursor + dedup
|
||||
|
||||
Pass a stable `session_id` to `delta` and the brain keeps a per-session cursor:
|
||||
the first wake establishes it, each wake advances it. Dedup is **cursor-based**
|
||||
— a delivered page reappears only if it changes again after delivery (and then
|
||||
it should). Delivery is **at-least-once**: pages arrive oldest-first, and when
|
||||
a budget or the fetch limit drops some, the response sets `has_more: true` and
|
||||
the cursor advances only to the newest *delivered* page, so the tail surfaces
|
||||
on the next wake — nothing is silently lost. With no `session_id` you can still
|
||||
pass an explicit `since` for a stateless delta. The cursor is namespaced per
|
||||
caller (`(source_id, client_id, session_id)`; authenticated remotes use their
|
||||
client id, auth-less remotes share a `remote` namespace, and `local` is
|
||||
reserved for the trusted CLI/hook lane), so a remote harness can never read or
|
||||
advance the local lane's cursor. Idle session cursors are garbage-collected
|
||||
after **7 days** — a wake on an expired session re-establishes the cursor at
|
||||
now and returns an empty delta, so a harness returning from a long sleep
|
||||
should run one stateless `since`-based catch-up first.
|
||||
|
||||
## Example — a cold session start (pull)
|
||||
|
||||
```bash
|
||||
gbrain context-pack --entities "acme-example,alice-example" --budget-tokens 4000
|
||||
```
|
||||
|
||||
Returns entity cards + open threads + hot facts, budget-packed, world-only. Inject
|
||||
the `text` field into the model's context before the first user message.
|
||||
+19
-1
@@ -93,11 +93,29 @@ You should see results from your GBrain knowledge base.
|
||||
> older release stay OFF until you opt in. Enable it on the host with
|
||||
> `gbrain config set mcp.publish_skills true`. Skill discovery and the core tools
|
||||
> named here (search, query, get_page, put_page, think, find_experts) are
|
||||
> full-surface — on `--surface verbs` the agent sees only the five memory verbs,
|
||||
> full-surface — on `--surface verbs` the agent sees only the seven memory verbs,
|
||||
> and `list_skills` isn't on the surface at all. Note: `capture` is a
|
||||
> CLI-only command, not an MCP tool — the agent writes over MCP with `put_page`.
|
||||
> Why brains differ on the default: [tutorial A1](../tutorials/connect-coding-agent.md#a1-on-the-host-serve-over-http).
|
||||
|
||||
## Ambient recall at session boundaries (v0.45.7)
|
||||
|
||||
Two frozen verbs close the "no question fired" gap for long-lived sessions:
|
||||
`context_pack` (session-start warm-up + post-compaction rehydration) and
|
||||
`delta` ("what changed since my last wake" for heartbeats). Both are zero-LLM,
|
||||
sub-second, world-visibility by default, and available on `--surface verbs`.
|
||||
|
||||
- **Automatic (PGLite brains via `gbrain bootstrap`):** the bootstrap hook
|
||||
installer wires `SessionStart` (injects a warm pack; also fires on
|
||||
post-compaction re-entry, `source=compact`) and `PreCompact` (banks the
|
||||
window's standing entities so that rehydration pack is warm) into
|
||||
`.claude/settings.local.json`. Nothing to call; `GBRAIN_HOOKS=0` disables.
|
||||
- **Manual (any brain, incl. remote/Postgres):** call the verbs yourself at
|
||||
boundaries — `context_pack(entities, budget_tokens)` at session start /
|
||||
after compaction, `delta(session_id, budget_tokens)` on wakes. See
|
||||
[ambient recall](../guides/ambient-recall.md) for the placement frontier
|
||||
and the per-verb latency table.
|
||||
|
||||
## Remove
|
||||
|
||||
```bash
|
||||
|
||||
+8
-1
@@ -72,6 +72,13 @@ codex mcp remove gbrain
|
||||
- The token is a long-lived, full-access secret. Keep `GBRAIN_REMOTE_TOKEN` out of
|
||||
version control and prefer a scoped token if your host supports one.
|
||||
- Local stdio also works if you run the brain on the same machine:
|
||||
`codex mcp add gbrain -- gbrain serve --surface verbs` — the five-verb memory
|
||||
`codex mcp add gbrain -- gbrain serve --surface verbs` — the memory-verb
|
||||
protocol ([MEMORY_VERBS v1](../protocol/MEMORY_VERBS_v1.md)); drop the flag
|
||||
for the full operation catalog.
|
||||
- **Ambient recall (Codex has no lifecycle hooks — use the pull path).** At the
|
||||
start of a topical thread and after a compaction, call
|
||||
`context_pack(entities, budget_tokens)` to warm the standing entities; on a
|
||||
periodic wake call `delta(session_id, budget_tokens)` for "what changed since
|
||||
my last wake" (deduped per session). Both are zero-LLM, sub-second, world-only
|
||||
by default, and on `--surface verbs`. See
|
||||
[ambient recall](../guides/ambient-recall.md) for the placement frontier.
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ clients over OAuth 2.1.
|
||||
|
||||
```bash
|
||||
gbrain serve # full operation catalog (default)
|
||||
gbrain serve --surface verbs # just the 5 memory verbs (quickstart surface)
|
||||
gbrain serve --surface verbs # just the 7 memory verbs (quickstart surface)
|
||||
```
|
||||
|
||||
Works with Claude Code, Cursor, Windsurf, and any MCP client that supports stdio.
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# MEMORY_VERBS v1 — the memory wire protocol
|
||||
|
||||
GBrain's frozen five-verb memory interface over MCP: `recall`, `remember`,
|
||||
`entity`, `synthesize`, `forget`. The contract every harness can rely on the
|
||||
GBrain's frozen memory-verb interface over MCP: `recall`, `remember`,
|
||||
`entity`, `synthesize`, `forget`, plus (v0.45.7, additive) `context_pack` and
|
||||
`delta` — seven verbs, all at `protocol_version: 1`. The contract every harness can rely on the
|
||||
way every Postgres client relies on the wire protocol — and the contract any
|
||||
OTHER memory server can implement and certify against
|
||||
(`gbrain protocol conformance --target <endpoint>`).
|
||||
@@ -10,7 +11,7 @@ OTHER memory server can implement and certify against
|
||||
agent (any MCP harness)
|
||||
│ remember("picked Stripe over Adyen", provenance: "chat 2026-06-11")
|
||||
▼
|
||||
five verbs ── recall ── remember ── entity ── synthesize ── forget
|
||||
seven verbs recall ─ remember ─ entity ─ synthesize ─ forget ─ context_pack ─ delta
|
||||
│ self-describing envelopes: protocol_version, evidence, provenance,
|
||||
│ budget meta, cost block, enumerated error codes + a populated fix
|
||||
▼
|
||||
@@ -38,12 +39,17 @@ the same registry.
|
||||
- Enum values are part of the contract. Where an enum's DERIVATION is
|
||||
implementation-defined (noted per field), implementations may improve the
|
||||
derivation without a version bump; the values and their meanings stay fixed.
|
||||
- **Adding a VERB is additive, not a version bump.** v0.45.7 grew the frozen set
|
||||
from 5 to 7 (`context_pack`, `delta`) at `protocol_version: 1`. New verbs are
|
||||
new optional surface a v1 client discovers via tool-listing; the existing five
|
||||
keep stamping `1`. Bumping `protocol_version` would rewrite the frozen five's
|
||||
wire output and break every client that pins `== 1` — so we don't.
|
||||
|
||||
## Install (the 4-command quickstart)
|
||||
|
||||
```bash
|
||||
gbrain init --pglite # 2-second local brain
|
||||
claude mcp add gbrain -- gbrain serve --surface verbs # the five-verb surface
|
||||
claude mcp add gbrain -- gbrain serve --surface verbs # the memory-verb surface
|
||||
gbrain remember "I prefer dark mode in every editor" --provenance demo --entity people/me
|
||||
gbrain recall --entity people/me # …now ask your agent in a NEW session
|
||||
```
|
||||
@@ -63,7 +69,7 @@ codex mcp add gbrain -- gbrain serve --surface verbs
|
||||
host, then `gbrain connect https://host/mcp --token gbrain_xxx --install` on
|
||||
each client.
|
||||
|
||||
**Surface modes:** `--surface verbs` exposes EXACTLY the five verbs —
|
||||
**Surface modes:** `--surface verbs` exposes EXACTLY the seven verbs —
|
||||
advertised list AND dispatch are filtered fail-closed (a hidden op returns
|
||||
`unknown_tool` even when called by name). `--surface full` (the default)
|
||||
exposes every operation, verbs included. Why default full: verbs is for
|
||||
@@ -186,7 +192,71 @@ already-expired fact returns `expired: false` (success); unknown id ⇒
|
||||
|
||||
Response: `{ id, expired, reason, protocol_version }`.
|
||||
|
||||
## Error contract (uniform across all five verbs)
|
||||
### context_pack(entities, budget_tokens?, since?, session_id?, include_private?) — read, zero LLM
|
||||
|
||||
v0.45.7 (issue #1). One deterministic, budget-packed bundle for a set of standing
|
||||
entities — entity cards + open threads + hot facts. Built for **session
|
||||
boundaries**: call it at session start to warm cold context, and immediately
|
||||
after compaction to rehydrate what the summary dropped. Composes existing arms
|
||||
(`entity` card builder + the hot-facts arm); never calls an LLM.
|
||||
|
||||
`entities` is comma-separated, capped at 8 (the response echoes the capped list). `budget_tokens` packs
|
||||
server-side (cards first, then facts) and the response reports
|
||||
`budget_used` + `dropped_count` — it never trims client-side. `since` filters
|
||||
open-thread events to those after the cursor. **Visibility is WORLD-ONLY by
|
||||
default** on every arm (a pack is injected into an agent context window that may
|
||||
be logged or synced to a cloud model). `include_private` widens ALL arms in
|
||||
lockstep, and is honored ONLY for trusted-local callers (`remote === false`); a
|
||||
remote caller never widens (fail-closed).
|
||||
|
||||
Response: `{ protocol_version, entities, cards[], open_threads[], facts[], text,
|
||||
degraded_reason?, budget_tokens?, budget_used?, dropped_count? }`. `text` is the
|
||||
pre-rendered, envelope-wrapped injectable block.
|
||||
|
||||
### delta(since?, entities?, budget_tokens?, session_id?, include_private?) — read, zero LLM
|
||||
|
||||
v0.45.7 (issue #1). "What changed since T" for heartbeats — pages updated after
|
||||
the cursor (oldest first) + facts recorded after the cursor + open-thread
|
||||
events after the cursor. Lets a periodic wake maintain warm state in
|
||||
O(changes) instead of re-deriving. Provide `since` (ISO 8601) OR a
|
||||
`session_id` whose cursor carries the last wake. Delivery is **at-least-once**:
|
||||
when a budget or the fetch limit drops pages, `has_more: true` is set and the
|
||||
session cursor advances only to the newest DELIVERED page — the undelivered
|
||||
tail surfaces on the next wake, never silently lost. Dedup is cursor-based (a
|
||||
delivered page reappears only if it changes again). Same world-only-default +
|
||||
`include_private` fail-closed rule as `context_pack`. The session cursor is
|
||||
keyed `(source_id, client_id, session_id)` — authenticated remote callers are
|
||||
namespaced by their auth client id, auth-less remotes share the `'remote'`
|
||||
sentinel, and `'local'` is RESERVED for the trusted CLI/hook lane, so a remote
|
||||
harness can never read or advance the local lane's cursor.
|
||||
|
||||
Delivery is at-least-once via a **keyset cursor `(updated_at, slug)`**: a cluster
|
||||
of pages sharing one `updated_at` (bulk syncs stamp identical timestamps) pages
|
||||
deterministically by slug, so a >fetch-limit cluster drains across wakes instead
|
||||
of livelocking. Stateless callers resume by passing the response's
|
||||
`next_cursor.since` + `next_cursor.slug` back as `since` + `since_slug`;
|
||||
`session_id` callers get this automatically.
|
||||
|
||||
Response: `{ protocol_version, since, pages[], facts[], threads[], text,
|
||||
has_more, next_cursor: { since, slug }, degraded_reason?, budget_tokens?,
|
||||
budget_used?, dropped_count? }`. `text` is rendered from the budget-packed sets
|
||||
(it honors the declared budget) and `since` is always normalized ISO (never the
|
||||
raw input string).
|
||||
|
||||
## Latency classes (per verb)
|
||||
|
||||
Published so harness authors place calls by cost, not by learning at timeout:
|
||||
|
||||
| Verb | Class | Notes |
|
||||
|---|---|---|
|
||||
| `entity` | zero-LLM, **p99 < 100ms** | CI-gated on a 20K-page corpus (below). Safe per entity-bearing message. |
|
||||
| `context_pack` | zero-LLM, sub-second | Fan-out capped at 8 entities. Session boundaries, not per-message. Push path passes a wall-clock deadline and returns a PARTIAL pack (`degraded_reason`) rather than overrun. |
|
||||
| `delta` | zero-LLM, sub-second | O(changes). Heartbeats — pull path only (there is no push heartbeat); session cursors expire after 7 idle days. |
|
||||
| `recall` | zero-LLM (keyword) to one embedding call (when `query` is passed) | Sub-second typical; the `query` arm adds one embedding round-trip. |
|
||||
| `remember` / `forget` | write, sub-second | One durable write; `remember` adds one embedding call for dedup when a provider is configured. |
|
||||
| `synthesize` | **EXPENSIVE / SLOW** | LLM calls, seconds-to-minutes, costs money. Never place on a hot or ambient path. |
|
||||
|
||||
## Error contract (uniform across all verbs)
|
||||
|
||||
```json
|
||||
{ "error": "<code>", "message": "...", "suggestion": "problem + cause + fix",
|
||||
|
||||
Reference in New Issue
Block a user