mirror of
https://github.com/garrytan/gbrain.git
synced 2026-08-14 08:53:22 +00:00
* refactor(context): extract pure gateVolunteeredPointers (no behavior change) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(context): volunteer-shaped resolve IPC — channel/cwd/volunteer fields, handler-result widening, server-side gate + validated source + delivery-point logging; listener on both engines Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(cli): gbrain volunteer-hook — one-shot harness hook entry (IPC-first; Postgres-only fallback; startup-hook-exempt) T0 evidence: a UserPromptSubmit hook's additionalContext is recorded in the session transcript as a structured hook_additional_context attachment and transcript_path is stable across turns — cross-turn dedupe reads ONLY those attachments (never raw tail substrings). Live smoke: seeded brain + running serve → pointer injected via IPC at 0.43s wall, turn 2 suppressed, one event banked channel=claude-code. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(recipes): claude-code-reflex + codex-reflex (experimental) hook adapters Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(doctor): volunteer_channels per-adapter visibility Engine-aware sibling of retrieval_reflex_health: groups context_volunteer_events by channel (7d) so operators can see which push adapters fire; message separates installed-but-unregistered from registered-but-quiet; pre-v117 tolerant; info-only status. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(context): hook-lane feedback loop + cross-turn dedupe — converge cathedral-3 onto the #3975 harness hooks The #3975 hook lane volunteered pages invisibly (nothing logged to context_volunteer_events) and re-volunteered the same page every turn an entity was mentioned (no priorContextText). This convergence ports the cathedral-3 branch's two gap-fills into the blessed lane and retires its parallel surface: - transcripts: parseTranscript extracts injectedContextBlocks (structured hook_additional_context attachments — the T0-verified recorded shape of our own injections; never raw-turn substring matching) - hook user-prompt: feeds those blocks as priorContextText (volunteer once per session, not once per mention) + sends a feedback-loop channel (--harness <claude-code|codex>, default claude-code); hook joins STARTUP_HOOK_SKIP_COMMANDS (per-prompt check-update spawn class, grep-pinned) - turn-context: result exposes post-trim volunteered survivors - resolve-ipc: additive TurnContextRequest.channel + onTurnContextDelivered seam (fires post-write only — abandoned blocks are never counted) - serve: delivery callback logs volunteered pages + pointers per channel - doctor: volunteer_channels check (per-channel 7d visibility; separates installed-but-unregistered from registered-but-quiet; pre-v117 tolerant) Retired (never released): gbrain volunteer-hook, hook-adapters.ts, claude-code-reflex + codex-reflex recipes, the volunteer-shaped resolve wire extension. Kept: gateVolunteeredPointers extraction, VolunteerChannel claude-code/codex values, the real hook-transcript fixture. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: regenerate llms bundles for the hook-lane doc updates Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: coverage for the hook-lane delivery wiring Extract logTurnContextDeliveryFireAndForget into volunteer-events.ts so the shipped serve callback is unit-testable (channel fallback, sessionId clamp, pointer+page rows in ONE synchronous registration — the double dynamic import could register the pending write too late to survive process exit). Direct units for isVolunteerChannel, explicit-channel logDeliveredReflexPointers, and the pure gate (idempotency pin; stale parity claim in its doc corrected). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: pre-landing review fixes — 7-reviewer batch Multi-confirmed: volunteer_channels now runs on the LOCAL doctor path too (buildChecks; docs pointed there); transient DB errors no longer misreported as pre-v117; priorContextText deduped + 32KB-capped hook-side. Red team: delivery counts reconciled against the hook heartbeat (CAUTION when mostly degraded — write-accept isn't injection); IPC clamp drops advisory priorContextText BEFORE any window turn; foreign hook_additional_context blocks (no gbrain marker) excluded from dedupe input; engine-aware quiet guidance (Postgres brains aren't told to chase hook registration); 2MB dedupe horizon documented. Security: wire channel claims restricted to harness channels. DRY: VOLUNTEER_CHANNELS single source, shared reflexPointerRationale, SESSION_ID_MAX_LEN shared with the op clamp; dead channel param dropped from logDeliveredReflexPointers. Tests: budget-trim volunteered invariant under real trim pressure, poll/ordering-based delivery-seam tests (no fixed sleeps), doctor branch + engine-parity-row tests, wiring source pins, cap pins; fixture pruned to load-bearing lines (environment inventory removed). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: adversarial-review fixes — cross-model P1 + Claude adversarial batch P1 (codex structured review + Claude adversarial, cross-model): the remote run_doctor path now threads the caller's source scope into volunteer_channels — a source-bound token can no longer see other sources' activity counts/timestamps (must-never-violate source-isolation rule); local trusted doctor stays brain-wide. Claude adversarial: IPC connection handler gains a one-request re-entrancy guard (trailing bytes mid-await double-processed the same line → duplicate handler work + duplicate delivery logging); partial client-side trims now heartbeat as reason 'trimmed' (delivery-count drift visibility); heartbeat reconciliation uses the same 7-day window as the counts + a 5-sample floor; one oversized dedupe block no longer evicts all smaller ones (continue, not break); quiet-channel guidance no longer claims old serves attribute hook traffic to reflex (they log nothing); 'transient' claim softened to evidence; marker-filter + unknown-channel-default comments state their honest limits. Pins: scoped-query SQL/params, re-entrancy single-fire, updated wiring grep. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: bump version and changelog (v0.45.1.0) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: restamp bootstrap runbook + regenerate vendored template repo for v0.45.1.0 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: update project documentation for v0.45.1.0 Post-ship /document-release pass: fold the post-doc review fixes into the reference docs (KEY_FILES: source-scoped remote volunteer_channels + heartbeat reconciliation, 32KB-capped dedupe input, marker filter, IPC one-request guard + clamp order), fix BRAINBENCH's now-stale memoryless-hook rationale, and mark the doctor-check half of the push-context TODO shipped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: cross-model doc-review fixes for v0.45.1.0 Codex doc review against the shipped diff: push-context channel table gains the harness-hook row (three-channels claim was stale), the guide states the PGLite-socket topology limit and the bounded dedupe horizon, delivery-point claims are hedged to match the code (write-accept isn't proof of injection; the doctor reconciles against the hook heartbeat, which can't inspect registration), BRAINBENCH's future-tense hooks-PR sentence updated now the hook has landed, and the adapter-flip + resolver-channel TODOS reflect the current lane count. llms-full.txt regenerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(hook): document --harness in the user-prompt usage text Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
117 lines
6.2 KiB
Markdown
117 lines
6.2 KiB
Markdown
# 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`):
|
|
|
|
| Channel | Surface | When to use |
|
|
|---|---|---|
|
|
| `reflex` | automatic, inside the context engine | default-on for plugin hosts; nothing to call |
|
|
| `op` | `gbrain volunteer-context` / MCP `volunteer_context` | agents without the plugin; one call per turn |
|
|
| `watch` | `gbrain watch` | stream a transcript in, volunteered pages stream out |
|
|
| `claude-code` / `codex` | `gbrain 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, and slug suffixes — each
|
|
arm carries an honest confidence: alias 0.9, exact title 0.8, slug-suffix 0.6,
|
|
+0.05 when mentioned in ≥2 turns or the newest turn.
|
|
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
|
|
|
|
```bash
|
|
# 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`). `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
|
|
|
|
| Key | Default | What it does |
|
|
|---|---|---|
|
|
| `retrieval_reflex_window_turns` | 4 | turns the ambient reflex extracts from; 1 = legacy current-turn-only (file/env plane: `GBRAIN_RETRIEVAL_REFLEX_WINDOW_TURNS`) |
|
|
| `retrieval_reflex` | true | the ambient channel's master switch |
|
|
| `retrieval_reflex_max_pointers` | 3 | pointer cap per turn |
|
|
|
|
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.
|