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
95 lines
2.9 KiB
Go
95 lines
2.9 KiB
Go
package workspace
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// WaitAgentSessionGraphSettled keeps the capture window open until every
|
|
// already-accepted canonical operation in the selected graph is terminal.
|
|
func (s *SQLiteStore) WaitAgentSessionGraphSettled(
|
|
ctx context.Context,
|
|
workspaceID string,
|
|
rootAgentSessionID string,
|
|
) error {
|
|
if s == nil || s.readDB == nil {
|
|
return errors.New("workspace database is not initialized")
|
|
}
|
|
ticker := time.NewTicker(100 * time.Millisecond)
|
|
defer ticker.Stop()
|
|
for {
|
|
var active int
|
|
err := s.readDB.QueryRowContext(ctx, `
|
|
WITH graph(agent_session_id) AS (
|
|
SELECT agent_session_id
|
|
FROM workspace_agent_sessions
|
|
WHERE workspace_id = ?
|
|
AND deleted_at_unix_ms = 0
|
|
AND (agent_session_id = ? OR root_agent_session_id = ?)
|
|
)
|
|
SELECT
|
|
(SELECT COUNT(*)
|
|
FROM workspace_agent_sessions
|
|
WHERE workspace_id = ? AND agent_session_id IN (SELECT agent_session_id FROM graph) AND active_turn_id IS NOT NULL)
|
|
+
|
|
(SELECT COUNT(*)
|
|
FROM workspace_agent_runtime_operations
|
|
WHERE workspace_id = ? AND agent_session_id IN (SELECT agent_session_id FROM graph) AND status IN ('prepared','leased'))
|
|
+
|
|
(SELECT COUNT(*)
|
|
FROM workspace_agent_goal_control_operations
|
|
WHERE workspace_id = ? AND agent_session_id IN (SELECT agent_session_id FROM graph) AND status IN ('prepared','dispatched'))
|
|
+
|
|
(SELECT COUNT(*)
|
|
FROM workspace_workflow_operations
|
|
WHERE workspace_id = ?
|
|
AND status IN ('pending','running')
|
|
AND workflow_id IN (
|
|
SELECT workflow_id FROM workspace_workflows
|
|
WHERE workspace_id = ? AND source_session_id IN (SELECT agent_session_id FROM graph)
|
|
))
|
|
`, workspaceID, rootAgentSessionID, rootAgentSessionID,
|
|
workspaceID, workspaceID, workspaceID, workspaceID, workspaceID,
|
|
).Scan(&active)
|
|
if err != nil {
|
|
return fmt.Errorf("read Agent Session graph finalization state: %w", err)
|
|
}
|
|
if active == 0 {
|
|
return nil
|
|
}
|
|
select {
|
|
case <-ctx.Done():
|
|
return fmt.Errorf("wait for Agent Session graph to settle: %w", ctx.Err())
|
|
case <-ticker.C:
|
|
}
|
|
}
|
|
}
|
|
|
|
// ResolveRootAgentSession returns the canonical root for either a root or
|
|
// child Session. It does not infer graph identity from provider state.
|
|
func (s *SQLiteStore) ResolveRootAgentSession(
|
|
ctx context.Context,
|
|
workspaceID string,
|
|
agentSessionID string,
|
|
) (string, error) {
|
|
if s == nil || s.readDB == nil {
|
|
return "", errors.New("workspace database is not initialized")
|
|
}
|
|
var rootID string
|
|
err := s.readDB.QueryRowContext(ctx, `
|
|
SELECT CASE
|
|
WHEN TRIM(COALESCE(root_agent_session_id, '')) = '' THEN agent_session_id
|
|
ELSE root_agent_session_id
|
|
END
|
|
FROM workspace_agent_sessions
|
|
WHERE workspace_id = ? AND agent_session_id = ? AND deleted_at_unix_ms = 0
|
|
`, strings.TrimSpace(workspaceID), strings.TrimSpace(agentSessionID)).Scan(&rootID)
|
|
if err != nil {
|
|
return "", fmt.Errorf("resolve Agent Session root: %w", err)
|
|
}
|
|
return strings.TrimSpace(rootID), nil
|
|
}
|