@volter/supercode-orchestrator 0.5.109 → 0.5.110
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/board/dispatch.mjs +40 -16
- package/board/machine-fleet.mjs +3 -1
- package/package.json +1 -1
package/board/dispatch.mjs
CHANGED
|
@@ -1117,7 +1117,12 @@ async function observeIdle(board, fleet, settings, report, entry, effects) {
|
|
|
1117
1117
|
};
|
|
1118
1118
|
const managers = [].concat(entry.params.managers ?? []).filter(Boolean);
|
|
1119
1119
|
const running = board.db.prepare(entry.workflow.run ? "SELECT id FROM tasks WHERE current_run_id IS NOT NULL" : "SELECT id FROM tasks WHERE status = 'running'").all();
|
|
1120
|
-
|
|
1120
|
+
// every running card a session is on: a session waiting on its user is told once, naming them all
|
|
1121
|
+
const cardsOf = new Map();
|
|
1122
|
+
for (const { id } of running) {
|
|
1123
|
+
const session = currentRun(board.db, id)?.metadata?.supercode; await readBefore(fleet, session); if (session?.session_id) updatedAt(session);
|
|
1124
|
+
if (addressOf(session)) cardsOf.set(addressOf(session), [...(cardsOf.get(addressOf(session)) ?? []), id]);
|
|
1125
|
+
}
|
|
1121
1126
|
for (const { id } of running) {
|
|
1122
1127
|
let staged = [];
|
|
1123
1128
|
txRead(board, (db) => {
|
|
@@ -1132,7 +1137,8 @@ async function observeIdle(board, fleet, settings, report, entry, effects) {
|
|
|
1132
1137
|
// a session in no pane has its turn on its machine's discovery row, which sessionAlive just read
|
|
1133
1138
|
let nativeRow = null;
|
|
1134
1139
|
if (session.surface === 'native') try { nativeRow = nativeReading(nativeSessions(session.machine, session.harness ?? 'claude-code', session.session_id), session).row; } catch { /* unread: no turn */ }
|
|
1135
|
-
const live = fleet.sessions.find((x) => x.address === addressOf(session)) ?? (nativeRow ? { address: addressOf(session), turn: nativeRow.activity?.turn ?? null
|
|
1140
|
+
const live = fleet.sessions.find((x) => x.address === addressOf(session)) ?? (nativeRow ? { address: addressOf(session), turn: nativeRow.activity?.turn ?? null, waiting_for: nativeRow.activity?.evidence?.waiting_for ?? null,
|
|
1141
|
+
turn_source: nativeRow.activity?.evidence?.source ?? null, turn_since_ms: nativeRow.activity?.evidence?.native_state_since_ms ?? null } : undefined);
|
|
1136
1142
|
const observedAt = now();
|
|
1137
1143
|
const lastAtMs = Number(live?.last_message_at_ms) || updatedAt(session);
|
|
1138
1144
|
const lastAt = Math.floor(Number(lastAtMs) / 1000);
|
|
@@ -1146,7 +1152,7 @@ async function observeIdle(board, fleet, settings, report, entry, effects) {
|
|
|
1146
1152
|
const retain = (decision, extra = {}) => {
|
|
1147
1153
|
// A reading that decides nothing is kept once per state: rewriting it each pass would wake the next pass.
|
|
1148
1154
|
const prior = session.idle_observation;
|
|
1149
|
-
if (!['dispatched', 'dispatch_failed'].includes(decision) && prior?.decision === decision && prior.last_message_at_ms === reading.last_message_at_ms && prior.status === reading.status) return prior;
|
|
1155
|
+
if (!['dispatched', 'dispatch_failed', 'waiting_on_user'].includes(decision) && prior?.decision === decision && prior.last_message_at_ms === reading.last_message_at_ms && prior.status === reading.status) return prior;
|
|
1150
1156
|
const observation = { ...reading, decision, turns_after: reading.turns_before, ...extra };
|
|
1151
1157
|
const patch = { idle_observation: observation };
|
|
1152
1158
|
if (decision === 'below_threshold') patch.idle_below_threshold = observation;
|
|
@@ -1154,21 +1160,39 @@ async function observeIdle(board, fleet, settings, report, entry, effects) {
|
|
|
1154
1160
|
setRunSession(db, runRow.id, patch);
|
|
1155
1161
|
return observation;
|
|
1156
1162
|
};
|
|
1157
|
-
// A session its
|
|
1158
|
-
// the manager with what
|
|
1159
|
-
// its user is
|
|
1160
|
-
const
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
const
|
|
1163
|
+
// A session waiting on its user (a question, a permission prompt) or working without writing past the quiet
|
|
1164
|
+
// threshold is told to the manager, with only what was read of it. Whether a harness's own state is a question
|
|
1165
|
+
// put to its user is its harness's to say (`needs_input`); what it waits for is carried as it was read.
|
|
1166
|
+
const address = addressOf(session);
|
|
1167
|
+
const asked = live?.turn === 'needs_input';
|
|
1168
|
+
// a reading that shows the session no longer waiting ends the prompt; a round that did not read it does not
|
|
1169
|
+
if (session.held_notice && live && !asked) setRunSession(db, runRow.id, { held_notice: null });
|
|
1170
|
+
if (asked) {
|
|
1171
|
+
// One prompt is one notice: the same wait (what it waits for, since when its harness says) on the same
|
|
1172
|
+
// session, whichever of its cards a round reads first and however long it stands.
|
|
1173
|
+
const key = [address, live.waiting_for ?? '', live.turn_since_ms ?? ''].join('|');
|
|
1174
|
+
if (session.held_notice?.key === key) return;
|
|
1175
|
+
// every run a card works under: an ended run can still be its card's current one
|
|
1176
|
+
const told = db.prepare('SELECT r.metadata FROM task_runs r WHERE r.id != ? AND (r.ended_at IS NULL OR EXISTS (SELECT 1 FROM tasks t WHERE t.current_run_id = r.id))').all(runRow.id).some((r) => json(r.metadata, {})?.supercode?.held_notice?.key === key);
|
|
1177
|
+
setRunSession(db, runRow.id, { held_notice: { key, at: observedAt } });
|
|
1178
|
+
if (told) return;
|
|
1179
|
+
const observation = retain('waiting_on_user', { waiting_for: live.waiting_for ?? null, since_ms: live.turn_since_ms ?? null });
|
|
1180
|
+
const others = (cardsOf.get(address) ?? []).filter((card) => card !== id);
|
|
1181
|
+
const what = live.turn_source === 'pane_prompt' ? 'shows a prompt on its pane that waits for an answer'
|
|
1182
|
+
: `reports that it is waiting on its user${live.waiting_for ? ` (${live.waiting_for})` : ''}${live.turn_since_ms ? ` since ${new Date(Number(live.turn_since_ms)).toISOString().slice(0, 19)}Z` : ''}`;
|
|
1183
|
+
event(db, id, 'session_held', observation, runRow.id);
|
|
1184
|
+
const to = managers[0] ?? null;
|
|
1185
|
+
if (to) event(db, id, 'board_message', { to, text: `${id}: its session ${address} ${what}.${others.length ? ` It is also on ${others.join(', ')}.` : ''}` }, null);
|
|
1186
|
+
(report.held ??= []).push([id, 'waiting_on_user', address]);
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
if (live?.turn === 'working' && lastAt > 0 && observedAt - lastAt >= settings.idleQuiet) {
|
|
1190
|
+
if (session.idle_observation?.decision === 'working_silent' && session.idle_observation.last_message_at_ms === reading.last_message_at_ms) return;
|
|
1191
|
+
const observation = retain('working_silent');
|
|
1168
1192
|
event(db, id, 'session_held', observation, runRow.id);
|
|
1169
1193
|
const to = managers[0] ?? null;
|
|
1170
|
-
if (to) event(db, id, 'board_message', { to, text }, null);
|
|
1171
|
-
(report.held ??= []).push([id,
|
|
1194
|
+
if (to) event(db, id, 'board_message', { to, text: `${id}: its session ${address} has reported working without writing anything for ${Math.round((observedAt - lastAt) / 60)} min.` }, null);
|
|
1195
|
+
(report.held ??= []).push([id, 'working_silent', address]);
|
|
1172
1196
|
return;
|
|
1173
1197
|
}
|
|
1174
1198
|
// No timestamp is no reading, and no reading gives no verdict.
|
package/board/machine-fleet.mjs
CHANGED
|
@@ -66,7 +66,9 @@ export async function readMachineFleet(machines, local) {
|
|
|
66
66
|
native.set(address, { alive: running ? true : null, states: [running ? true : null] });
|
|
67
67
|
if (running) alive.add(address);
|
|
68
68
|
sessions.push({ address, machine: row.machine, harness: activity.harness, session_id: activity.session_id, pane: pane.pane, name: pane.title ?? pane.label ?? null,
|
|
69
|
-
state: running ? 'running' : 'unknown', turn: activity.turn ?? null, native_state: activity.evidence?.native_state ?? null
|
|
69
|
+
state: running ? 'running' : 'unknown', turn: activity.turn ?? null, native_state: activity.evidence?.native_state ?? null,
|
|
70
|
+
// what a session that needs input says it waits for and since when, where its harness publishes them
|
|
71
|
+
waiting_for: activity.evidence?.waiting_for ?? null, turn_source: activity.evidence?.source ?? null, turn_since_ms: activity.evidence?.native_state_since_ms ?? null });
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
74
|
const ok = online.has(local);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.110",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The orchestrator runtime over the Volter Harness ontology: one typed operational model whose folder is its serialization, read and written through the harness orchestration doors (docs/ORCHESTRATOR-IR.md)",
|
|
6
6
|
"exports": {
|