Files
tutti/services/tuttid/api/daemon_agent_edit_retry.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

245 lines
8.7 KiB
Go

package api
import (
"context"
"errors"
"strings"
agenthost "github.com/tutti-os/tutti/packages/agent/host"
tuttigenerated "github.com/tutti-os/tutti/services/tuttid/api/generated"
"github.com/tutti-os/tutti/services/tuttid/apierrors"
agentservice "github.com/tutti-os/tutti/services/tuttid/service/agent"
)
type agentEditRetryService interface {
EditRetry(context.Context, string, string, string, agentservice.EditRetryInput) (agentservice.EditRetryResult, error)
RecoverEditRetry(context.Context, string, string, string, agentservice.EditRetryRecoveryAction) (agentservice.EditRetryResult, error)
}
func (api DaemonAPI) EditRetryWorkspaceAgentTurn(
ctx context.Context,
request tuttigenerated.EditRetryWorkspaceAgentTurnRequestObject,
) (tuttigenerated.EditRetryWorkspaceAgentTurnResponseObject, error) {
service, ok := api.AgentSessionService.(agentEditRetryService)
if !ok {
return tuttigenerated.EditRetryWorkspaceAgentTurn503JSONResponse{
ServiceUnavailableErrorJSONResponse: agentSessionServiceUnavailableError(),
}, nil
}
if request.Body == nil {
return tuttigenerated.EditRetryWorkspaceAgentTurn400JSONResponse{
InvalidRequestErrorJSONResponse: invalidRequestError(apierrors.EmptyBody()),
}, nil
}
result, err := service.EditRetry(
ctx,
string(request.WorkspaceID),
string(request.AgentSessionID),
string(request.TurnID),
agentservice.EditRetryInput{
EditedText: request.Body.EditedText,
ClientOperationID: request.Body.ClientOperationId,
ExpectedHistoryRevision: uint64(request.Body.ExpectedHistoryRevision),
},
)
response := generatedAgentEditRetryResult(result, err)
if editRetryResultIsDurablyPending(result) {
return tuttigenerated.EditRetryWorkspaceAgentTurn202JSONResponse(response), nil
}
if err != nil {
return writeEditRetryWorkspaceAgentTurnError(err), nil
}
return tuttigenerated.EditRetryWorkspaceAgentTurn200JSONResponse(response), nil
}
func (api DaemonAPI) RecoverWorkspaceAgentEditRetry(
ctx context.Context,
request tuttigenerated.RecoverWorkspaceAgentEditRetryRequestObject,
) (tuttigenerated.RecoverWorkspaceAgentEditRetryResponseObject, error) {
service, ok := api.AgentSessionService.(agentEditRetryService)
if !ok {
return tuttigenerated.RecoverWorkspaceAgentEditRetry503JSONResponse{
ServiceUnavailableErrorJSONResponse: agentSessionServiceUnavailableError(),
}, nil
}
if request.Body == nil {
return tuttigenerated.RecoverWorkspaceAgentEditRetry400JSONResponse{
InvalidRequestErrorJSONResponse: invalidRequestError(apierrors.EmptyBody()),
}, nil
}
result, err := service.RecoverEditRetry(
ctx,
string(request.WorkspaceID),
string(request.AgentSessionID),
request.OperationID,
agentservice.EditRetryRecoveryAction(request.Body.Action),
)
response := generatedAgentEditRetryResult(result, err)
if editRetryResultIsDurablyPending(result) {
return tuttigenerated.RecoverWorkspaceAgentEditRetry202JSONResponse(response), nil
}
if err != nil {
return writeRecoverWorkspaceAgentEditRetryError(err), nil
}
return tuttigenerated.RecoverWorkspaceAgentEditRetry200JSONResponse(response), nil
}
func generatedAgentEditRetryAvailability(
availability agenthost.EditRetryAvailability,
) tuttigenerated.WorkspaceAgentEditRetryAvailability {
recoveryState := availability.RecoveryState
if recoveryState == "" {
recoveryState = agenthost.EditRetryStatePrepared
}
result := tuttigenerated.WorkspaceAgentEditRetryAvailability{
Supported: availability.Supported,
Eligible: availability.Eligible,
HistoryRevision: int64(availability.HistoryRevision),
RecoveryState: tuttigenerated.WorkspaceAgentEditRetryAvailabilityRecoveryState(recoveryState),
AvailableActions: make([]tuttigenerated.WorkspaceAgentEditRetryRecoveryAction, 0, len(availability.AvailableActions)),
}
if value := strings.TrimSpace(availability.TurnID); value != "" {
result.TurnId = &value
}
if value := strings.TrimSpace(availability.OperationID); value != "" {
result.OperationId = &value
}
for _, action := range availability.AvailableActions {
result.AvailableActions = append(result.AvailableActions, tuttigenerated.WorkspaceAgentEditRetryRecoveryAction(action))
}
if reason := generatedAgentEditRetryReasonCode(availability.ReasonCode); reason != nil {
result.ReasonCode = reason
}
return result
}
func generatedAgentEditRetryResult(
result agentservice.EditRetryResult,
err error,
) tuttigenerated.WorkspaceAgentEditRetryResponse {
response := tuttigenerated.WorkspaceAgentEditRetryResponse{
OperationId: strings.TrimSpace(result.OperationID),
State: tuttigenerated.WorkspaceAgentEditRetryResponseState(result.State),
RetractedTurnId: strings.TrimSpace(result.RetractedTurnID),
HistoryRevision: int64(result.HistoryRevision),
}
if value := strings.TrimSpace(result.ReplacementTurnID); value != "" {
response.ReplacementTurnId = &value
}
reason := agentEditRetryReasonCode(result, err)
if generated := generatedAgentEditRetryReasonCode(reason); generated != nil {
response.ReasonCode = generated
}
return response
}
func editRetryResultIsDurablyPending(result agentservice.EditRetryResult) bool {
if strings.TrimSpace(result.OperationID) == "" {
return false
}
switch result.State {
case agenthost.EditRetryStateRollingBack,
agenthost.EditRetryStateResendPending,
agenthost.EditRetryStateRecoveryRequired:
return true
default:
return false
}
}
func agentEditRetryReasonCode(
result agentservice.EditRetryResult,
err error,
) agenthost.EditRetryReasonCode {
if result.ReasonCode != "" {
return result.ReasonCode
}
switch {
case errors.Is(err, agenthost.ErrEditRetryHistoryConflict):
return agenthost.EditRetryReasonCodeHistoryRevisionConflict
case errors.Is(err, agenthost.ErrRuntimeOperationIdentityMismatch):
return agenthost.EditRetryReasonCodeOperationConflict
case errors.Is(err, agenthost.ErrRuntimeHistoryUnsupported):
return agenthost.EditRetryReasonCodeProviderUnsupported
case errors.Is(err, agenthost.ErrEditRetryRecoveryRequired):
return agenthost.EditRetryReasonCodeRecoveryRequired
default:
return ""
}
}
func generatedAgentEditRetryReasonCode(
reason agenthost.EditRetryReasonCode,
) *tuttigenerated.WorkspaceAgentEditRetryReasonCode {
if reason == "" {
return nil
}
value := tuttigenerated.WorkspaceAgentEditRetryReasonCode(reason)
return &value
}
func editRetryConflictProtocolError(err error) *apierrors.ProtocolError {
reason := agentEditRetryReasonCode(agentservice.EditRetryResult{}, err)
if reason == "" {
reason = agenthost.EditRetryReasonCodeOperationConflict
}
return apierrors.New(
apierrors.StatusConflict,
tuttigenerated.InvalidRequest,
string(reason),
)
}
func writeEditRetryWorkspaceAgentTurnError(
err error,
) tuttigenerated.EditRetryWorkspaceAgentTurnResponseObject {
switch {
case isEditRetryConflict(err):
return tuttigenerated.EditRetryWorkspaceAgentTurn409JSONResponse(
protocolErrorResponse(editRetryConflictProtocolError(err)),
)
case errors.Is(err, agenthost.ErrInvalidArgument):
return tuttigenerated.EditRetryWorkspaceAgentTurn400JSONResponse{
InvalidRequestErrorJSONResponse: invalidRequestError(apierrors.Classify(err)),
}
case errors.Is(err, agenthost.ErrSessionNotFound):
return tuttigenerated.EditRetryWorkspaceAgentTurn404JSONResponse{
WorkspaceNotFoundErrorJSONResponse: workspaceNotFoundError(apierrors.Classify(err)),
}
default:
return tuttigenerated.EditRetryWorkspaceAgentTurn502JSONResponse{
WorkspaceOperationErrorJSONResponse: workspaceOperationError(apierrors.WorkspaceOperationFailed()),
}
}
}
func writeRecoverWorkspaceAgentEditRetryError(
err error,
) tuttigenerated.RecoverWorkspaceAgentEditRetryResponseObject {
switch {
case isEditRetryConflict(err):
return tuttigenerated.RecoverWorkspaceAgentEditRetry409JSONResponse(
protocolErrorResponse(editRetryConflictProtocolError(err)),
)
case errors.Is(err, agenthost.ErrInvalidArgument):
return tuttigenerated.RecoverWorkspaceAgentEditRetry400JSONResponse{
InvalidRequestErrorJSONResponse: invalidRequestError(apierrors.Classify(err)),
}
case errors.Is(err, agenthost.ErrSessionNotFound):
return tuttigenerated.RecoverWorkspaceAgentEditRetry404JSONResponse{
WorkspaceNotFoundErrorJSONResponse: workspaceNotFoundError(apierrors.Classify(err)),
}
default:
return tuttigenerated.RecoverWorkspaceAgentEditRetry502JSONResponse{
WorkspaceOperationErrorJSONResponse: workspaceOperationError(apierrors.WorkspaceOperationFailed()),
}
}
}
func isEditRetryConflict(err error) bool {
return errors.Is(err, agenthost.ErrEditRetryNotEligible) ||
errors.Is(err, agenthost.ErrEditRetryHistoryConflict) ||
errors.Is(err, agenthost.ErrRuntimeHistoryUnsupported) ||
errors.Is(err, agenthost.ErrRuntimeOperationIdentityMismatch)
}