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

754 lines
26 KiB
Go

package workspace
import (
"context"
"database/sql"
"encoding/json"
"errors"
"fmt"
"strings"
"time"
workspaceissues "github.com/tutti-os/tutti/packages/workspace/issues"
executionbiz "github.com/tutti-os/tutti/services/tuttid/biz/tuttimodeexecution"
)
func (s *SQLiteStore) AdmitTuttiModeSchedule(
ctx context.Context,
admission executionbiz.ScheduleAdmission,
) (executionbiz.ScheduleResult, error) {
if err := s.ensureIssueDatabase(); err != nil {
return executionbiz.ScheduleResult{}, err
}
tx, err := s.writeDB.BeginTx(ctx, nil)
if err != nil {
return executionbiz.ScheduleResult{}, fmt.Errorf("begin Tutti mode schedule admission: %w", err)
}
defer func() { _ = tx.Rollback() }()
replayed, found, err := getTuttiModeScheduleReplay(ctx, tx, admission)
if err != nil {
return executionbiz.ScheduleResult{}, err
}
if found {
return replayed, nil
}
executionID, err := validateTuttiModeScheduleFence(ctx, tx, admission)
if err != nil {
return executionbiz.ScheduleResult{}, err
}
if err := acceptTuttiModeSettlementSubjectForSchedule(
ctx, tx, executionID, admission,
); err != nil {
return executionbiz.ScheduleResult{}, err
}
tasks, issue, err := validateTuttiModeScheduleTasks(ctx, tx, admission)
if err != nil {
return executionbiz.ScheduleResult{}, err
}
if err := validateTuttiModeScheduleCapacity(ctx, tx, admission.WorkspaceID, issue, len(tasks)); err != nil {
return executionbiz.ScheduleResult{}, err
}
if err := insertTuttiModeScheduledRuns(ctx, tx, admission, tasks); err != nil {
return executionbiz.ScheduleResult{}, err
}
if err := projectWorkspaceIssueTasks(ctx, tx, admission.WorkspaceID, admission.IssueID, admission.Now); err != nil {
return executionbiz.ScheduleResult{}, err
}
if err := resolveTuttiModeScheduleCheckpoint(ctx, tx, executionID, admission); err != nil {
return executionbiz.ScheduleResult{}, err
}
result := executionbiz.ScheduleResult{
ExecutionID: executionID,
CheckpointID: admission.CheckpointID,
GraphRevision: admission.ExpectedGraphRevision,
RunIDs: make([]string, 0, len(admission.Runs)),
}
for _, run := range admission.Runs {
result.RunIDs = append(result.RunIDs, run.RunID)
}
resultJSON, err := json.Marshal(result)
if err != nil {
return executionbiz.ScheduleResult{}, fmt.Errorf("encode Tutti mode schedule result: %w", err)
}
_, err = tx.ExecContext(ctx, `
INSERT INTO workspace_tutti_execution_mutations (
workspace_id, execution_id, issue_id, source_session_id, kind, request_id,
input_sha256, checkpoint_id, expected_graph_revision, result_graph_revision,
result_json, created_at_unix_ms
) VALUES (?, ?, ?, ?, 'schedule', ?, ?, ?, ?, ?, ?, ?)
`, admission.WorkspaceID, executionID, admission.IssueID, admission.SourceSessionID,
admission.RequestID, admission.InputSHA256, admission.CheckpointID,
admission.ExpectedGraphRevision, admission.ExpectedGraphRevision,
string(resultJSON), unixMs(admission.Now))
if err != nil {
if isSQLiteUniqueConstraintError(err) {
return executionbiz.ScheduleResult{}, executionbiz.ErrScheduleMutationConflict
}
return executionbiz.ScheduleResult{}, fmt.Errorf("record Tutti mode schedule mutation: %w", err)
}
if err := tx.Commit(); err != nil {
return executionbiz.ScheduleResult{}, fmt.Errorf("commit Tutti mode schedule admission: %w", err)
}
return result, nil
}
func acceptTuttiModeSettlementSubjectForSchedule(
ctx context.Context,
tx *sql.Tx,
executionID string,
admission executionbiz.ScheduleAdmission,
) error {
var kind, subjectTaskID, subjectRunID string
err := tx.QueryRowContext(ctx, `
SELECT kind, subject_task_id, subject_run_id
FROM workspace_tutti_execution_checkpoints
WHERE workspace_id = ? AND execution_id = ? AND checkpoint_id = ?
`, admission.WorkspaceID, executionID, admission.CheckpointID).
Scan(&kind, &subjectTaskID, &subjectRunID)
if err != nil {
return fmt.Errorf("get scheduled settlement subject: %w", err)
}
if kind != string(executionbiz.CheckpointKindTaskSettled) {
return nil
}
result, err := tx.ExecContext(ctx, `
UPDATE workspace_issue_tasks
SET status = 'completed', acceptance_state = 'user_accepted',
updated_at_unix_ms = ?
WHERE workspace_id = ? AND issue_id = ? AND task_id = ?
AND status = 'pending_acceptance'
AND EXISTS (
SELECT 1 FROM workspace_issue_runs
WHERE workspace_id = ? AND issue_id = ? AND task_id = ?
AND run_id = ? AND status = 'completed'
)
`, unixMs(admission.Now), admission.WorkspaceID, admission.IssueID, subjectTaskID,
admission.WorkspaceID, admission.IssueID, subjectTaskID, subjectRunID)
if err != nil {
return fmt.Errorf("accept settlement subject for schedule: %w", err)
}
return requireRowsAffected(
result, executionbiz.ErrScheduleRejected,
"accept settlement subject for schedule",
)
}
func getTuttiModeScheduleReplay(
ctx context.Context,
tx *sql.Tx,
admission executionbiz.ScheduleAdmission,
) (executionbiz.ScheduleResult, bool, error) {
var inputSHA256, resultJSON string
err := tx.QueryRowContext(ctx, `
SELECT input_sha256, result_json
FROM workspace_tutti_execution_mutations
WHERE workspace_id = ? AND source_session_id = ? AND kind = 'schedule'
AND issue_id = ? AND request_id = ?
`, admission.WorkspaceID, admission.SourceSessionID, admission.IssueID, admission.RequestID).
Scan(&inputSHA256, &resultJSON)
if errors.Is(err, sql.ErrNoRows) {
return executionbiz.ScheduleResult{}, false, nil
}
if err != nil {
return executionbiz.ScheduleResult{}, false, fmt.Errorf("get Tutti mode schedule replay: %w", err)
}
if inputSHA256 != admission.InputSHA256 {
return executionbiz.ScheduleResult{}, false, executionbiz.ErrScheduleMutationConflict
}
var result executionbiz.ScheduleResult
if err := json.Unmarshal([]byte(resultJSON), &result); err != nil {
return executionbiz.ScheduleResult{}, false, fmt.Errorf("decode Tutti mode schedule replay: %w", err)
}
result.Replayed = true
return result, true, nil
}
func validateTuttiModeScheduleFence(
ctx context.Context,
tx *sql.Tx,
admission executionbiz.ScheduleAdmission,
) (string, error) {
var executionID, sourceSessionID, status string
var graphRevision int64
err := tx.QueryRowContext(ctx, `
SELECT execution_id, source_session_id, status, graph_revision
FROM workspace_tutti_executions
WHERE workspace_id = ? AND issue_id = ?
`, admission.WorkspaceID, admission.IssueID).
Scan(&executionID, &sourceSessionID, &status, &graphRevision)
if errors.Is(err, sql.ErrNoRows) {
return "", executionbiz.ErrExecutionNotFound
}
if err != nil {
return "", fmt.Errorf("get Tutti mode execution schedule fence: %w", err)
}
if sourceSessionID != admission.SourceSessionID {
return "", executionbiz.Reject(
executionbiz.ErrScheduleRejected, executionbiz.RejectionWrongSourceSession, "",
)
}
if graphRevision != admission.ExpectedGraphRevision {
return "", executionbiz.Reject(
executionbiz.ErrScheduleRejected, executionbiz.RejectionStaleGraphRevision, "",
)
}
if status != string(executionbiz.StatusAwaitingSchedule) &&
status != string(executionbiz.StatusAwaitingMain) {
return "", executionbiz.Reject(
executionbiz.ErrScheduleRejected, executionbiz.RejectionInactiveExecution, "",
)
}
var checkpointStatus string
var checkpointRevision int64
err = tx.QueryRowContext(ctx, `
SELECT status, graph_revision
FROM workspace_tutti_execution_checkpoints
WHERE workspace_id = ? AND execution_id = ? AND checkpoint_id = ?
`, admission.WorkspaceID, executionID, admission.CheckpointID).
Scan(&checkpointStatus, &checkpointRevision)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return "", executionbiz.Reject(
executionbiz.ErrScheduleRejected,
executionbiz.RejectionInactiveCheckpoint,
"",
)
}
return "", fmt.Errorf("get Tutti mode schedule checkpoint fence: %w", err)
}
if checkpointStatus != string(executionbiz.CheckpointStatusActive) {
return "", executionbiz.Reject(
executionbiz.ErrScheduleRejected, executionbiz.RejectionInactiveCheckpoint, "",
)
}
if checkpointRevision != admission.ExpectedGraphRevision {
return "", executionbiz.Reject(
executionbiz.ErrScheduleRejected, executionbiz.RejectionStaleGraphRevision, "",
)
}
return executionID, nil
}
func validateTuttiModeScheduleTasks(
ctx context.Context,
tx *sql.Tx,
admission executionbiz.ScheduleAdmission,
) (map[string]workspaceissues.Task, workspaceissues.Issue, error) {
row := tx.QueryRowContext(ctx, fmt.Sprintf(`
SELECT %s
FROM workspace_issues
WHERE workspace_id = ? AND issue_id = ?
`, issueSelectColumns), admission.WorkspaceID, admission.IssueID)
issue, err := scanWorkspaceIssue(row)
if errors.Is(err, sql.ErrNoRows) {
return nil, workspaceissues.Issue{}, workspaceissues.ErrIssueNotFound
}
if err != nil {
return nil, workspaceissues.Issue{}, fmt.Errorf("get scheduled Tutti mode Issue: %w", err)
}
if issue.PlanningSource != workspaceissues.PlanningSourceTuttiModePlan {
return nil, workspaceissues.Issue{}, executionbiz.Reject(
executionbiz.ErrScheduleRejected, executionbiz.RejectionInactiveExecution, "",
)
}
if issue.SourceSessionID != admission.SourceSessionID {
return nil, workspaceissues.Issue{}, executionbiz.Reject(
executionbiz.ErrScheduleRejected, executionbiz.RejectionWrongSourceSession, "",
)
}
if issue.DispatchPaused {
return nil, workspaceissues.Issue{}, executionbiz.Reject(
executionbiz.ErrScheduleRejected, executionbiz.RejectionDispatchPaused, "",
)
}
if issue.Budget.Status != workspaceissues.BudgetStatusActive {
return nil, workspaceissues.Issue{}, executionbiz.Reject(
executionbiz.ErrScheduleRejected, executionbiz.RejectionBudgetUnavailable, "",
)
}
rows, err := tx.QueryContext(ctx, fmt.Sprintf(`
SELECT %s
FROM workspace_issue_tasks
WHERE workspace_id = ? AND issue_id = ? AND superseded_at_unix_ms = 0
ORDER BY sort_index ASC, id ASC
`, taskSelectColumns), admission.WorkspaceID, admission.IssueID)
if err != nil {
return nil, workspaceissues.Issue{}, fmt.Errorf("list tasks for Tutti mode schedule: %w", err)
}
allTasks, err := scanWorkspaceIssueTasks(rows)
closeErr := rows.Close()
if err != nil {
return nil, workspaceissues.Issue{}, err
}
if closeErr != nil {
return nil, workspaceissues.Issue{}, fmt.Errorf("close tasks for Tutti mode schedule: %w", closeErr)
}
byID := make(map[string]workspaceissues.Task, len(allTasks))
for _, task := range allTasks {
byID[task.TaskID] = task
}
selected := make(map[string]workspaceissues.Task, len(admission.Runs))
for _, run := range admission.Runs {
if _, duplicate := selected[run.TaskID]; duplicate {
return nil, workspaceissues.Issue{}, executionbiz.Reject(
executionbiz.ErrScheduleRejected, executionbiz.RejectionDuplicateTask, run.TaskID,
)
}
task, ok := byID[run.TaskID]
if !ok {
return nil, workspaceissues.Issue{}, executionbiz.Reject(
executionbiz.ErrScheduleRejected, executionbiz.RejectionTaskNotFound, run.TaskID,
)
}
if blocker := workspaceissues.IssueTaskRunBlocker(task, byID); blocker != "" {
return nil, workspaceissues.Issue{}, executionbiz.Reject(
executionbiz.ErrScheduleRejected,
scheduleRejectionReasonForBlocker(blocker),
run.TaskID,
)
}
selected[run.TaskID] = task
}
return selected, issue, nil
}
func validateTuttiModeScheduleCapacity(
ctx context.Context,
tx *sql.Tx,
workspaceID string,
issue workspaceissues.Issue,
requested int,
) error {
var running, activeIssueRuns int
if err := tx.QueryRowContext(ctx, `
SELECT COUNT(*), COALESCE(SUM(CASE WHEN issue_id = ? THEN 1 ELSE 0 END), 0)
FROM workspace_issue_runs
WHERE workspace_id = ? AND status = 'running' AND TRIM(agent_session_id) <> ''
`, issue.IssueID, workspaceID).Scan(&running, &activeIssueRuns); err != nil {
return fmt.Errorf("count running Issue Runs for Tutti mode schedule: %w", err)
}
if requested < 1 ||
requested > workspaceissues.IssueAutomaticRunAdmissionSlots(issue, running, activeIssueRuns) {
return executionbiz.Reject(
executionbiz.ErrScheduleRejected, executionbiz.RejectionCapacityExhausted, "",
)
}
return nil
}
func scheduleRejectionReasonForBlocker(
blocker workspaceissues.TaskRunBlocker,
) executionbiz.RejectionReason {
switch blocker {
case workspaceissues.TaskRunBlockerSuperseded:
return executionbiz.RejectionTaskSuperseded
case workspaceissues.TaskRunBlockerNotStarted:
return executionbiz.RejectionTaskNotStarted
case workspaceissues.TaskRunBlockerMissingAgentTarget:
return executionbiz.RejectionMissingAgentTarget
default:
return executionbiz.RejectionDependencyUnsatisfied
}
}
func insertTuttiModeScheduledRuns(
ctx context.Context,
tx *sql.Tx,
admission executionbiz.ScheduleAdmission,
tasks map[string]workspaceissues.Task,
) error {
for _, run := range admission.Runs {
task := tasks[run.TaskID]
_, err := tx.ExecContext(ctx, `
INSERT INTO workspace_issue_runs (
run_id, task_id, issue_id, workspace_id, requester_user_id, agent_user_id,
agent_target_id, agent_session_id, agent_provider, model_plan_id, model,
reasoning_intensity, status, input_tokens, output_tokens, cache_read_tokens,
cache_write_tokens, summary, error_message, output_dir, execution_directory,
created_at_unix_ms, started_at_unix_ms, completed_at_unix_ms, updated_at_unix_ms
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, run.RunID, run.TaskID, run.IssueID, run.WorkspaceID, run.RequesterUserID,
run.AgentUserID, run.AgentTargetID, run.AgentSessionID, run.AgentProvider,
run.ModelPlanID, run.Model, run.ReasoningIntensity, string(run.Status),
run.Usage.InputTokens, run.Usage.OutputTokens, run.Usage.CacheReadTokens,
run.Usage.CacheWriteTokens, run.Summary, run.ErrorMessage, run.OutputDir,
run.ExecutionDirectory, run.CreatedAtUnixMS, run.StartedAtUnixMS,
run.CompletedAtUnixMS, run.UpdatedAtUnixMS)
if err != nil {
if isSQLiteUniqueConstraintError(err) {
return executionbiz.ErrScheduleRejected
}
return fmt.Errorf("create scheduled Tutti mode Run: %w", err)
}
result, err := tx.ExecContext(ctx, `
UPDATE workspace_issue_tasks
SET status = 'running', acceptance_state = 'agent_claimed',
acceptance_summary = '', latest_run_id = ?, updated_at_unix_ms = ?
WHERE workspace_id = ? AND issue_id = ? AND task_id = ? AND status = 'not_started'
`, run.RunID, run.UpdatedAtUnixMS, admission.WorkspaceID, admission.IssueID, run.TaskID)
if err != nil {
return fmt.Errorf("claim scheduled Tutti mode task: %w", err)
}
if err := requireRowsAffected(result, executionbiz.ErrScheduleRejected, "claim scheduled Tutti mode task"); err != nil {
return err
}
_, err = tx.ExecContext(ctx, `
INSERT INTO workspace_issue_run_launch_intents (
workspace_id, issue_id, task_id, run_id, launch_intent_id, client_submit_id,
status, created_at_unix_ms, updated_at_unix_ms
) VALUES (?, ?, ?, ?, ?, ?, 'prepared', ?, ?)
`, admission.WorkspaceID, admission.IssueID, task.TaskID, run.RunID,
"launch-intent:"+run.RunID, workspaceissues.IssueRunClientSubmitID(run.RunID),
run.CreatedAtUnixMS, run.UpdatedAtUnixMS)
if err != nil {
return fmt.Errorf("create scheduled Tutti mode launch intent: %w", err)
}
}
return nil
}
func projectWorkspaceIssueTasks(
ctx context.Context,
tx *sql.Tx,
workspaceID string,
issueID string,
now time.Time,
) error {
var all, notStarted, running, pending, completed, failed, canceled int
err := tx.QueryRowContext(ctx, `
SELECT COUNT(*),
SUM(CASE WHEN status = 'not_started' THEN 1 ELSE 0 END),
SUM(CASE WHEN status = 'running' THEN 1 ELSE 0 END),
SUM(CASE WHEN status = 'pending_acceptance' THEN 1 ELSE 0 END),
SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END),
SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END),
SUM(CASE WHEN status = 'canceled' THEN 1 ELSE 0 END)
FROM workspace_issue_tasks
WHERE workspace_id = ? AND issue_id = ? AND superseded_at_unix_ms = 0
`, workspaceID, issueID).Scan(&all, &notStarted, &running, &pending, &completed, &failed, &canceled)
if err != nil {
return fmt.Errorf("project scheduled Tutti mode Issue: %w", err)
}
status := workspaceissues.ProjectIssueStatus(workspaceissues.StatusCounts{
All: all, NotStarted: notStarted, Running: running,
PendingAcceptance: pending, Completed: completed, Failed: failed, Canceled: canceled,
})
result, err := tx.ExecContext(ctx, `
UPDATE workspace_issues
SET status = ?, task_count = ?, not_started_count = ?, running_count = ?,
pending_acceptance_count = ?, completed_count = ?, failed_count = ?,
canceled_count = ?, updated_at_unix_ms = ?
WHERE workspace_id = ? AND issue_id = ?
`, string(status), all, notStarted, running, pending, completed, failed, canceled,
unixMs(now), workspaceID, issueID)
if err != nil {
return fmt.Errorf("update scheduled Tutti mode Issue projection: %w", err)
}
return requireRowsAffected(result, workspaceissues.ErrIssueNotFound, "update scheduled Tutti mode Issue projection")
}
func resolveTuttiModeScheduleCheckpoint(
ctx context.Context,
tx *sql.Tx,
executionID string,
admission executionbiz.ScheduleAdmission,
) error {
result, err := tx.ExecContext(ctx, `
UPDATE workspace_tutti_execution_checkpoints
SET status = 'resolved', updated_at_unix_ms = ?, resolved_at_unix_ms = ?
WHERE workspace_id = ? AND execution_id = ? AND checkpoint_id = ?
AND status = 'active' AND graph_revision = ?
`, unixMs(admission.Now), unixMs(admission.Now), admission.WorkspaceID,
executionID, admission.CheckpointID, admission.ExpectedGraphRevision)
if err != nil {
return fmt.Errorf("resolve Tutti mode schedule checkpoint: %w", err)
}
if err := requireRowsAffected(result, executionbiz.ErrScheduleRejected, "resolve Tutti mode schedule checkpoint"); err != nil {
return err
}
if err := acknowledgeTuttiModeCheckpointWakesTx(
ctx, tx, admission.WorkspaceID, executionID, admission.CheckpointID, admission.Now,
); err != nil {
return err
}
nextStatus := executionbiz.StatusRunning
var nextCheckpointID string
err = tx.QueryRowContext(ctx, `
SELECT checkpoint_id
FROM workspace_tutti_execution_checkpoints
WHERE workspace_id = ? AND execution_id = ? AND status = 'pending'
ORDER BY sequence ASC
LIMIT 1
`, admission.WorkspaceID, executionID).Scan(&nextCheckpointID)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return fmt.Errorf("get next Tutti mode schedule checkpoint: %w", err)
}
if err == nil {
result, err = tx.ExecContext(ctx, `
UPDATE workspace_tutti_execution_checkpoints
SET status = 'active', graph_revision = ?, updated_at_unix_ms = ?
WHERE workspace_id = ? AND execution_id = ? AND checkpoint_id = ?
AND status = 'pending'
`, admission.ExpectedGraphRevision, unixMs(admission.Now),
admission.WorkspaceID, executionID, nextCheckpointID)
if err != nil {
return fmt.Errorf("promote next Tutti mode schedule checkpoint: %w", err)
}
if err := requireRowsAffected(result, executionbiz.ErrScheduleRejected, "promote next Tutti mode schedule checkpoint"); err != nil {
return err
}
nextCheckpoint, found, err := getTuttiModeCheckpointTx(
ctx, tx, admission.WorkspaceID, executionID, nextCheckpointID,
)
if err != nil || !found {
if err == nil {
err = executionbiz.ErrScheduleRejected
}
return err
}
if err := prepareTuttiModeMainWakeTx(
ctx, tx, admission.WorkspaceID, executionID, nextCheckpoint, admission.Now,
); err != nil {
return err
}
nextStatus = executionbiz.StatusAwaitingMain
}
result, err = tx.ExecContext(ctx, `
UPDATE workspace_tutti_executions
SET status = ?, last_orchestrator_activity_at_unix_ms = ?,
watchdog_due_at_unix_ms = ?, updated_at_unix_ms = ?
WHERE workspace_id = ? AND execution_id = ? AND graph_revision = ?
`, string(nextStatus), unixMs(admission.Now), unixMs(admission.Now.Add(executionbiz.WatchdogInterval)),
unixMs(admission.Now), admission.WorkspaceID, executionID,
admission.ExpectedGraphRevision)
if err != nil {
return fmt.Errorf("advance Tutti mode execution after schedule: %w", err)
}
return requireRowsAffected(result, executionbiz.ErrScheduleRejected, "advance Tutti mode execution after schedule")
}
func (s *SQLiteStore) ListPreparedTuttiModeRunLaunches(
ctx context.Context,
workspaceID string,
issueID string,
runIDs []string,
_ time.Time,
) ([]executionbiz.PreparedRunLaunch, error) {
if err := s.ensureIssueDatabase(); err != nil {
return nil, err
}
where := []string{
"runs.workspace_id = ?",
"runs.status = 'running'",
"intents.status = 'prepared'",
}
args := []any{strings.TrimSpace(workspaceID)}
if strings.TrimSpace(issueID) != "" {
where = append(where, "runs.issue_id = ?")
args = append(args, strings.TrimSpace(issueID))
}
placeholders := make([]string, len(runIDs))
for index, runID := range runIDs {
placeholders[index] = "?"
args = append(args, strings.TrimSpace(runID))
}
if len(placeholders) > 0 {
where = append(where, "runs.run_id IN ("+strings.Join(placeholders, ",")+")")
}
rows, err := s.readDB.QueryContext(ctx, fmt.Sprintf(`
SELECT %s, intents.client_submit_id
FROM workspace_issue_runs AS runs
JOIN workspace_issue_run_launch_intents AS intents
ON intents.workspace_id = runs.workspace_id
AND intents.issue_id = runs.issue_id
AND intents.task_id = runs.task_id
AND intents.run_id = runs.run_id
JOIN workspace_issues AS issues
ON issues.workspace_id = runs.workspace_id
AND issues.issue_id = runs.issue_id
JOIN workspace_tutti_executions AS executions
ON executions.workspace_id = runs.workspace_id
AND executions.issue_id = runs.issue_id
WHERE %s
AND issues.dispatch_paused = 0
AND executions.status = 'running'
ORDER BY runs.created_at_unix_ms ASC, runs.id ASC
`, prefixedRunSelectColumns("runs"), strings.Join(where, " AND ")), args...)
if err != nil {
return nil, fmt.Errorf("list prepared Tutti mode Run launches: %w", err)
}
defer rows.Close()
runs := make([]executionbiz.PreparedRunLaunch, 0, len(runIDs))
for rows.Next() {
run, clientSubmitID, scanErr := scanPreparedIssueRunLaunch(rows, nil)
if scanErr != nil {
return nil, scanErr
}
runs = append(runs, executionbiz.PreparedRunLaunch{
Run: run, ClientSubmitID: clientSubmitID,
})
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate prepared Tutti mode Run launches: %w", err)
}
return runs, nil
}
func (s *SQLiteStore) MarkTuttiModeRunLaunchIntentDispatched(
ctx context.Context,
workspaceID string,
issueID string,
runID string,
leaseOwner string,
now time.Time,
) error {
return s.markIssueRunLaunchIntentDispatched(
ctx, workspaceID, issueID, runID, leaseOwner, now,
executionbiz.ErrScheduleRejected,
)
}
func (s *SQLiteStore) GetTuttiModeRunLaunchClientSubmitID(
ctx context.Context,
workspaceID string,
issueID string,
runID string,
) (string, bool, error) {
if err := s.ensureIssueDatabase(); err != nil {
return "", false, err
}
var clientSubmitID string
err := s.readDB.QueryRowContext(ctx, `
SELECT client_submit_id
FROM workspace_issue_run_launch_intents
WHERE workspace_id = ? AND issue_id = ? AND run_id = ?
`, strings.TrimSpace(workspaceID), strings.TrimSpace(issueID), strings.TrimSpace(runID)).
Scan(&clientSubmitID)
if errors.Is(err, sql.ErrNoRows) {
return "", false, nil
}
if err != nil {
return "", false, fmt.Errorf("get Tutti mode Run launch submit identity: %w", err)
}
return clientSubmitID, true, nil
}
func (s *SQLiteStore) ClaimTuttiModeRunLaunchIntent(
ctx context.Context,
workspaceID string,
issueID string,
runID string,
leaseOwner string,
now time.Time,
leaseExpires time.Time,
) (bool, error) {
if err := s.ensureIssueDatabase(); err != nil {
return false, err
}
result, err := s.writeDB.ExecContext(ctx, `
UPDATE workspace_issue_run_launch_intents
SET status = 'leased', attempt_count = attempt_count + 1,
lease_owner = ?, lease_expires_at_unix_ms = ?, updated_at_unix_ms = ?
WHERE workspace_id = ? AND issue_id = ? AND run_id = ?
AND status = 'prepared'
AND EXISTS (
SELECT 1
FROM workspace_issue_runs AS runs
JOIN workspace_issues AS issues
ON issues.workspace_id = runs.workspace_id
AND issues.issue_id = runs.issue_id
JOIN workspace_tutti_executions AS executions
ON executions.workspace_id = runs.workspace_id
AND executions.issue_id = runs.issue_id
WHERE runs.workspace_id = workspace_issue_run_launch_intents.workspace_id
AND runs.issue_id = workspace_issue_run_launch_intents.issue_id
AND runs.run_id = workspace_issue_run_launch_intents.run_id
AND runs.status = 'running'
AND issues.dispatch_paused = 0
AND executions.status = 'running'
)
`, strings.TrimSpace(leaseOwner), unixMs(leaseExpires), unixMs(now),
strings.TrimSpace(workspaceID), strings.TrimSpace(issueID),
strings.TrimSpace(runID))
if err != nil {
return false, fmt.Errorf("claim Tutti mode Run launch intent: %w", err)
}
return rowsWereAffected(result, "claim Tutti mode Run launch intent")
}
func (s *SQLiteStore) ReleaseTuttiModeRunLaunchIntent(
ctx context.Context,
workspaceID string,
issueID string,
runID string,
leaseOwner string,
now time.Time,
) error {
return s.releaseIssueRunLaunchIntent(
ctx, workspaceID, issueID, runID, leaseOwner, now,
executionbiz.ErrScheduleRejected,
)
}
func (s *SQLiteStore) RenewTuttiModeRunLaunchIntent(
ctx context.Context,
workspaceID string,
issueID string,
runID string,
leaseOwner string,
now time.Time,
leaseExpires time.Time,
) error {
return s.renewIssueRunLaunchIntent(
ctx, workspaceID, issueID, runID, leaseOwner, now, leaseExpires,
executionbiz.ErrScheduleRejected,
)
}
func (s *SQLiteStore) RequeueLeasedTuttiModeRunLaunchIntents(
ctx context.Context,
workspaceID string,
now time.Time,
) error {
return s.requeueLeasedIssueRunLaunchIntents(ctx, workspaceID, now)
}
func scanPreparedIssueRunLaunch(
scanner issueScanner,
payloadJSON *string,
) (workspaceissues.Run, string, error) {
var item workspaceissues.Run
var id int64
var status string
var clientSubmitID string
destinations := []any{
&id, &item.RunID, &item.TaskID, &item.IssueID, &item.WorkspaceID,
&item.RequesterUserID, &item.AgentUserID, &item.AgentTargetID,
&item.AgentSessionID, &item.AgentProvider, &item.ModelPlanID, &item.Model,
&item.ReasoningIntensity, &status, &item.Usage.InputTokens,
&item.Usage.OutputTokens, &item.Usage.CacheReadTokens, &item.Usage.CacheWriteTokens,
&item.Summary, &item.ErrorMessage, &item.OutputDir, &item.ExecutionDirectory,
&item.CreatedAtUnixMS, &item.StartedAtUnixMS, &item.CompletedAtUnixMS,
&item.UpdatedAtUnixMS, &clientSubmitID,
}
if payloadJSON != nil {
destinations = append(destinations, payloadJSON)
}
err := scanner.Scan(destinations...)
item.ID = uint64(id)
item.Status = workspaceissues.Status(status)
return item, clientSubmitID, err
}
func prefixedRunSelectColumns(alias string) string {
columns := strings.Split(strings.TrimSpace(runSelectColumns), ",")
for index, column := range columns {
columns[index] = alias + "." + strings.TrimSpace(column)
}
return strings.Join(columns, ", ")
}