@volter/supercode-orchestrator 0.5.32 → 0.5.33
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/bin/runtime.mjs +6 -0
- package/board.mjs +32 -3
- package/loop.mjs +4 -2
- package/package.json +1 -1
package/bin/runtime.mjs
CHANGED
|
@@ -746,7 +746,13 @@ async function main() {
|
|
|
746
746
|
// binary that spawned this daemon, so the worker doors it opens are that
|
|
747
747
|
// build's, not whatever `supercode` a PATH lookup happens to find.
|
|
748
748
|
const client = new SupercodeHarnessClient(process.env.SUPERCODE_BIN ? { command: process.env.SUPERCODE_BIN } : {});
|
|
749
|
+
// `SUPERCODE_BOARD_DISPATCHER=serve`: whoever started this daemon also runs `workflow serve` on the home, the board's
|
|
750
|
+
// one dispatcher, so this daemon runs no dispatch round (board.mjs). It is this process's alone: the sessions it
|
|
751
|
+
// launches do not inherit it.
|
|
752
|
+
const boardDispatcher = process.env.SUPERCODE_BOARD_DISPATCHER || null;
|
|
753
|
+
delete process.env.SUPERCODE_BOARD_DISPATCHER;
|
|
749
754
|
const orchestrator = new Orchestrator({
|
|
755
|
+
boardDispatcher,
|
|
750
756
|
executeJob: args.executorUrl ? httpJobExecutor({ url: args.executorUrl, tokenFile: args.executorTokenFile, root }) : null,
|
|
751
757
|
state,
|
|
752
758
|
vault,
|
package/board.mjs
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
// format and lifecycle, each profile's lane decided by its `worker` in the IR. Where a Hermes gateway would run
|
|
4
4
|
// its dispatcher, the daemon runs this one the same way: when the board's workflow says so (its `dispatch.serve`),
|
|
5
5
|
// taking no file locks, waking on events with `dispatch.tick` as a safety sweep.
|
|
6
|
+
// One dispatcher per board holds by construction, not by a lock: whoever starts the daemon beside a
|
|
7
|
+
// `supercode workflow serve` on the same home (an Open Autonomy install's start) says so (`dispatcher: 'serve'`, from
|
|
8
|
+
// SUPERCODE_BOARD_DISPATCHER), and the daemon then runs no round and no board event relay of its own. Chat-platform
|
|
9
|
+
// subscriptions are delivered here whoever dispatches: the daemon hosts the channel adapters and a serve hosts none.
|
|
6
10
|
// Each tick runs as
|
|
7
11
|
// `workflow dispatch` in a child process: a tick waits on supercode (the fleet, launches, mail), and the daemon's
|
|
8
12
|
// own loop never does.
|
|
@@ -32,13 +36,38 @@ function dispatchOnce(root) {
|
|
|
32
36
|
});
|
|
33
37
|
}
|
|
34
38
|
|
|
35
|
-
/**
|
|
36
|
-
|
|
39
|
+
/**
|
|
40
|
+
* The chat-platform notices alone, for a daemon that does not dispatch the board: each sweep sends what the board's
|
|
41
|
+
* subscriptions have not yet been told through this daemon's channel adapters. Null when there is nothing it could send.
|
|
42
|
+
*/
|
|
43
|
+
function startChatNotices({ root, entry, seconds, log, adapters, wake }) {
|
|
44
|
+
if (!Object.keys(entry.workflow.notify?.chat ?? {}).length) return null;
|
|
45
|
+
if (!Object.keys(adapters).length) { log('warn', 'board: the board workflow tells chats (notify.chat) and this daemon hosts no channel adapter to tell them'); return null; }
|
|
46
|
+
let running = false;
|
|
47
|
+
const sweep = async () => {
|
|
48
|
+
if (running) return;
|
|
49
|
+
running = true;
|
|
50
|
+
try { await notifyChats(root, adapters, log, wake); } catch (error) { log('warn', `board: chat notices failed: ${error.message}`); } finally { running = false; }
|
|
51
|
+
};
|
|
52
|
+
const timer = setInterval(() => { void sweep(); }, seconds * 1000);
|
|
53
|
+
timer.unref?.();
|
|
54
|
+
void sweep();
|
|
55
|
+
log('info', `board: chat notices every ${seconds}s through this daemon's channel adapters`);
|
|
56
|
+
return { tick() { void sweep(); }, stop() { clearInterval(timer); } };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Start the board's dispatcher for the home at `root`; answers `{ tick, stop }`, or null when it neither dispatches
|
|
61
|
+
* nor has chat notices to send. `dispatcher: 'serve'` says a `supercode workflow serve` dispatches this home's board:
|
|
62
|
+
* the daemon then runs no round of its own, only the chat notices its adapters deliver.
|
|
63
|
+
*/
|
|
64
|
+
export async function startBoard({ root, orchestration = null, log = () => {}, adapters = {}, wake = null, dispatcher = null }) {
|
|
37
65
|
// whether and how often the served home dispatches is the board workflow's (`dispatch.serve`, `dispatch.tick`)
|
|
38
66
|
let entry;
|
|
39
67
|
try { entry = workflowEntry(orchestration); } catch (error) { log('warn', `board: not dispatched: ${error.message}`); return null; }
|
|
40
|
-
if (!holds(entry.workflow.dispatch.serve ?? null, { params: entry.params })) { log('info', 'board: not dispatched (the board workflow\'s dispatch.serve)'); return null; }
|
|
41
68
|
const seconds = Math.max(Number(evaluate(entry.workflow.dispatch.tick, { params: entry.params })) || 60, 1);
|
|
69
|
+
if (dispatcher === 'serve') { log('info', 'board: not dispatched here (its dispatcher is the `workflow serve` beside this daemon)'); return startChatNotices({ root, entry, seconds, log, adapters, wake }); }
|
|
70
|
+
if (!holds(entry.workflow.dispatch.serve ?? null, { params: entry.params })) { log('info', 'board: not dispatched (the board workflow\'s dispatch.serve)'); return startChatNotices({ root, entry, seconds, log, adapters, wake }); }
|
|
42
71
|
let running = false;
|
|
43
72
|
let stopped = false;
|
|
44
73
|
const said = new Set();
|
package/loop.mjs
CHANGED
|
@@ -54,8 +54,10 @@ export class Orchestrator {
|
|
|
54
54
|
* @param {(profile, worker, env) => object|undefined} [o.launchFor] launch override per worker (tests)
|
|
55
55
|
* @param {(level, message) => void} [o.log]
|
|
56
56
|
*/
|
|
57
|
-
constructor({ state, vault = new Map(), client, adapters = {}, clock = null, launchFor = null, executeJob = null, log = () => {} }) {
|
|
57
|
+
constructor({ state, vault = new Map(), client, adapters = {}, clock = null, launchFor = null, executeJob = null, boardDispatcher = null, log = () => {} }) {
|
|
58
58
|
this.state = state;
|
|
59
|
+
// who dispatches the home's board: 'serve' when a `workflow serve` beside this daemon does (board.mjs)
|
|
60
|
+
this.boardDispatcher = boardDispatcher;
|
|
59
61
|
this.executeJob = executeJob;
|
|
60
62
|
this.hostedTasks = new Set();
|
|
61
63
|
this.identityRecord = new SenderIdentityRecord();
|
|
@@ -96,7 +98,7 @@ export class Orchestrator {
|
|
|
96
98
|
if (this.autoTick) { this.timer = setInterval(() => { this.tick().catch(() => undefined); }, TICK_MS); this.timer.unref?.(); }
|
|
97
99
|
// the home's board, dispatched as a Hermes gateway dispatches it (docs/plans/hermes-compat.md row 14)
|
|
98
100
|
if (this.autoTick) this.board = await startBoard({
|
|
99
|
-
root: this.state.root, orchestration: this.state, log: this.log, adapters: this.adapters,
|
|
101
|
+
root: this.state.root, orchestration: this.state, log: this.log, adapters: this.adapters, dispatcher: this.boardDispatcher,
|
|
100
102
|
// a board notice as a turn in the subscribed chat's own session (Hermes's wake), sent as the subscriber
|
|
101
103
|
wake: (sub, text) => this.dispatchLogged(events.inbound({
|
|
102
104
|
profile: sub.notifier_profile || 'default', key: { platform: sub.platform, kind: sub.chat_type || 'dm', chat_id: sub.chat_id, ...(sub.thread_id ? { thread_id: sub.thread_id } : {}) },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.33",
|
|
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": {
|