Files
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

103 lines
3.4 KiB
Go

package storesqlite
import (
"context"
"database/sql"
"errors"
"fmt"
"strings"
)
var ErrTurnIdentityAnchorConflict = errors.New("workspace agent turn identity anchor conflicts with canonical state")
// bindTurnIdentityAnchorTx gives one canonical Turn the already-established
// external identity of another Turn in the same Session. The stored value is
// always flattened to an ultimate anchor so consumers need only one lookup.
// updatedAtUnixMS may be zero for migration repair that must preserve the
// historical lifecycle timestamp.
func bindTurnIdentityAnchorTx(
ctx context.Context,
tx *sql.Tx,
workspaceID, agentSessionID, turnID, requestedAnchorTurnID string,
updatedAtUnixMS int64,
) (Turn, bool, error) {
workspaceID = strings.TrimSpace(workspaceID)
agentSessionID = strings.TrimSpace(agentSessionID)
turnID = strings.TrimSpace(turnID)
requestedAnchorTurnID = strings.TrimSpace(requestedAnchorTurnID)
if tx == nil || workspaceID == "" || agentSessionID == "" || turnID == "" || requestedAnchorTurnID == "" || turnID == requestedAnchorTurnID {
return Turn{}, false, ErrTurnIdentityAnchorConflict
}
turn, found, err := getAgentTurnTx(ctx, tx, workspaceID, agentSessionID, turnID)
if err != nil {
return Turn{}, false, err
}
if !found {
return Turn{}, false, ErrTurnIdentityAnchorConflict
}
requestedAnchor, found, err := getAgentTurnTx(ctx, tx, workspaceID, agentSessionID, requestedAnchorTurnID)
if err != nil {
return Turn{}, false, err
}
if !found {
return Turn{}, false, ErrTurnIdentityAnchorConflict
}
anchorTurnID := requestedAnchor.TurnID
if inherited := strings.TrimSpace(requestedAnchor.IdentityAnchorTurnID); inherited != "" {
anchorTurnID = inherited
ultimate, ultimateFound, ultimateErr := getAgentTurnTx(ctx, tx, workspaceID, agentSessionID, anchorTurnID)
if ultimateErr != nil {
return Turn{}, false, ultimateErr
}
if !ultimateFound || strings.TrimSpace(ultimate.IdentityAnchorTurnID) != "" {
return Turn{}, false, ErrTurnIdentityAnchorConflict
}
}
if anchorTurnID == turnID {
return Turn{}, false, ErrTurnIdentityAnchorConflict
}
if current := strings.TrimSpace(turn.IdentityAnchorTurnID); current != "" {
if current != anchorTurnID {
return Turn{}, false, ErrTurnIdentityAnchorConflict
}
return turn, false, nil
}
query := `
UPDATE workspace_agent_turns
SET identity_anchor_turn_id = ?
WHERE workspace_id = ? AND agent_session_id = ? AND turn_id = ?
AND identity_anchor_turn_id IS NULL`
args := []any{anchorTurnID, workspaceID, agentSessionID, turnID}
if updatedAtUnixMS > 0 {
query = `
UPDATE workspace_agent_turns
SET identity_anchor_turn_id = ?, updated_at_unix_ms = MAX(updated_at_unix_ms, ?)
WHERE workspace_id = ? AND agent_session_id = ? AND turn_id = ?
AND identity_anchor_turn_id IS NULL`
args = []any{anchorTurnID, updatedAtUnixMS, workspaceID, agentSessionID, turnID}
}
result, err := tx.ExecContext(ctx, query, args...)
if err != nil {
return Turn{}, false, fmt.Errorf("bind workspace agent turn identity anchor: %w", err)
}
changed, err := rowsWereAffected(result, "bind workspace agent turn identity anchor")
if err != nil {
return Turn{}, false, err
}
if !changed {
return Turn{}, false, ErrTurnIdentityAnchorConflict
}
stored, found, err := getAgentTurnTx(ctx, tx, workspaceID, agentSessionID, turnID)
if err != nil {
return Turn{}, false, err
}
if !found {
return Turn{}, false, ErrTurnIdentityAnchorConflict
}
return stored, true, nil
}