package loop import ( "context" "encoding/json" "fmt" "git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model" "git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/persist" ) // checkpointState is the durable snapshot persisted at every step. Carrying the // message window and the step index lets a long-running task be resumed from the // latest checkpoint without losing context (design ยง1 durable execution). type checkpointState struct { Step int `json:"step"` Messages []model.Message `json:"messages"` StopReason string `json:"stop_reason,omitempty"` } // saveCheckpoint persists a snapshot at step. It never fails the run: a nil store // is a no-op and a marshal/save error is logged. func (l *Loop) saveCheckpoint(ctx context.Context, sessionID string, step int, messages []model.Message, stopReason string) { if l.checkpoint == nil { return } state := checkpointState{ Step: step, Messages: messages, StopReason: stopReason, } buf, err := json.Marshal(state) if err != nil { l.log.Warn("checkpoint marshal", "step", step, "error", err) return } cp := &persist.Checkpoint{ ID: fmt.Sprintf("%s:%d", sessionID, step), AgentID: sessionID, Step: step, State: buf, } if err := l.checkpoint.Save(ctx, cp); err != nil { l.log.Warn("checkpoint save", "step", step, "error", err) } }