Skip to content

feat(agent): resume a persisted AgentRun through the runner surface #2244

Description

@nazq

Decomposed from #2116 per the #2118 Phase 1 task "clarify and decompose
#2116". Refs #1968; complementary to #2121, which adds safe-boundary
checkpoint capture for manually driven runs and lists runner-level durable
resumption as remaining work.

Summary

AgentRun is a sans-IO, Serialize + Deserialize state machine, and its
module docs advertise persisting a run between steps and resuming it in
another process. The capture half works today by hand-driving next_step() /
model_response() / tool_results().

The resume half is missing: AgentRunner::run / stream build their
AgentRun internally and cannot accept a restored one. Anyone resuming a
persisted run must hand-write the entire drive loop for the resume leg — and
thereby loses the runner's hook stack, tool-server dispatch (including MCP
tools), conversation-memory append, and telemetry. A process restart or an
approval-gate suspension leaves the second half of the run without the
runner's integrations.

Proposal

One additive entry point that hands a deserialized run back to the existing
shared drive loop, so a resumed run behaves identically to one that was
never suspended:

let run: AgentRun = serde_json::from_str(&persisted)?;
let response = agent.resume(run).run().await?;   // or .stream().await

Scoped to the epic's stated boundary — "define safe pause/checkpoint
boundaries; do not attempt to serialize or suspend arbitrary Rust futures":
resume operates only on the states AgentRun already serializes (model
boundary, pending tool batch). No future suspension, no new checkpoint
mechanism — this is the missing consumer of the serialization AgentRun
already has.

Acceptance criteria

  • A restored run drives through the same engine as run()/stream()
    no second loop; hooks, tool server, memory, and telemetry wired.
  • A run restored with pending tool calls re-enters execution preserving
    internal_call_ids; behavior when the tool registry changed across
    the suspend/resume boundary is defined and documented.
  • Runner builder options (max_turns, add_hook, …) keep working after
    resume, seeded from the suspended run's values.
  • Conversation memory is not loaded on resume (the run carries its own
    history) and the completed run's messages are appended exactly once
    across the boundary.
  • Usage aggregates across the suspending and resuming processes.

I have an implementation ready and will open a PR against this issue.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions