Files
tutti/packages/agent/store-sqlite/runtime_operation_edit_retry_validation.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

197 lines
7.2 KiB
Go

package storesqlite
import (
"context"
"database/sql"
"errors"
"fmt"
"strings"
)
func validateRuntimeOperationPrepare(input RuntimeOperationPrepare) error {
if input.OperationID == "" || input.WorkspaceID == "" || input.AgentSessionID == "" || input.TurnID == "" {
return errors.New("operation, workspace, session, and turn ids are required")
}
switch input.Kind {
case RuntimeOperationKindInteractiveResponse:
if input.RequestID == "" {
return errors.New("interactive runtime operation request id is required")
}
case RuntimeOperationKindCancelTurn:
if input.RequestID != "" {
return errors.New("cancel runtime operation must not have a request id")
}
if _, err := cancelTargetsFromPayload(input.AgentSessionID, input.TurnID, input.Payload); err != nil {
return err
}
case RuntimeOperationKindPlanDecision:
if input.RequestID == "" || input.RequestID != input.TurnID {
return errors.New("plan decision request id must equal its plan turn id")
}
if err := validatePlanDecisionOperationPayload(input.OperationID, input.Payload); err != nil {
return err
}
if payloadString(input.Payload, "step") != "prepared" {
return errors.New("new plan decision operation must start prepared")
}
case RuntimeOperationKindEditRetry:
if input.RequestID == "" {
return errors.New("edit retry client operation id is required")
}
if err := validateEditRetryOperationPayload(input.OperationID, input.Payload); err != nil {
return err
}
payload, err := DecodeEditRetryOperationPayload(input.Payload)
if err != nil {
return err
}
if payload.Checkpoint != EditRetryCheckpointPrepared {
return errors.New("new edit retry operation must start prepared")
}
default:
return fmt.Errorf("unknown runtime operation kind %q", input.Kind)
}
return nil
}
func validatePlanDecisionOperationPayload(operationID string, payload map[string]any) error {
if payloadString(payload, "promptKind") != "plan-implementation" || payloadString(payload, "action") != "implement" {
return errors.New("plan decision prompt kind and action are invalid")
}
if payloadString(payload, "idempotencyKey") == "" || payloadString(payload, "clientSubmitId") != "plan-decision:"+strings.TrimSpace(operationID) {
return errors.New("plan decision identity payload is invalid")
}
switch payloadString(payload, "step") {
case "prepared", "settings_applied", "send_dispatched":
if payloadString(payload, "confirmedTurnId") != "" {
return errors.New("unconfirmed plan decision must not carry a confirmed turn")
}
case "send_confirmed":
if payloadString(payload, "confirmedTurnId") == "" {
return errors.New("confirmed plan decision turn is required")
}
default:
return errors.New("plan decision step is invalid")
}
return nil
}
func cancelTargetsFromRuntimeOperation(operation RuntimeOperation) ([]runtimeCancelTarget, error) {
return cancelTargetsFromPayload(operation.AgentSessionID, operation.TurnID, operation.Payload)
}
func cancelTargetsFromPayload(agentSessionID string, turnID string, payload map[string]any) ([]runtimeCancelTarget, error) {
rootAgentSessionID := payloadString(payload, "rootAgentSessionId")
if rootAgentSessionID == "" {
return nil, errors.New("cancel runtime operation root agent session id is required")
}
rawTargets, ok := payload["targets"].([]any)
if !ok || len(rawTargets) == 0 {
return nil, errors.New("cancel runtime operation targets are required")
}
result := make([]runtimeCancelTarget, 0, len(rawTargets))
seen := make(map[string]struct{}, len(rawTargets))
subjectFound := false
for _, raw := range rawTargets {
value, ok := raw.(map[string]any)
if !ok {
return nil, errors.New("cancel runtime operation target must be an object")
}
target := runtimeCancelTarget{
AgentSessionID: payloadString(value, "agentSessionId"),
TurnID: payloadString(value, "turnId"),
}
if target.AgentSessionID == "" || target.TurnID == "" {
return nil, errors.New("cancel runtime operation target session and turn ids are required")
}
key := target.AgentSessionID + "\x00" + target.TurnID
if _, exists := seen[key]; exists {
return nil, errors.New("cancel runtime operation targets must be unique")
}
seen[key] = struct{}{}
if target.AgentSessionID == agentSessionID && target.TurnID == turnID {
subjectFound = true
}
result = append(result, target)
}
if !subjectFound {
return nil, errors.New("cancel runtime operation targets must include the operation subject")
}
return result, nil
}
func validateEditRetryRuntimeOperationSubjectTx(
ctx context.Context,
tx *sql.Tx,
input RuntimeOperationPrepare,
turn Turn,
) error {
payload, err := DecodeEditRetryOperationPayload(input.Payload)
if err != nil {
return err
}
if turn.Phase != TurnPhaseSettled || turn.Origin != TurnOriginUserPrompt ||
strings.TrimSpace(turn.RootProviderTurnID) == "" {
return ErrRuntimeOperationSubjectState
}
var activeTurnID sql.NullString
var historyRevision int64
if err := tx.QueryRowContext(ctx, `
SELECT sessions.active_turn_id, history.history_revision
FROM workspace_agent_sessions AS sessions
JOIN workspace_agent_session_history AS history
ON history.workspace_id = sessions.workspace_id
AND history.agent_session_id = sessions.agent_session_id
WHERE sessions.workspace_id = ? AND sessions.agent_session_id = ?
AND sessions.deleted_at_unix_ms = 0
`, input.WorkspaceID, input.AgentSessionID).Scan(&activeTurnID, &historyRevision); err != nil {
return fmt.Errorf("read edit retry session history: %w", err)
}
if activeTurnID.Valid && strings.TrimSpace(activeTurnID.String) != "" {
return ErrRuntimeOperationSubjectState
}
if historyRevision != payload.ExpectedRevision {
return ErrRuntimeOperationSubjectState
}
var validSubject int
if err := tx.QueryRowContext(ctx, `
SELECT (
EXISTS(
SELECT 1 FROM workspace_agent_turn_history
WHERE workspace_id = ? AND agent_session_id = ? AND turn_id = ?
AND history_state = 'effective'
)
AND EXISTS(
SELECT 1 FROM workspace_agent_turn_submissions
WHERE workspace_id = ? AND agent_session_id = ? AND turn_id = ?
)
AND NOT EXISTS(
SELECT 1
FROM workspace_agent_turns AS candidate
JOIN workspace_agent_turn_history AS candidate_history
ON candidate_history.workspace_id = candidate.workspace_id
AND candidate_history.agent_session_id = candidate.agent_session_id
AND candidate_history.turn_id = candidate.turn_id
WHERE candidate.workspace_id = ? AND candidate.agent_session_id = ?
AND candidate_history.history_state = 'effective'
AND (candidate.started_at_unix_ms > ?
OR (candidate.started_at_unix_ms = ? AND candidate.turn_id > ?))
)
AND NOT EXISTS(
SELECT 1 FROM workspace_agent_sessions
WHERE workspace_id = ? AND root_agent_session_id = ?
AND session_kind = 'child' AND deleted_at_unix_ms = 0
)
)
`, input.WorkspaceID, input.AgentSessionID, input.TurnID,
input.WorkspaceID, input.AgentSessionID, input.TurnID,
input.WorkspaceID, input.AgentSessionID, turn.StartedAtUnixMS, turn.StartedAtUnixMS, input.TurnID,
input.WorkspaceID, input.AgentSessionID).Scan(&validSubject); err != nil {
return fmt.Errorf("validate edit retry subject: %w", err)
}
if validSubject != 1 {
return ErrRuntimeOperationSubjectState
}
return nil
}