Add checkpoint persistence to agent core loop

coreLoop now saves incremental checkpoints after each tool-call turn
and checks a cooperative stop signal at turn boundaries. SuspendedError
is handled in finishRun, executeParallel, and executeSingleTool.
Approval-interrupted checkpoints are persisted for both flat and
nested interruptions.

Introduce RunOption, WithCheckpointStore, RunWithOpts, ResumeWithOpts,
and RunStreamedWithOpts so callers can provide checkpoint storage.
Add StreamEventSuspended and OnRunRestore hook.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
This commit is contained in:
Aurélien Sibiril
2026-04-10 18:53:06 +02:00
parent 51df618c93
commit 7d5b933aa4
3 changed files with 272 additions and 54 deletions

View File

@@ -49,6 +49,7 @@ const (
StreamEventToolEnd StreamEventType = "tool_end"
StreamEventHandoff StreamEventType = "handoff"
StreamEventComplete StreamEventType = "complete"
StreamEventSuspended StreamEventType = "suspended"
StreamEventError StreamEventType = "error"
)
@@ -59,6 +60,10 @@ func (sr *StreamedRun) Wait() (*Result, error) {
}
func (a *Agent) RunStreamed(ctx context.Context, messages []llm.Message) *StreamedRun {
return a.RunStreamedWithOpts(ctx, messages)
}
func (a *Agent) RunStreamedWithOpts(ctx context.Context, messages []llm.Message, opts ...RunOption) *StreamedRun {
events := make(chan StreamEvent, 64)
sr := &StreamedRun{
Events: events,
@@ -69,17 +74,17 @@ func (a *Agent) RunStreamed(ctx context.Context, messages []llm.Message) *Stream
defer close(sr.done)
defer close(events)
result, err := coreLoop(
ctx,
a,
messages,
runOpts{
callLLM: streamingCallLLM(events),
onEvent: func(ctx context.Context, ev StreamEvent) {
trySendEvent(ctx, events, ev)
},
ro := runOpts{
callLLM: streamingCallLLM(events),
onEvent: func(ctx context.Context, ev StreamEvent) {
trySendEvent(ctx, events, ev)
},
)
}
for _, opt := range opts {
opt(&ro)
}
result, err := coreLoop(ctx, a, messages, ro)
sr.result = result
sr.err = err