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
245 lines
8.7 KiB
Go
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)
|
|
}
|