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

316 lines
9.0 KiB
Go

package storesqlite
import (
"context"
"errors"
"fmt"
"strings"
"time"
)
// CompareAndSwapSessionRuntimeContext replaces provider-private runtime context
// only when it still equals the caller's observed value. Runtime rebind uses
// this fence so a prepared connection and its durable launch snapshot cannot
// silently diverge.
func (s *Store) CompareAndSwapSessionRuntimeContext(
ctx context.Context,
workspaceID string,
agentSessionID string,
expected map[string]any,
replacement map[string]any,
) (Session, bool, error) {
if s == nil || s.db == nil {
return Session{}, false, errors.New("workspace database is not initialized")
}
workspaceID = strings.TrimSpace(workspaceID)
agentSessionID = strings.TrimSpace(agentSessionID)
if workspaceID == "" || agentSessionID == "" {
return Session{}, false, nil
}
expectedJSON, err := marshalJSONMap(expected)
if err != nil {
return Session{}, false, err
}
replacementJSON, err := marshalJSONMap(replacement)
if err != nil {
return Session{}, false, err
}
now := unixMs(time.Now().UTC())
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
return Session{}, false, err
}
committed := false
defer func() {
if !committed {
_ = tx.Rollback()
}
}()
result, err := tx.ExecContext(ctx, `
UPDATE workspace_agent_sessions
SET internal_runtime_context_json = ?,
updated_at_unix_ms = CASE
WHEN ? > updated_at_unix_ms THEN ?
ELSE updated_at_unix_ms + 1
END
WHERE workspace_id = ? AND agent_session_id = ? AND deleted_at_unix_ms = 0
AND internal_runtime_context_json = ?
`, replacementJSON, now, now, workspaceID, agentSessionID, expectedJSON)
if err != nil {
return Session{}, false, fmt.Errorf("compare and swap workspace agent runtime context: %w", err)
}
updated, err := rowsWereAffected(result, "compare and swap workspace agent runtime context")
if err != nil {
return Session{}, false, err
}
if !updated {
if _, err := s.commitTransaction(ctx, tx, workspaceID, nil); err != nil {
return Session{}, false, err
}
committed = true
return Session{}, false, nil
}
delta, err := s.commitTransaction(ctx, tx, workspaceID, []TransactionMutation{
transactionMutation(workspaceID, agentSessionID, MutationEntitySession, agentSessionID, "upsert", now),
})
if err != nil {
return Session{}, false, err
}
committed = true
session, ok, err := s.GetSession(ctx, workspaceID, agentSessionID)
if err != nil {
return Session{}, false, err
}
session.CommitTransactionID = delta.TransactionID
session.CommitDelta = delta
return session, ok, nil
}
func (s *Store) UpdateSessionPinned(
ctx context.Context,
workspaceID string,
agentSessionID string,
pinned bool,
) (Session, bool, error) {
if s == nil || s.db == nil {
return Session{}, false, errors.New("workspace database is not initialized")
}
workspaceID = strings.TrimSpace(workspaceID)
agentSessionID = strings.TrimSpace(agentSessionID)
if workspaceID == "" || agentSessionID == "" {
return Session{}, false, nil
}
now := unixMs(time.Now().UTC())
pinnedAtUnixMS := int64(0)
if pinned {
pinnedAtUnixMS = now
}
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
return Session{}, false, err
}
committed := false
defer func() {
if !committed {
_ = tx.Rollback()
}
}()
result, err := tx.ExecContext(ctx, `
UPDATE workspace_agent_sessions
SET pinned_at_unix_ms = ?,
updated_at_unix_ms = CASE
WHEN ? > updated_at_unix_ms THEN ?
ELSE updated_at_unix_ms + 1
END
WHERE workspace_id = ? AND agent_session_id = ? AND deleted_at_unix_ms = 0
`, pinnedAtUnixMS, now, now, workspaceID, agentSessionID)
if err != nil {
return Session{}, false, fmt.Errorf("update workspace agent session pinned state: %w", err)
}
updated, err := rowsWereAffected(result, "update workspace agent session pinned state")
if err != nil {
return Session{}, false, err
}
if !updated {
if _, err := s.commitTransaction(ctx, tx, workspaceID, nil); err != nil {
return Session{}, false, err
}
committed = true
return Session{}, false, nil
}
delta, err := s.commitTransaction(ctx, tx, workspaceID, []TransactionMutation{
transactionMutation(workspaceID, agentSessionID, MutationEntitySession, agentSessionID, "upsert", now),
})
if err != nil {
return Session{}, false, err
}
committed = true
session, ok, err := s.GetSession(ctx, workspaceID, agentSessionID)
if err != nil {
return Session{}, false, err
}
session.CommitTransactionID = delta.TransactionID
session.CommitDelta = delta
return session, ok, nil
}
func (s *Store) UpdateSessionTitle(
ctx context.Context,
workspaceID string,
agentSessionID string,
title string,
) (Session, bool, error) {
if s == nil || s.db == nil {
return Session{}, false, errors.New("workspace database is not initialized")
}
workspaceID = strings.TrimSpace(workspaceID)
agentSessionID = strings.TrimSpace(agentSessionID)
title = strings.TrimSpace(title)
if workspaceID == "" || agentSessionID == "" {
return Session{}, false, nil
}
now := unixMs(time.Now().UTC())
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
return Session{}, false, err
}
committed := false
defer func() {
if !committed {
_ = tx.Rollback()
}
}()
result, err := tx.ExecContext(ctx, `
UPDATE workspace_agent_sessions
SET title = ?,
internal_runtime_context_json = json_set(
internal_runtime_context_json,
'$.tuttiInitialTitleEstablished',
json('true')
),
updated_at_unix_ms = CASE
WHEN ? > updated_at_unix_ms THEN ?
ELSE updated_at_unix_ms + 1
END
WHERE workspace_id = ? AND agent_session_id = ? AND deleted_at_unix_ms = 0
`, title, now, now, workspaceID, agentSessionID)
if err != nil {
return Session{}, false, fmt.Errorf("update workspace agent session title: %w", err)
}
updated, err := rowsWereAffected(result, "update workspace agent session title")
if err != nil {
return Session{}, false, err
}
if !updated {
if _, err := s.commitTransaction(ctx, tx, workspaceID, nil); err != nil {
return Session{}, false, err
}
committed = true
return Session{}, false, nil
}
delta, err := s.commitTransaction(ctx, tx, workspaceID, []TransactionMutation{
transactionMutation(workspaceID, agentSessionID, MutationEntitySession, agentSessionID, "upsert", now),
})
if err != nil {
return Session{}, false, err
}
committed = true
session, ok, err := s.GetSession(ctx, workspaceID, agentSessionID)
if err != nil {
return Session{}, false, err
}
session.CommitTransactionID = delta.TransactionID
session.CommitDelta = delta
return session, ok, nil
}
func (s *Store) UpdateSessionSettings(
ctx context.Context,
workspaceID string,
agentSessionID string,
model string,
settings map[string]any,
) (Session, bool, error) {
if s == nil || s.db == nil {
return Session{}, false, errors.New("workspace database is not initialized")
}
workspaceID = strings.TrimSpace(workspaceID)
agentSessionID = strings.TrimSpace(agentSessionID)
if workspaceID == "" || agentSessionID == "" {
return Session{}, false, nil
}
settingsJSON, err := marshalJSONMap(settings)
if err != nil {
return Session{}, false, err
}
now := unixMs(time.Now().UTC())
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
return Session{}, false, err
}
committed := false
defer func() {
if !committed {
_ = tx.Rollback()
}
}()
if err := requireSessionForkSourceWritableTx(
ctx,
tx,
workspaceID,
agentSessionID,
); err != nil {
return Session{}, false, err
}
result, err := tx.ExecContext(ctx, `
UPDATE workspace_agent_sessions
SET model = ?,
settings_json = ?,
session_metadata_json = CASE
WHEN trim(model) <> trim(?) THEN
CASE
WHEN json_array_length(json_extract(session_metadata_json, '$.usage.quotas')) > 0
THEN json_remove(session_metadata_json, '$.usage.contextWindow')
ELSE json_remove(session_metadata_json, '$.usage')
END
ELSE session_metadata_json
END,
updated_at_unix_ms = CASE
WHEN ? > updated_at_unix_ms THEN ?
ELSE updated_at_unix_ms + 1
END
WHERE workspace_id = ? AND agent_session_id = ? AND deleted_at_unix_ms = 0
`, strings.TrimSpace(model), settingsJSON, strings.TrimSpace(model), now, now, workspaceID, agentSessionID)
if err != nil {
return Session{}, false, fmt.Errorf("update workspace agent session settings: %w", err)
}
updated, err := rowsWereAffected(result, "update workspace agent session settings")
if err != nil {
return Session{}, false, err
}
if !updated {
if _, err := s.commitTransaction(ctx, tx, workspaceID, nil); err != nil {
return Session{}, false, err
}
committed = true
return Session{}, false, nil
}
delta, err := s.commitTransaction(ctx, tx, workspaceID, []TransactionMutation{
transactionMutation(workspaceID, agentSessionID, MutationEntitySession, agentSessionID, "upsert", now),
})
if err != nil {
return Session{}, false, err
}
committed = true
session, ok, err := s.GetSession(ctx, workspaceID, agentSessionID)
if err != nil {
return Session{}, false, err
}
session.CommitTransactionID = delta.TransactionID
session.CommitDelta = delta
return session, ok, nil
}