Documentation
¶
Overview ¶
Package continuation is the ONE durable suspend/resume mechanism for an agent turn that stops mid-flight to wait for a human.
It exists as its own package because two very different callers need it and neither should depend on the other: an interactive session (which already holds the conversation in memory and only needs the missing pair of messages back) and an unattended executive (which keeps no transcript at all — it rebuilds context from durable state each wake, so the continuation must carry the conversation itself). Before this package there were three partial designs: a typed-but-unused one in internal/agent/runtime, a set of declared-but-never-written fields on jobs.Job, and a hand-rolled map[string]any in the personal executive that was the only one actually running. Keep it one.
The invariant that makes resume exact: a suspending tool must be the SOLE tool use in its assistant response (ValidateSingletonSuspension). Otherwise a sibling tool call in the same batch would be silently dropped on resume, or re-executed — both wrong. Save refuses to write a suspension that violates it, so the error surfaces at suspend time rather than as corruption at resume.
Index ¶
- func MarkResolved(dir, interactionID string) error
- func Resolve(dir string, s Suspension, result wire.Block) ([]wire.Message, error)
- func Save(dir string, s Suspension) error
- func SessionDir(root, sessionID string) string
- func ValidateSingletonSuspension(msg wire.Message, toolUseID string) error
- type Suspension
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func MarkResolved ¶
MarkResolved records that a continuation has been consumed without producing resume messages — for a caller that resolved the interaction by another route (cancelled, or a resumed run that itself re-suspended on a new question) and must not leave the old file loadable.
func Resolve ¶
Resolve builds the resume messages and marks the continuation resolved in one step, for a caller that can afford to lose the retry (see ResumeMessages).
func Save ¶
func Save(dir string, s Suspension) error
Save writes the continuation atomically. A crash mid-write must not be able to leave a truncated file — that would strand the interaction with no way to resume it.
func SessionDir ¶
SessionDir is where an interactive session's continuations live.
Types ¶
type Suspension ¶
type Suspension struct {
Version int `json:"version"`
SessionID string `json:"session_id,omitempty"`
RunID string `json:"run_id,omitempty"`
InteractionID string `json:"interaction_id"`
ToolUseID string `json:"tool_use_id"`
ToolName string `json:"tool_name,omitempty"`
ToolInput json.RawMessage `json:"tool_input,omitempty"`
// Assistant is the response carrying the suspending tool use.
Assistant wire.Message `json:"assistant"`
// Messages is the full transcript up to and including Assistant. Set it
// when the caller keeps no transcript of its own; Resolve then hands back
// the whole conversation plus the answer. Leave it empty when the caller
// still holds the prior turns — Resolve then returns only the two messages
// to append, so the transcript is never duplicated.
Messages []wire.Message `json:"messages,omitempty"`
CreatedAt time.Time `json:"created_at"`
Resolved bool `json:"resolved"`
}
Suspension is the exact continuation of a turn paused on a human answer.
func Load ¶
func Load(dir, interactionID string) (Suspension, error)
Load reads an unresolved continuation. An already-resolved one is an error, not an empty result: resuming it twice would re-execute real side effects.
func (Suspension) ResumeMessages ¶
ResumeMessages builds the messages to resume with — the full transcript plus the answer when Messages was recorded, or just the assistant/answer pair when the caller holds the transcript itself — WITHOUT marking the continuation resolved.
Separated from Resolve because the two callers differ on when it is safe to mark: an interactive session has the human right there and can simply ask again, so it marks immediately; an unattended executive must keep the interaction retryable until the resumed run actually reaches a terminal state, or a transient model error would strand the answer with no way to re-give it.
The result block must match the suspended tool_use_id — a mismatched pairing is how a resume silently answers the wrong question.