Skip to content

refactor(agent)!: replace Flow with lifecycle-specific hook actions #2139

Description

@gold-silver-copper
  • I have looked for existing issues (including closed) about this

Context

This is the hook-specific portion of #2126, extracted from #2132. It depends on the canonical tool execution API in #2138.

Feature Request

Replace universal AgentHook::on_event(StepEvent) -> Flow control with one hook method, event payload, and action type for each lifecycle event.

The current API lets every Flow action compile for every StepEvent, even when an action is meaningless for that event. Composition rules are also hidden: completion patches accumulate, argument and presentation rewrites chain, terminal actions short-circuit, and observe-only events should not expose steering actions.

Required design

Lifecycle methods should cover:

  • before a completion request;
  • after a non-streaming completion response;
  • after an accepted model turn;
  • invalid tool calls;
  • before tool execution;
  • after tool execution;
  • streaming text/tool-call deltas;
  • streaming response completion.

Every method receives a run-scoped HookContext with run ID, one-based turn, streaming flag, optional agent name, and shared typed Scratchpad.

Use event-specific actions:

  • CompletionCallAction: continue, per-turn RequestPatch, stop;
  • ToolCallAction: run, rewrite arguments, skip, stop;
  • ToolResultAction: keep presentation, rewrite presentation, stop;
  • InvalidToolCallAction: fail, retry, repair, skip, stop;
  • ObservationAction: continue, stop.

Invalid-call deferral must be an explicit documented action/stack behavior, not an undocumented Option<Action>. Use one canonical public spelling per event; do not add aliases such as both CompletionCall and CompletionCallEvent.

HookStack contract

Hooks execute in registration order.

RequestPatch merging:

  • append extra context;
  • shallow-merge object additional parameters;
  • intersect active tools;
  • last writer wins for scalar fields/history, with a warning;
  • never persist a patch into later turns.

Tool calls thread rewrites into later hooks; Skip and Stop are terminal. Tool results thread presentation rewrites into later hooks while keeping raw ToolResult and ToolContext immutable. Nested stacks must preserve the same merge/chaining behavior. Observe-only Stop short-circuits.

Scope

Replace StepEvent, Flow, and on_event; add typed events/actions; update HookStack, blocking runner, streaming path, exports, examples, tests, and API docs; delete old names and aliases in the same PR.

Non-goals

Dependencies

Depends on #2138. It does not depend on registry-snapshot or MCP-refresh work.

Acceptance criteria

  • One hook method exists per supported lifecycle event.
  • Every method receives HookContext and returns its event-specific action.
  • Unsupported event/action combinations are unrepresentable.
  • Invalid-call deferral/default semantics are explicit.
  • Flow, universal StepEvent control, and compatibility aliases are removed.
  • RequestPatch follows the documented non-sticky merge rules.
  • Tool-call and tool-result rewrites chain through flat and nested stacks.
  • Raw result/context are unaffected by presentation rewrites.
  • Terminal actions short-circuit predictably.
  • Blocking and streaming shared events behave identically.
  • Public docs and examples cover observation, patching, rewriting, redaction, and invalid-call recovery.

Tests

Add focused tests for every patch merge rule, conflicts, active-tool intersections, chained/nested rewrites, rewrite-before-skip/stop, raw-result immutability, every invalid-call action, observe-only stop, and streaming event-interest aggregation. Add paired run/stream agent tests for patches, rewrites, skips, result redaction, invalid calls, and stop actions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions