ai-whisper 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/dist/bin/broker-daemon.js +105 -80
- package/dist/bin/companion-agent.js +106 -81
- package/dist/bin/relay-monitor.js +108 -80
- package/dist/bin/whisper.js +308 -116
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -105,6 +105,13 @@ The first `mount` creates the collab and starts the broker daemon for the worksp
|
|
|
105
105
|
whisper collab dashboard
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
+
- `whisper collab dashboard` — live wall of recently-active collabs + per-run inspector.
|
|
109
|
+
Add `--all` to show every workflow run (no per-collab masking); combine with
|
|
110
|
+
`--window all` for the full run ledger.
|
|
111
|
+
- From the dashboard you can pause/resume/cancel a workflow run in place
|
|
112
|
+
(`p`/`r`/`c`, each confirmed), and a header bar shows live counts of running /
|
|
113
|
+
paused / stuck / done / canceled / idle runs.
|
|
114
|
+
|
|
108
115
|
> Running from a repo checkout instead of a packaged install? Build first (`pnpm build`) and invoke the CLI as `node packages/cli/dist/bin/whisper.js ...` wherever these examples say `whisper ...`.
|
|
109
116
|
|
|
110
117
|
## What happens if it fails?
|
|
@@ -1913,7 +1913,8 @@ function listActiveCollabSummaries(db, input) {
|
|
|
1913
1913
|
GROUP BY c.collab_id
|
|
1914
1914
|
HAVING MAX(h.last_activity_at) >= ?
|
|
1915
1915
|
OR EXISTS (SELECT 1 FROM workflows w
|
|
1916
|
-
WHERE w.collab_id = c.collab_id
|
|
1916
|
+
WHERE w.collab_id = c.collab_id
|
|
1917
|
+
AND w.status IN ('running','paused'))`).all(cutoff);
|
|
1917
1918
|
const out = [];
|
|
1918
1919
|
const seen = /* @__PURE__ */ new Set();
|
|
1919
1920
|
for (const e of eligible) {
|
|
@@ -1938,87 +1939,105 @@ function listActiveCollabSummaries(db, input) {
|
|
|
1938
1939
|
}
|
|
1939
1940
|
return out;
|
|
1940
1941
|
}
|
|
1941
|
-
function
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
FROM workflow_phases WHERE workflow_id = ?
|
|
1960
|
-
ORDER BY (ended_at IS NULL) DESC, started_at DESC
|
|
1961
|
-
LIMIT 1`).get(wf.workflowId);
|
|
1962
|
-
if (ph) {
|
|
1963
|
-
currentPhaseRunId = ph.phaseRunId;
|
|
1964
|
-
phaseIndex = ph.phaseIndex;
|
|
1965
|
-
phaseName = ph.phaseName;
|
|
1966
|
-
chainId = ph.chainId;
|
|
1967
|
-
}
|
|
1942
|
+
function buildWorkflowSummary(db, collabId2, wf) {
|
|
1943
|
+
const collab2 = db.prepare(`SELECT display_name AS displayName, workspace_root AS workspaceRoot
|
|
1944
|
+
FROM collab WHERE collab_id = ?`).get(collabId2);
|
|
1945
|
+
let currentPhaseRunId = null;
|
|
1946
|
+
let phaseIndex = null;
|
|
1947
|
+
let phaseName = null;
|
|
1948
|
+
let chainId = null;
|
|
1949
|
+
if (wf) {
|
|
1950
|
+
const ph = db.prepare(`SELECT phase_run_id AS phaseRunId, phase_index AS phaseIndex,
|
|
1951
|
+
phase_name AS phaseName, chain_id AS chainId
|
|
1952
|
+
FROM workflow_phases WHERE workflow_id = ?
|
|
1953
|
+
ORDER BY (ended_at IS NULL) DESC, started_at DESC
|
|
1954
|
+
LIMIT 1`).get(wf.workflowId);
|
|
1955
|
+
if (ph) {
|
|
1956
|
+
currentPhaseRunId = ph.phaseRunId;
|
|
1957
|
+
phaseIndex = ph.phaseIndex;
|
|
1958
|
+
phaseName = ph.phaseName;
|
|
1959
|
+
chainId = ph.chainId;
|
|
1968
1960
|
}
|
|
1969
|
-
const chain = chainId ? db.prepare(`SELECT status, current_round AS currentRound, max_rounds AS maxRounds
|
|
1970
|
-
FROM relay_chains WHERE chain_id = ?`).get(chainId) : void 0;
|
|
1971
|
-
const turn = db.prepare(`SELECT turn_owner AS owner, waiting_agent AS waiting, handoff_state AS handoffState
|
|
1972
|
-
FROM relay_turn_state WHERE collab_id = ?`).get(e.collabId);
|
|
1973
|
-
const sessions = db.prepare(`SELECT agentType, healthState FROM (
|
|
1974
|
-
SELECT s.agent_type AS agentType,
|
|
1975
|
-
s.health_state AS healthState,
|
|
1976
|
-
ROW_NUMBER() OVER (
|
|
1977
|
-
PARTITION BY s.agent_type
|
|
1978
|
-
ORDER BY CASE WHEN sb.active_session_id = s.session_id
|
|
1979
|
-
THEN 0 ELSE 1 END ASC,
|
|
1980
|
-
s.registered_at DESC,
|
|
1981
|
-
s.rowid DESC
|
|
1982
|
-
) AS rn
|
|
1983
|
-
FROM session s
|
|
1984
|
-
LEFT JOIN session_binding sb
|
|
1985
|
-
ON sb.collab_id = s.collab_id
|
|
1986
|
-
AND sb.agent_type = s.agent_type
|
|
1987
|
-
WHERE s.collab_id = ?
|
|
1988
|
-
) ranked
|
|
1989
|
-
WHERE rn = 1
|
|
1990
|
-
ORDER BY agentType ASC`).all(e.collabId);
|
|
1991
|
-
const label = wf?.name && wf.name.trim() || collab2?.displayName && collab2.displayName.trim() || (collab2?.workspaceRoot ? basename2(collab2.workspaceRoot) : "") || e.collabId.slice(0, 12);
|
|
1992
|
-
const runLastActRow = wf ? db.prepare(`SELECT COALESCE(MAX(last_activity_at), '') AS lastAct
|
|
1993
|
-
FROM relay_handoff
|
|
1994
|
-
WHERE collab_id = ? AND workflow_id = ?`).get(e.collabId, wf.workflowId) : db.prepare(`SELECT COALESCE(MAX(last_activity_at), '') AS lastAct
|
|
1995
|
-
FROM relay_handoff
|
|
1996
|
-
WHERE collab_id = ? AND workflow_id IS NULL`).get(e.collabId);
|
|
1997
|
-
const runLastAct = runLastActRow?.lastAct ?? "";
|
|
1998
|
-
return {
|
|
1999
|
-
collabId: e.collabId,
|
|
2000
|
-
label,
|
|
2001
|
-
workspaceRoot: collab2?.workspaceRoot ?? "",
|
|
2002
|
-
workflowId: wf?.workflowId ?? null,
|
|
2003
|
-
workflowType: wf?.workflowType ?? null,
|
|
2004
|
-
workflowStatus: wf?.status ?? null,
|
|
2005
|
-
currentPhaseRunId,
|
|
2006
|
-
phaseIndex,
|
|
2007
|
-
phaseName,
|
|
2008
|
-
currentRound: chain?.currentRound ?? null,
|
|
2009
|
-
maxRounds: chain?.maxRounds ?? null,
|
|
2010
|
-
chainStatus: chain?.status ?? null,
|
|
2011
|
-
turn: {
|
|
2012
|
-
owner: turn?.owner ?? "none",
|
|
2013
|
-
waiting: turn?.waiting ?? null,
|
|
2014
|
-
handoffState: turn?.handoffState ?? "idle"
|
|
2015
|
-
},
|
|
2016
|
-
sessions,
|
|
2017
|
-
workflowCreatedAt: wf?.createdAt ?? null,
|
|
2018
|
-
specPath: wf ? displayArtifactPath(wf.specPath, collab2?.workspaceRoot ?? "") : null,
|
|
2019
|
-
lastActivityAt: runLastAct
|
|
2020
|
-
};
|
|
2021
1961
|
}
|
|
1962
|
+
const chain = chainId ? db.prepare(`SELECT status, current_round AS currentRound, max_rounds AS maxRounds
|
|
1963
|
+
FROM relay_chains WHERE chain_id = ?`).get(chainId) : void 0;
|
|
1964
|
+
const turn = db.prepare(`SELECT turn_owner AS owner, waiting_agent AS waiting, handoff_state AS handoffState
|
|
1965
|
+
FROM relay_turn_state WHERE collab_id = ?`).get(collabId2);
|
|
1966
|
+
const sessions = db.prepare(`SELECT agentType, healthState FROM (
|
|
1967
|
+
SELECT s.agent_type AS agentType,
|
|
1968
|
+
s.health_state AS healthState,
|
|
1969
|
+
ROW_NUMBER() OVER (
|
|
1970
|
+
PARTITION BY s.agent_type
|
|
1971
|
+
ORDER BY CASE WHEN sb.active_session_id = s.session_id
|
|
1972
|
+
THEN 0 ELSE 1 END ASC,
|
|
1973
|
+
s.registered_at DESC,
|
|
1974
|
+
s.rowid DESC
|
|
1975
|
+
) AS rn
|
|
1976
|
+
FROM session s
|
|
1977
|
+
LEFT JOIN session_binding sb
|
|
1978
|
+
ON sb.collab_id = s.collab_id
|
|
1979
|
+
AND sb.agent_type = s.agent_type
|
|
1980
|
+
WHERE s.collab_id = ?
|
|
1981
|
+
) ranked
|
|
1982
|
+
WHERE rn = 1
|
|
1983
|
+
ORDER BY agentType ASC`).all(collabId2);
|
|
1984
|
+
const label = wf?.name && wf.name.trim() || collab2?.displayName && collab2.displayName.trim() || (collab2?.workspaceRoot ? basename2(collab2.workspaceRoot) : "") || collabId2.slice(0, 12);
|
|
1985
|
+
const runLastActRow = wf ? db.prepare(`SELECT COALESCE(MAX(last_activity_at), '') AS lastAct
|
|
1986
|
+
FROM relay_handoff
|
|
1987
|
+
WHERE collab_id = ? AND workflow_id = ?`).get(collabId2, wf.workflowId) : db.prepare(`SELECT COALESCE(MAX(last_activity_at), '') AS lastAct
|
|
1988
|
+
FROM relay_handoff
|
|
1989
|
+
WHERE collab_id = ? AND workflow_id IS NULL`).get(collabId2);
|
|
1990
|
+
const runLastAct = runLastActRow?.lastAct ?? "";
|
|
1991
|
+
return {
|
|
1992
|
+
collabId: collabId2,
|
|
1993
|
+
label,
|
|
1994
|
+
workspaceRoot: collab2?.workspaceRoot ?? "",
|
|
1995
|
+
workflowId: wf?.workflowId ?? null,
|
|
1996
|
+
workflowType: wf?.workflowType ?? null,
|
|
1997
|
+
workflowStatus: wf?.status ?? null,
|
|
1998
|
+
currentPhaseRunId,
|
|
1999
|
+
phaseIndex,
|
|
2000
|
+
phaseName,
|
|
2001
|
+
currentRound: chain?.currentRound ?? null,
|
|
2002
|
+
maxRounds: chain?.maxRounds ?? null,
|
|
2003
|
+
chainStatus: chain?.status ?? null,
|
|
2004
|
+
turn: {
|
|
2005
|
+
owner: turn?.owner ?? "none",
|
|
2006
|
+
waiting: turn?.waiting ?? null,
|
|
2007
|
+
handoffState: turn?.handoffState ?? "idle"
|
|
2008
|
+
},
|
|
2009
|
+
sessions,
|
|
2010
|
+
workflowCreatedAt: wf?.createdAt ?? null,
|
|
2011
|
+
specPath: wf ? displayArtifactPath(wf.specPath, collab2?.workspaceRoot ?? "") : null,
|
|
2012
|
+
lastActivityAt: runLastAct
|
|
2013
|
+
};
|
|
2014
|
+
}
|
|
2015
|
+
function buildCollabSummary(db, collabId2) {
|
|
2016
|
+
const wf = db.prepare(`SELECT workflow_id AS workflowId, workflow_type AS workflowType,
|
|
2017
|
+
name, status, current_phase_index AS currentPhaseIndex,
|
|
2018
|
+
created_at AS createdAt, spec_path AS specPath
|
|
2019
|
+
FROM workflows WHERE collab_id = ?
|
|
2020
|
+
ORDER BY (status = 'running') DESC, created_at DESC
|
|
2021
|
+
LIMIT 1`).get(collabId2);
|
|
2022
|
+
return buildWorkflowSummary(db, collabId2, wf ?? null);
|
|
2023
|
+
}
|
|
2024
|
+
function listAllWorkflowSummaries(db, input) {
|
|
2025
|
+
const nowMs = Date.parse(input.now ?? (/* @__PURE__ */ new Date()).toISOString());
|
|
2026
|
+
const base = Number.isFinite(nowMs) ? nowMs : Date.now();
|
|
2027
|
+
const cutoff = new Date(Math.max(0, base - input.sinceMs)).toISOString();
|
|
2028
|
+
const rows = db.prepare(`SELECT w.workflow_id AS workflowId, w.workflow_type AS workflowType,
|
|
2029
|
+
w.name AS name, w.status AS status,
|
|
2030
|
+
w.current_phase_index AS currentPhaseIndex,
|
|
2031
|
+
w.created_at AS createdAt, w.spec_path AS specPath,
|
|
2032
|
+
w.collab_id AS collabId
|
|
2033
|
+
FROM workflows w
|
|
2034
|
+
LEFT JOIN relay_handoff h ON h.workflow_id = w.workflow_id
|
|
2035
|
+
GROUP BY w.workflow_id
|
|
2036
|
+
HAVING w.status IN ('running','paused')
|
|
2037
|
+
OR MAX(h.last_activity_at) >= ?
|
|
2038
|
+
OR (MAX(h.last_activity_at) IS NULL AND w.created_at >= ?)
|
|
2039
|
+
ORDER BY w.created_at DESC, w.rowid DESC`).all(cutoff, cutoff);
|
|
2040
|
+
return rows.map((r) => buildWorkflowSummary(db, r.collabId, r));
|
|
2022
2041
|
}
|
|
2023
2042
|
function listWorkflowsForCollab(db, collabId2) {
|
|
2024
2043
|
const rows = db.prepare(`SELECT workflow_id AS workflowId, workflow_type AS workflowType,
|
|
@@ -4327,6 +4346,12 @@ function createControlService(db, events) {
|
|
|
4327
4346
|
...now !== void 0 ? { now } : {}
|
|
4328
4347
|
});
|
|
4329
4348
|
},
|
|
4349
|
+
listAllWorkflowSummaries(sinceMs, now) {
|
|
4350
|
+
return listAllWorkflowSummaries(db, {
|
|
4351
|
+
sinceMs,
|
|
4352
|
+
...now !== void 0 ? { now } : {}
|
|
4353
|
+
});
|
|
4354
|
+
},
|
|
4330
4355
|
listRunCostRows(collabId2, workflowId) {
|
|
4331
4356
|
return listRunCostRows(db, { collabId: collabId2, workflowId });
|
|
4332
4357
|
},
|
|
@@ -1946,7 +1946,8 @@ function listActiveCollabSummaries(db, input) {
|
|
|
1946
1946
|
GROUP BY c.collab_id
|
|
1947
1947
|
HAVING MAX(h.last_activity_at) >= ?
|
|
1948
1948
|
OR EXISTS (SELECT 1 FROM workflows w
|
|
1949
|
-
WHERE w.collab_id = c.collab_id
|
|
1949
|
+
WHERE w.collab_id = c.collab_id
|
|
1950
|
+
AND w.status IN ('running','paused'))`).all(cutoff);
|
|
1950
1951
|
const out = [];
|
|
1951
1952
|
const seen = /* @__PURE__ */ new Set();
|
|
1952
1953
|
for (const e of eligible) {
|
|
@@ -1971,87 +1972,105 @@ function listActiveCollabSummaries(db, input) {
|
|
|
1971
1972
|
}
|
|
1972
1973
|
return out;
|
|
1973
1974
|
}
|
|
1975
|
+
function buildWorkflowSummary(db, collabId, wf) {
|
|
1976
|
+
const collab = db.prepare(`SELECT display_name AS displayName, workspace_root AS workspaceRoot
|
|
1977
|
+
FROM collab WHERE collab_id = ?`).get(collabId);
|
|
1978
|
+
let currentPhaseRunId = null;
|
|
1979
|
+
let phaseIndex = null;
|
|
1980
|
+
let phaseName = null;
|
|
1981
|
+
let chainId = null;
|
|
1982
|
+
if (wf) {
|
|
1983
|
+
const ph = db.prepare(`SELECT phase_run_id AS phaseRunId, phase_index AS phaseIndex,
|
|
1984
|
+
phase_name AS phaseName, chain_id AS chainId
|
|
1985
|
+
FROM workflow_phases WHERE workflow_id = ?
|
|
1986
|
+
ORDER BY (ended_at IS NULL) DESC, started_at DESC
|
|
1987
|
+
LIMIT 1`).get(wf.workflowId);
|
|
1988
|
+
if (ph) {
|
|
1989
|
+
currentPhaseRunId = ph.phaseRunId;
|
|
1990
|
+
phaseIndex = ph.phaseIndex;
|
|
1991
|
+
phaseName = ph.phaseName;
|
|
1992
|
+
chainId = ph.chainId;
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
const chain = chainId ? db.prepare(`SELECT status, current_round AS currentRound, max_rounds AS maxRounds
|
|
1996
|
+
FROM relay_chains WHERE chain_id = ?`).get(chainId) : void 0;
|
|
1997
|
+
const turn = db.prepare(`SELECT turn_owner AS owner, waiting_agent AS waiting, handoff_state AS handoffState
|
|
1998
|
+
FROM relay_turn_state WHERE collab_id = ?`).get(collabId);
|
|
1999
|
+
const sessions = db.prepare(`SELECT agentType, healthState FROM (
|
|
2000
|
+
SELECT s.agent_type AS agentType,
|
|
2001
|
+
s.health_state AS healthState,
|
|
2002
|
+
ROW_NUMBER() OVER (
|
|
2003
|
+
PARTITION BY s.agent_type
|
|
2004
|
+
ORDER BY CASE WHEN sb.active_session_id = s.session_id
|
|
2005
|
+
THEN 0 ELSE 1 END ASC,
|
|
2006
|
+
s.registered_at DESC,
|
|
2007
|
+
s.rowid DESC
|
|
2008
|
+
) AS rn
|
|
2009
|
+
FROM session s
|
|
2010
|
+
LEFT JOIN session_binding sb
|
|
2011
|
+
ON sb.collab_id = s.collab_id
|
|
2012
|
+
AND sb.agent_type = s.agent_type
|
|
2013
|
+
WHERE s.collab_id = ?
|
|
2014
|
+
) ranked
|
|
2015
|
+
WHERE rn = 1
|
|
2016
|
+
ORDER BY agentType ASC`).all(collabId);
|
|
2017
|
+
const label = wf?.name && wf.name.trim() || collab?.displayName && collab.displayName.trim() || (collab?.workspaceRoot ? basename2(collab.workspaceRoot) : "") || collabId.slice(0, 12);
|
|
2018
|
+
const runLastActRow = wf ? db.prepare(`SELECT COALESCE(MAX(last_activity_at), '') AS lastAct
|
|
2019
|
+
FROM relay_handoff
|
|
2020
|
+
WHERE collab_id = ? AND workflow_id = ?`).get(collabId, wf.workflowId) : db.prepare(`SELECT COALESCE(MAX(last_activity_at), '') AS lastAct
|
|
2021
|
+
FROM relay_handoff
|
|
2022
|
+
WHERE collab_id = ? AND workflow_id IS NULL`).get(collabId);
|
|
2023
|
+
const runLastAct = runLastActRow?.lastAct ?? "";
|
|
2024
|
+
return {
|
|
2025
|
+
collabId,
|
|
2026
|
+
label,
|
|
2027
|
+
workspaceRoot: collab?.workspaceRoot ?? "",
|
|
2028
|
+
workflowId: wf?.workflowId ?? null,
|
|
2029
|
+
workflowType: wf?.workflowType ?? null,
|
|
2030
|
+
workflowStatus: wf?.status ?? null,
|
|
2031
|
+
currentPhaseRunId,
|
|
2032
|
+
phaseIndex,
|
|
2033
|
+
phaseName,
|
|
2034
|
+
currentRound: chain?.currentRound ?? null,
|
|
2035
|
+
maxRounds: chain?.maxRounds ?? null,
|
|
2036
|
+
chainStatus: chain?.status ?? null,
|
|
2037
|
+
turn: {
|
|
2038
|
+
owner: turn?.owner ?? "none",
|
|
2039
|
+
waiting: turn?.waiting ?? null,
|
|
2040
|
+
handoffState: turn?.handoffState ?? "idle"
|
|
2041
|
+
},
|
|
2042
|
+
sessions,
|
|
2043
|
+
workflowCreatedAt: wf?.createdAt ?? null,
|
|
2044
|
+
specPath: wf ? displayArtifactPath(wf.specPath, collab?.workspaceRoot ?? "") : null,
|
|
2045
|
+
lastActivityAt: runLastAct
|
|
2046
|
+
};
|
|
2047
|
+
}
|
|
1974
2048
|
function buildCollabSummary(db, collabId) {
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2002
|
-
const chain = chainId ? db.prepare(`SELECT status, current_round AS currentRound, max_rounds AS maxRounds
|
|
2003
|
-
FROM relay_chains WHERE chain_id = ?`).get(chainId) : void 0;
|
|
2004
|
-
const turn = db.prepare(`SELECT turn_owner AS owner, waiting_agent AS waiting, handoff_state AS handoffState
|
|
2005
|
-
FROM relay_turn_state WHERE collab_id = ?`).get(e.collabId);
|
|
2006
|
-
const sessions = db.prepare(`SELECT agentType, healthState FROM (
|
|
2007
|
-
SELECT s.agent_type AS agentType,
|
|
2008
|
-
s.health_state AS healthState,
|
|
2009
|
-
ROW_NUMBER() OVER (
|
|
2010
|
-
PARTITION BY s.agent_type
|
|
2011
|
-
ORDER BY CASE WHEN sb.active_session_id = s.session_id
|
|
2012
|
-
THEN 0 ELSE 1 END ASC,
|
|
2013
|
-
s.registered_at DESC,
|
|
2014
|
-
s.rowid DESC
|
|
2015
|
-
) AS rn
|
|
2016
|
-
FROM session s
|
|
2017
|
-
LEFT JOIN session_binding sb
|
|
2018
|
-
ON sb.collab_id = s.collab_id
|
|
2019
|
-
AND sb.agent_type = s.agent_type
|
|
2020
|
-
WHERE s.collab_id = ?
|
|
2021
|
-
) ranked
|
|
2022
|
-
WHERE rn = 1
|
|
2023
|
-
ORDER BY agentType ASC`).all(e.collabId);
|
|
2024
|
-
const label = wf?.name && wf.name.trim() || collab?.displayName && collab.displayName.trim() || (collab?.workspaceRoot ? basename2(collab.workspaceRoot) : "") || e.collabId.slice(0, 12);
|
|
2025
|
-
const runLastActRow = wf ? db.prepare(`SELECT COALESCE(MAX(last_activity_at), '') AS lastAct
|
|
2026
|
-
FROM relay_handoff
|
|
2027
|
-
WHERE collab_id = ? AND workflow_id = ?`).get(e.collabId, wf.workflowId) : db.prepare(`SELECT COALESCE(MAX(last_activity_at), '') AS lastAct
|
|
2028
|
-
FROM relay_handoff
|
|
2029
|
-
WHERE collab_id = ? AND workflow_id IS NULL`).get(e.collabId);
|
|
2030
|
-
const runLastAct = runLastActRow?.lastAct ?? "";
|
|
2031
|
-
return {
|
|
2032
|
-
collabId: e.collabId,
|
|
2033
|
-
label,
|
|
2034
|
-
workspaceRoot: collab?.workspaceRoot ?? "",
|
|
2035
|
-
workflowId: wf?.workflowId ?? null,
|
|
2036
|
-
workflowType: wf?.workflowType ?? null,
|
|
2037
|
-
workflowStatus: wf?.status ?? null,
|
|
2038
|
-
currentPhaseRunId,
|
|
2039
|
-
phaseIndex,
|
|
2040
|
-
phaseName,
|
|
2041
|
-
currentRound: chain?.currentRound ?? null,
|
|
2042
|
-
maxRounds: chain?.maxRounds ?? null,
|
|
2043
|
-
chainStatus: chain?.status ?? null,
|
|
2044
|
-
turn: {
|
|
2045
|
-
owner: turn?.owner ?? "none",
|
|
2046
|
-
waiting: turn?.waiting ?? null,
|
|
2047
|
-
handoffState: turn?.handoffState ?? "idle"
|
|
2048
|
-
},
|
|
2049
|
-
sessions,
|
|
2050
|
-
workflowCreatedAt: wf?.createdAt ?? null,
|
|
2051
|
-
specPath: wf ? displayArtifactPath(wf.specPath, collab?.workspaceRoot ?? "") : null,
|
|
2052
|
-
lastActivityAt: runLastAct
|
|
2053
|
-
};
|
|
2054
|
-
}
|
|
2049
|
+
const wf = db.prepare(`SELECT workflow_id AS workflowId, workflow_type AS workflowType,
|
|
2050
|
+
name, status, current_phase_index AS currentPhaseIndex,
|
|
2051
|
+
created_at AS createdAt, spec_path AS specPath
|
|
2052
|
+
FROM workflows WHERE collab_id = ?
|
|
2053
|
+
ORDER BY (status = 'running') DESC, created_at DESC
|
|
2054
|
+
LIMIT 1`).get(collabId);
|
|
2055
|
+
return buildWorkflowSummary(db, collabId, wf ?? null);
|
|
2056
|
+
}
|
|
2057
|
+
function listAllWorkflowSummaries(db, input) {
|
|
2058
|
+
const nowMs = Date.parse(input.now ?? (/* @__PURE__ */ new Date()).toISOString());
|
|
2059
|
+
const base = Number.isFinite(nowMs) ? nowMs : Date.now();
|
|
2060
|
+
const cutoff = new Date(Math.max(0, base - input.sinceMs)).toISOString();
|
|
2061
|
+
const rows = db.prepare(`SELECT w.workflow_id AS workflowId, w.workflow_type AS workflowType,
|
|
2062
|
+
w.name AS name, w.status AS status,
|
|
2063
|
+
w.current_phase_index AS currentPhaseIndex,
|
|
2064
|
+
w.created_at AS createdAt, w.spec_path AS specPath,
|
|
2065
|
+
w.collab_id AS collabId
|
|
2066
|
+
FROM workflows w
|
|
2067
|
+
LEFT JOIN relay_handoff h ON h.workflow_id = w.workflow_id
|
|
2068
|
+
GROUP BY w.workflow_id
|
|
2069
|
+
HAVING w.status IN ('running','paused')
|
|
2070
|
+
OR MAX(h.last_activity_at) >= ?
|
|
2071
|
+
OR (MAX(h.last_activity_at) IS NULL AND w.created_at >= ?)
|
|
2072
|
+
ORDER BY w.created_at DESC, w.rowid DESC`).all(cutoff, cutoff);
|
|
2073
|
+
return rows.map((r) => buildWorkflowSummary(db, r.collabId, r));
|
|
2055
2074
|
}
|
|
2056
2075
|
function listWorkflowsForCollab(db, collabId) {
|
|
2057
2076
|
const rows = db.prepare(`SELECT workflow_id AS workflowId, workflow_type AS workflowType,
|
|
@@ -4347,6 +4366,12 @@ function createControlService(db, events) {
|
|
|
4347
4366
|
...now !== void 0 ? { now } : {}
|
|
4348
4367
|
});
|
|
4349
4368
|
},
|
|
4369
|
+
listAllWorkflowSummaries(sinceMs, now) {
|
|
4370
|
+
return listAllWorkflowSummaries(db, {
|
|
4371
|
+
sinceMs,
|
|
4372
|
+
...now !== void 0 ? { now } : {}
|
|
4373
|
+
});
|
|
4374
|
+
},
|
|
4350
4375
|
listRunCostRows(collabId, workflowId) {
|
|
4351
4376
|
return listRunCostRows(db, { collabId, workflowId });
|
|
4352
4377
|
},
|
|
@@ -1916,7 +1916,8 @@ function listActiveCollabSummaries(db, input) {
|
|
|
1916
1916
|
GROUP BY c.collab_id
|
|
1917
1917
|
HAVING MAX(h.last_activity_at) >= ?
|
|
1918
1918
|
OR EXISTS (SELECT 1 FROM workflows w
|
|
1919
|
-
WHERE w.collab_id = c.collab_id
|
|
1919
|
+
WHERE w.collab_id = c.collab_id
|
|
1920
|
+
AND w.status IN ('running','paused'))`).all(cutoff);
|
|
1920
1921
|
const out = [];
|
|
1921
1922
|
const seen = /* @__PURE__ */ new Set();
|
|
1922
1923
|
for (const e of eligible) {
|
|
@@ -1941,87 +1942,105 @@ function listActiveCollabSummaries(db, input) {
|
|
|
1941
1942
|
}
|
|
1942
1943
|
return out;
|
|
1943
1944
|
}
|
|
1944
|
-
function
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
FROM workflow_phases WHERE workflow_id = ?
|
|
1963
|
-
ORDER BY (ended_at IS NULL) DESC, started_at DESC
|
|
1964
|
-
LIMIT 1`).get(wf.workflowId);
|
|
1965
|
-
if (ph) {
|
|
1966
|
-
currentPhaseRunId = ph.phaseRunId;
|
|
1967
|
-
phaseIndex = ph.phaseIndex;
|
|
1968
|
-
phaseName = ph.phaseName;
|
|
1969
|
-
chainId = ph.chainId;
|
|
1970
|
-
}
|
|
1945
|
+
function buildWorkflowSummary(db, collabId2, wf) {
|
|
1946
|
+
const collab = db.prepare(`SELECT display_name AS displayName, workspace_root AS workspaceRoot
|
|
1947
|
+
FROM collab WHERE collab_id = ?`).get(collabId2);
|
|
1948
|
+
let currentPhaseRunId = null;
|
|
1949
|
+
let phaseIndex = null;
|
|
1950
|
+
let phaseName = null;
|
|
1951
|
+
let chainId = null;
|
|
1952
|
+
if (wf) {
|
|
1953
|
+
const ph = db.prepare(`SELECT phase_run_id AS phaseRunId, phase_index AS phaseIndex,
|
|
1954
|
+
phase_name AS phaseName, chain_id AS chainId
|
|
1955
|
+
FROM workflow_phases WHERE workflow_id = ?
|
|
1956
|
+
ORDER BY (ended_at IS NULL) DESC, started_at DESC
|
|
1957
|
+
LIMIT 1`).get(wf.workflowId);
|
|
1958
|
+
if (ph) {
|
|
1959
|
+
currentPhaseRunId = ph.phaseRunId;
|
|
1960
|
+
phaseIndex = ph.phaseIndex;
|
|
1961
|
+
phaseName = ph.phaseName;
|
|
1962
|
+
chainId = ph.chainId;
|
|
1971
1963
|
}
|
|
1972
|
-
const chain = chainId ? db.prepare(`SELECT status, current_round AS currentRound, max_rounds AS maxRounds
|
|
1973
|
-
FROM relay_chains WHERE chain_id = ?`).get(chainId) : void 0;
|
|
1974
|
-
const turn = db.prepare(`SELECT turn_owner AS owner, waiting_agent AS waiting, handoff_state AS handoffState
|
|
1975
|
-
FROM relay_turn_state WHERE collab_id = ?`).get(e.collabId);
|
|
1976
|
-
const sessions = db.prepare(`SELECT agentType, healthState FROM (
|
|
1977
|
-
SELECT s.agent_type AS agentType,
|
|
1978
|
-
s.health_state AS healthState,
|
|
1979
|
-
ROW_NUMBER() OVER (
|
|
1980
|
-
PARTITION BY s.agent_type
|
|
1981
|
-
ORDER BY CASE WHEN sb.active_session_id = s.session_id
|
|
1982
|
-
THEN 0 ELSE 1 END ASC,
|
|
1983
|
-
s.registered_at DESC,
|
|
1984
|
-
s.rowid DESC
|
|
1985
|
-
) AS rn
|
|
1986
|
-
FROM session s
|
|
1987
|
-
LEFT JOIN session_binding sb
|
|
1988
|
-
ON sb.collab_id = s.collab_id
|
|
1989
|
-
AND sb.agent_type = s.agent_type
|
|
1990
|
-
WHERE s.collab_id = ?
|
|
1991
|
-
) ranked
|
|
1992
|
-
WHERE rn = 1
|
|
1993
|
-
ORDER BY agentType ASC`).all(e.collabId);
|
|
1994
|
-
const label = wf?.name && wf.name.trim() || collab?.displayName && collab.displayName.trim() || (collab?.workspaceRoot ? basename2(collab.workspaceRoot) : "") || e.collabId.slice(0, 12);
|
|
1995
|
-
const runLastActRow = wf ? db.prepare(`SELECT COALESCE(MAX(last_activity_at), '') AS lastAct
|
|
1996
|
-
FROM relay_handoff
|
|
1997
|
-
WHERE collab_id = ? AND workflow_id = ?`).get(e.collabId, wf.workflowId) : db.prepare(`SELECT COALESCE(MAX(last_activity_at), '') AS lastAct
|
|
1998
|
-
FROM relay_handoff
|
|
1999
|
-
WHERE collab_id = ? AND workflow_id IS NULL`).get(e.collabId);
|
|
2000
|
-
const runLastAct = runLastActRow?.lastAct ?? "";
|
|
2001
|
-
return {
|
|
2002
|
-
collabId: e.collabId,
|
|
2003
|
-
label,
|
|
2004
|
-
workspaceRoot: collab?.workspaceRoot ?? "",
|
|
2005
|
-
workflowId: wf?.workflowId ?? null,
|
|
2006
|
-
workflowType: wf?.workflowType ?? null,
|
|
2007
|
-
workflowStatus: wf?.status ?? null,
|
|
2008
|
-
currentPhaseRunId,
|
|
2009
|
-
phaseIndex,
|
|
2010
|
-
phaseName,
|
|
2011
|
-
currentRound: chain?.currentRound ?? null,
|
|
2012
|
-
maxRounds: chain?.maxRounds ?? null,
|
|
2013
|
-
chainStatus: chain?.status ?? null,
|
|
2014
|
-
turn: {
|
|
2015
|
-
owner: turn?.owner ?? "none",
|
|
2016
|
-
waiting: turn?.waiting ?? null,
|
|
2017
|
-
handoffState: turn?.handoffState ?? "idle"
|
|
2018
|
-
},
|
|
2019
|
-
sessions,
|
|
2020
|
-
workflowCreatedAt: wf?.createdAt ?? null,
|
|
2021
|
-
specPath: wf ? displayArtifactPath(wf.specPath, collab?.workspaceRoot ?? "") : null,
|
|
2022
|
-
lastActivityAt: runLastAct
|
|
2023
|
-
};
|
|
2024
1964
|
}
|
|
1965
|
+
const chain = chainId ? db.prepare(`SELECT status, current_round AS currentRound, max_rounds AS maxRounds
|
|
1966
|
+
FROM relay_chains WHERE chain_id = ?`).get(chainId) : void 0;
|
|
1967
|
+
const turn = db.prepare(`SELECT turn_owner AS owner, waiting_agent AS waiting, handoff_state AS handoffState
|
|
1968
|
+
FROM relay_turn_state WHERE collab_id = ?`).get(collabId2);
|
|
1969
|
+
const sessions = db.prepare(`SELECT agentType, healthState FROM (
|
|
1970
|
+
SELECT s.agent_type AS agentType,
|
|
1971
|
+
s.health_state AS healthState,
|
|
1972
|
+
ROW_NUMBER() OVER (
|
|
1973
|
+
PARTITION BY s.agent_type
|
|
1974
|
+
ORDER BY CASE WHEN sb.active_session_id = s.session_id
|
|
1975
|
+
THEN 0 ELSE 1 END ASC,
|
|
1976
|
+
s.registered_at DESC,
|
|
1977
|
+
s.rowid DESC
|
|
1978
|
+
) AS rn
|
|
1979
|
+
FROM session s
|
|
1980
|
+
LEFT JOIN session_binding sb
|
|
1981
|
+
ON sb.collab_id = s.collab_id
|
|
1982
|
+
AND sb.agent_type = s.agent_type
|
|
1983
|
+
WHERE s.collab_id = ?
|
|
1984
|
+
) ranked
|
|
1985
|
+
WHERE rn = 1
|
|
1986
|
+
ORDER BY agentType ASC`).all(collabId2);
|
|
1987
|
+
const label = wf?.name && wf.name.trim() || collab?.displayName && collab.displayName.trim() || (collab?.workspaceRoot ? basename2(collab.workspaceRoot) : "") || collabId2.slice(0, 12);
|
|
1988
|
+
const runLastActRow = wf ? db.prepare(`SELECT COALESCE(MAX(last_activity_at), '') AS lastAct
|
|
1989
|
+
FROM relay_handoff
|
|
1990
|
+
WHERE collab_id = ? AND workflow_id = ?`).get(collabId2, wf.workflowId) : db.prepare(`SELECT COALESCE(MAX(last_activity_at), '') AS lastAct
|
|
1991
|
+
FROM relay_handoff
|
|
1992
|
+
WHERE collab_id = ? AND workflow_id IS NULL`).get(collabId2);
|
|
1993
|
+
const runLastAct = runLastActRow?.lastAct ?? "";
|
|
1994
|
+
return {
|
|
1995
|
+
collabId: collabId2,
|
|
1996
|
+
label,
|
|
1997
|
+
workspaceRoot: collab?.workspaceRoot ?? "",
|
|
1998
|
+
workflowId: wf?.workflowId ?? null,
|
|
1999
|
+
workflowType: wf?.workflowType ?? null,
|
|
2000
|
+
workflowStatus: wf?.status ?? null,
|
|
2001
|
+
currentPhaseRunId,
|
|
2002
|
+
phaseIndex,
|
|
2003
|
+
phaseName,
|
|
2004
|
+
currentRound: chain?.currentRound ?? null,
|
|
2005
|
+
maxRounds: chain?.maxRounds ?? null,
|
|
2006
|
+
chainStatus: chain?.status ?? null,
|
|
2007
|
+
turn: {
|
|
2008
|
+
owner: turn?.owner ?? "none",
|
|
2009
|
+
waiting: turn?.waiting ?? null,
|
|
2010
|
+
handoffState: turn?.handoffState ?? "idle"
|
|
2011
|
+
},
|
|
2012
|
+
sessions,
|
|
2013
|
+
workflowCreatedAt: wf?.createdAt ?? null,
|
|
2014
|
+
specPath: wf ? displayArtifactPath(wf.specPath, collab?.workspaceRoot ?? "") : null,
|
|
2015
|
+
lastActivityAt: runLastAct
|
|
2016
|
+
};
|
|
2017
|
+
}
|
|
2018
|
+
function buildCollabSummary(db, collabId2) {
|
|
2019
|
+
const wf = db.prepare(`SELECT workflow_id AS workflowId, workflow_type AS workflowType,
|
|
2020
|
+
name, status, current_phase_index AS currentPhaseIndex,
|
|
2021
|
+
created_at AS createdAt, spec_path AS specPath
|
|
2022
|
+
FROM workflows WHERE collab_id = ?
|
|
2023
|
+
ORDER BY (status = 'running') DESC, created_at DESC
|
|
2024
|
+
LIMIT 1`).get(collabId2);
|
|
2025
|
+
return buildWorkflowSummary(db, collabId2, wf ?? null);
|
|
2026
|
+
}
|
|
2027
|
+
function listAllWorkflowSummaries(db, input) {
|
|
2028
|
+
const nowMs = Date.parse(input.now ?? (/* @__PURE__ */ new Date()).toISOString());
|
|
2029
|
+
const base = Number.isFinite(nowMs) ? nowMs : Date.now();
|
|
2030
|
+
const cutoff = new Date(Math.max(0, base - input.sinceMs)).toISOString();
|
|
2031
|
+
const rows = db.prepare(`SELECT w.workflow_id AS workflowId, w.workflow_type AS workflowType,
|
|
2032
|
+
w.name AS name, w.status AS status,
|
|
2033
|
+
w.current_phase_index AS currentPhaseIndex,
|
|
2034
|
+
w.created_at AS createdAt, w.spec_path AS specPath,
|
|
2035
|
+
w.collab_id AS collabId
|
|
2036
|
+
FROM workflows w
|
|
2037
|
+
LEFT JOIN relay_handoff h ON h.workflow_id = w.workflow_id
|
|
2038
|
+
GROUP BY w.workflow_id
|
|
2039
|
+
HAVING w.status IN ('running','paused')
|
|
2040
|
+
OR MAX(h.last_activity_at) >= ?
|
|
2041
|
+
OR (MAX(h.last_activity_at) IS NULL AND w.created_at >= ?)
|
|
2042
|
+
ORDER BY w.created_at DESC, w.rowid DESC`).all(cutoff, cutoff);
|
|
2043
|
+
return rows.map((r) => buildWorkflowSummary(db, r.collabId, r));
|
|
2025
2044
|
}
|
|
2026
2045
|
function listWorkflowsForCollab(db, collabId2) {
|
|
2027
2046
|
const rows = db.prepare(`SELECT workflow_id AS workflowId, workflow_type AS workflowType,
|
|
@@ -4317,6 +4336,12 @@ function createControlService(db, events) {
|
|
|
4317
4336
|
...now !== void 0 ? { now } : {}
|
|
4318
4337
|
});
|
|
4319
4338
|
},
|
|
4339
|
+
listAllWorkflowSummaries(sinceMs, now) {
|
|
4340
|
+
return listAllWorkflowSummaries(db, {
|
|
4341
|
+
sinceMs,
|
|
4342
|
+
...now !== void 0 ? { now } : {}
|
|
4343
|
+
});
|
|
4344
|
+
},
|
|
4320
4345
|
listRunCostRows(collabId2, workflowId) {
|
|
4321
4346
|
return listRunCostRows(db, { collabId: collabId2, workflowId });
|
|
4322
4347
|
},
|
|
@@ -6092,6 +6117,9 @@ function computeLiveness(snap) {
|
|
|
6092
6117
|
let why = null;
|
|
6093
6118
|
let stuck = false;
|
|
6094
6119
|
let liveOverride = null;
|
|
6120
|
+
if (terminal === "paused") {
|
|
6121
|
+
return { stuck: false, why: null, liveText: "paused" };
|
|
6122
|
+
}
|
|
6095
6123
|
if (terminal === "done") {
|
|
6096
6124
|
return { stuck: false, why: null, liveText: "done" };
|
|
6097
6125
|
} else if (terminal === "halted" || terminal === "canceled") {
|
package/dist/bin/whisper.js
CHANGED
|
@@ -2305,7 +2305,8 @@ function listActiveCollabSummaries(db, input) {
|
|
|
2305
2305
|
GROUP BY c.collab_id
|
|
2306
2306
|
HAVING MAX(h.last_activity_at) >= ?
|
|
2307
2307
|
OR EXISTS (SELECT 1 FROM workflows w
|
|
2308
|
-
WHERE w.collab_id = c.collab_id
|
|
2308
|
+
WHERE w.collab_id = c.collab_id
|
|
2309
|
+
AND w.status IN ('running','paused'))`).all(cutoff);
|
|
2309
2310
|
const out = [];
|
|
2310
2311
|
const seen = /* @__PURE__ */ new Set();
|
|
2311
2312
|
for (const e of eligible) {
|
|
@@ -2330,87 +2331,105 @@ function listActiveCollabSummaries(db, input) {
|
|
|
2330
2331
|
}
|
|
2331
2332
|
return out;
|
|
2332
2333
|
}
|
|
2334
|
+
function buildWorkflowSummary(db, collabId, wf) {
|
|
2335
|
+
const collab = db.prepare(`SELECT display_name AS displayName, workspace_root AS workspaceRoot
|
|
2336
|
+
FROM collab WHERE collab_id = ?`).get(collabId);
|
|
2337
|
+
let currentPhaseRunId = null;
|
|
2338
|
+
let phaseIndex = null;
|
|
2339
|
+
let phaseName = null;
|
|
2340
|
+
let chainId = null;
|
|
2341
|
+
if (wf) {
|
|
2342
|
+
const ph = db.prepare(`SELECT phase_run_id AS phaseRunId, phase_index AS phaseIndex,
|
|
2343
|
+
phase_name AS phaseName, chain_id AS chainId
|
|
2344
|
+
FROM workflow_phases WHERE workflow_id = ?
|
|
2345
|
+
ORDER BY (ended_at IS NULL) DESC, started_at DESC
|
|
2346
|
+
LIMIT 1`).get(wf.workflowId);
|
|
2347
|
+
if (ph) {
|
|
2348
|
+
currentPhaseRunId = ph.phaseRunId;
|
|
2349
|
+
phaseIndex = ph.phaseIndex;
|
|
2350
|
+
phaseName = ph.phaseName;
|
|
2351
|
+
chainId = ph.chainId;
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
const chain = chainId ? db.prepare(`SELECT status, current_round AS currentRound, max_rounds AS maxRounds
|
|
2355
|
+
FROM relay_chains WHERE chain_id = ?`).get(chainId) : void 0;
|
|
2356
|
+
const turn = db.prepare(`SELECT turn_owner AS owner, waiting_agent AS waiting, handoff_state AS handoffState
|
|
2357
|
+
FROM relay_turn_state WHERE collab_id = ?`).get(collabId);
|
|
2358
|
+
const sessions = db.prepare(`SELECT agentType, healthState FROM (
|
|
2359
|
+
SELECT s.agent_type AS agentType,
|
|
2360
|
+
s.health_state AS healthState,
|
|
2361
|
+
ROW_NUMBER() OVER (
|
|
2362
|
+
PARTITION BY s.agent_type
|
|
2363
|
+
ORDER BY CASE WHEN sb.active_session_id = s.session_id
|
|
2364
|
+
THEN 0 ELSE 1 END ASC,
|
|
2365
|
+
s.registered_at DESC,
|
|
2366
|
+
s.rowid DESC
|
|
2367
|
+
) AS rn
|
|
2368
|
+
FROM session s
|
|
2369
|
+
LEFT JOIN session_binding sb
|
|
2370
|
+
ON sb.collab_id = s.collab_id
|
|
2371
|
+
AND sb.agent_type = s.agent_type
|
|
2372
|
+
WHERE s.collab_id = ?
|
|
2373
|
+
) ranked
|
|
2374
|
+
WHERE rn = 1
|
|
2375
|
+
ORDER BY agentType ASC`).all(collabId);
|
|
2376
|
+
const label = wf?.name && wf.name.trim() || collab?.displayName && collab.displayName.trim() || (collab?.workspaceRoot ? basename2(collab.workspaceRoot) : "") || collabId.slice(0, 12);
|
|
2377
|
+
const runLastActRow = wf ? db.prepare(`SELECT COALESCE(MAX(last_activity_at), '') AS lastAct
|
|
2378
|
+
FROM relay_handoff
|
|
2379
|
+
WHERE collab_id = ? AND workflow_id = ?`).get(collabId, wf.workflowId) : db.prepare(`SELECT COALESCE(MAX(last_activity_at), '') AS lastAct
|
|
2380
|
+
FROM relay_handoff
|
|
2381
|
+
WHERE collab_id = ? AND workflow_id IS NULL`).get(collabId);
|
|
2382
|
+
const runLastAct = runLastActRow?.lastAct ?? "";
|
|
2383
|
+
return {
|
|
2384
|
+
collabId,
|
|
2385
|
+
label,
|
|
2386
|
+
workspaceRoot: collab?.workspaceRoot ?? "",
|
|
2387
|
+
workflowId: wf?.workflowId ?? null,
|
|
2388
|
+
workflowType: wf?.workflowType ?? null,
|
|
2389
|
+
workflowStatus: wf?.status ?? null,
|
|
2390
|
+
currentPhaseRunId,
|
|
2391
|
+
phaseIndex,
|
|
2392
|
+
phaseName,
|
|
2393
|
+
currentRound: chain?.currentRound ?? null,
|
|
2394
|
+
maxRounds: chain?.maxRounds ?? null,
|
|
2395
|
+
chainStatus: chain?.status ?? null,
|
|
2396
|
+
turn: {
|
|
2397
|
+
owner: turn?.owner ?? "none",
|
|
2398
|
+
waiting: turn?.waiting ?? null,
|
|
2399
|
+
handoffState: turn?.handoffState ?? "idle"
|
|
2400
|
+
},
|
|
2401
|
+
sessions,
|
|
2402
|
+
workflowCreatedAt: wf?.createdAt ?? null,
|
|
2403
|
+
specPath: wf ? displayArtifactPath(wf.specPath, collab?.workspaceRoot ?? "") : null,
|
|
2404
|
+
lastActivityAt: runLastAct
|
|
2405
|
+
};
|
|
2406
|
+
}
|
|
2333
2407
|
function buildCollabSummary(db, collabId) {
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
}
|
|
2360
|
-
}
|
|
2361
|
-
const chain = chainId ? db.prepare(`SELECT status, current_round AS currentRound, max_rounds AS maxRounds
|
|
2362
|
-
FROM relay_chains WHERE chain_id = ?`).get(chainId) : void 0;
|
|
2363
|
-
const turn = db.prepare(`SELECT turn_owner AS owner, waiting_agent AS waiting, handoff_state AS handoffState
|
|
2364
|
-
FROM relay_turn_state WHERE collab_id = ?`).get(e.collabId);
|
|
2365
|
-
const sessions = db.prepare(`SELECT agentType, healthState FROM (
|
|
2366
|
-
SELECT s.agent_type AS agentType,
|
|
2367
|
-
s.health_state AS healthState,
|
|
2368
|
-
ROW_NUMBER() OVER (
|
|
2369
|
-
PARTITION BY s.agent_type
|
|
2370
|
-
ORDER BY CASE WHEN sb.active_session_id = s.session_id
|
|
2371
|
-
THEN 0 ELSE 1 END ASC,
|
|
2372
|
-
s.registered_at DESC,
|
|
2373
|
-
s.rowid DESC
|
|
2374
|
-
) AS rn
|
|
2375
|
-
FROM session s
|
|
2376
|
-
LEFT JOIN session_binding sb
|
|
2377
|
-
ON sb.collab_id = s.collab_id
|
|
2378
|
-
AND sb.agent_type = s.agent_type
|
|
2379
|
-
WHERE s.collab_id = ?
|
|
2380
|
-
) ranked
|
|
2381
|
-
WHERE rn = 1
|
|
2382
|
-
ORDER BY agentType ASC`).all(e.collabId);
|
|
2383
|
-
const label = wf?.name && wf.name.trim() || collab?.displayName && collab.displayName.trim() || (collab?.workspaceRoot ? basename2(collab.workspaceRoot) : "") || e.collabId.slice(0, 12);
|
|
2384
|
-
const runLastActRow = wf ? db.prepare(`SELECT COALESCE(MAX(last_activity_at), '') AS lastAct
|
|
2385
|
-
FROM relay_handoff
|
|
2386
|
-
WHERE collab_id = ? AND workflow_id = ?`).get(e.collabId, wf.workflowId) : db.prepare(`SELECT COALESCE(MAX(last_activity_at), '') AS lastAct
|
|
2387
|
-
FROM relay_handoff
|
|
2388
|
-
WHERE collab_id = ? AND workflow_id IS NULL`).get(e.collabId);
|
|
2389
|
-
const runLastAct = runLastActRow?.lastAct ?? "";
|
|
2390
|
-
return {
|
|
2391
|
-
collabId: e.collabId,
|
|
2392
|
-
label,
|
|
2393
|
-
workspaceRoot: collab?.workspaceRoot ?? "",
|
|
2394
|
-
workflowId: wf?.workflowId ?? null,
|
|
2395
|
-
workflowType: wf?.workflowType ?? null,
|
|
2396
|
-
workflowStatus: wf?.status ?? null,
|
|
2397
|
-
currentPhaseRunId,
|
|
2398
|
-
phaseIndex,
|
|
2399
|
-
phaseName,
|
|
2400
|
-
currentRound: chain?.currentRound ?? null,
|
|
2401
|
-
maxRounds: chain?.maxRounds ?? null,
|
|
2402
|
-
chainStatus: chain?.status ?? null,
|
|
2403
|
-
turn: {
|
|
2404
|
-
owner: turn?.owner ?? "none",
|
|
2405
|
-
waiting: turn?.waiting ?? null,
|
|
2406
|
-
handoffState: turn?.handoffState ?? "idle"
|
|
2407
|
-
},
|
|
2408
|
-
sessions,
|
|
2409
|
-
workflowCreatedAt: wf?.createdAt ?? null,
|
|
2410
|
-
specPath: wf ? displayArtifactPath(wf.specPath, collab?.workspaceRoot ?? "") : null,
|
|
2411
|
-
lastActivityAt: runLastAct
|
|
2412
|
-
};
|
|
2413
|
-
}
|
|
2408
|
+
const wf = db.prepare(`SELECT workflow_id AS workflowId, workflow_type AS workflowType,
|
|
2409
|
+
name, status, current_phase_index AS currentPhaseIndex,
|
|
2410
|
+
created_at AS createdAt, spec_path AS specPath
|
|
2411
|
+
FROM workflows WHERE collab_id = ?
|
|
2412
|
+
ORDER BY (status = 'running') DESC, created_at DESC
|
|
2413
|
+
LIMIT 1`).get(collabId);
|
|
2414
|
+
return buildWorkflowSummary(db, collabId, wf ?? null);
|
|
2415
|
+
}
|
|
2416
|
+
function listAllWorkflowSummaries(db, input) {
|
|
2417
|
+
const nowMs = Date.parse(input.now ?? (/* @__PURE__ */ new Date()).toISOString());
|
|
2418
|
+
const base = Number.isFinite(nowMs) ? nowMs : Date.now();
|
|
2419
|
+
const cutoff = new Date(Math.max(0, base - input.sinceMs)).toISOString();
|
|
2420
|
+
const rows = db.prepare(`SELECT w.workflow_id AS workflowId, w.workflow_type AS workflowType,
|
|
2421
|
+
w.name AS name, w.status AS status,
|
|
2422
|
+
w.current_phase_index AS currentPhaseIndex,
|
|
2423
|
+
w.created_at AS createdAt, w.spec_path AS specPath,
|
|
2424
|
+
w.collab_id AS collabId
|
|
2425
|
+
FROM workflows w
|
|
2426
|
+
LEFT JOIN relay_handoff h ON h.workflow_id = w.workflow_id
|
|
2427
|
+
GROUP BY w.workflow_id
|
|
2428
|
+
HAVING w.status IN ('running','paused')
|
|
2429
|
+
OR MAX(h.last_activity_at) >= ?
|
|
2430
|
+
OR (MAX(h.last_activity_at) IS NULL AND w.created_at >= ?)
|
|
2431
|
+
ORDER BY w.created_at DESC, w.rowid DESC`).all(cutoff, cutoff);
|
|
2432
|
+
return rows.map((r) => buildWorkflowSummary(db, r.collabId, r));
|
|
2414
2433
|
}
|
|
2415
2434
|
function listWorkflowsForCollab(db, collabId) {
|
|
2416
2435
|
const rows = db.prepare(`SELECT workflow_id AS workflowId, workflow_type AS workflowType,
|
|
@@ -4748,6 +4767,12 @@ function createControlService(db, events) {
|
|
|
4748
4767
|
...now !== void 0 ? { now } : {}
|
|
4749
4768
|
});
|
|
4750
4769
|
},
|
|
4770
|
+
listAllWorkflowSummaries(sinceMs, now) {
|
|
4771
|
+
return listAllWorkflowSummaries(db, {
|
|
4772
|
+
sinceMs,
|
|
4773
|
+
...now !== void 0 ? { now } : {}
|
|
4774
|
+
});
|
|
4775
|
+
},
|
|
4751
4776
|
listRunCostRows(collabId, workflowId) {
|
|
4752
4777
|
return listRunCostRows(db, { collabId, workflowId });
|
|
4753
4778
|
},
|
|
@@ -6668,6 +6693,9 @@ function computeLiveness(snap) {
|
|
|
6668
6693
|
let why = null;
|
|
6669
6694
|
let stuck = false;
|
|
6670
6695
|
let liveOverride = null;
|
|
6696
|
+
if (terminal === "paused") {
|
|
6697
|
+
return { stuck: false, why: null, liveText: "paused" };
|
|
6698
|
+
}
|
|
6671
6699
|
if (terminal === "done") {
|
|
6672
6700
|
return { stuck: false, why: null, liveText: "done" };
|
|
6673
6701
|
} else if (terminal === "halted" || terminal === "canceled") {
|
|
@@ -6800,6 +6828,9 @@ function statusGlyph(input) {
|
|
|
6800
6828
|
if (input.workflowStatus === null) {
|
|
6801
6829
|
return { glyph: "\u25CC", color: THEME.muted, key: "idle" };
|
|
6802
6830
|
}
|
|
6831
|
+
if (input.workflowStatus === "paused") {
|
|
6832
|
+
return { glyph: "\u2016", color: THEME.muted, key: "paused" };
|
|
6833
|
+
}
|
|
6803
6834
|
if (input.workflowStatus === "done") {
|
|
6804
6835
|
return { glyph: "\u2713", color: THEME.ok, key: "done" };
|
|
6805
6836
|
}
|
|
@@ -6821,6 +6852,16 @@ var init_dashboard_glyph = __esm({
|
|
|
6821
6852
|
// src/runtime/dashboard-view.tsx
|
|
6822
6853
|
import { Box as Box2, Text as Text2, useInput as useInput2, useStdin as useStdin2 } from "ink";
|
|
6823
6854
|
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
6855
|
+
function ActionStatusLine(props) {
|
|
6856
|
+
if (props.confirm) {
|
|
6857
|
+
return /* @__PURE__ */ jsx3(Text2, { wrap: "truncate", color: THEME.warn, children: `${ACTION_VERB[props.confirm.action]} ${props.confirm.workflowId}? (y/n)` });
|
|
6858
|
+
}
|
|
6859
|
+
if (props.feedback) {
|
|
6860
|
+
const color = props.feedback.kind === "ok" ? THEME.ok : props.feedback.kind === "err" ? THEME.err : THEME.muted;
|
|
6861
|
+
return /* @__PURE__ */ jsx3(Text2, { wrap: "truncate", color, children: props.feedback.text });
|
|
6862
|
+
}
|
|
6863
|
+
return null;
|
|
6864
|
+
}
|
|
6824
6865
|
function abbreviateWorkflowType(full) {
|
|
6825
6866
|
const known = TYPE_ABBREVIATION[full];
|
|
6826
6867
|
if (known) return known;
|
|
@@ -6983,7 +7024,7 @@ function FullCard(props) {
|
|
|
6983
7024
|
}
|
|
6984
7025
|
function CompactCard(props) {
|
|
6985
7026
|
const { pane } = props;
|
|
6986
|
-
const statusWord = pane.statusKey === "done" ? "done" : pane.statusKey === "canceled" ? "canceled" : pane.statusKey === "stuck" ? "halted" : pane.statusKey === "idle" ? "idle" : "running";
|
|
7027
|
+
const statusWord = pane.statusKey === "done" ? "done" : pane.statusKey === "canceled" ? "canceled" : pane.statusKey === "stuck" ? "halted" : pane.statusKey === "idle" ? "idle" : pane.statusKey === "paused" ? "paused" : "running";
|
|
6987
7028
|
const glyph = statusGlyph({
|
|
6988
7029
|
workflowStatus: pane.statusKey === "idle" ? null : pane.statusKey === "stuck" ? "halted" : pane.statusKey,
|
|
6989
7030
|
stuck: false
|
|
@@ -7045,6 +7086,10 @@ function Wall(props) {
|
|
|
7045
7086
|
const paneWidth = Math.floor(props.cols / colsCount);
|
|
7046
7087
|
let globalIdx = 0;
|
|
7047
7088
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: props.cols, children: [
|
|
7089
|
+
props.counts ? /* @__PURE__ */ jsx3(Text2, { wrap: "truncate", children: SUMMARY_SEGMENTS.map((seg, i) => {
|
|
7090
|
+
const n = props.counts[seg.key];
|
|
7091
|
+
return /* @__PURE__ */ jsx3(Text2, { color: n > 0 ? seg.color : THEME.muted, children: (i > 0 ? " " : "") + `${seg.glyph} ${n} ${seg.label}` }, seg.key);
|
|
7092
|
+
}) }) : null,
|
|
7048
7093
|
state.sections.map((sec) => {
|
|
7049
7094
|
const rows = [];
|
|
7050
7095
|
for (let i = 0; i < sec.panes.length; i += colsCount) {
|
|
@@ -7062,7 +7107,7 @@ function Wall(props) {
|
|
|
7062
7107
|
selected,
|
|
7063
7108
|
width: paneWidth
|
|
7064
7109
|
},
|
|
7065
|
-
pane.collabId
|
|
7110
|
+
pane.workflowId ?? pane.collabId
|
|
7066
7111
|
) : /* @__PURE__ */ jsx3(
|
|
7067
7112
|
CompactCard,
|
|
7068
7113
|
{
|
|
@@ -7070,13 +7115,20 @@ function Wall(props) {
|
|
|
7070
7115
|
selected,
|
|
7071
7116
|
width: paneWidth
|
|
7072
7117
|
},
|
|
7073
|
-
pane.collabId
|
|
7118
|
+
pane.workflowId ?? pane.collabId
|
|
7074
7119
|
);
|
|
7075
7120
|
}) }, ri))
|
|
7076
7121
|
] }, sec.group);
|
|
7077
7122
|
}),
|
|
7078
|
-
/* @__PURE__ */ jsx3(Text2, { color: THEME.muted, children: `page ${state.page + 1}/${Math.max(1, state.pageCount)} \xB7 ${state.totalRuns} runs \xB7 \u2191\u2193/jk select \xB7 \u21B5 inspect \xB7 [ ] page \xB7 q quit` }),
|
|
7079
|
-
/* @__PURE__ */ jsx3(Text2, { color: THEME.muted, children: "\u25CF running \u26A0 stuck/halted \u2713 done \u2716 canceled \u25CC idle" })
|
|
7123
|
+
/* @__PURE__ */ jsx3(Text2, { color: THEME.muted, children: `page ${state.page + 1}/${Math.max(1, state.pageCount)} \xB7 ${props.showAll ? `${state.totalRuns} runs (every run, unmasked)` : `${state.totalRuns} collabs (one latest run each)`} \xB7 \u2191\u2193/jk select \xB7 \u21B5 inspect \xB7 p/r/c act \xB7 [ ] page \xB7 q quit` }),
|
|
7124
|
+
/* @__PURE__ */ jsx3(Text2, { color: THEME.muted, children: "\u25CF running \u2016 paused \u26A0 stuck/halted \u2713 done \u2716 canceled \u25CC idle" }),
|
|
7125
|
+
/* @__PURE__ */ jsx3(
|
|
7126
|
+
ActionStatusLine,
|
|
7127
|
+
{
|
|
7128
|
+
...props.confirm != null ? { confirm: props.confirm } : {},
|
|
7129
|
+
...props.feedback != null ? { feedback: props.feedback } : {}
|
|
7130
|
+
}
|
|
7131
|
+
)
|
|
7080
7132
|
] });
|
|
7081
7133
|
}
|
|
7082
7134
|
function TabBar(props) {
|
|
@@ -7113,7 +7165,7 @@ function Inspector(props) {
|
|
|
7113
7165
|
] }),
|
|
7114
7166
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7115
7167
|
/* @__PURE__ */ jsx3(TabBar, { active: props.section }),
|
|
7116
|
-
/* @__PURE__ */ jsx3(Text2, { color: THEME.muted, children: ` 1-4 section${props.section === "live" ? " \xB7 \u2191\u2193/g/G/f scroll" : ""} \xB7 Esc wall \xB7 q quit` })
|
|
7168
|
+
/* @__PURE__ */ jsx3(Text2, { color: THEME.muted, children: ` 1-4 section${props.section === "live" ? " \xB7 \u2191\u2193/g/G/f scroll" : ""} \xB7 p/r/c act \xB7 Esc wall \xB7 q quit` })
|
|
7117
7169
|
] }),
|
|
7118
7170
|
props.section === "live" ? /* @__PURE__ */ jsx3(
|
|
7119
7171
|
RelayView,
|
|
@@ -7127,8 +7179,7 @@ function Inspector(props) {
|
|
|
7127
7179
|
s.workflowHistory.length > 0 ? /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
7128
7180
|
/* @__PURE__ */ jsx3(Text2, { wrap: "truncate", color: THEME.muted, children: `WORKFLOW HISTORY (${s.workflowHistory.length})` }),
|
|
7129
7181
|
s.workflowHistory.map((w) => {
|
|
7130
|
-
const
|
|
7131
|
-
const g = statusGlyph({ workflowStatus: wfStatus, stuck: false });
|
|
7182
|
+
const g = statusGlyph({ workflowStatus: w.status, stuck: false });
|
|
7132
7183
|
return /* @__PURE__ */ jsxs2(
|
|
7133
7184
|
Text2,
|
|
7134
7185
|
{
|
|
@@ -7180,7 +7231,14 @@ function Inspector(props) {
|
|
|
7180
7231
|
] }) : /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
7181
7232
|
/* @__PURE__ */ jsx3(Text2, { wrap: "truncate", children: `total ${fmtDur(s.cost.totalMs)} \xB7 in \u2248${s.cost.estInputTokens} \xB7 out \u2248${s.cost.estOutputTokens} (est, not metered)` }),
|
|
7182
7233
|
s.cost.perPhase.map((p, i) => /* @__PURE__ */ jsx3(Text2, { wrap: "truncate", color: THEME.muted, children: `${p.phaseName} in \u2248${p.estInTokens} out \u2248${p.estOutTokens} ${p.durationMs == null ? "\u2013" : fmtDur(p.durationMs)}` }, i))
|
|
7183
|
-
] })
|
|
7234
|
+
] }),
|
|
7235
|
+
/* @__PURE__ */ jsx3(
|
|
7236
|
+
ActionStatusLine,
|
|
7237
|
+
{
|
|
7238
|
+
...props.confirm != null ? { confirm: props.confirm } : {},
|
|
7239
|
+
...props.feedback != null ? { feedback: props.feedback } : {}
|
|
7240
|
+
}
|
|
7241
|
+
)
|
|
7184
7242
|
] });
|
|
7185
7243
|
}
|
|
7186
7244
|
function DashInput(props) {
|
|
@@ -7196,7 +7254,7 @@ function DashboardApp(props) {
|
|
|
7196
7254
|
const { isRawModeSupported } = useStdin2();
|
|
7197
7255
|
return isRawModeSupported ? /* @__PURE__ */ jsx3(DashInput, { onKey: props.onKey, children: props.node }) : props.node;
|
|
7198
7256
|
}
|
|
7199
|
-
var MIN_PANE_COLS, NARROW_PANE_COLS, BAR_FILLED, BAR_EMPTY, TYPE_ABBREVIATION;
|
|
7257
|
+
var MIN_PANE_COLS, NARROW_PANE_COLS, BAR_FILLED, BAR_EMPTY, SUMMARY_SEGMENTS, ACTION_VERB, TYPE_ABBREVIATION;
|
|
7200
7258
|
var init_dashboard_view = __esm({
|
|
7201
7259
|
"src/runtime/dashboard-view.tsx"() {
|
|
7202
7260
|
"use strict";
|
|
@@ -7208,6 +7266,19 @@ var init_dashboard_view = __esm({
|
|
|
7208
7266
|
NARROW_PANE_COLS = 48;
|
|
7209
7267
|
BAR_FILLED = "\u25B0";
|
|
7210
7268
|
BAR_EMPTY = "\u25B1";
|
|
7269
|
+
SUMMARY_SEGMENTS = [
|
|
7270
|
+
{ key: "running", glyph: "\u25CF", color: THEME.accent, label: "running" },
|
|
7271
|
+
{ key: "paused", glyph: "\u2016", color: THEME.muted, label: "paused" },
|
|
7272
|
+
{ key: "stuck", glyph: "\u26A0", color: THEME.err, label: "stuck" },
|
|
7273
|
+
{ key: "done", glyph: "\u2713", color: THEME.ok, label: "done" },
|
|
7274
|
+
{ key: "canceled", glyph: "\u2716", color: THEME.err, label: "canceled" },
|
|
7275
|
+
{ key: "idle", glyph: "\u25CC", color: THEME.muted, label: "idle" }
|
|
7276
|
+
];
|
|
7277
|
+
ACTION_VERB = {
|
|
7278
|
+
pause: "Pause",
|
|
7279
|
+
resume: "Resume",
|
|
7280
|
+
cancel: "Cancel"
|
|
7281
|
+
};
|
|
7211
7282
|
TYPE_ABBREVIATION = {
|
|
7212
7283
|
"complex-bug-fixing": "bugfix",
|
|
7213
7284
|
"spec-driven-development": "sdd",
|
|
@@ -7237,6 +7308,9 @@ function abbreviateCwd(absPath, home) {
|
|
|
7237
7308
|
if (home && (p === home || p.startsWith(home + "/"))) p = "~" + p.slice(home.length);
|
|
7238
7309
|
return p;
|
|
7239
7310
|
}
|
|
7311
|
+
function runKey(s) {
|
|
7312
|
+
return s.workflowId ?? s.collabId;
|
|
7313
|
+
}
|
|
7240
7314
|
function partitionWallGroups(summaries) {
|
|
7241
7315
|
const active = [];
|
|
7242
7316
|
const idleManual = [];
|
|
@@ -7244,7 +7318,8 @@ function partitionWallGroups(summaries) {
|
|
|
7244
7318
|
const doneCanceled = [];
|
|
7245
7319
|
for (const s of summaries) {
|
|
7246
7320
|
if (s.workflowStatus === null) idleManual.push(s);
|
|
7247
|
-
else if (s.workflowStatus === "running"
|
|
7321
|
+
else if (s.workflowStatus === "running" || s.workflowStatus === "paused")
|
|
7322
|
+
active.push(s);
|
|
7248
7323
|
else if (s.workflowStatus === "halted") halted.push(s);
|
|
7249
7324
|
else if (s.workflowStatus === "done" || s.workflowStatus === "canceled")
|
|
7250
7325
|
doneCanceled.push(s);
|
|
@@ -7260,6 +7335,33 @@ function partitionWallGroups(summaries) {
|
|
|
7260
7335
|
doneCanceled.sort((a, b) => cmpDesc(recencyKey(a), recencyKey(b)));
|
|
7261
7336
|
return { active, idleManual, halted, doneCanceled };
|
|
7262
7337
|
}
|
|
7338
|
+
function summarizeWall(summaries) {
|
|
7339
|
+
const counts = {
|
|
7340
|
+
running: 0,
|
|
7341
|
+
paused: 0,
|
|
7342
|
+
stuck: 0,
|
|
7343
|
+
done: 0,
|
|
7344
|
+
canceled: 0,
|
|
7345
|
+
idle: 0
|
|
7346
|
+
};
|
|
7347
|
+
for (const s of summaries) {
|
|
7348
|
+
const st = s.workflowStatus;
|
|
7349
|
+
if (st === null) counts.idle++;
|
|
7350
|
+
else if (st === "halted") counts.stuck++;
|
|
7351
|
+
else if (st === "running") {
|
|
7352
|
+
if (isStuckRunning(s)) counts.stuck++;
|
|
7353
|
+
else counts.running++;
|
|
7354
|
+
} else if (st === "paused") counts.paused++;
|
|
7355
|
+
else if (st === "done") counts.done++;
|
|
7356
|
+
else if (st === "canceled") counts.canceled++;
|
|
7357
|
+
}
|
|
7358
|
+
return counts;
|
|
7359
|
+
}
|
|
7360
|
+
function actionsForStatus(status) {
|
|
7361
|
+
if (status === "running") return ["pause", "cancel"];
|
|
7362
|
+
if (status === "paused" || status === "halted") return ["resume", "cancel"];
|
|
7363
|
+
return [];
|
|
7364
|
+
}
|
|
7263
7365
|
function cardKindFor(group) {
|
|
7264
7366
|
return group === "active" ? "full" : "compact";
|
|
7265
7367
|
}
|
|
@@ -7573,7 +7675,7 @@ function buildWallState(input) {
|
|
|
7573
7675
|
label: sec.label,
|
|
7574
7676
|
cardKind: sec.cardKind,
|
|
7575
7677
|
panes: sec.cards.map((sum) => {
|
|
7576
|
-
const snap = input.snapshots[sum.collabId] ?? {
|
|
7678
|
+
const snap = input.snapshots[runKey(sum)] ?? input.snapshots[sum.collabId] ?? {
|
|
7577
7679
|
handoffs: [],
|
|
7578
7680
|
phaseRuns: [],
|
|
7579
7681
|
totalPhases: 0
|
|
@@ -7682,13 +7784,19 @@ function createDashboardRuntime(input) {
|
|
|
7682
7784
|
let inspectorSection = "live";
|
|
7683
7785
|
let wallPage = 0;
|
|
7684
7786
|
let wallSelected = 0;
|
|
7685
|
-
let
|
|
7787
|
+
let lastPaneRuns = [];
|
|
7788
|
+
let pendingConfirm = null;
|
|
7789
|
+
let actionFeedback = null;
|
|
7790
|
+
let actionFeedbackAtMs = 0;
|
|
7791
|
+
const nowMs = input.nowMs ?? (() => Date.now());
|
|
7792
|
+
const FEEDBACK_TTL_MS = 4e3;
|
|
7686
7793
|
const viewport = { offset: 0, follow: true };
|
|
7687
7794
|
let loopResolve;
|
|
7688
7795
|
const loopDone = new Promise((r) => loopResolve = r);
|
|
7689
7796
|
const cols = input.stdout.columns ?? 120;
|
|
7690
7797
|
const rows = input.stdout.rows ?? 40;
|
|
7691
7798
|
const windowMs = resolveDashboardWindowMs(input.windowMs);
|
|
7799
|
+
const showAll = input.showAll ?? false;
|
|
7692
7800
|
let pendingSig = "";
|
|
7693
7801
|
let lastSig = null;
|
|
7694
7802
|
let renderCount = 0;
|
|
@@ -7816,9 +7924,58 @@ function createDashboardRuntime(input) {
|
|
|
7816
7924
|
selectedWorkflowId: inspectorWorkflowId
|
|
7817
7925
|
});
|
|
7818
7926
|
}
|
|
7927
|
+
function setFeedback(kind, text) {
|
|
7928
|
+
actionFeedback = { kind, text };
|
|
7929
|
+
actionFeedbackAtMs = nowMs();
|
|
7930
|
+
}
|
|
7931
|
+
function expireFeedback() {
|
|
7932
|
+
if (actionFeedback && nowMs() - actionFeedbackAtMs >= FEEDBACK_TTL_MS) {
|
|
7933
|
+
actionFeedback = null;
|
|
7934
|
+
}
|
|
7935
|
+
}
|
|
7936
|
+
function freshStatusFor(collabId, workflowId) {
|
|
7937
|
+
const sums = showAll ? input.broker.control.listAllWorkflowSummaries(windowMs, (/* @__PURE__ */ new Date()).toISOString()) : input.broker.control.listActiveCollabSummaries(windowMs, (/* @__PURE__ */ new Date()).toISOString());
|
|
7938
|
+
const s = sums.find((x) => x.collabId === collabId && x.workflowId === workflowId);
|
|
7939
|
+
return s?.workflowStatus ?? null;
|
|
7940
|
+
}
|
|
7941
|
+
function requestAction(target, action) {
|
|
7942
|
+
if (!target.workflowId) {
|
|
7943
|
+
setFeedback("hint", "no workflow on this card");
|
|
7944
|
+
return;
|
|
7945
|
+
}
|
|
7946
|
+
const status = freshStatusFor(target.collabId, target.workflowId);
|
|
7947
|
+
if (!actionsForStatus(status).includes(action)) {
|
|
7948
|
+
setFeedback("hint", `${action} not available (${status ?? "no workflow"})`);
|
|
7949
|
+
return;
|
|
7950
|
+
}
|
|
7951
|
+
pendingConfirm = { workflowId: target.workflowId, action };
|
|
7952
|
+
}
|
|
7953
|
+
function executeConfirmed() {
|
|
7954
|
+
if (!pendingConfirm) return;
|
|
7955
|
+
const { workflowId, action } = pendingConfirm;
|
|
7956
|
+
pendingConfirm = null;
|
|
7957
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7958
|
+
const c = input.broker.control;
|
|
7959
|
+
try {
|
|
7960
|
+
if (action === "pause") c.pauseWorkflow({ workflowId, now });
|
|
7961
|
+
else if (action === "resume") c.resumeWorkflow({ workflowId, now });
|
|
7962
|
+
else c.cancelWorkflow({ workflowId, now });
|
|
7963
|
+
const verb = action === "pause" ? "paused" : action === "resume" ? "resumed" : "canceled";
|
|
7964
|
+
setFeedback("ok", `${verb} ${workflowId}`);
|
|
7965
|
+
} catch (err) {
|
|
7966
|
+
setFeedback("err", err instanceof Error ? err.message : String(err));
|
|
7967
|
+
}
|
|
7968
|
+
}
|
|
7969
|
+
function actionForKey(key) {
|
|
7970
|
+
if (key === "p") return "pause";
|
|
7971
|
+
if (key === "r") return "resume";
|
|
7972
|
+
if (key === "c") return "cancel";
|
|
7973
|
+
return null;
|
|
7974
|
+
}
|
|
7819
7975
|
function node() {
|
|
7820
7976
|
const c = input.broker.control;
|
|
7821
7977
|
const isoNow = (/* @__PURE__ */ new Date()).toISOString();
|
|
7978
|
+
expireFeedback();
|
|
7822
7979
|
if (mode === "inspector" && inspectorCollabId) {
|
|
7823
7980
|
const st = inspectorState();
|
|
7824
7981
|
if (st) {
|
|
@@ -7829,7 +7986,9 @@ function createDashboardRuntime(input) {
|
|
|
7829
7986
|
label: inspectorLabel,
|
|
7830
7987
|
type: inspectorType,
|
|
7831
7988
|
cols,
|
|
7832
|
-
rows
|
|
7989
|
+
rows,
|
|
7990
|
+
confirm: pendingConfirm,
|
|
7991
|
+
feedback: actionFeedback
|
|
7833
7992
|
})}`;
|
|
7834
7993
|
return createElement2(Inspector, {
|
|
7835
7994
|
state: st,
|
|
@@ -7839,11 +7998,13 @@ function createDashboardRuntime(input) {
|
|
|
7839
7998
|
rows,
|
|
7840
7999
|
label: inspectorLabel,
|
|
7841
8000
|
workflowStatus: inspectorWorkflowStatus,
|
|
7842
|
-
workflowType: inspectorType
|
|
8001
|
+
workflowType: inspectorType,
|
|
8002
|
+
confirm: pendingConfirm,
|
|
8003
|
+
feedback: actionFeedback
|
|
7843
8004
|
});
|
|
7844
8005
|
}
|
|
7845
8006
|
}
|
|
7846
|
-
const summaries = c.listActiveCollabSummaries(windowMs, isoNow);
|
|
8007
|
+
const summaries = showAll ? c.listAllWorkflowSummaries(windowMs, isoNow) : c.listActiveCollabSummaries(windowMs, isoNow);
|
|
7847
8008
|
const groups = partitionWallGroups(summaries);
|
|
7848
8009
|
const allocPreview = allocateWallSections({
|
|
7849
8010
|
groups,
|
|
@@ -7859,7 +8020,7 @@ function createDashboardRuntime(input) {
|
|
|
7859
8020
|
});
|
|
7860
8021
|
const phaseRaw = s.workflowId ? c.getWorkflowPhaseRuns(s.workflowId) : [];
|
|
7861
8022
|
const def = s.workflowType ? getWorkflowDefinition(s.workflowType) : null;
|
|
7862
|
-
snapshots[s
|
|
8023
|
+
snapshots[runKey(s)] = {
|
|
7863
8024
|
handoffs,
|
|
7864
8025
|
phaseRuns: toPhaseRuns(phaseRaw),
|
|
7865
8026
|
totalPhases: def ? def.phases.length : 0
|
|
@@ -7884,9 +8045,10 @@ function createDashboardRuntime(input) {
|
|
|
7884
8045
|
});
|
|
7885
8046
|
wallPage = wallState.page;
|
|
7886
8047
|
wallSelected = wallState.selected;
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
8048
|
+
lastPaneRuns = wallState.panes.map((p) => ({ collabId: p.collabId, workflowId: p.workflowId }));
|
|
8049
|
+
const counts = summarizeWall(summaries);
|
|
8050
|
+
pendingSig = `w:${JSON.stringify({ wallState, cols, rows, counts, confirm: pendingConfirm, feedback: actionFeedback })}`;
|
|
8051
|
+
return createElement2(Wall, { state: wallState, cols, rows, showAll, counts, confirm: pendingConfirm, feedback: actionFeedback });
|
|
7890
8052
|
}
|
|
7891
8053
|
const inkOptions = {
|
|
7892
8054
|
stdout: input.stdout,
|
|
@@ -7923,6 +8085,12 @@ function createDashboardRuntime(input) {
|
|
|
7923
8085
|
}
|
|
7924
8086
|
}
|
|
7925
8087
|
function handleKey(ev) {
|
|
8088
|
+
if (pendingConfirm) {
|
|
8089
|
+
if (ev.key === "y" || ev.key === "\r" || ev.key === "\n") executeConfirmed();
|
|
8090
|
+
else if (ev.key === "n" || ev.escape) pendingConfirm = null;
|
|
8091
|
+
rerender();
|
|
8092
|
+
return;
|
|
8093
|
+
}
|
|
7926
8094
|
if (mode === "wall") {
|
|
7927
8095
|
if (ev.upArrow || ev.key === "k")
|
|
7928
8096
|
wallSelected = Math.max(0, wallSelected - 1);
|
|
@@ -7930,23 +8098,31 @@ function createDashboardRuntime(input) {
|
|
|
7930
8098
|
else if (ev.key === "[") wallPage = Math.max(0, wallPage - 1);
|
|
7931
8099
|
else if (ev.key === "]") wallPage = wallPage + 1;
|
|
7932
8100
|
else if (ev.key === "\r" || ev.key === "\n") {
|
|
7933
|
-
const
|
|
7934
|
-
if (
|
|
7935
|
-
const sums = input.broker.control.
|
|
8101
|
+
const run = lastPaneRuns[wallSelected];
|
|
8102
|
+
if (run) {
|
|
8103
|
+
const sums = showAll ? input.broker.control.listAllWorkflowSummaries(
|
|
8104
|
+
windowMs,
|
|
8105
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
8106
|
+
) : input.broker.control.listActiveCollabSummaries(
|
|
7936
8107
|
windowMs,
|
|
7937
8108
|
(/* @__PURE__ */ new Date()).toISOString()
|
|
7938
8109
|
);
|
|
7939
|
-
const s = sums.find(
|
|
7940
|
-
|
|
7941
|
-
|
|
8110
|
+
const s = sums.find(
|
|
8111
|
+
(x) => x.collabId === run.collabId && x.workflowId === run.workflowId
|
|
8112
|
+
);
|
|
8113
|
+
inspectorCollabId = run.collabId;
|
|
8114
|
+
inspectorWorkflowId = run.workflowId;
|
|
7942
8115
|
inspectorType = s?.workflowType ?? null;
|
|
7943
|
-
inspectorLabel = s?.label ?? collabId;
|
|
8116
|
+
inspectorLabel = s?.label ?? run.collabId;
|
|
7944
8117
|
inspectorWorkflowStatus = s?.workflowStatus ?? null;
|
|
7945
8118
|
inspectorSection = "live";
|
|
7946
8119
|
viewport.offset = 0;
|
|
7947
8120
|
viewport.follow = true;
|
|
7948
8121
|
mode = "inspector";
|
|
7949
8122
|
}
|
|
8123
|
+
} else if (actionForKey(ev.key)) {
|
|
8124
|
+
const run = lastPaneRuns[wallSelected];
|
|
8125
|
+
if (run) requestAction(run, actionForKey(ev.key));
|
|
7950
8126
|
} else if (ev.key === "q") stopping = true;
|
|
7951
8127
|
} else {
|
|
7952
8128
|
if (ev.key === "1") inspectorSection = "live";
|
|
@@ -7955,7 +8131,14 @@ function createDashboardRuntime(input) {
|
|
|
7955
8131
|
else if (ev.key === "4") inspectorSection = "cost";
|
|
7956
8132
|
else if (ev.escape) mode = "wall";
|
|
7957
8133
|
else if (ev.key === "q") stopping = true;
|
|
7958
|
-
else if (
|
|
8134
|
+
else if (actionForKey(ev.key)) {
|
|
8135
|
+
if (inspectorCollabId) {
|
|
8136
|
+
requestAction(
|
|
8137
|
+
{ collabId: inspectorCollabId, workflowId: inspectorWorkflowId },
|
|
8138
|
+
actionForKey(ev.key)
|
|
8139
|
+
);
|
|
8140
|
+
}
|
|
8141
|
+
} else if (inspectorSection === "live") {
|
|
7959
8142
|
const visibleH = logViewportHeight(rows);
|
|
7960
8143
|
const linesLen = inspectorState()?.live.logLines.length ?? 0;
|
|
7961
8144
|
const tailStart = Math.max(0, linesLen - visibleH);
|
|
@@ -8021,7 +8204,10 @@ function createDashboardRuntime(input) {
|
|
|
8021
8204
|
}),
|
|
8022
8205
|
__renderCount: () => renderCount,
|
|
8023
8206
|
__recycles: () => recycles,
|
|
8024
|
-
__clears: () => clearCount
|
|
8207
|
+
__clears: () => clearCount,
|
|
8208
|
+
__inspectorWorkflowId: () => inspectorWorkflowId,
|
|
8209
|
+
__pendingConfirm: () => pendingConfirm,
|
|
8210
|
+
__actionFeedback: () => actionFeedback
|
|
8025
8211
|
};
|
|
8026
8212
|
}
|
|
8027
8213
|
var DEFAULT_WINDOW_MS;
|
|
@@ -12870,8 +13056,8 @@ function compareSemver(a, b) {
|
|
|
12870
13056
|
var EZIO_PROVENANCE = {
|
|
12871
13057
|
ezioCliVersion: "0.2.0-beta.5",
|
|
12872
13058
|
ezioGitSha: "45823b4",
|
|
12873
|
-
builtAt: "2026-06-
|
|
12874
|
-
whisperVersion: "0.8.
|
|
13059
|
+
builtAt: "2026-06-25T03:27:07.318Z",
|
|
13060
|
+
whisperVersion: "0.8.1"
|
|
12875
13061
|
};
|
|
12876
13062
|
|
|
12877
13063
|
// src/ezio-provenance-types.ts
|
|
@@ -16373,10 +16559,12 @@ async function runCollabDashboard(input) {
|
|
|
16373
16559
|
runBrokerDaemonSweep: false
|
|
16374
16560
|
});
|
|
16375
16561
|
const dashboardId = `dash_${randomBytes4(9).toString("base64url")}`;
|
|
16376
|
-
const
|
|
16562
|
+
const showAll = input?.showAll ?? false;
|
|
16563
|
+
const runtime = input?.__createRuntime?.(broker, dashboardId, stdout, { showAll }) ?? createDashboardRuntime({
|
|
16377
16564
|
broker,
|
|
16378
16565
|
dashboardId,
|
|
16379
16566
|
stdout,
|
|
16567
|
+
showAll,
|
|
16380
16568
|
...input?.windowMs != null ? { windowMs: input.windowMs } : {}
|
|
16381
16569
|
});
|
|
16382
16570
|
let stoppedBySignal = false;
|
|
@@ -17473,6 +17661,9 @@ Not attached (stdin/stdout is not a TTY). To view the tmux session, run:
|
|
|
17473
17661
|
collab.command("dashboard").description("Full-screen dashboard: live wall of recently-active runs + per-run inspector").option(
|
|
17474
17662
|
"--window <duration>",
|
|
17475
17663
|
"Eligible-collab activity window. Accepts ms or Ns/Nm/Nh/Nd (e.g. 45s, 30m, 2h, 1d), or 'all' for no limit. Default: 30m."
|
|
17664
|
+
).option(
|
|
17665
|
+
"--all",
|
|
17666
|
+
"Show one card per workflow RUN (no per-collab masking). Still respects --window per run; pair with '--window all' for the complete run ledger."
|
|
17476
17667
|
).action(async (opts) => {
|
|
17477
17668
|
const { parseDashboardWindow: parseDashboardWindow2 } = await Promise.resolve().then(() => (init_dashboard(), dashboard_exports));
|
|
17478
17669
|
const windowMs = opts.window != null ? parseDashboardWindow2(opts.window) : null;
|
|
@@ -17482,9 +17673,10 @@ Not attached (stdin/stdout is not a TTY). To view the tmux session, run:
|
|
|
17482
17673
|
);
|
|
17483
17674
|
process.exit(2);
|
|
17484
17675
|
}
|
|
17485
|
-
await runCollabDashboard(
|
|
17486
|
-
windowMs != null ? { windowMs } :
|
|
17487
|
-
|
|
17676
|
+
await runCollabDashboard({
|
|
17677
|
+
...windowMs != null ? { windowMs } : {},
|
|
17678
|
+
...opts.all ? { showAll: true } : {}
|
|
17679
|
+
});
|
|
17488
17680
|
});
|
|
17489
17681
|
collab.command("stop").description("Stop the active collaboration session").option(
|
|
17490
17682
|
"--collab <id>",
|