Files
tutti/packages/agent/host/edit_retry_projection.go
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

135 lines
4.4 KiB
Go

package agenthost
import (
"encoding/json"
"errors"
"strings"
storesqlite "github.com/tutti-os/tutti/packages/agent/store-sqlite"
)
func editRetryProviderTurnIDs(turns []storesqlite.Turn, targetTurnID string) ([]string, error) {
if len(turns) == 0 || turns[len(turns)-1].TurnID != strings.TrimSpace(targetTurnID) {
return nil, ErrEditRetryNotEligible
}
result := make([]string, 0, len(turns))
for _, turn := range turns {
providerTurnID := strings.TrimSpace(turn.RootProviderTurnID)
if providerTurnID == "" {
return nil, errors.New("effective history contains a turn without provider provenance")
}
result = append(result, providerTurnID)
}
return result, nil
}
func runtimeHistoryTurnIDs(snapshot RuntimeHistorySnapshot) []string {
result := make([]string, 0, len(snapshot.Turns))
for _, turn := range snapshot.Turns {
result = append(result, strings.TrimSpace(turn.ID))
}
return result
}
func equalEditRetryIDs(left, right []string) bool {
if len(left) != len(right) {
return false
}
for index := range left {
if strings.TrimSpace(left[index]) != strings.TrimSpace(right[index]) {
return false
}
}
return true
}
func editRetryRequestMatches(operation storesqlite.RuntimeOperation, turnID string, input EditRetryInput) bool {
payload, err := storesqlite.DecodeEditRetryOperationPayload(operation.Payload)
return err == nil &&
operation.Kind == storesqlite.RuntimeOperationKindEditRetry &&
operation.TurnID == strings.TrimSpace(turnID) &&
operation.RequestID == strings.TrimSpace(input.ClientOperationID) &&
payload.ClientOperationID == strings.TrimSpace(input.ClientOperationID) &&
payload.EditedText == input.EditedText &&
payload.ExpectedRevision == int64(input.ExpectedHistoryRevision)
}
func editRetryResult(operation storesqlite.RuntimeOperation, history storesqlite.SessionHistory) EditRetryResult {
state := EditRetryStatePrepared
switch {
case operation.Status == storesqlite.RuntimeOperationStatusCompleted:
state = EditRetryStateCompleted
case history.RecoveryState == storesqlite.SessionHistoryRecoveryRequired:
state = EditRetryStateRecoveryRequired
case history.RecoveryState == storesqlite.SessionHistoryRecoveryResendPending:
state = EditRetryStateResendPending
case history.RecoveryState == storesqlite.SessionHistoryRecoveryRollbackPending:
state = EditRetryStateRollingBack
}
payload, _ := storesqlite.DecodeEditRetryOperationPayload(operation.Payload)
replacementTurnID := ""
if state == EditRetryStateCompleted {
replacementTurnID = payload.ReplacementTurnID
}
return EditRetryResult{
OperationID: strings.TrimSpace(operation.OperationID),
State: state, RetractedTurnID: operation.TurnID,
ReplacementTurnID: replacementTurnID, HistoryRevision: history.Revision,
ReasonCode: editRetryReasonFromOperation(operation),
}
}
func editRetryReplacementInput(
envelope storesqlite.TurnSubmission,
editedText string,
) (SendInput, error) {
var content []PromptContentBlock
if err := json.Unmarshal([]byte(envelope.ContentJSON), &content); err != nil {
return SendInput{}, err
}
replaced := false
for index := range content {
if strings.TrimSpace(content[index].Type) == "text" {
content[index].Text = editedText
replaced = true
break
}
}
if !replaced {
return SendInput{}, ErrEditRetryNotEligible
}
normalized, _, err := normalizePromptContent(content)
if err != nil {
return SendInput{}, err
}
var capabilityRefs []CapabilityReference
if err := json.Unmarshal([]byte(envelope.CapabilityRefsJSON), &capabilityRefs); err != nil {
return SendInput{}, err
}
var tuttiModeSnapshot *TuttiModeTurnSnapshot
if err := json.Unmarshal([]byte(envelope.TuttiModeSnapshotJSON), &tuttiModeSnapshot); err != nil {
return SendInput{}, err
}
var metadata map[string]any
if strings.TrimSpace(envelope.MetadataJSON) != "" {
if err := json.Unmarshal([]byte(envelope.MetadataJSON), &metadata); err != nil {
return SendInput{}, err
}
}
return SendInput{
Content: normalized, DisplayPrompt: editedText,
CapabilityRefs: capabilityRefs, Metadata: metadata, TuttiModeSnapshot: tuttiModeSnapshot,
}, nil
}
func editRetryReasonFromOperation(operation storesqlite.RuntimeOperation) EditRetryReasonCode {
reason := EditRetryReasonCode(strings.TrimSpace(operation.LastError))
if reason.Validate() == nil {
return reason
}
if operation.Status == storesqlite.RuntimeOperationStatusFailed {
return EditRetryReasonCodeRecoveryRequired
}
return ""
}