Files
tutti/services/tuttid/data/workspace/sqlite_tutti_mode_activations.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

439 lines
18 KiB
Go

package workspace
import (
"context"
"database/sql"
"errors"
"fmt"
"strings"
"time"
activationbiz "github.com/tutti-os/tutti/services/tuttid/biz/tuttimodeactivation"
)
type tuttiModeActivationRowQuerier interface {
QueryRowContext(context.Context, string, ...any) *sql.Row
}
func (s *SQLiteStore) GetTuttiModeActivation(ctx context.Context, workspaceID, agentSessionID string) (activationbiz.Activation, bool, error) {
if s == nil || s.writeDB == nil {
return activationbiz.Activation{}, false, errors.New("workspace database is not initialized")
}
return getTuttiModeActivation(ctx, s.writeDB, strings.TrimSpace(workspaceID), strings.TrimSpace(agentSessionID))
}
func (s *SQLiteStore) ListTuttiModeActivations(ctx context.Context, workspaceID string, agentSessionIDs []string) (map[string]activationbiz.Activation, error) {
result := make(map[string]activationbiz.Activation)
if s == nil || s.writeDB == nil {
return nil, errors.New("workspace database is not initialized")
}
workspaceID = strings.TrimSpace(workspaceID)
seen := make(map[string]struct{}, len(agentSessionIDs))
ids := make([]string, 0, len(agentSessionIDs))
for _, sessionID := range agentSessionIDs {
sessionID = strings.TrimSpace(sessionID)
if sessionID == "" {
continue
}
if _, ok := seen[sessionID]; !ok {
seen[sessionID] = struct{}{}
ids = append(ids, sessionID)
}
}
if workspaceID == "" || len(ids) == 0 {
return result, nil
}
placeholders := strings.TrimSuffix(strings.Repeat("?,", len(ids)), ",")
args := make([]any, 0, len(ids)+1)
args = append(args, workspaceID)
for _, id := range ids {
args = append(args, id)
}
rows, err := s.writeDB.QueryContext(ctx, `
SELECT a.agent_session_id, a.activation_id, a.created_at_unix_ms, a.updated_at_unix_ms,
r.revision_id, r.revision, r.state, r.source, r.orchestration_intensity, r.speed, r.created_at_unix_ms
FROM tutti_mode_activations a
JOIN tutti_mode_activation_revisions r
ON r.workspace_id = a.workspace_id
AND r.activation_id = a.activation_id
AND r.revision_id = a.current_revision_id
AND r.revision = a.current_revision
WHERE a.workspace_id = ? AND a.agent_session_id IN (`+placeholders+`)
`, args...)
if err != nil {
return nil, fmt.Errorf("list Tutti mode activations: %w", err)
}
defer rows.Close()
for rows.Next() {
var value activationbiz.Activation
var createdAt, updatedAt, revisionCreatedAt int64
if err := rows.Scan(
&value.AgentSessionID, &value.ID, &createdAt, &updatedAt,
&value.CurrentRevision.ID, &value.CurrentRevision.Revision,
&value.CurrentRevision.State, &value.CurrentRevision.Source,
&value.CurrentRevision.Effect, &value.CurrentRevision.Speed, &revisionCreatedAt,
); err != nil {
return nil, fmt.Errorf("scan Tutti mode activation list: %w", err)
}
value.WorkspaceID = workspaceID
value.CurrentRevision.ActivationID = value.ID
value.CurrentRevision.CreatedAt = time.UnixMilli(revisionCreatedAt).UTC()
value.CreatedAt = time.UnixMilli(createdAt).UTC()
value.UpdatedAt = time.UnixMilli(updatedAt).UTC()
normalized, err := activationbiz.NormalizeActivation(value)
if err != nil {
return nil, fmt.Errorf("normalize listed Tutti mode activation: %w", err)
}
result[value.AgentSessionID] = normalized
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate Tutti mode activations: %w", err)
}
return result, nil
}
func (s *SQLiteStore) SetTuttiModeActivation(ctx context.Context, input SetTuttiModeActivationInput) (activationbiz.Activation, bool, error) {
if s == nil || s.writeDB == nil {
return activationbiz.Activation{}, false, errors.New("workspace database is not initialized")
}
input.WorkspaceID = strings.TrimSpace(input.WorkspaceID)
input.AgentSessionID = strings.TrimSpace(input.AgentSessionID)
input.ActivationID = strings.TrimSpace(input.ActivationID)
input.RevisionID = strings.TrimSpace(input.RevisionID)
input.ChangedAt = input.ChangedAt.UTC()
if input.WorkspaceID == "" || input.AgentSessionID == "" || input.RevisionID == "" || input.ChangedAt.IsZero() {
return activationbiz.Activation{}, false, activationbiz.ErrInvalidActivation
}
effect := input.Effect
if effect == nil {
effect = input.OrchestrationIntensity
}
if effect != nil && !activationbiz.IsPreference(*effect) ||
input.Speed != nil && !activationbiz.IsPreference(*input.Speed) {
return activationbiz.Activation{}, false, fmt.Errorf("%w: effect and speed must be between 0 and 100", activationbiz.ErrInvalidActivation)
}
if _, err := activationbiz.NormalizeRevision(activationbiz.Revision{
ID: input.RevisionID, ActivationID: firstNonBlank(input.ActivationID, "pending"), Revision: 1,
State: input.State, Source: input.Source,
Effect: activationbiz.DefaultEffect, Speed: activationbiz.DefaultSpeed,
CreatedAt: input.ChangedAt,
}); err != nil {
return activationbiz.Activation{}, false, err
}
tx, err := s.writeDB.BeginTx(ctx, nil)
if err != nil {
return activationbiz.Activation{}, false, fmt.Errorf("begin set Tutti mode activation: %w", err)
}
defer func() { _ = tx.Rollback() }()
current, exists, err := getTuttiModeActivation(ctx, tx, input.WorkspaceID, input.AgentSessionID)
if err != nil {
return activationbiz.Activation{}, false, err
}
if input.ExpectedRevision != nil {
actual := int64(0)
if exists {
actual = current.CurrentRevision.Revision
}
if actual != *input.ExpectedRevision {
return activationbiz.Activation{}, false, ErrTuttiModeActivationRevisionConflict
}
}
effectiveEffect := activationbiz.DefaultEffect
effectiveSpeed := activationbiz.DefaultSpeed
if exists {
effectiveEffect = current.CurrentRevision.Effect
effectiveSpeed = current.CurrentRevision.Speed
}
if effect != nil {
effectiveEffect = *effect
}
if input.Speed != nil {
effectiveSpeed = *input.Speed
}
if exists && current.CurrentRevision.State == input.State && current.CurrentRevision.Source == input.Source &&
current.CurrentRevision.Effect == effectiveEffect &&
current.CurrentRevision.Speed == effectiveSpeed {
return current, false, nil
}
if !exists && input.State == activationbiz.StateInactive {
return activationbiz.Activation{}, false, nil
}
if exists && input.ChangedAt.Before(current.UpdatedAt) {
// Revision order is authoritative, but activation timestamps still form a
// non-decreasing projection. Clamp a wall-clock rollback so one valid
// transition cannot make the durable root unreadable on the next GET.
input.ChangedAt = current.UpdatedAt
}
if !exists {
if input.ActivationID == "" {
return activationbiz.Activation{}, false, activationbiz.ErrInvalidActivation
}
current = activationbiz.Activation{
ID: input.ActivationID, WorkspaceID: input.WorkspaceID, AgentSessionID: input.AgentSessionID,
CreatedAt: input.ChangedAt,
}
if _, err := tx.ExecContext(ctx, `
INSERT INTO tutti_mode_activations (
workspace_id, activation_id, agent_session_id, current_revision_id,
current_revision, created_at_unix_ms, updated_at_unix_ms
) VALUES (?, ?, ?, ?, 1, ?, ?)
`, input.WorkspaceID, input.ActivationID, input.AgentSessionID, input.RevisionID,
unixMs(input.ChangedAt), unixMs(input.ChangedAt)); err != nil {
return activationbiz.Activation{}, false, fmt.Errorf("insert Tutti mode activation: %w", err)
}
} else {
input.ActivationID = current.ID
}
nextRevision := current.CurrentRevision.Revision + 1
if !exists {
nextRevision = 1
}
revision := activationbiz.Revision{
ID: input.RevisionID, ActivationID: input.ActivationID, Revision: nextRevision,
State: input.State, Source: input.Source,
Effect: effectiveEffect, Speed: effectiveSpeed, CreatedAt: input.ChangedAt,
}
if _, err := activationbiz.NormalizeRevision(revision); err != nil {
return activationbiz.Activation{}, false, err
}
if _, err := tx.ExecContext(ctx, `
INSERT INTO tutti_mode_activation_revisions (
workspace_id, activation_id, revision_id, revision, state, source, orchestration_intensity, speed, created_at_unix_ms
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`, input.WorkspaceID, input.ActivationID, input.RevisionID, nextRevision,
string(input.State), string(input.Source), effectiveEffect, effectiveSpeed, unixMs(input.ChangedAt)); err != nil {
return activationbiz.Activation{}, false, fmt.Errorf("insert Tutti mode activation revision: %w", err)
}
if exists {
result, err := tx.ExecContext(ctx, `
UPDATE tutti_mode_activations
SET current_revision_id = ?, current_revision = ?, updated_at_unix_ms = ?
WHERE workspace_id = ? AND activation_id = ? AND current_revision = ?
`, input.RevisionID, nextRevision, unixMs(input.ChangedAt), input.WorkspaceID, input.ActivationID, current.CurrentRevision.Revision)
if err != nil {
return activationbiz.Activation{}, false, fmt.Errorf("advance Tutti mode activation revision: %w", err)
}
rows, err := result.RowsAffected()
if err != nil || rows != 1 {
return activationbiz.Activation{}, false, ErrTuttiModeActivationRevisionConflict
}
}
if err := tx.Commit(); err != nil {
return activationbiz.Activation{}, false, fmt.Errorf("commit Tutti mode activation: %w", err)
}
current.CurrentRevision = revision
current.UpdatedAt = input.ChangedAt
if current.CreatedAt.IsZero() {
current.CreatedAt = input.ChangedAt
}
return current, true, nil
}
func getTuttiModeActivation(ctx context.Context, q tuttiModeActivationRowQuerier, workspaceID, agentSessionID string) (activationbiz.Activation, bool, error) {
if workspaceID == "" || agentSessionID == "" {
return activationbiz.Activation{}, false, nil
}
var value activationbiz.Activation
var createdAt, updatedAt, revisionCreatedAt int64
err := q.QueryRowContext(ctx, `
SELECT a.activation_id, a.created_at_unix_ms, a.updated_at_unix_ms,
r.revision_id, r.revision, r.state, r.source, r.orchestration_intensity, r.speed, r.created_at_unix_ms
FROM tutti_mode_activations a
JOIN tutti_mode_activation_revisions r
ON r.workspace_id = a.workspace_id
AND r.activation_id = a.activation_id
AND r.revision_id = a.current_revision_id
AND r.revision = a.current_revision
WHERE a.workspace_id = ? AND a.agent_session_id = ?
`, workspaceID, agentSessionID).Scan(
&value.ID, &createdAt, &updatedAt,
&value.CurrentRevision.ID, &value.CurrentRevision.Revision,
&value.CurrentRevision.State, &value.CurrentRevision.Source,
&value.CurrentRevision.Effect, &value.CurrentRevision.Speed, &revisionCreatedAt,
)
if errors.Is(err, sql.ErrNoRows) {
return activationbiz.Activation{}, false, nil
}
if err != nil {
return activationbiz.Activation{}, false, fmt.Errorf("get Tutti mode activation: %w", err)
}
value.WorkspaceID = workspaceID
value.AgentSessionID = agentSessionID
value.CurrentRevision.ActivationID = value.ID
value.CurrentRevision.CreatedAt = time.UnixMilli(revisionCreatedAt).UTC()
value.CreatedAt = time.UnixMilli(createdAt).UTC()
value.UpdatedAt = time.UnixMilli(updatedAt).UTC()
normalized, err := activationbiz.NormalizeActivation(value)
if err != nil {
return activationbiz.Activation{}, false, fmt.Errorf("normalize stored Tutti mode activation: %w", err)
}
return normalized, true, nil
}
func (s *SQLiteStore) GetTuttiModeTurnSnapshot(ctx context.Context, workspaceID, agentSessionID, turnID string) (activationbiz.TurnSnapshot, bool, error) {
if s == nil || s.writeDB == nil {
return activationbiz.TurnSnapshot{}, false, errors.New("workspace database is not initialized")
}
var value activationbiz.TurnSnapshot
err := s.writeDB.QueryRowContext(ctx, `
SELECT activation_id, revision_id, revision, state, source, orchestration_intensity, speed
FROM tutti_mode_turn_snapshots
WHERE workspace_id = ? AND agent_session_id = ? AND turn_id = ?
`, strings.TrimSpace(workspaceID), strings.TrimSpace(agentSessionID), strings.TrimSpace(turnID)).Scan(
&value.ActivationID, &value.RevisionID, &value.Revision, &value.State, &value.Source,
&value.Effect, &value.Speed,
)
if errors.Is(err, sql.ErrNoRows) {
return activationbiz.TurnSnapshot{}, false, nil
}
if err != nil {
return activationbiz.TurnSnapshot{}, false, fmt.Errorf("get Tutti mode turn snapshot: %w", err)
}
value, err = activationbiz.NormalizeTurnSnapshot(value)
return value, err == nil, err
}
func (s *SQLiteStore) PutTuttiModeTurnSnapshot(ctx context.Context, workspaceID, agentSessionID, turnID string, snapshot activationbiz.TurnSnapshot, createdAt time.Time) (activationbiz.TurnSnapshot, bool, error) {
if s == nil || s.writeDB == nil {
return activationbiz.TurnSnapshot{}, false, errors.New("workspace database is not initialized")
}
workspaceID = strings.TrimSpace(workspaceID)
agentSessionID = strings.TrimSpace(agentSessionID)
turnID = strings.TrimSpace(turnID)
createdAt = createdAt.UTC()
if workspaceID == "" || agentSessionID == "" || turnID == "" || createdAt.IsZero() {
return activationbiz.TurnSnapshot{}, false, activationbiz.ErrInvalidActivation
}
normalized, err := activationbiz.NormalizeTurnSnapshot(snapshot)
if err != nil {
return activationbiz.TurnSnapshot{}, false, err
}
existing, ok, err := s.GetTuttiModeTurnSnapshot(ctx, workspaceID, agentSessionID, turnID)
if err != nil || ok {
return existing, false, err
}
_, err = s.writeDB.ExecContext(ctx, `
INSERT INTO tutti_mode_turn_snapshots (
workspace_id, agent_session_id, turn_id, activation_id, revision_id,
revision, state, source, orchestration_intensity, speed, created_at_unix_ms, dispatch_state
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'prepared')
`, workspaceID, agentSessionID, turnID, normalized.ActivationID, normalized.RevisionID,
normalized.Revision, string(normalized.State), string(normalized.Source),
normalized.Effect, normalized.Speed, unixMs(createdAt))
if err != nil {
if existing, ok, readErr := s.GetTuttiModeTurnSnapshot(ctx, workspaceID, agentSessionID, turnID); readErr == nil && ok {
return existing, false, nil
}
return activationbiz.TurnSnapshot{}, false, fmt.Errorf("insert Tutti mode turn snapshot: %w", err)
}
return normalized, true, nil
}
func (s *SQLiteStore) AcceptTuttiModeTurnSnapshot(ctx context.Context, workspaceID, agentSessionID, turnID string, acceptedAt time.Time) (bool, error) {
if s == nil || s.writeDB == nil {
return false, errors.New("workspace database is not initialized")
}
workspaceID = strings.TrimSpace(workspaceID)
agentSessionID = strings.TrimSpace(agentSessionID)
turnID = strings.TrimSpace(turnID)
acceptedAt = acceptedAt.UTC()
if workspaceID == "" || agentSessionID == "" || turnID == "" || acceptedAt.IsZero() {
return false, activationbiz.ErrInvalidActivation
}
result, err := s.writeDB.ExecContext(ctx, `
UPDATE tutti_mode_turn_snapshots
SET dispatch_state = 'accepted', accepted_at_unix_ms = ?
WHERE workspace_id = ? AND agent_session_id = ? AND turn_id = ? AND dispatch_state = 'prepared'
`, unixMs(acceptedAt), workspaceID, agentSessionID, turnID)
if err != nil {
return false, fmt.Errorf("accept Tutti mode turn snapshot: %w", err)
}
rows, err := result.RowsAffected()
return rows > 0, err
}
func (s *SQLiteStore) IsTuttiModeTurnSnapshotAccepted(ctx context.Context, workspaceID, agentSessionID, turnID string) (bool, error) {
if s == nil || s.writeDB == nil {
return false, errors.New("workspace database is not initialized")
}
var dispatchState string
err := s.writeDB.QueryRowContext(ctx, `
SELECT dispatch_state
FROM tutti_mode_turn_snapshots
WHERE workspace_id = ? AND agent_session_id = ? AND turn_id = ?
`, strings.TrimSpace(workspaceID), strings.TrimSpace(agentSessionID), strings.TrimSpace(turnID)).Scan(&dispatchState)
if errors.Is(err, sql.ErrNoRows) {
return false, nil
}
if err != nil {
return false, fmt.Errorf("get Tutti mode turn snapshot dispatch state: %w", err)
}
return dispatchState == "accepted", nil
}
func (s *SQLiteStore) AbandonTuttiModeTurnSnapshot(ctx context.Context, workspaceID, agentSessionID, turnID string, snapshot activationbiz.TurnSnapshot) (bool, error) {
if s == nil || s.writeDB == nil {
return false, errors.New("workspace database is not initialized")
}
workspaceID = strings.TrimSpace(workspaceID)
agentSessionID = strings.TrimSpace(agentSessionID)
turnID = strings.TrimSpace(turnID)
normalized, err := activationbiz.NormalizeTurnSnapshot(snapshot)
if err != nil || workspaceID == "" || agentSessionID == "" || turnID == "" {
return false, activationbiz.ErrInvalidActivation
}
result, err := s.writeDB.ExecContext(ctx, `
DELETE FROM tutti_mode_turn_snapshots
WHERE workspace_id = ? AND agent_session_id = ? AND turn_id = ?
AND dispatch_state = 'prepared'
AND activation_id = ? AND revision_id = ? AND revision = ? AND state = ? AND source = ?
AND orchestration_intensity = ? AND speed = ?
`, workspaceID, agentSessionID, turnID, normalized.ActivationID, normalized.RevisionID,
normalized.Revision, string(normalized.State), string(normalized.Source),
normalized.Effect, normalized.Speed)
if err != nil {
return false, fmt.Errorf("abandon Tutti mode turn snapshot: %w", err)
}
rows, err := result.RowsAffected()
return rows > 0, err
}
func (s *SQLiteStore) DeleteTuttiModeActivationSessionState(ctx context.Context, workspaceID, agentSessionID string) error {
if s == nil || s.writeDB == nil {
return errors.New("workspace database is not initialized")
}
workspaceID = strings.TrimSpace(workspaceID)
agentSessionID = strings.TrimSpace(agentSessionID)
if workspaceID == "" || agentSessionID == "" {
return activationbiz.ErrInvalidActivation
}
tx, err := s.writeDB.BeginTx(ctx, nil)
if err != nil {
return fmt.Errorf("begin delete Tutti mode session state: %w", err)
}
defer func() { _ = tx.Rollback() }()
if _, err := tx.ExecContext(ctx, `DELETE FROM tutti_mode_turn_snapshots WHERE workspace_id = ? AND agent_session_id = ?`, workspaceID, agentSessionID); err != nil {
return fmt.Errorf("delete Tutti mode turn snapshots: %w", err)
}
if _, err := tx.ExecContext(ctx, `DELETE FROM tutti_mode_activations WHERE workspace_id = ? AND agent_session_id = ?`, workspaceID, agentSessionID); err != nil {
return fmt.Errorf("delete Tutti mode activation: %w", err)
}
if err := tx.Commit(); err != nil {
return fmt.Errorf("commit delete Tutti mode session state: %w", err)
}
return nil
}
func firstNonBlank(values ...string) string {
for _, value := range values {
if value = strings.TrimSpace(value); value != "" {
return value
}
}
return ""
}