@volter/supercode-orchestrator 0.4.116 → 0.4.117
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 +32 -12
- package/board/store.mjs +1 -1
- package/package.json +1 -1
package/board/dispatch.mjs
CHANGED
|
@@ -525,10 +525,6 @@ export async function startCard(board, id, { roles = ['manager'], lanes, setting
|
|
|
525
525
|
return lane ? launchRun(board, entry, id, lane, fleet, settings) : null;
|
|
526
526
|
}
|
|
527
527
|
|
|
528
|
-
/**
|
|
529
|
-
* The heartbeat, observed: a running pane session whose last message is a whole tick old is sent `session_idle` with
|
|
530
|
-
* how many times it has been asked this run and whether it worked since the last ask; the workflow decides the rest.
|
|
531
|
-
*/
|
|
532
528
|
/** Reconcile durable close intent and ended runs, including old unverified closed_at records.
|
|
533
529
|
* Reads and process I/O stay outside the writer. An active successor owns its pane until it ends. */
|
|
534
530
|
export function closeRequested(board, report, { taskId = null, caller = null } = {}) {
|
|
@@ -611,6 +607,8 @@ export function closeRequested(board, report, { taskId = null, caller = null } =
|
|
|
611
607
|
}
|
|
612
608
|
}
|
|
613
609
|
|
|
610
|
+
/** Quiet time is independent of dispatch cadence and the harness's busy label.
|
|
611
|
+
* Keep bounded readings on the run so an ordinary quiet stretch can be reviewed later. */
|
|
614
612
|
function observeIdle(board, fleet, settings, report, entry, effects) {
|
|
615
613
|
if (!fleet.ok) return;
|
|
616
614
|
for (const { id } of 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()) {
|
|
@@ -622,22 +620,44 @@ function observeIdle(board, fleet, settings, report, entry, effects) {
|
|
|
622
620
|
const session = runRow?.metadata?.supercode;
|
|
623
621
|
if (!session?.session_id || session.surface !== 'pane') return;
|
|
624
622
|
const live = fleet.sessions.find((x) => x.address?.endsWith(`:${session.session_id}`));
|
|
625
|
-
|
|
623
|
+
const observedAt = now();
|
|
626
624
|
const lastAt = Math.floor(Number(live?.last_message_at_ms) / 1000);
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
625
|
+
const reading = {
|
|
626
|
+
session_id: session.session_id, observed_at: observedAt,
|
|
627
|
+
last_message_at: lastAt > 0 ? lastAt : null,
|
|
628
|
+
last_message_at_ms: lastAt > 0 ? Number(live.last_message_at_ms) : null,
|
|
629
|
+
status: live?.status ?? null, quiet_seconds: lastAt > 0 ? observedAt - lastAt : null,
|
|
630
|
+
threshold_seconds: settings.idleQuiet, turns_before: Number(session.goal_turns ?? 0),
|
|
631
|
+
};
|
|
632
|
+
const retain = (decision, extra = {}) => {
|
|
633
|
+
const observation = { ...reading, decision, turns_after: reading.turns_before, ...extra };
|
|
634
|
+
const patch = { idle_observation: observation };
|
|
635
|
+
if (decision === 'below_threshold') patch.idle_below_threshold = observation;
|
|
636
|
+
if (decision === 'dispatched' || decision === 'dispatch_failed') patch.idle_eligible = observation;
|
|
637
|
+
setRunSession(db, runRow.id, patch);
|
|
638
|
+
return observation;
|
|
639
|
+
};
|
|
640
|
+
// No timestamp is no reading, and no reading gives no verdict.
|
|
641
|
+
if (!(lastAt > 0)) { retain('no_timestamp'); return; }
|
|
642
|
+
if (reading.quiet_seconds < settings.idleQuiet) { retain('below_threshold'); return; }
|
|
643
|
+
if (session.goal_nudged_at && observedAt - session.goal_nudged_at < settings.idleQuiet) { retain('nudge_cooldown'); return; }
|
|
630
644
|
const worked = Boolean(session.goal_nudged_at) && lastAt > session.goal_nudged_at;
|
|
631
645
|
try {
|
|
632
646
|
const where = whereOf(board, task);
|
|
633
647
|
// the ask names what the session may do, as its run's prompt did
|
|
634
648
|
const said = doors(entry.workflow, status, session.role ?? runSpec(entry, status)?.role ?? 'implementer', id, where, session.surface === 'pane' ? 'pane' : 'headless');
|
|
635
|
-
const r = fire(db, board, entry, id, 'session_idle', { roles: ['platform'], payload: { turns:
|
|
649
|
+
const r = fire(db, board, entry, id, 'session_idle', { roles: ['platform'], payload: { ...reading, turns: reading.turns_before, worked }, extra: { where, doors: said } });
|
|
636
650
|
effects.push(...r.effects);
|
|
637
651
|
const after = currentRun(db, id);
|
|
638
|
-
|
|
652
|
+
const stored = json(db.prepare('SELECT metadata FROM task_runs WHERE id=?').get(runRow.id)?.metadata, {})?.supercode;
|
|
653
|
+
const observation = retain('dispatched', { turns_after: Number(stored?.goal_turns ?? 0), nudge_queued: r.effects.some(e => e.kind === 'send_to_actor'), run_ended: after?.id !== runRow.id });
|
|
654
|
+
event(db, id, 'session_idle_observed', observation, runRow.id);
|
|
655
|
+
if (after?.id === runRow.id) setRunSession(db, runRow.id, { goal_nudged_at: observedAt });
|
|
639
656
|
report.notified.push([id, 'session_idle', live.address]);
|
|
640
|
-
} catch (error) {
|
|
657
|
+
} catch (error) {
|
|
658
|
+
retain('dispatch_failed', { error: String(error.message).slice(-200) });
|
|
659
|
+
report.failed.push([id, `session_idle: ${String(error.message).slice(-200)}`]);
|
|
660
|
+
}
|
|
641
661
|
});
|
|
642
662
|
}
|
|
643
663
|
}
|
|
@@ -927,7 +947,7 @@ function launchPane(board, task, runId, lane, fleet, { input = null, slot = 'wor
|
|
|
927
947
|
if (paneLive && (prev.closed_at || prev.close_requested || prev.forgotten)) throw new Error(`session ${prev.session_id} still has live pane ${prev.pane}; cannot resume or replace it`);
|
|
928
948
|
if (!prev.closed_at && !prev.close_requested && (prev.pane ? paneLive : sessionAlive(fleet, prev))) {
|
|
929
949
|
// the session is kept; the heartbeat's count belongs to the run that ended, so the new run starts it again
|
|
930
|
-
const { missing_since: _m, goal_turns: _t, goal_nudged_at: _n, close_requested: _q, closed_at: _c, close_confirmed_at: _cc, close_error: _ce, close_attempts: _ca, close_attempted_at: _ct, ...kept } = prev;
|
|
950
|
+
const { missing_since: _m, goal_turns: _t, goal_nudged_at: _n, idle_observation: _io, idle_below_threshold: _ib, idle_eligible: _ie, close_requested: _q, closed_at: _c, close_confirmed_at: _cc, close_error: _ce, close_attempts: _ca, close_attempted_at: _ct, ...kept } = prev;
|
|
931
951
|
session = { ...kept, mode: 'adopt', launched_at: now(), seen_at: now() };
|
|
932
952
|
// a kept session is told what this run is for (its card came back to it, with what happened since)
|
|
933
953
|
tx(board, (db) => setRunSession(db, runId, { ...session, ...meta }));
|
package/board/store.mjs
CHANGED
|
@@ -205,7 +205,7 @@ export function setActor(address) { actor = address || null; }
|
|
|
205
205
|
export function event(db, taskId, kind, payload = null, runId = null) {
|
|
206
206
|
// Session observations belong to the run machine. Legacy runs without a machine state retain
|
|
207
207
|
// their Hermes event stream; a new instance never sends supervision as card notices.
|
|
208
|
-
if (runId && ['claimed', 'spawned', 'session_started', 'session_resumed', 'session_replaced', 'session_adopted', 'claim_extended'].includes(kind)) {
|
|
208
|
+
if (runId && ['claimed', 'spawned', 'session_started', 'session_resumed', 'session_replaced', 'session_adopted', 'claim_extended', 'session_idle_observed'].includes(kind)) {
|
|
209
209
|
const row = db.prepare('SELECT metadata FROM task_runs WHERE id = ?').get(runId);
|
|
210
210
|
const meta = json(row?.metadata, {}) ?? {};
|
|
211
211
|
if (meta.supercode?.state) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.117",
|
|
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": {
|