@volter/supercode-orchestrator 0.5.227 → 0.5.229
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 +18 -2
- package/activation.mjs +1 -1
- package/adapters/agent-mailbox.mjs +10 -0
- package/adapters/api-server.mjs +6 -0
- package/agents.mjs +149 -40
- package/apply/doors.mjs +3 -1
- package/bin/agent-channel.mjs +7 -2
- package/bin/connection-runner.mjs +60 -19
- package/bin/hermes-worker.mjs +2 -1
- package/bin/orchestrator.mjs +3 -3
- package/bin/runtime.mjs +3 -2
- package/board/agent-current.mjs +82 -0
- package/board/agent-declarations.mjs +10 -3
- package/board/args.mjs +137 -6
- package/board/call.mjs +16 -1
- package/board/caller-session.mjs +3 -1
- package/board/capacity.mjs +59 -45
- package/board/card-state.mjs +14 -5
- package/board/ci.mjs +521 -178
- package/board/cli.mjs +614 -209
- package/board/client.mjs +64 -21
- package/board/close-current.mjs +36 -0
- package/board/current-records.mjs +44 -0
- package/board/custody.mjs +2 -1
- package/board/dispatch.mjs +1973 -846
- package/board/doors.mjs +2 -2
- package/board/engine.mjs +623 -85
- package/board/event-stream.mjs +10 -5
- package/board/fence.mjs +2 -0
- package/board/files.mjs +93 -62
- package/board/generation-owner.mjs +188 -53
- package/board/git-document.mjs +3 -1
- package/board/github-rest.mjs +29 -7
- package/board/incidents.mjs +5 -17
- package/board/listing.mjs +14 -8
- package/board/local-admission.mjs +59 -0
- package/board/lock-holder.mjs +18 -6
- package/board/machine-fleet.mjs +66 -13
- package/board/owner-door-worker.mjs +3 -3
- package/board/owner-door.mjs +17 -7
- package/board/owner-health.mjs +12 -15
- package/board/owner-interface.mjs +6 -0
- package/board/publication-reader.mjs +50 -0
- package/board/published.mjs +40 -12
- package/board/queries.mjs +24 -4
- package/board/record-view.mjs +30 -3
- package/board/request-mail.mjs +37 -6
- package/board/resting-admission.mjs +318 -0
- package/board/resting-codec-qualifications.json +33 -0
- package/board/resting-protocol.mjs +71 -0
- package/board/resting-state-protocol.json +120 -0
- package/board/resting-state.mjs +113 -0
- package/board/runtime-door.mjs +2 -2
- package/board/session-continuity.mjs +127 -0
- package/board/session-work.mjs +25 -13
- package/board/standalone-declaration.mjs +24 -0
- package/board/storage-contract.mjs +129 -7
- package/board/store.mjs +73 -11
- package/board/tools.mjs +56 -13
- package/board/wake.mjs +18 -5
- package/board/workflow.mjs +284 -82
- package/board/workspace.mjs +86 -9
- package/board/ztrack.mjs +344 -91
- package/connection.mjs +72 -17
- package/gates.mjs +2 -1
- package/hand-off.mjs +4 -3
- package/hermes-cli.mjs +5 -3
- package/hermes-locks.mjs +5 -2
- package/hosted.mjs +2 -1
- package/index.d.ts +2 -0
- package/index.mjs +1 -1
- package/kanban-worker.mjs +63 -13
- package/loop.mjs +33 -0
- package/mcp/hermes-kanban-tools.json +109 -50
- package/mcp/server.mjs +7 -1
- package/ontology.d.ts +26 -0
- package/orchestration.mjs +15 -4
- package/package.json +19 -12
- package/session-recovery.mjs +2 -1
- package/worker-route.mjs +37 -19
package/orchestration.mjs
CHANGED
|
@@ -25,8 +25,8 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
25
25
|
import { join } from 'node:path';
|
|
26
26
|
import { SupercodeHarnessClient } from '@volter/supercode-harness-sdk';
|
|
27
27
|
import { emptyRuntime, persistedView } from './ir.mjs';
|
|
28
|
-
import { lockedProgram } from './source-lock.mjs';
|
|
29
|
-
import { sourceRoot } from '@volter/supercode-teams/source-lock';
|
|
28
|
+
import { lockedProgram } from './source-lock.mjs'; // layer: the orchestration names the program it runs from its pinned source (the host's source lock); owed: the host hands it in
|
|
29
|
+
import { sourceRoot } from '@volter/supercode-teams/source-lock'; // layer: the orchestration names the program it runs from its pinned source (the host's source lock); owed: the host hands it in
|
|
30
30
|
import { SESSION_READ_REQUEST_UNITS, stringifySessionWire } from '@volter/supercode-teams/session-read-client';
|
|
31
31
|
|
|
32
32
|
/** Selected CLI for clients that need it: the locked Node entry, explicit
|
|
@@ -41,6 +41,9 @@ export function supercodeCommand() {
|
|
|
41
41
|
|
|
42
42
|
const IDLE_MS = 250;
|
|
43
43
|
let shared = null;
|
|
44
|
+
// A new door can open while its predecessor closes. Keep each close until it succeeds, so an explicit shutdown
|
|
45
|
+
// joins an idle close already in flight and still reports an earlier unconfirmed close.
|
|
46
|
+
const closing = new Map();
|
|
44
47
|
let holders = 0;
|
|
45
48
|
let inFlight = 0;
|
|
46
49
|
let idle = null;
|
|
@@ -55,13 +58,21 @@ export async function closeWorld() {
|
|
|
55
58
|
if (idle) { clearTimeout(idle); idle = null; }
|
|
56
59
|
const client = shared;
|
|
57
60
|
shared = null;
|
|
58
|
-
if (client
|
|
61
|
+
if (client && !closing.has(client)) {
|
|
62
|
+
const work = Promise.resolve().then(() => client.close());
|
|
63
|
+
closing.set(client, work);
|
|
64
|
+
void work.then(() => closing.delete(client), () => { /* retained for the next explicit close */ });
|
|
65
|
+
}
|
|
66
|
+
const results = await Promise.allSettled([...closing.values()]);
|
|
67
|
+
const errors = results.filter(result => result.status === 'rejected').map(result => result.reason);
|
|
68
|
+
if (errors.length === 1) throw errors[0];
|
|
69
|
+
if (errors.length) throw new AggregateError(errors, 'orchestration door shutdown failed', { cause: errors[0] });
|
|
59
70
|
}
|
|
60
71
|
|
|
61
72
|
function settle() {
|
|
62
73
|
if (idle) { clearTimeout(idle); idle = null; }
|
|
63
74
|
if (holders > 0 || inFlight > 0 || !shared) return;
|
|
64
|
-
idle = setTimeout(() => { idle = null; if (holders === 0 && inFlight === 0) void closeWorld(); }, IDLE_MS);
|
|
75
|
+
idle = setTimeout(() => { idle = null; if (holders === 0 && inFlight === 0) void closeWorld().catch(() => { /* retained for explicit shutdown */ }); }, IDLE_MS);
|
|
65
76
|
idle.unref?.();
|
|
66
77
|
}
|
|
67
78
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"
|
|
3
|
+
"devEngines": {
|
|
4
|
+
"packageManager": {
|
|
5
|
+
"name": "pnpm",
|
|
6
|
+
"version": ">=12.15.0",
|
|
7
|
+
"onFail": "error"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"version": "0.5.229",
|
|
4
11
|
"type": "module",
|
|
5
12
|
"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
13
|
"exports": {
|
|
@@ -31,16 +38,12 @@
|
|
|
31
38
|
}
|
|
32
39
|
},
|
|
33
40
|
"types": "./index.d.ts",
|
|
34
|
-
"scripts": {
|
|
35
|
-
"generate-types": "node scripts/generate-types.mjs",
|
|
36
|
-
"check-types": "node scripts/generate-types.mjs --check"
|
|
37
|
-
},
|
|
38
41
|
"dependencies": {
|
|
39
|
-
"@volter/supercode-teams": "
|
|
40
|
-
"@volter/supercode-terminal": "
|
|
41
|
-
"@volter/supercode-harness-sdk": "
|
|
42
|
-
"@volter/teams": "0.2.
|
|
43
|
-
"@volter/ztrack": "
|
|
42
|
+
"@volter/supercode-teams": "0.3.393",
|
|
43
|
+
"@volter/supercode-terminal": "0.3.15",
|
|
44
|
+
"@volter/supercode-harness-sdk": "0.3.112",
|
|
45
|
+
"@volter/teams": "0.2.112",
|
|
46
|
+
"@volter/ztrack": "3.0.1-reader.0",
|
|
44
47
|
"yaml": "^2.9.0"
|
|
45
48
|
},
|
|
46
49
|
"devDependencies": {
|
|
@@ -65,5 +68,9 @@
|
|
|
65
68
|
"board",
|
|
66
69
|
"apply",
|
|
67
70
|
"README.md"
|
|
68
|
-
]
|
|
69
|
-
|
|
71
|
+
],
|
|
72
|
+
"scripts": {
|
|
73
|
+
"generate-types": "node scripts/generate-types.mjs",
|
|
74
|
+
"check-types": "node scripts/generate-types.mjs --check"
|
|
75
|
+
}
|
|
76
|
+
}
|
package/session-recovery.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// The optional local-session retry policy. The machine owns observation and native input receipts;
|
|
2
2
|
// this orchestrator command owns when to ask for another turn. One invocation is bounded to eight seconds.
|
|
3
|
+
import { readMachineRecord } from '@volter/teams/connector-records';
|
|
3
4
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from 'node:fs';
|
|
4
5
|
import { join, resolve } from 'node:path';
|
|
5
6
|
import { randomUUID } from 'node:crypto';
|
|
@@ -75,7 +76,7 @@ export async function runSessionRecovery(args = []) {
|
|
|
75
76
|
log({ ...summary, durationMs: Date.now() - started, error: 'recovery pass exceeded its eight-second deadline' });
|
|
76
77
|
channel?.close(); owner?.close(); process.exit(1);
|
|
77
78
|
}, 8500);
|
|
78
|
-
const machine =
|
|
79
|
+
const machine = readMachineRecord(join(opts.home, 'teams'));
|
|
79
80
|
channel = await openLocalMachine(machine.socket, { timeoutMs: 1000 });
|
|
80
81
|
const rpc = new StreamRpc(await channel.open('node'));
|
|
81
82
|
const call = (method, params, maximum = 1800) => {
|
package/worker-route.mjs
CHANGED
|
@@ -60,13 +60,20 @@ export function hermesModel(profile) {
|
|
|
60
60
|
* writing the provider table into the worker's config home. The launch's `worker.model` (a job's own
|
|
61
61
|
* model, or the `worker:` block's) wins over `model.default`.
|
|
62
62
|
*/
|
|
63
|
-
export function workerRoute(profile, worker, vault) {
|
|
63
|
+
export function workerRoute(profile, worker, vault, nativeHome = null) {
|
|
64
64
|
const route = hermesModel(profile);
|
|
65
65
|
// `model.default` reaches only a worker that takes a model at launch; one that reads the home's own
|
|
66
66
|
// `model:` itself (Hermes over ACP) keeps it there
|
|
67
67
|
const model = worker?.model ?? (MODEL_ARGUMENTS[worker?.harness] ? route?.model ?? null : null);
|
|
68
68
|
if (route?.provider && worker?.harness === 'claude-code') return claudeRoute(profile, route, model, vault);
|
|
69
69
|
if (!route?.provider || worker?.harness !== 'codex') return { model, args: [], env: {} };
|
|
70
|
+
const home = nativeHome?.env?.CODEX_HOME ?? join(profile.dir, 'codex');
|
|
71
|
+
const { id, table, env } = codexProvider(route, vault);
|
|
72
|
+
if (id !== 'openai' || route.base_url) writeProviderTable(home, id, table);
|
|
73
|
+
return { model, args: ['-c', `model_provider=${JSON.stringify(id)}`], env };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function codexProvider(route, vault) {
|
|
70
77
|
const id = PROVIDER_ID[route.provider];
|
|
71
78
|
if (!id) throw new Error(`model.provider ${route.provider} has no Codex route in this orchestrator yet; refused rather than run on Codex's default`);
|
|
72
79
|
const env = {};
|
|
@@ -78,18 +85,16 @@ export function workerRoute(profile, worker, vault) {
|
|
|
78
85
|
table.requires_openai_auth = false;
|
|
79
86
|
table.env_key = LITERAL_KEY_ENV;
|
|
80
87
|
env[LITERAL_KEY_ENV] = 'valve';
|
|
81
|
-
|
|
82
|
-
|
|
88
|
+
} else {
|
|
89
|
+
if (route.base_url) table.base_url = resolve(route.base_url, vault).value;
|
|
90
|
+
if (route.api_key) {
|
|
91
|
+
const key = resolve(route.api_key, vault);
|
|
92
|
+
const name = key.name ?? LITERAL_KEY_ENV;
|
|
93
|
+
env[name] = key.value;
|
|
94
|
+
table.env_key = name;
|
|
95
|
+
}
|
|
83
96
|
}
|
|
84
|
-
|
|
85
|
-
if (route.api_key) {
|
|
86
|
-
const key = resolve(route.api_key, vault);
|
|
87
|
-
const name = key.name ?? LITERAL_KEY_ENV;
|
|
88
|
-
env[name] = key.value;
|
|
89
|
-
table.env_key = name;
|
|
90
|
-
}
|
|
91
|
-
if (id !== 'openai' || route.base_url) writeProviderTable(join(profile.dir, 'codex'), id, table);
|
|
92
|
-
return { model, args: ['-c', `model_provider=${JSON.stringify(id)}`], env };
|
|
97
|
+
return { id, table, env };
|
|
93
98
|
}
|
|
94
99
|
|
|
95
100
|
/**
|
|
@@ -140,11 +145,9 @@ function writeProviderTable(home, id, table) {
|
|
|
140
145
|
if (next !== text) writeFileSync(file, next, { mode: 0o600 });
|
|
141
146
|
}
|
|
142
147
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
export function codexPaneRoute(profile, worker, vault) {
|
|
147
|
-
if (worker?.harness !== 'codex' || hermesModel(profile)?.provider !== 'openai-codex')
|
|
148
|
+
function paneProvider(profile, worker, vault) {
|
|
149
|
+
const route = hermesModel(profile);
|
|
150
|
+
if (worker?.harness !== 'codex' || route?.provider !== 'openai-codex')
|
|
148
151
|
throw new Error('headful Codex requires model.provider openai-codex and its configured HERMES_CODEX_BASE_URL; no shared-login pane was launched');
|
|
149
152
|
if (worker.home === 'user') throw new Error('headful Codex requires a profile home; no shared-login pane was launched');
|
|
150
153
|
const forward = vault?.get?.('HERMES_CODEX_BASE_URL') ?? process.env.HERMES_CODEX_BASE_URL;
|
|
@@ -153,6 +156,21 @@ export function codexPaneRoute(profile, worker, vault) {
|
|
|
153
156
|
try { endpoint = new URL(forward); } catch { throw new Error('the configured Codex forward is not an HTTP endpoint; no pane was launched'); }
|
|
154
157
|
if (!['http:', 'https:'].includes(endpoint.protocol) || endpoint.username || endpoint.password)
|
|
155
158
|
throw new Error('the configured Codex forward must be HTTP(S) without URL credentials; no pane was launched');
|
|
159
|
+
return { model: worker.model ?? route.model ?? null, ...codexProvider(route, vault) };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** A login-free Codex route carried entirely in native launch arguments. */
|
|
163
|
+
export function codexPaneRouteArguments(profile, worker, vault) {
|
|
164
|
+
const { id, table, env } = paneProvider(profile, worker, vault);
|
|
165
|
+
return { env, args: ['-c', `model_provider=${JSON.stringify(id)}`,
|
|
166
|
+
...Object.entries(table).flatMap(([name, value]) => ['-c', `model_providers.${id}.${name}=${JSON.stringify(value)}`])] };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** The headful Codex lane must never fall back to the shared ChatGPT login.
|
|
170
|
+
* This returns only the configured forward's stand-in key and provider flags;
|
|
171
|
+
* it neither copies a credential nor starts a program or login. */
|
|
172
|
+
export function codexPaneRoute(profile, worker, vault) {
|
|
173
|
+
const { model, id, table, env } = paneProvider(profile, worker, vault);
|
|
156
174
|
const home = join(profile.dir, 'codex');
|
|
157
175
|
if (Object.hasOwn(worker.env ?? {}, 'CODEX_HOME') && worker.env.CODEX_HOME !== home)
|
|
158
176
|
throw new Error('headful Codex has a different worker.env.CODEX_HOME; no shared-login pane was launched');
|
|
@@ -163,6 +181,6 @@ export function codexPaneRoute(profile, worker, vault) {
|
|
|
163
181
|
} catch (error) { if (error.code !== 'ENOENT') throw error; }
|
|
164
182
|
materializePersona(profile);
|
|
165
183
|
materializeSkills(profile);
|
|
166
|
-
|
|
167
|
-
return {
|
|
184
|
+
writeProviderTable(home, id, table);
|
|
185
|
+
return { model, args: ['-c', `model_provider=${JSON.stringify(id)}`], env: { CODEX_HOME: home, ...env } };
|
|
168
186
|
}
|