feat(hook): carry locked + relevant beliefs into dispatched subagent prompts - #1070
Conversation
New PreToolUse hook module hook_agent_context.py: on an Agent/Task dispatch, rewrite tool_input.prompt via the harness updatedInput channel, prepending a bounded <aelfrice-worker-context> block (L0 locks + hits relevant to the worker prompt at the Grep|Glob lane's auxiliary budget). No permissionDecision is emitted, so the user's permission flow is untouched (probed live on the target harness; see issue). Fail-open on every miss, idempotent on already-tagged prompts, kill switch AELFRICE_AGENT_CONTEXT=0. Worker tags join hook.py's _ESCAPE_TAGS so stored content cannot spoof the envelope.
…1068) install/uninstall/resolve functions with an anchored ^(Agent|Task)$ matcher (never partial-matches TaskCreate etc.), manifest row (default_on, since 3.9.0), auto-install dispatch entry, aelf setup --agent-context/--no-agent-context with opt-out persistence, and the matching unsetup flag. Codex desired-set unchanged (gated on #1056).
Hook lane: dispatch discrimination (incl. TaskCreate non-match), updatedInput shape (field preservation, no permissionDecision), L0 ride-along, envelope-spoof escaping, feedback-loop stamp, fail-open passthroughs, idempotency, kill switch. Setup lane: anchored-matcher install/idempotency/coexistence, unique-basename uninstall, CLI flag round-trips.
There was a problem hiding this comment.
Sorry @robotrocketscience, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
|
Warning Review limit reached
Next review available in: 40 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (10)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR-size soft capThis PR is over the advisory size threshold:
Bigger PRs collide with more open work, which under the parallel-session workflow tends to produce repeated This is advisory only — nothing is blocked. If the size is intentional (large refactor, module removal, generated code), apply the |
|
[claim:review:garsecg:2026-07-04T07:04:57Z] |
robotrocketscience
left a comment
There was a problem hiding this comment.
Reviewed the full diff; verifying claims against call sites rather than the PR body:
- Envelope spoof-guard confirmed: worker-context tags are appended to
hook.py:_ESCAPE_TAGS, so stored belief content can't fake the idempotency tag or close the envelope early;test_belief_content_cannot_spoof_envelopecovers it. - Channel posture confirmed in code:
_emitwritesupdatedInputwith nopermissionDecision, single write at the end of the fallible path — any exception before it yields byte-identical passthrough (exit 0, no stdout). Fail-open paths all have tests (malformed stdin, non-dict tool_input, missing prompt, missing db, empty store, tagged prompt, kill switch). - Anchored matcher + exact tool-name membership both present (
^(Agent|Task)$in setup,AGENT_TOOL_NAMESre-check in the module) —TaskCreate/TaskUpdatecan't match. - Wiring follows the house per-hook pattern: install/uninstall/resolve triplet in setup.py, manifest row (
default_on, since 3.9.0), auto_install dispatch entry, opt-out map key, console script,--no-agent-contextflags. - Independent verification: fetched the PR head locally, both new test files pass 27/27 in a fresh dev venv; CI pytest matrix (3.12/3.13) green; no unresolved review threads.
- Discretion grep on the diff: clean (the one hit is a pre-existing context line).
One non-blocking flag for a docs follow-up rather than this PR: bench-run contamination. Benchmark reader/judge workers are dispatched through the same tool this hook matches, so a default-on install will inject store context into bench dispatch prompts. docs/concepts/BENCHMARKS.md (and the lab runner conventions) should instruct setting AELFRICE_AGENT_CONTEXT=0 for reader/judge dispatches — otherwise injected beliefs are a fresh gold-leak vector of exactly the class the bench protocol gates against.
LGTM — adding ready-to-merge.
|
[release:review:garsecg:2026-07-04T07:09:20Z] |
|
merge-train: merged 1d13b94 → |
Closes #1068.
What this does
Dispatched subagents now inherit memory context. A new default-on hook (
aelf-agent-context-hook,PreToolUsematcher^(Agent|Task)$) rewrites the worker's prompt via the harnessupdatedInputchannel before dispatch, prepending a bounded<aelfrice-worker-context>block:manifest_reference_locks=True).retrieve()against the worker's prompt at the Grep|Glob lane's auxiliary budget (token_budget=600,l1_limit=10), with the UserPromptSubmit lane's post-filters (project-context feat(hook): project-scoped retrieval filtering — surface beliefs only from active scope (#855 defect 3) #858, session scope-outs feat(hook): session-scoped belief exclusion via aelf:scope-out (#855 defect 1) #856) and feedback-loop audit rows.Design notes
updatedInputapplies without apermissionDecision, so the hook never touches the user's permission flow;SubagentStartwas rejected for this lane because its payload carries no prompt text (no query-aware retrieval possible). This follows the fix(hook): PreCompact rebuild block rejected by harness — move injection to SessionStart(source=compact) #1031 lesson — verify the harness channel before building on it.^(Agent|Task)$can never partial-matchTaskCreate/TaskGet/etc.; the module additionally re-checkstool_nameby exact membership.hook_search_tool.hook.py:_ESCAPE_TAGSso stored belief content cannot spoof the envelope (feat(lock): inject reference-tier locks as a bounded manifest (#1016-B) #1037 posture).AELFRICE_AGENT_CONTEXT=0; setup flag--no-agent-contextwith feat(setup): self-installing hook manifest at aelf-CLI first run #623 opt-out persistence.host_codex.pyis unchanged, pending [Tracking] First-class OpenAI Codex CLI support #1056 live validation.retrieve()only, no new ranking machinery.What ships
src/aelfrice/hook_agent_context.py+aelf-agent-context-hookconsole script.setup.pyinstall/uninstall/resolve; manifest row (default_on: true, since 3.9.0);auto_installdispatch entry;aelf setup/aelf unsetupflags + opt-out map.Verification
tests/test_agent_context_hook.py+tests/test_aelf_setup_agent_context.py: 27/27 pass.lock="user", original prompt preserved verbatim at the end), and the worker correctly answered a question whose answer existed only in the belief store. Passthrough paths (no store, tagged prompt, kill switch) verified by unit tests.hook_search_tool.py(same pre-existing finding categories: lazy private reuse,db_pathcwd shim); pyright is not a CI gate.Reviewer notes
default_on: truematches every sibling hook; flag if you want this landing default-off first. The mutation risk is bounded by fail-open + the idempotency tag, and the emit changes only thepromptkey.record_retrieval(exposure valence 0.1), so worker-lane exposure feeds the posterior like the UserPromptSubmit lane does.