Repository navigation
Feature request / design discussion: task-scoped memory prefetch by ID for subagent delegation #449
Description
Activity
Update: I've rewritten this issue based on the feedback above. The core request is now stated as a user pain point, and all design decisions are explicitly left to maintainers. I am not requesting approval for any specific syntax, default configuration, or single-harness implementation. The previous prototype is kept only as reference material.
- changed the title
[-]Feature Request: task-requested memory injection via ⟦mc-mem: …⟧ markers (Pi v1)[/-][+]Feature request / design discussion: task-scoped memory prefetch by ID for subagent delegation[/+]on Sep 15, 2026 Cross-harness design for task-scoped memory prefetch by ID
Thanks for the detailed review and for opening this discussion. I agree the current PR is shaped incorrectly — it's single-harness, and it introduced agent-facing syntax and a default-on config surface without a prior design approval step. I'll keep the implementation frozen until we agree on the cross-harness contract here.
Below is a proposal for the orchestrator-to-child contract, addressing the questions you raised.
1. Orchestrator-to-child contract
I propose moving IDs out of task text and into a structured delegation field. The orchestrator passes memory prefetch instructions as part of the subagent invocation payload, not as a marker inside the natural-language task.
Proposed shape:
{ "task": "Fix the race condition in the scheduler", "memory": { "prefetch_ids": ["11", "55"], "mode": "frozen" } }Or, more preferably, passing a snapshot reference:
{ "task": "Fix the race condition in the scheduler", "memory": { "snapshot_ref": "snap_abc123" } }In both cases, the task text stays clean. The child's memory adapter reads the structured field and injects the corresponding snapshot through the existing
ctx_memory getpath.The key principle: IDs travel through a structured orchestration API, not through task-text markers. The child harness never needs to parse the task string for memory instructions.
2. Reuse of
ctx_memory getandm[0]; no separate memory policyThe prefetch mechanism should be a thin batch optimization over the existing
ctx_memory getprimitive, not a parallel memory policy.- If
prefetch_idsis provided, the child resolves each ID throughctx_memory getand injects the results. - If
snapshot_refis provided, the child resolves the frozen snapshot and injects it directly. - The existing
m[0]semantics (auto-recall of the most relevant memory) remain untouched; prefetch is additive and explicit, not a replacement.
This keeps one memory resolution path and avoids divergent behavior between prefetch and normal recall.
3. Enforcement of memory-off, project-config trust, workspace visibility, and Rust-facade ownership
These should be enforced at the point of snapshot creation, not at the point of injection. That is, the orchestrator (or the Rust facade) decides what IDs are eligible for prefetch based on:
- memory-off: if memory is disabled for the project or session, no prefetch IDs are emitted; the structured field is absent.
- project-config trust: only memories from trusted project configs are eligible for prefetch; untrusted sources are filtered before the ID list is assembled.
- workspace visibility: IDs are resolved against the workspace's visibility scope; cross-workspace IDs are rejected at snapshot creation time.
- Rust-facade ownership: the Rust facade owns snapshot creation and ID validation; the child harness only consumes the validated result. This keeps policy enforcement in one place.
4. Frozen delivery across restart and compaction windows
The owner of frozen delivery should be the snapshot creation layer (Rust facade or orchestrator-side memory service), not the child harness.
- When a
snapshot_refis created, it is persisted with a defined TTL and a frozen state. - Across restart: the child resolves
snapshot_reffrom the persisted store; the snapshot bytes are unchanged. - Across compaction: the snapshot is pinned and exempt from compaction eviction; the orchestrator re-validates the ref before handing it to the child.
- Outgoing capacity check: before emitting a snapshot or ID list, the orchestrator checks the serialized size of the memory payload against the child's context budget. If it exceeds the budget, the prefetch is either trimmed (by priority or recency) or split into a paginated fetch.
5. Persisted namespace and schema restart
I do not think a new persisted namespace is required if
snapshot_refpoints to the existing memory store.The frozen snapshot can be represented as a normal memory entry with a
frozenflag and a stable ID. This reuses the existing schema and avoids a coordinated schema restart. If the current schema cannot express a frozen pin, the minimal change would be an additive column or a side table, which is a much smaller blast radius than a namespace change.If we can avoid a new namespace, we should. If we cannot, the cost of a coordinated schema restart should be justified against the simpler additive change.
6. Open questions for the maintainers
- Harness support: Which harnesses currently support structured delegation metadata? I am least familiar with OpenCode and the Rust lane. If the contract cannot be uniform today, where should the adapter layer live?
- Snapshot vs. ID list: Do you prefer passing
snapshot_ref(frozen at creation) orprefetch_ids(resolved by the child)? The former gives stronger frozen-delivery guarantees; the latter is simpler and closer to the currentctx_memory getpath. - Fallback: Should we keep the text-marker parser as a deprecated fallback for harnesses that cannot pass structured metadata, or drop it entirely once the contract is agreed?
- Orchestrator ownership: Should the orchestrator emit the structured
memoryfield, or should the Rust facade intercept the delegation call and inject it? This affects where capacity checks and trust filtering live.
Next steps
I'll hold the implementation until we converge on the contract. Once the shape is agreed, I can implement across OpenCode, Pi/OMP, and the Rust lane as a single coherent change, with tests for each harness and for the frozen-delivery path.
Thank you for the detailed questions — they made the right shape much clearer.
- If
Thanks for taking the decline the right way and for the proposal — it is the shape of thinking we wanted, and the questions it raises are the real ones.
Two facts first, because they decide what can be built today. There is no structured delegation carrier on OpenCode: on both 1.18.x and 2.0.x the task/subagent tool passes only the prompt text to the child; the child session row has a
metadatacolumn that the spawn path never populates, and on 2.0.x a plugin cannot create a parented child itself. So amemory.prefetch_idsfield on the invocation is not something a plugin can honour on OpenCode without an upstream change, which would make it a Pi/OMP-only feature in practice. What every harness does carry reliably is the child's parent session id, and that is the join key any cross-harness delivery has to hang off.On the direction: you are right that there should be a way for an orchestrator to hand memories to the agents it spawns, and we agree it belongs in the structured layer rather than in task prose. We are designing it, but under a bigger umbrella than this plugin — it has to line up with how the harness integration we are building delegates work, what a child receives at spawn, and how frozen delivery survives compaction and restart on every lane at once. That design needs to be done carefully and as one piece, so we are not going to converge on a contract in this issue and then have it superseded a few weeks later.
Concretely: we will keep this issue open as the tracking point, post the shape here once it is settled enough to build against, and would be glad to have you implement or review it at that stage. Until then there is no version of this that would merge, so please hold the implementation as you offered.
Reacted by G0.0000
Feature request / design discussion: task-scoped memory prefetch by ID for subagent delegation
Motivation: users are forced to maintain private patches
I use magic-context for multi-agent orchestration. When a parent agent delegates a task, it often already knows exactly which memories the child needs. There is currently no task-scoped prefetch-by-ID capability, so I maintain several local patches to simulate it. Every upstream upgrade requires rebasing and re-adapting those patches. This is costly and fragile.
I am not opening this issue to define the cross-harness contract, and I am not pushing a specific syntax. I want to describe the use case and its user cost. The design shape is for maintainers to decide.
How I use it now, and where it hurts
My current options are roughly:
ctx_memory getitself;None are good:
ctx_memory get: adds tool-round-trip latency, and the child may not know it needs to.This is especially visible with
defaultContext=freshchildren: they share the project memory store but have no parent conversation, so there is no stable channel for IDs the parent already knows.What I want
A parent can request memories by ID at delegation time, and the child receives the full content before its first token:
For typical SOP / config payloads, this can reduce parent output by hundreds to over a thousand tokens per delegation, with corresponding output-latency savings. Exact numbers depend on memory size and task shape.
What I am not trying to decide
These are yours to define, not mine:
My earlier prototype only validated the Pi path. It is not a claim for a single-harness design. If the final design only needs part of it, I can adapt to whatever shape you define.
Existing prototype (material only, not an implementation request)
On the Pi path I validated the core mechanics: primary-key reads, no embedding, opaque
Unavailablefor invisible IDs, snapshot freezing, replay, and explicit capacity refusal. Tests covered parsing boundaries, invisible IDs, replay, and transient-fault retry.But this is only material. Whether to use it, how much, and in which layer, is up to you.
One question I would like to discuss
Is this use case worth including in the cross-harness design discussion?