Skip to main content
The hook runtime lets Pioneer run domain work around a turn without putting every domain rule inside the agent loop. It lives in crates/hooks and is wired by crates/agent and crates/gateway. The design goal is simple: the agent loop should know lifecycle phases and typed contributions. It should not need memory-specific, skills-specific, or future domain-specific branches for every side behavior.

Why hooks exist

Some work must happen around a turn but should not be part of the main model/tool loop:
  • classify turn policy before prompt assembly;
  • add compact context before prompt compilation;
  • contribute model-visible tool bundles;
  • add prompt sections;
  • run post-turn extraction or cleanup;
  • persist hook-run diagnostics for recovery and audit.
Without hooks, this logic would accumulate as branches in the agent loop. Hooks keep these side behaviors explicit, typed, ordered, and observable.

Core types

Handlers declare capabilities. A hook that contributes tool bundles must declare the tool-bundle capability. A hook that only reads context and writes internal domain state should not be able to emit prompt or tool contributions by accident.

Turn phases

The phase names are stable because they are also useful in diagnostics, hook-run persistence, and future operator tooling.

Contributions

Hooks communicate through typed contributions instead of raw JSON blobs. The agent loop aggregates these sets at phase boundaries. Domain hooks interpret their own policy and context; the loop stays generic.

Execution policy

Each subscription has execution behavior: Failure policy is separate. Best-effort hooks can fail without failing the turn; required hooks can fail closed. The runtime records safe diagnostics and avoids putting raw sensitive payloads in summaries.

Dependencies and ordering

Subscriptions are sorted by priority and id, while explicit dependencies determine required ordering. A hook can require another subscription on the same phase to run first. The runtime detects missing dependencies, disabled dependencies, and cycles. Memory uses phase ordering for recall: deterministic memory runs at turn.pre_prompt_context, then active memory runs at turn.post_preflight_prompt_context after the internal preflight plan has been built. The active hook receives typed prompt context plus the preflight-owned active recall plan or fallback. Thread episodic context follows the same rule. The hook/runtime boundary carries typed prompt context and recall contributions; the agent loop should not know how memvid capsules, thread chunks, or artifact refs are searched. It only dispatches the phase and later consumes the compiled prompt context.

Persistence and recovery

Gateway hook-run persistence records lifecycle state for subscribed work:
  • started;
  • completed;
  • skipped;
  • failed;
  • timed out;
  • backgrounded;
  • latency and attempt metadata;
  • safe diagnostics and contribution hashes.
Domain hooks should not invent their own retry tables for ordinary hook execution. Generic hook-run persistence owns that operational layer.

Memory as a hooked domain

Agent memory is the first heavy domain built on this runtime:
  • MemoryPolicyClassifierHook runs at turn.pre_policy.
  • MemoryDeterministicRecallHook runs at turn.pre_prompt_context.
  • ActiveMemoryRecallHook runs at turn.post_preflight_prompt_context.
  • MemoryToolBundleHook runs at turn.pre_tool_materialization.
  • MemoryPromptContractHook runs at turn.pre_prompt_compile.
  • MemoryPostTurnExtractorHook runs at turn.post_turn.
The result is that memory can recall context, expose tools, render prompt policy, and extract durable facts without adding memory-specific extraction branches to the agent loop. Thread context and artifact continuity build on the same pattern. A domain can enrich prompt context with recalled snippets or artifact references, while the prompt compiler and tool materialization layer decide what the model sees. The loop does not need a special “if this old message had a photo” branch.

Adding a hook

When adding a hook:
  1. Define typed input/output using existing hook request and contribution types where possible.
  2. Give the handler a stable id and kind.
  3. Declare only the capabilities it needs.
  4. Register it with a phase, priority, execution policy, failure policy, and visibility.
  5. Add tests for descriptor stability, capabilities, ordering/dependencies, policy behavior, and diagnostics.
  6. Keep domain decisions inside the hook or its domain service, not in the agent loop.