Files
tutti/packages/agent/daemon/runtime/codex_appserver_turn_binding_recovery.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

82 lines
2.5 KiB
Go

package agentruntime
import (
"context"
"errors"
"strings"
)
func (a *CodexAppServerAdapter) RecoverProviderTurnBinding(
ctx context.Context,
input ProviderTurnBindingRecoveryInput,
) (ProviderTurnBindingRecoveryResult, error) {
token := strings.TrimSpace(input.RecoveryToken)
source := input.Source
expectedThreadID := strings.TrimSpace(source.ProviderSessionID)
if a == nil || expectedThreadID == "" || token == "" {
// Codex thread/read is authoritative for clientUserMessageId. Old
// history without that identity has no equally authoritative text shape
// in the stable protocol, so it must fail closed.
return ProviderTurnBindingRecoveryResult{},
errors.New("codex provider turn recovery token is unavailable")
}
trace := newCodexAppServerStartupTrace(source, a.startupSpanObserver, nil)
client, _, err := a.startInitializedClient(ctx, source, trace)
if err != nil {
trace.Finish(err)
return ProviderTurnBindingRecoveryResult{}, err
}
defer client.Close()
raw, err := client.ThreadReadNoHandler(
ctx,
acpStartCallTimeout,
map[string]any{
"threadId": expectedThreadID,
"includeTurns": true,
},
)
trace.Finish(err)
if err != nil {
return ProviderTurnBindingRecoveryResult{}, err
}
history, err := decodeCodexEffectiveHistory(raw, expectedThreadID)
if err != nil {
return ProviderTurnBindingRecoveryResult{}, err
}
var matched EffectiveHistoryTurn
matchCount := 0
for _, turn := range history.Turns {
if strings.TrimSpace(turn.ClientUserMessageID) != token {
continue
}
switch strings.TrimSpace(turn.Status) {
case "completed", "failed", "interrupted", "canceled", "cancelled":
default:
return ProviderTurnBindingRecoveryResult{},
errors.New("codex provider turn recovery matched a nonterminal turn")
}
matched = turn
matchCount++
}
if matchCount != 1 || strings.TrimSpace(matched.ID) == "" {
return ProviderTurnBindingRecoveryResult{},
errors.New("codex provider turn recovery token is absent or ambiguous")
}
result := ProviderTurnBindingRecoveryResult{
ProviderSessionID: expectedThreadID,
ProviderTurnID: strings.TrimSpace(matched.ID),
}
result.ProviderTurnBindingJSON, err = a.WriteProviderTurnBinding(
ProviderTurnBindingWriteInput{
Kind: ProviderTurnBindingWriteRecovered,
ProviderTurnID: result.ProviderTurnID,
},
)
if err != nil {
return ProviderTurnBindingRecoveryResult{}, err
}
return result, nil
}
var _ ProviderTurnBindingRecoveryAdapter = (*CodexAppServerAdapter)(nil)