garrytan/gbrainmarkdown explorer
garrytan/gbrainmaster
docs / guides

push context

docs/guides/push-context.md

Push-based context (#2095, v0.42.43.0)

Retrieval used to be pull-only: the agent had to know to ask before the brain contributed anything. Push-based context inverts that — the brain volunteers relevant pages from the recent conversation, confidence-gated so push noise never becomes worse than pull silence.

The push channels share one zero-LLM core (src/core/context/volunteer.ts):

ChannelSurfaceWhen to use
reflexautomatic, inside the context enginedefault-on for plugin hosts; nothing to call
opgbrain volunteer-context / MCP volunteer_contextagents without the plugin; one call per turn
watchgbrain watchstream a transcript in, volunteered pages stream out
claude-code / codex / opencodegbrain hook user-prompt (registered by gbrain bootstrap)per-prompt injection inside a harness; see "Harness hooks" below

How it decides

  1. Extract entities across the last N turns (capitalized runs, @handles), merged with recency / frequency / user-role salience. Assistant-introduced entities and "what did she invest in?" follow-ups whose antecedent was named in the window now resolve.
  2. Resolve through the alias table, exact titles, surnames, and slug suffixes — each arm carries an honest confidence: alias 0.9, exact title 0.8, surname 0.72, slug-suffix 0.6, +0.05 when mentioned in ≥2 turns or the newest turn. Lowercase mentions ("remind me what alice said") probe the alias table only, and only when the alias is unique across every source in play; a surname-only reference ("Did Galewright follow up?") resolves when exactly one person page carries that surname. Ambiguity in either arm injects nothing — silence beats a wrong pointer. Kill switch for both: retrieval_reflex_lexical_arms (default on).
  3. Gate at min_confidence (default 0.7 — slug-suffix matches need an explicit lower gate), suppress pages already surfaced (slug-presence only), cap at 3 pages (hard cap 5).

CLI

# one-shot: pipe recent turns (oldest → newest)
printf 'user: ask alice-example about the deal\nassistant: noted\nuser: what did she say?\n' \
  | gbrain volunteer-context

# streaming: volunteered pages print as the transcript flows
some-transcript-feed | gbrain watch --json

# the feedback loop: how often were volunteered pages actually opened?
gbrain volunteer-context --stats

Stats are approximate by design: "used" means pages.last_retrieved_at > volunteered_at — the 5-minute last-retrieved throttle causes false negatives and unrelated reads of the same page cause false positives. Use the per-arm precision to tune min_confidence, not as an exact metric.

PGLite + gbrain watch: PGLite is single-connection, and watch holds its connection for the whole session — a concurrent gbrain serve or any write path blocks until watch exits. On a PGLite brain, run watch in bursts (piped input exits at EOF) or use the ambient reflex channel instead, which routes through a running serve's resolve socket rather than taking the lock. Routing watch through that same socket is a filed follow-up (TODOS.md). Postgres brains are unaffected.

Harness hooks (the prompt-time channel)

gbrain bootstrap registers gbrain hook user-prompt as a Claude Code UserPromptSubmit hook: every prompt is assembled into a per-turn context block (reflex pointers + volunteered pages + hot facts) through a running serve's IPC socket and injected as additionalContext. Two properties make this channel production-grade rather than spammy-and-invisible:

  • Cross-turn dedupe. The hook reads its OWN previous injections back out of the session transcript (Claude Code records them as structured hook_additional_context attachments; only gbrain-marked blocks count) and passes them as prior context — so a page is volunteered once per session, not once per mention. The dedupe horizon is bounded (the recent transcript window, byte-capped), so a marathon session can eventually re-volunteer its oldest injections. The extraction is structural, never substring matching over raw turn text, so a short slug appearing in a tool payload can't over-suppress.
  • The feedback loop. The serve logs each DELIVERED block's volunteered pages and pointers to context_volunteer_events under the hook's channel (claude-code by default; a codex hook registration passes --harness codex / --harness opencode). gbrain volunteer-context --stats then shows per-harness precision, and gbrain doctor's volunteer_channels check shows which channels actually fire, with guidance for the two quiet cases: "hook installed but never registered (restart the session)" and "registered but quiet". Logging happens at the delivery point only — a block abandoned before the serve responded is never counted — and because a delivered response still isn't proof of injection (the hook can trim or drop it client-side), the doctor check reconciles the counts against the hook's own heartbeat and cautions when they diverge.

The hook lane rides the PGLite serve's IPC socket: on a Postgres brain or a thin-client install the hook stays quiet by design (pull-mode retrieval covers those; extending the lane is a filed follow-up in TODOS.md).

Kill switch: GBRAIN_HOOKS=0. Install/uninstall: docs/guides/bootstrap.md.

Config

KeyDefaultWhat it does
retrieval_reflex_window_turns4turns the ambient reflex extracts from; 1 = legacy current-turn-only (file/env plane: GBRAIN_RETRIEVAL_REFLEX_WINDOW_TURNS)
retrieval_reflextruethe ambient channel's master switch
retrieval_reflex_max_pointers3pointer cap per turn
retrieval_reflex_lexical_armstruethe lowercase-alias + surname recall arms (env: GBRAIN_RETRIEVAL_REFLEX_LEXICAL_ARMS); off = pre-v0.46.15 arm set

Per-call knobs: max_pages + min_confidence on both the op and gbrain watch (--max-pages / --min-confidence, plus --window-turns / --source on watch); on the op only: prior_context (text whose already-surfaced slugs are suppressed), session_id / turn attribution params (watch stamps its own per-session id and turn numbers in the feedback log), and days to size the --stats window.

Storage + privacy

Volunteered pages log to context_volunteer_events (migration v117): slug, arm, confidence, channel, optional session/turn — the rationale is a deterministic template string, never raw conversation text. Event writes are best-effort (fire-and-forget, drained at CLI exit) — the log is a tuning signal, not an audit trail. Rows are pruned after 90 days by the dream cycle's purge phase. Synopses always strip the takes/facts fences — the same strip get_page applies to untrusted callers, applied unconditionally here so private fence rows never reach a prompt regardless of caller trust.

Continue exploring589 Markdown documents in the local repository