@volter/supercode-orchestrator 0.4.75 → 0.4.76
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/engine.mjs +1 -1
- package/board/store.mjs +11 -0
- package/board/workflow.mjs +21 -3
- package/package.json +1 -1
package/board/engine.mjs
CHANGED
|
@@ -105,7 +105,7 @@ export function currentRun(db, taskId) {
|
|
|
105
105
|
export function setRunSession(db, runId, session) {
|
|
106
106
|
const row = one(db, 'SELECT metadata FROM task_runs WHERE id = ?', runId);
|
|
107
107
|
const whole = json(row?.metadata, {}) ?? {};
|
|
108
|
-
whole.supercode = session;
|
|
108
|
+
whole.supercode = { ...whole.supercode, ...session };
|
|
109
109
|
run(db, 'UPDATE task_runs SET metadata = ? WHERE id = ?', JSON.stringify(whole), runId);
|
|
110
110
|
return whole;
|
|
111
111
|
}
|
package/board/store.mjs
CHANGED
|
@@ -203,6 +203,17 @@ export const actorOf = () => actor;
|
|
|
203
203
|
export function setActor(address) { actor = address || null; }
|
|
204
204
|
|
|
205
205
|
export function event(db, taskId, kind, payload = null, runId = null) {
|
|
206
|
+
// Session observations belong to the run machine. Legacy runs without a machine state retain
|
|
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)) {
|
|
209
|
+
const row = db.prepare('SELECT metadata FROM task_runs WHERE id = ?').get(runId);
|
|
210
|
+
const meta = json(row?.metadata, {}) ?? {};
|
|
211
|
+
if (meta.supercode?.state) {
|
|
212
|
+
meta.events = [...(meta.events ?? []), { kind, payload, by: actor, at: now() }];
|
|
213
|
+
db.prepare('UPDATE task_runs SET metadata = ? WHERE id = ?').run(JSON.stringify(meta), runId);
|
|
214
|
+
return 0;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
206
217
|
const said = actor && !(payload && 'by' in payload) ? { ...(payload ?? {}), by: actor } : payload;
|
|
207
218
|
const r = db.prepare('INSERT INTO task_events (task_id, run_id, kind, payload, created_at) VALUES (?, ?, ?, ?, ?)')
|
|
208
219
|
.run(taskId, runId, kind, said ? JSON.stringify(said) : null, now());
|
package/board/workflow.mjs
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// the engine's primitives (the rows Hermes's tables hold); the I/O an action asks for (stopping a worker, closing a
|
|
6
6
|
// pane, sending a session a message, cleaning a workspace) is answered as effects the caller runs after commit.
|
|
7
7
|
import { spawnSync } from 'node:child_process';
|
|
8
|
-
import { readFileSync } from 'node:fs';
|
|
8
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
9
9
|
import { resolve, relative, isAbsolute } from 'node:path';
|
|
10
10
|
import { randomBytes } from 'node:crypto';
|
|
11
11
|
import { actorOf, event as record, json, now } from './store.mjs';
|
|
@@ -64,6 +64,7 @@ export function storedAs(workflow, status) {
|
|
|
64
64
|
|
|
65
65
|
/** Where a card interrupted in `status` resumes: its `exited` transition's unguarded target (data, not a rule here). */
|
|
66
66
|
export function resumeOf(workflow, status) {
|
|
67
|
+
if (workflow.statuses[status]?.resume_as) return workflow.statuses[status].resume_as;
|
|
67
68
|
const exited = workflow.statuses[status]?.on?.exited ?? [];
|
|
68
69
|
const fallback = [...exited].reverse().find((t) => !t.guard && t.target && !t.target.includes('{{'));
|
|
69
70
|
return fallback?.target ?? status;
|
|
@@ -221,6 +222,8 @@ export function scopeOf(db, board, task, { params, event = {}, extra = {} } = {}
|
|
|
221
222
|
return lastCommitAt(own ?? arcWs ?? null, task.id);
|
|
222
223
|
},
|
|
223
224
|
};
|
|
225
|
+
const runMeta = json(currentRun?.metadata, {}) ?? {};
|
|
226
|
+
for (const e of runMeta.events ?? []) card.last_events[e.kind] = { created_at: e.at, payload: e.payload ?? {} };
|
|
224
227
|
for (const [key, expr] of Object.entries(loaded.get(board.root)?.workflow?.derived ?? {})) card[key] = evaluate(expr, { card, event, params, now: now() });
|
|
225
228
|
return {
|
|
226
229
|
card,
|
|
@@ -278,6 +281,7 @@ export function fire(db, board, entry, id, name, { roles = [], payload = {}, ext
|
|
|
278
281
|
const effects = [];
|
|
279
282
|
const to = take(db, board, entry, task, from, taken, scope, effects);
|
|
280
283
|
settle(db, board, entry, id, effects);
|
|
284
|
+
projectStatus(db, board, entry, id);
|
|
281
285
|
return { from, to: statusOf(db, cardOf(db, id)), moved: to !== from, effects };
|
|
282
286
|
}
|
|
283
287
|
|
|
@@ -346,7 +350,7 @@ export function settle(db, board, entry, id, effects = []) {
|
|
|
346
350
|
const scope = scopeOf(db, board, task, { params, extra: { lanes: lanesIn(entry) } });
|
|
347
351
|
let t = (workflow.statuses[from]?.always ?? []).find((x) => holds(x.guard ?? null, scope));
|
|
348
352
|
if (!t) t = dueAfter(db, workflow, task, from, scope);
|
|
349
|
-
if (!t) return effects;
|
|
353
|
+
if (!t) { projectStatus(db, board, entry, id); return effects; }
|
|
350
354
|
const to = take(db, board, entry, task, from, t, scope, effects);
|
|
351
355
|
if (to === from) return effects;
|
|
352
356
|
}
|
|
@@ -708,6 +712,7 @@ export function runSpec(entry, status) {
|
|
|
708
712
|
const file = resolve(entry.root, spec.prompt_file);
|
|
709
713
|
const rel = relative(entry.root, file);
|
|
710
714
|
if (rel.startsWith('..') || isAbsolute(rel)) throw new Error('prompt file must live in the orchestration home');
|
|
715
|
+
if (!existsSync(file) && spec.prompt) return spec; // embedded prompts of the built-in instance
|
|
711
716
|
return { ...spec, prompt: readFileSync(file, 'utf8') };
|
|
712
717
|
}
|
|
713
718
|
|
|
@@ -728,7 +733,7 @@ export function fireRun(db, board, entry, id, name, { roles = [], payload = {},
|
|
|
728
733
|
const from = meta.supercode?.state ?? machine.initial;
|
|
729
734
|
const scope = scopeOf(db, board, task, { params: entry.params, event: payload, extra: { ...extra, sender: roles, run: { ...row, state: from } } });
|
|
730
735
|
const candidates = [...(machine.states[from]?.on?.[name] ?? []), ...(machine.on?.[name] ?? [])];
|
|
731
|
-
const t = candidates.find((t) => (!t.by?.length || t.by.some((r) => roles.includes(r))) && holds(t.guard ?? null, scope));
|
|
736
|
+
const t = candidates.find((t) => (!t.from?.length || t.from.includes(from)) && (!t.by?.length || t.by.some((r) => roles.includes(r))) && holds(t.guard ?? null, scope));
|
|
732
737
|
if (!t) { const error = new Error(`${id}: run event ${name} does not apply`); error.noTransition = true; throw error; }
|
|
733
738
|
for (const v of t.validate ?? []) if (!holds(v.expr, scope)) throw new Error(v.message);
|
|
734
739
|
const to = t.target ? String(templateValue(t.target, scope)) : from;
|
|
@@ -746,6 +751,7 @@ export function fireRun(db, board, entry, id, name, { roles = [], payload = {},
|
|
|
746
751
|
const outcome = (machine.outcomes?.[to] ?? []).find((t) => holds(t.guard ?? null, after));
|
|
747
752
|
if (outcome) take(db, board, entry, cardOf(db, id), statusOf(db, cardOf(db, id)), outcome, after, effects);
|
|
748
753
|
}
|
|
754
|
+
projectStatus(db, board, entry, id);
|
|
749
755
|
return { from: status, to: statusOf(db, cardOf(db, id)), effects };
|
|
750
756
|
}
|
|
751
757
|
|
|
@@ -762,3 +768,15 @@ function settleRelations(db, board, entry, id, effects) {
|
|
|
762
768
|
if (name) effects.push(...fire(db, board, entry, id, name, { roles: ['platform'], payload: { on: block.id, reason: answer ? `reply ${answer}` : `${block.id} ${status}` } }).effects);
|
|
763
769
|
}
|
|
764
770
|
}
|
|
771
|
+
|
|
772
|
+
|
|
773
|
+
export function projectStatus(db, board, entry, id) {
|
|
774
|
+
if (!entry.workflow.stored_as?.length) return;
|
|
775
|
+
const task = cardOf(db, id);
|
|
776
|
+
const status = statusOf(db, task);
|
|
777
|
+
const scope = scopeOf(db, board, task, { params: entry.params });
|
|
778
|
+
const rule = entry.workflow.stored_as.find((r) => holds(r.guard, scope));
|
|
779
|
+
const stored = rule?.status ?? storedAs(entry.workflow, status);
|
|
780
|
+
run(db, 'UPDATE tasks SET status = ? WHERE id = ?', stored, id);
|
|
781
|
+
run(db, 'INSERT INTO supercode_cards (task_id, stage) VALUES (?, ?) ON CONFLICT(task_id) DO UPDATE SET stage = excluded.stage', id, status);
|
|
782
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.76",
|
|
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": {
|