Expand description
AgentContextMiddleware — the innermost SpawnerLayer that
materializes AgentContextView once per spawn (Contract C, GH #20),
now also the receptacle that resolves and merges the BP-declared
agent-context supply tiers (GH #21 Phase 1).
Layered FIRST (innermost, i.e. added earliest in
service::task_launch::TaskLaunchService::launch, before the alias /
worker-binding blocks) so it observes the ctx.meta.runtime keys the
outer TaskInputMiddleware / ProjectNameAliasMiddleware /
WorkerBindingMiddleware layers insert. See the module doc on
crate::core::agent_context for the full Contract C narrative and
the two-axis fan-out diagram.
Unlike TaskInputMiddleware / ProjectNameAliasMiddleware (which only
mutate ctx), this layer ALSO snapshots the view (paired with the
resolved policy, GH #23) into EngineState.agent_ctx, keyed
(task_id, attempt) — the Worker axis
(Engine::fetch_worker_payload{,_trusted}) reads the view half back
from there, mirroring how EngineState.prompts / .systems are
populated and later fetched.
§GH #21 Phase 1: the supply tiers this layer merges
service::task_launch::derive_agent_ctx / derive_context_policies
resolve four pieces out of the launched Blueprint — default_agent_ctx
/ per-agent AgentMeta.ctx (the context tiers) and
default_context_policy / per-agent AgentMeta.context_policy (the
policy tiers) — and hand them to AgentContextMiddleware::new. On
every spawn:
- The context tiers are shallow-merged (agent wins on key collision;
a tier whose declared value isn’t a JSON
Objectis warned and skipped, never failing the spawn). - Every merged key is inserted into
ctx.meta.runtimeonly-if-absent — an outer, runtime-supplied value (e.g.TaskInputMiddleware’swork_dir) always outranks this BP-declared default, with no priority code beyond insertion order (SpawnerStackouter-to-inner = later tier wins the race to insert first). AgentContextView::from_ctxthen reads the (possibly BP-defaulted)ctx.meta.runtimeback out, so known-key defaults (project_root/work_dir/ …) flow into the view automatically; merged keys that aren’t one of those named fields are folded intoview.extrainstead (also only-if-absent).- The policy tiers resolve to a single effective
ContextPolicy(per-agent outranks BP-global; pass-all when neither is declared) and are applied viaAgentContextView::apply_policy.
§GH #21 Phase 2: the Step tier
Before the Agent/BP-global merge above, this layer also reads
ctx.meta.runtime[STEP_CTX_KEY] — the Step tier’s resolved bundle,
threaded through by Engine::dispatch_attempt_with from
TaskSpec.step_ctx (itself resolved by EngineDispatcher::dispatch’s
$step_meta envelope handling in crate::blueprint). When it is a
JSON Object, its keys are applied with the SAME only-if-absent +
extra-fold mechanics as the Agent/BP-global tiers, but ORDERED FIRST —
so a Step-declared key wins over an Agent/BP-global-declared key for
the same name (Run/Task tier keys are already individually present in
ctx.meta.runtime by the time this layer runs and are therefore
untouched either way — the full cascade is Run > Task > Step > Agent >
BP-global). A non-Object STEP_CTX_KEY value is warned and skipped,
same as a malformed Agent/BP-global tier. The raw STEP_CTX_KEY
bundle itself stays in the runtime bag verbatim (in-process workers
may read it directly) — it is NOT folded into view.extra as a whole;
only its individual keys are.
Structs§
- Agent
Context Middleware SpawnerLayerthat materializes anAgentContextViewfromctx(after merging in the BP-declared agent-context supply tiers — see the module doc), snapshots it into engine state (Worker axis source), and stashes the serialized view intoctx.meta.runtime[AGENT_CONTEXT_KEY](Spawner axis source) before delegating toinner.