@volter/supercode-orchestrator 0.5.25 → 0.5.27
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/agent-door.mjs +15 -5
- package/board/cli.mjs +26 -8
- package/board/dispatch.mjs +42 -10
- package/board/event-fleet.mjs +13 -6
- package/package.json +3 -3
package/bin/agent-door.mjs
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// The orchestrator's synchronous host reaches Teams through the installed neutral SDK.
|
|
3
3
|
import { fromContext } from '@volter/teams/context';
|
|
4
4
|
import { supercodeHome } from '@volter/teams/home';
|
|
5
|
-
import { createHash } from 'node:crypto';
|
|
6
5
|
import { openMachine, normalMachineName } from '@volter/teams/machine-door';
|
|
7
6
|
import { hostname } from 'node:os';
|
|
8
7
|
import { StreamRpc } from '@volter/teams/client';
|
|
@@ -31,10 +30,21 @@ try{
|
|
|
31
30
|
}
|
|
32
31
|
else if(op==='reader')result=await team.agents.delegateReader(args[0],{},{...options,ifMatch:args[1]});
|
|
33
32
|
else if(op==='bindContext'){
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
// The binding names the agent's reader context on the session's machine, so it installs that context itself: a
|
|
34
|
+
// launch installs one before its session starts, but an adopted session never had one, and a context installed a
|
|
35
|
+
// day before has expired. An ended agent has neither a reader nor a live session: Teams revoked its credentials
|
|
36
|
+
// and ended its sessions when it ended, so there is nothing to bind.
|
|
37
|
+
const agent=(await team.agents.get(args[0],options)).data;
|
|
38
|
+
if(agent.state==='ended')result={bound:false,reason:`agent ${agent.id} ended`};
|
|
39
|
+
else{
|
|
40
|
+
const reader=await team.agents.delegateReader(agent.id,{expires_in_ms:86400000},{...options,ifMatch:agent.revision});
|
|
41
|
+
const channel=await openMachine({machine:reach(args[2]),context:context??process.env.SUPERCODE_CONTEXT});
|
|
42
|
+
try{
|
|
43
|
+
const rpc=new StreamRpc(await channel.open('node'));
|
|
44
|
+
const installed=await rpc.call('harness.v1.agents.context',{origin:scope.origin,team:scope.team_id,agent:agent.id,credential:reader.data});
|
|
45
|
+
result=await rpc.call('harness.v1.agents.bindSession',{session:args[1],context:installed.context,assignments:args[3]??[],intended:args[4]??null});
|
|
46
|
+
}finally{channel.close();}
|
|
47
|
+
}
|
|
38
48
|
}
|
|
39
49
|
else if(op==='readerContext'){
|
|
40
50
|
const identity=await team.agents.get(args[0],options);
|
package/board/cli.mjs
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { taskStatusOf } from './published.mjs';
|
|
8
8
|
import { appendFileSync, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
9
9
|
import { spawnSync } from 'node:child_process';
|
|
10
|
+
import { recordSlow, takeSteps } from '@volter/supercode-harness-sdk/slow-log';
|
|
10
11
|
import { basename, join, resolve } from 'node:path';
|
|
11
12
|
import { homedir, tmpdir, userInfo } from 'node:os';
|
|
12
13
|
import { read } from '../orchestration.mjs';
|
|
@@ -298,13 +299,13 @@ const metadataOf = (v) => {
|
|
|
298
299
|
};
|
|
299
300
|
|
|
300
301
|
/**
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
* from the registry at most once an hour.
|
|
302
|
+
* What is wrong with this orchestrator's version, or null: a build no release names (a hand install of a branch) or one
|
|
303
|
+
* older than npm's latest. Every workflow call says it and the dispatcher tells its managers; action is required until
|
|
304
|
+
* the release is installed. The latest is read from the registry at most once an hour.
|
|
304
305
|
*/
|
|
305
|
-
function
|
|
306
|
+
function releaseBehind() {
|
|
306
307
|
let running;
|
|
307
|
-
try { running = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version; } catch { return; }
|
|
308
|
+
try { running = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version; } catch { return null; }
|
|
308
309
|
const cache = join(tmpdir(), 'supercode-orchestrator-latest.json');
|
|
309
310
|
let latest = null;
|
|
310
311
|
let fresh = false;
|
|
@@ -315,13 +316,18 @@ function sayReleaseBehind() {
|
|
|
315
316
|
latest = r.status === 0 ? r.stdout.trim() || null : null;
|
|
316
317
|
try { writeFileSync(cache, JSON.stringify({ at: Date.now(), latest })); } catch { /* read again next call */ }
|
|
317
318
|
}
|
|
318
|
-
if (!latest || running === latest) return;
|
|
319
|
+
if (!latest || running === latest) return null;
|
|
319
320
|
const parts = (v) => v.split('-')[0].split('.').map(Number);
|
|
320
321
|
const [a, b] = [parts(running), parts(latest)];
|
|
321
322
|
const older = a[0] - b[0] || a[1] - b[1] || a[2] - b[2];
|
|
322
323
|
const fix = `npm install -g @volter/supercode-orchestrator@${latest} (and each install's pinned copy)`;
|
|
323
|
-
if (running.includes('-'))
|
|
324
|
-
|
|
324
|
+
if (running.includes('-')) return { key: `${running}>${latest}`, text: `orchestrator ${running} is not a published release (latest ${latest}); action required: ${fix}` };
|
|
325
|
+
if (older < 0) return { key: `${running}>${latest}`, text: `orchestrator ${running} is behind the published ${latest}; action required: ${fix}` };
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
function sayReleaseBehind() {
|
|
329
|
+
const behind = releaseBehind();
|
|
330
|
+
if (behind) process.stderr.write(`board: error: ${behind.text}\n`);
|
|
325
331
|
}
|
|
326
332
|
|
|
327
333
|
export async function runWorkflow(argv) {
|
|
@@ -1394,7 +1400,17 @@ async function serve(root, args = {}) {
|
|
|
1394
1400
|
const FAILED_ROUNDS_ALERT = 5;
|
|
1395
1401
|
const failing = new Map();
|
|
1396
1402
|
let round = 0;
|
|
1403
|
+
// A dispatcher that is not the published release is told up the chain, once per version pair: it keeps running, and
|
|
1404
|
+
// its manager hears that action is required (a caller's stderr line alone reached no one).
|
|
1405
|
+
const versionsTold = new Set();
|
|
1406
|
+
const tellReleaseBehind = async () => {
|
|
1407
|
+
const behind = releaseBehind();
|
|
1408
|
+
if (!behind || versionsTold.has(behind.key)) return;
|
|
1409
|
+
// the send's key makes a retry safe; a pair counts as told only once a manager got it
|
|
1410
|
+
if (await alertManagers(root, 'default', `The dispatcher of ${root} runs ${behind.text}`, `release-behind-${behind.key}`) > 0) versionsTold.add(behind.key);
|
|
1411
|
+
};
|
|
1397
1412
|
const watchRounds = async (reports) => {
|
|
1413
|
+
await tellReleaseBehind().catch(() => {});
|
|
1398
1414
|
if (reports.every((r) => !r.error)) {
|
|
1399
1415
|
const temporary = `${heartbeat}.${process.pid}.tmp`;
|
|
1400
1416
|
try {
|
|
@@ -1491,6 +1507,8 @@ async function serve(root, args = {}) {
|
|
|
1491
1507
|
log(`tick failed: ${error.message}`);
|
|
1492
1508
|
await watchRounds([{ board: '*', error: `tick failed: ${error.message}` }]);
|
|
1493
1509
|
}
|
|
1510
|
+
// each round is a line of its own in the slow log, with the steps it spent its time on
|
|
1511
|
+
recordSlow({ kind: 'call', name: 'dispatch round', ms: Date.now() - began, slowest: null, extra: { steps: takeSteps() } });
|
|
1494
1512
|
// a newer install is looked for after each tick and through the wait before the next
|
|
1495
1513
|
const newer = () => { const now = installed(); return running && now && now !== running ? now : null; };
|
|
1496
1514
|
let moved = newer();
|
package/board/dispatch.mjs
CHANGED
|
@@ -143,6 +143,16 @@ const pause = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0,
|
|
|
143
143
|
// headless worker by its pid, a pane by that machine's pane list. Null only when the machine cannot answer.
|
|
144
144
|
const sessionAlive = (fleet, session) => fleet.native?.has(addressOf(session)) ? fleet.native.get(addressOf(session)).alive : unpublishedAlive(session);
|
|
145
145
|
const paneLists = new Map();
|
|
146
|
+
// A run the agent sync holds names only its machine and native session (`harness:id`), never a pane or pid. Teams'
|
|
147
|
+
// fleet answers for a session it published; one it never published (a gateway worker in its profile's directory,
|
|
148
|
+
// outside every synced root) is read through its machine's native discovery, as a native close reads it.
|
|
149
|
+
function syncRowAlive(fleet, row) {
|
|
150
|
+
const machine = row.machine || localMachine(), at = row.session_ref.indexOf(':');
|
|
151
|
+
const session = { machine, harness: row.session_ref.slice(0, at), session_id: row.session_ref.slice(at + 1), address: `sc:${machine}:${row.session_ref}` };
|
|
152
|
+
const alive = sessionAlive(fleet, session);
|
|
153
|
+
if (alive !== null) return alive;
|
|
154
|
+
try { return nativeReading(nativeSessions(machine, session.harness), session).alive; } catch { return null; }
|
|
155
|
+
}
|
|
146
156
|
function unpublishedAlive(session) {
|
|
147
157
|
if (!session) return null;
|
|
148
158
|
if (session.surface === 'headless' && session.pid) return pidAlive(session.pid);
|
|
@@ -302,7 +312,10 @@ function supervise(board, fleet, settings, report, entry, effects) {
|
|
|
302
312
|
}
|
|
303
313
|
};
|
|
304
314
|
const limits = limitsOf(db, board, entry, task, session);
|
|
305
|
-
|
|
315
|
+
// A verified adoption binds the run to its session; a launch key still pending beside it is the reconciler's to
|
|
316
|
+
// retire at its machine, and no longer what the run waits on.
|
|
317
|
+
const adopted = session?.mode === 'adopt' && Boolean(session.address);
|
|
318
|
+
if (runRow && ((session?.launch_pending && !adopted) || session?.launch_deferred || (!session?.session_id && !session?.pid))) {
|
|
306
319
|
const age = t - Number(session?.launched_at ?? runRow.started_at);
|
|
307
320
|
if (age >= settings.launchWindow) {
|
|
308
321
|
const reason = session?.launch_deferred_error ?? session?.launch_error ?? 'no session or process was bound to the dispatched run';
|
|
@@ -795,13 +808,16 @@ export function closeRequested(board, report, { taskId = null, caller = null } =
|
|
|
795
808
|
receipt = launchReceipt(board, session);
|
|
796
809
|
if (!receipt) receipt = cancelUnissuedLaunch(board, session);
|
|
797
810
|
} catch (error) { receiptError = error.message; }
|
|
798
|
-
|
|
811
|
+
const retired = unissuedLaunch(receipt, session) && retireUnissuedLaunch(board, row, session, receipt);
|
|
812
|
+
if (retired === true) {
|
|
799
813
|
(report.closed ??= []).push({ task_id: row.task_id, ...session, receipt });
|
|
800
814
|
continue;
|
|
801
815
|
}
|
|
816
|
+
// the key was never issued, and the session adopted onto the run is closed (or released) as its own below
|
|
817
|
+
if (retired === 'released') session = json(board.db.prepare('SELECT metadata FROM task_runs WHERE id = ?').get(row.id)?.metadata, {})?.supercode;
|
|
802
818
|
// A receipt that names its pane and reads it not live is terminal enough to settle, with or without a status (a
|
|
803
819
|
// daemon that predates launch statuses answers none): the board's own pane reading below confirms the close
|
|
804
|
-
if (receipt?.pane && receipt.status !== 'issued' && (receipt.status || typeof receipt.live === 'boolean' || receipt.exit)) { bindLaunch(board, row, session, receipt.pane, session.session_id); session = { ...session, pane: receipt.pane }; }
|
|
820
|
+
else if (receipt?.pane && receipt.status !== 'issued' && (receipt.status || typeof receipt.live === 'boolean' || receipt.exit)) { bindLaunch(board, row, session, receipt.pane, session.session_id); session = { ...session, pane: receipt.pane }; }
|
|
805
821
|
else {
|
|
806
822
|
const error = `close deferred: ${receiptError ?? `awaiting terminal launch receipt ${session.launch_key} (${receipt?.status ?? 'unknown'})`}`;
|
|
807
823
|
tx(board, (db) => {
|
|
@@ -1187,12 +1203,24 @@ function unissuedLaunch(receipt, session) {
|
|
|
1187
1203
|
receipt?.outcome === 'not_issued' && receipt?.live === false && receipt?.pane === null;
|
|
1188
1204
|
}
|
|
1189
1205
|
|
|
1206
|
+
/**
|
|
1207
|
+
* Retire a launch key its machine confirmed it never issued. Returns true when the run's launch is settled and closed,
|
|
1208
|
+
* 'released' when the key is retired but the run keeps a session adopted onto it, false when the run is not this key's.
|
|
1209
|
+
*/
|
|
1190
1210
|
function retireUnissuedLaunch(board, row, session, receipt, { entry = null, effects = [] } = {}) {
|
|
1191
1211
|
if (!unissuedLaunch(receipt, session)) return false;
|
|
1192
1212
|
const result = tx(board, db => {
|
|
1193
1213
|
const current = db.prepare('SELECT ended_at, metadata FROM task_runs WHERE id=?').get(row.id);
|
|
1194
1214
|
const saved = json(current?.metadata, {})?.supercode;
|
|
1195
|
-
if (!saved || saved.launch_key !== session.launch_key
|
|
1215
|
+
if (!saved || saved.launch_key !== session.launch_key) return { retired: false, effects: [] };
|
|
1216
|
+
// A session adopted onto a run whose launch was still pending did not come from that launch (the machine never
|
|
1217
|
+
// issued the key): the key is retired and the adopted session stays the run's, supervised and closed as its own.
|
|
1218
|
+
if (saved.mode === 'adopt' && saved.address && saved.launch_pending) {
|
|
1219
|
+
setRunSession(db, row.id, { launch_pending: false, launch_cancellation: receipt, launch_error: null, launch_key: null });
|
|
1220
|
+
event(db, row.task_id, 'session_launch_cancelled', { key: session.launch_key, machine: session.machine, receipt, kept: saved.address }, row.id);
|
|
1221
|
+
return { retired: 'released', effects: [] };
|
|
1222
|
+
}
|
|
1223
|
+
if (saved.pane || saved.address || saved.pid) return { retired: false, effects: [] };
|
|
1196
1224
|
if (!current.ended_at && (!entry || getTask(db, row.task_id)?.current_run_id !== row.id)) return { retired: false, effects: [] };
|
|
1197
1225
|
if (saved.close_confirmed_at) return { retired: true, effects: [] };
|
|
1198
1226
|
const collected = [];
|
|
@@ -1313,13 +1341,16 @@ function reconcileLaunches(board, fleet, report, entry, effects, settings, lanes
|
|
|
1313
1341
|
for(const [org,context] of orgs)for(const row of pendingAgentRuns(board.root,org)){
|
|
1314
1342
|
const launch=agentRuns.find(run=>String(run.id)===String(row.run_id));
|
|
1315
1343
|
try{synchronizeAgentRun(board.root,org,row,{context,intended:json(launch?.metadata,{})?.supercode?.agent_profile_intent});}
|
|
1316
|
-
catch(error){if(error.code==='rate_limited'){report.queued.push([row.run_id,`agent session sync pending: ${error.message}`]);break;}const gone=error.claimRefused&&error.status===404&&fleet.ok&&
|
|
1344
|
+
catch(error){if(error.code==='rate_limited'){report.queued.push([row.run_id,`agent session sync pending: ${error.message}`]);break;}const gone=error.claimRefused&&error.status===404&&fleet.ok&&syncRowAlive(fleet,row)===false;if(gone)abandonAgentRun(board.root,org,row.session_ref);report.failed.push([row.run_id,`agent session sync failed: ${error.message}${gone?`; ${row.session_ref} is stopped and Teams refuses it, so it leaves the sync`:''}`]);}
|
|
1317
1345
|
}
|
|
1318
|
-
const deferred=view(board,db=>db.prepare("SELECT t.id,r.started_at FROM tasks t JOIN task_runs r ON r.id=t.current_run_id WHERE r.ended_at IS NULL AND json_extract(r.metadata,'$.supercode.launch_deferred')=1").all());
|
|
1346
|
+
const deferred=view(board,db=>db.prepare("SELECT t.id,r.id run_id,COALESCE(json_extract(r.metadata,'$.supercode.launched_at'),r.started_at) launched_at FROM tasks t JOIN task_runs r ON r.id=t.current_run_id WHERE r.ended_at IS NULL AND json_extract(r.metadata,'$.supercode.launch_deferred')=1").all());
|
|
1319
1347
|
// A deferred launch was never issued (a reading or door was not yet available): it is the start its manager, creator
|
|
1320
|
-
// or workflow asked for, carried out within the launch window; supervise surfaces it once when the window ends
|
|
1321
|
-
|
|
1322
|
-
|
|
1348
|
+
// or workflow asked for, carried out within the launch window; supervise surfaces it once when the window ends. The
|
|
1349
|
+
// window counts only time the fleet could be read: a launch waiting on an unreadable fleet was never tried, so its
|
|
1350
|
+
// clock moves with the wait instead of running out (every resumed card failed so in the 22:17Z event-stream outage).
|
|
1351
|
+
for(const {id,run_id,launched_at} of deferred) {
|
|
1352
|
+
if (now() - launched_at >= settings.launchWindow) continue;
|
|
1353
|
+
if (!fleet.ok) { tx(board,db=>setRunSession(db,run_id,{launched_at:now()})); report.queued.push([id,`launch waits for the fleet: ${fleet.error}`]); continue; }
|
|
1323
1354
|
try {
|
|
1324
1355
|
const task=getTaskNow(board,id),status=view(board,db=>statusOf(db,task)),spec=runSpec(entry,status,task);
|
|
1325
1356
|
const profile=spec&&view(board,db=>evaluate(entry.workflow.roles?.[spec.role]??'card.assignee',scopeOf(db,board,task,{params:entry.params})));
|
|
@@ -1758,7 +1789,8 @@ function adoptSession(board, id, who, { workspace = null, ttl, assignee = null,
|
|
|
1758
1789
|
const session = { surface: found.pane ? 'pane' : 'native', session_id: sessionId, harness, machine: host, pane: found.pane ?? null, address: found.address, mode: 'adopt', launched_at: now(), seen_at: now(),
|
|
1759
1790
|
...(agent ? { agent_id: agent.identity.id, agent_org: team.org, agent_context: context ?? null,
|
|
1760
1791
|
agent_profile_intent: { ...structuredClone(agent.intended.resolved), worker: { ...agent.intended.resolved?.worker, harness: lane.harness, model: lane.model ?? null, cwd: dir ?? task.workspace_path ?? null } } } : {}) };
|
|
1761
|
-
|
|
1792
|
+
// a deferred launch was never issued: the adopted session is the run's, and no later round launches another
|
|
1793
|
+
setRunSession(db, runId, { ...(currentRun(db, id)?.metadata?.supercode ?? {}), ...session, launch_deferred: false, slot: currentRun(db, id)?.metadata?.supercode?.slot ?? beforeSession?.slot ?? expected?.session ?? 'worker', close_requested: null, closed_at: null, close_confirmed_at: null, close_error: null, missing_since: null });
|
|
1762
1794
|
event(db, id, 'session_adopted', { session_id: sessionId, machine: host, pane: session.pane, address: found.address, mode: 'adopt', role: currentRun(db, id)?.metadata?.supercode?.role }, runId);
|
|
1763
1795
|
subscribeIn(db, id, found.address, 'worker');
|
|
1764
1796
|
for (const p of db.prepare('SELECT parent_id FROM task_links WHERE child_id = ?').all(id)) subscribeIn(db, p.parent_id, found.address, 'dependency');
|
package/board/event-fleet.mjs
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import { readFileSync, writeFileSync, renameSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { randomUUID } from 'node:crypto';
|
|
5
|
-
import { eventClient } from '@volter/teams/event-client';
|
|
5
|
+
import { eventClient, retryAfterMs } from '@volter/teams/event-client';
|
|
6
6
|
const pathOf=root=>join(root,'.session-events.json');
|
|
7
7
|
const normalize=value=>String(value??'').toLowerCase().replace(/[^a-z0-9-]/g,'-').replace(/-+/g,'-').replace(/^-|-$/g,'');
|
|
8
|
-
const wait=signal=>new Promise(resolve=>{const timer=setTimeout(resolve,
|
|
8
|
+
const wait=(signal,ms)=>new Promise(resolve=>{const timer=setTimeout(resolve,ms);signal.addEventListener('abort',()=>{clearTimeout(timer);resolve();},{once:true});});
|
|
9
9
|
|
|
10
10
|
export function followFleet({root,context,supercodeHome,wake,log}){
|
|
11
11
|
const abort=new AbortController();let resources={},cursor=null,connected=false,snapshot=null;
|
|
@@ -20,20 +20,27 @@ export function followFleet({root,context,supercodeHome,wake,log}){
|
|
|
20
20
|
target[key]={sequence:frame.sequence,data:event.data};return true;
|
|
21
21
|
};
|
|
22
22
|
void(async()=>{
|
|
23
|
+
// Refused for load, the follower waits out the server's retry_after, doubling while it is refused (to 5 min), and
|
|
24
|
+
// keeps its cursor: asking every 5 s spent the slot it waited for on the next refusal.
|
|
25
|
+
let refused=0;
|
|
23
26
|
while(!abort.signal.aborted){
|
|
27
|
+
let pause=5000;
|
|
24
28
|
try{
|
|
25
29
|
const {team}=eventClient({context,supercodeHome});
|
|
26
30
|
// Every connection begins with a complete, ACL-filtered snapshot. A process restart cannot trust an old cache.
|
|
27
31
|
for await(const frame of team.events.follow({kinds:['session.started','session.ended','session.changed','resource.changed']}, {signal:abort.signal,after:cursor})){
|
|
28
32
|
if(frame.type==='snapshot_begin'){snapshot={};connected=false;persist();}
|
|
29
33
|
else if(frame.type==='snapshot_item')apply(snapshot??resources,frame);
|
|
30
|
-
else if(frame.type==='snapshot_end'){resources=snapshot??resources;snapshot=null;cursor=frame.cursor;connected=true;persist();wake('session-events-snapshot');}
|
|
34
|
+
else if(frame.type==='snapshot_end'){resources=snapshot??resources;snapshot=null;cursor=frame.cursor;connected=true;refused=0;persist();wake('session-events-snapshot');}
|
|
31
35
|
else if(frame.type==='event'){const changed=apply(resources,frame);cursor=frame.cursor;connected=true;persist();if(changed)wake('session-events');}
|
|
32
|
-
else if(frame.type==='checkpoint'){const recovered=!connected;cursor=frame.cursor;connected=true;persist();if(recovered)wake('session-events-reconnected');}
|
|
36
|
+
else if(frame.type==='checkpoint'){const recovered=!connected;cursor=frame.cursor;connected=true;refused=0;persist();if(recovered)wake('session-events-reconnected');}
|
|
33
37
|
else if(frame.terminal){cursor=null;connected=false;persist();wake('session-events-unavailable');break;}
|
|
34
38
|
}
|
|
35
|
-
}catch(error){
|
|
36
|
-
|
|
39
|
+
}catch(error){
|
|
40
|
+
const asked=retryAfterMs(error);if(asked){pause=Math.min(300000,asked*2**refused);refused++;}
|
|
41
|
+
if(!abort.signal.aborted)log(`dispatch session events unavailable: ${error.code??error.message}${asked?` (retrying in ${Math.round(pause/1000)} s)`:''}`);if(error.status===409)cursor=null;
|
|
42
|
+
}
|
|
43
|
+
connected=false;persist();wake('session-events-unavailable');if(!abort.signal.aborted)await wait(abort.signal,pause);
|
|
37
44
|
}
|
|
38
45
|
})();
|
|
39
46
|
return {close(){abort.abort();connected=false;persist();}};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.27",
|
|
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": {
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"check-types": "node scripts/generate-types.mjs --check"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@volter/supercode-harness-sdk": "^0.3.
|
|
28
|
-
"@volter/teams": "0.2.
|
|
27
|
+
"@volter/supercode-harness-sdk": "^0.3.60",
|
|
28
|
+
"@volter/teams": "0.2.12",
|
|
29
29
|
"@volter/ztrack": "^2.4.23",
|
|
30
30
|
"yaml": "^2.9.0"
|
|
31
31
|
},
|