@dougbots/avenor-core 0.9.0 → 0.10.1

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/dist/index.js CHANGED
@@ -6,6 +6,8 @@ import * as fs from "node:fs";
6
6
  import * as os from "node:os";
7
7
  import * as path from "node:path";
8
8
  import * as crypto from "node:crypto";
9
+ import { umpire } from "@umpire/core";
10
+ import { fromJson } from "@umpire/json";
9
11
  //#region src/client.ts
10
12
  function normalizeSubscribeOptions(options) {
11
13
  if (!options) return void 0;
@@ -280,6 +282,107 @@ var Client = class {
280
282
  const params = runtimeId ? { runtime_id: runtimeId } : void 0;
281
283
  await this.call("cancel", params);
282
284
  }
285
+ async workflowStatus(workflowId) {
286
+ return this.call("workflow.status", { workflow_id: workflowId });
287
+ }
288
+ async workflowWait(workflowId, timeoutMs) {
289
+ const params = { workflow_id: workflowId };
290
+ if (timeoutMs !== void 0) params.timeout_ms = timeoutMs;
291
+ return this.call("workflow.wait", params);
292
+ }
293
+ async workflowInspect(workflowId) {
294
+ return this.call("workflow.inspect", { workflow_id: workflowId });
295
+ }
296
+ async workflowEvents(workflowId, opts) {
297
+ const params = { workflow_id: workflowId };
298
+ if (opts?.afterSeq !== void 0) params.after_seq = opts.afterSeq;
299
+ if (opts?.limit !== void 0) params.limit = opts.limit;
300
+ return this.call("workflow.events", params);
301
+ }
302
+ async workflowCommand(workflowId, command) {
303
+ return this.call("workflow.command", {
304
+ workflow_id: workflowId,
305
+ command
306
+ });
307
+ }
308
+ async workflowComplete(params) {
309
+ const command = {
310
+ op: "complete",
311
+ node_id: params.node_id,
312
+ activation_id: params.activation_id,
313
+ attempt_id: params.attempt_id,
314
+ lease_id: params.lease_id,
315
+ owner_token: params.owner_token,
316
+ outcome: params.outcome
317
+ };
318
+ if (params.outputs !== void 0) command.outputs = params.outputs;
319
+ if (params.artifacts !== void 0) command.artifacts = params.artifacts;
320
+ return this.workflowCommand(params.workflow_id, command);
321
+ }
322
+ async workflowGate(params) {
323
+ const command = {
324
+ op: "gate",
325
+ node_id: params.node_id,
326
+ activation_id: params.activation_id,
327
+ gate_id: params.gate_id,
328
+ operation: params.operation
329
+ };
330
+ const optional = [
331
+ ["actor", params.actor],
332
+ ["reason", params.reason],
333
+ ["outcome", params.outcome],
334
+ ["subject", params.subject],
335
+ ["poll_id", params.poll_id],
336
+ ["source", params.source],
337
+ ["result", params.result],
338
+ ["response_hash", params.response_hash],
339
+ ["observed_at", params.observed_at],
340
+ ["evidence_ids", params.evidence_ids]
341
+ ];
342
+ for (const [key, value] of optional) if (value !== void 0) command[key] = value;
343
+ return this.workflowCommand(params.workflow_id, command);
344
+ }
345
+ async workflowHeartbeat(params) {
346
+ const command = {
347
+ op: "heartbeat",
348
+ node_id: params.node_id,
349
+ lease_id: params.lease_id,
350
+ owner_token: params.owner_token
351
+ };
352
+ if (params.activation_id !== void 0) command.activation_id = params.activation_id;
353
+ return this.workflowCommand(params.workflow_id, command);
354
+ }
355
+ async brokerSend(fromRunId, toRunId, message, role) {
356
+ await this.call("broker_send", {
357
+ from_run_id: fromRunId,
358
+ to_run_id: toRunId,
359
+ message,
360
+ role: role ?? "agent"
361
+ });
362
+ }
363
+ async brokerAsk(toRunId, message, role) {
364
+ return await this.call("broker_ask", {
365
+ to_run_id: toRunId,
366
+ message,
367
+ role: role ?? "agent"
368
+ });
369
+ }
370
+ async brokerPeers() {
371
+ return (await this.call("broker_peers")).sessions ?? [];
372
+ }
373
+ async brokerCancel(messageId) {
374
+ await this.call("broker_cancel", { message_id: messageId });
375
+ }
376
+ async brokerReceive() {
377
+ return await this.call("broker_receive");
378
+ }
379
+ async brokerReply(toRunId, replyTo, message) {
380
+ await this.call("broker_reply", {
381
+ to_run_id: toRunId,
382
+ reply_to: replyTo,
383
+ message
384
+ });
385
+ }
283
386
  isClosed() {
284
387
  return this.closed || this.socket.destroyed || this.socket.writableEnded;
285
388
  }
@@ -1074,6 +1177,95 @@ function observeRun(client, options = {}) {
1074
1177
  };
1075
1178
  }
1076
1179
  //#endregion
1180
+ //#region ../../schemas/spawn_selection.umpire.json
1181
+ var spawn_selection_umpire_default = {
1182
+ version: 1,
1183
+ conditions: { "rosterConfigured": {
1184
+ "type": "boolean",
1185
+ "description": "Whether the caller has a configured roster context"
1186
+ } },
1187
+ fields: {
1188
+ "agent": { "isEmpty": "string" },
1189
+ "model": { "isEmpty": "string" },
1190
+ "backend": { "isEmpty": "string" },
1191
+ "roster_file": { "isEmpty": "string" },
1192
+ "roster_entry": { "isEmpty": "string" }
1193
+ },
1194
+ rules: [
1195
+ {
1196
+ "type": "requires",
1197
+ "field": "roster_file",
1198
+ "dependency": "roster_entry",
1199
+ "reason": "roster_file requires roster_entry"
1200
+ },
1201
+ {
1202
+ "type": "fairWhen",
1203
+ "field": "roster_entry",
1204
+ "when": {
1205
+ "op": "or",
1206
+ "exprs": [{
1207
+ "op": "present",
1208
+ "field": "roster_file"
1209
+ }, {
1210
+ "op": "cond",
1211
+ "condition": "rosterConfigured"
1212
+ }]
1213
+ },
1214
+ "reason": "roster_entry requires roster_file unless a roster context is configured"
1215
+ },
1216
+ {
1217
+ "type": "disables",
1218
+ "source": "roster_entry",
1219
+ "targets": [
1220
+ "agent",
1221
+ "model",
1222
+ "backend"
1223
+ ],
1224
+ "reason": "direct identity fields are disabled in roster mode"
1225
+ }
1226
+ ]
1227
+ };
1228
+ //#endregion
1229
+ //#region src/spawn-selection.ts
1230
+ const selectorFields = [
1231
+ "agent",
1232
+ "model",
1233
+ "backend",
1234
+ "roster_file",
1235
+ "roster_entry"
1236
+ ];
1237
+ const selectorFieldSet = new Set(selectorFields);
1238
+ const parsedSchema = fromJson(spawn_selection_umpire_default);
1239
+ const spawnSelectionUmpire = umpire({
1240
+ fields: parsedSchema.fields,
1241
+ rules: parsedSchema.rules
1242
+ });
1243
+ function assertInput(input) {
1244
+ if (input === null || typeof input !== "object" || Array.isArray(input)) throw new Error("invalid spawn selector: expected an object");
1245
+ for (const key of Object.keys(input)) {
1246
+ if (!selectorFieldSet.has(key)) throw new Error(`invalid spawn selector: unknown field "${key}"`);
1247
+ const value = input[key];
1248
+ if (value !== void 0 && typeof value !== "string") throw new Error(`invalid spawn selector: ${key} must be a string`);
1249
+ }
1250
+ }
1251
+ /** Validate the direct-versus-roster selector contract. */
1252
+ function validateSpawnSelection(input, rosterConfigured = false) {
1253
+ assertInput(input);
1254
+ if (typeof rosterConfigured !== "boolean") throw new Error("invalid spawn selector: rosterConfigured must be a boolean");
1255
+ const values = {};
1256
+ for (const field of selectorFields) {
1257
+ const value = input[field];
1258
+ if (value !== void 0 && value !== "") values[field] = value;
1259
+ }
1260
+ const availability = spawnSelectionUmpire.check(values, { rosterConfigured });
1261
+ for (const field of selectorFields) {
1262
+ const value = values[field];
1263
+ const status = availability[field];
1264
+ if (value === void 0 || status?.enabled && status.fair) continue;
1265
+ throw new Error(`invalid spawn selector: ${status?.reason ?? `${field} is disabled`}`);
1266
+ }
1267
+ }
1268
+ //#endregion
1077
1269
  //#region src/paths.ts
1078
1270
  function avenorHome() {
1079
1271
  return process.env.AVENOR_HOME || path.join(os.homedir(), ".avenor");
@@ -1098,6 +1290,37 @@ function ensureRunPaths(runId) {
1098
1290
  }
1099
1291
  //#endregion
1100
1292
  //#region src/supervisor.ts
1293
+ function retainLiveIdentity(runInfo, liveStatus) {
1294
+ const value = (candidate) => typeof candidate === "string" && candidate.length > 0 ? candidate : void 0;
1295
+ const liveIdentity = (effectiveKey, directKey) => {
1296
+ if (typeof liveStatus[effectiveKey] === "string") return {
1297
+ present: true,
1298
+ value: value(liveStatus[effectiveKey])
1299
+ };
1300
+ if (typeof liveStatus[directKey] === "string") return {
1301
+ present: true,
1302
+ value: value(liveStatus[directKey])
1303
+ };
1304
+ return { present: false };
1305
+ };
1306
+ const effectiveAgent = liveIdentity("effective_agent", "agent");
1307
+ const effectiveModel = liveIdentity("effective_model", "model");
1308
+ const effectiveBackend = liveIdentity("effective_backend", "backend");
1309
+ const sessionId = value(liveStatus.session_id);
1310
+ const rosterFilePresent = typeof liveStatus.roster_file === "string";
1311
+ const rosterFile = value(liveStatus.roster_file);
1312
+ const rosterEntryPresent = typeof liveStatus.roster_entry === "string";
1313
+ const rosterEntry = value(liveStatus.roster_entry);
1314
+ const agentProfilePresent = typeof liveStatus.agent_profile === "string";
1315
+ const agentProfile = value(liveStatus.agent_profile);
1316
+ if (effectiveAgent.present) runInfo.agent = runInfo.effectiveAgent = effectiveAgent.value;
1317
+ if (effectiveModel.present) runInfo.model = runInfo.effectiveModel = effectiveModel.value;
1318
+ if (effectiveBackend.present) runInfo.backend = runInfo.effectiveBackend = effectiveBackend.value;
1319
+ if (sessionId) runInfo.sessionId = sessionId;
1320
+ if (rosterFilePresent) runInfo.rosterFile = rosterFile;
1321
+ if (rosterEntryPresent) runInfo.rosterEntry = rosterEntry;
1322
+ if (agentProfilePresent) runInfo.agentProfile = agentProfile;
1323
+ }
1101
1324
  function findAvenorBinary() {
1102
1325
  const envBin = process.env.AVENOR_BIN;
1103
1326
  if (envBin) try {
@@ -1154,6 +1377,7 @@ var Supervisor = class Supervisor {
1154
1377
  crashed = false;
1155
1378
  crashCode = null;
1156
1379
  runs = /* @__PURE__ */ new Map();
1380
+ aliases = /* @__PURE__ */ new Map();
1157
1381
  binaryPath;
1158
1382
  callTimeoutMs;
1159
1383
  constructor(binaryPath, socketPath, opts) {
@@ -1292,7 +1516,14 @@ var Supervisor = class Supervisor {
1292
1516
  }
1293
1517
  return this.client;
1294
1518
  }
1295
- async spawn(params, runId = crypto.randomUUID()) {
1519
+ async spawn(params, runId = crypto.randomUUID(), metadata) {
1520
+ if (!(typeof params.loop_file === "string" || typeof params.team_file === "string")) validateSpawnSelection({
1521
+ agent: params.agent,
1522
+ model: params.model,
1523
+ backend: params.backend,
1524
+ roster_file: params.roster_file,
1525
+ roster_entry: params.roster_entry
1526
+ });
1296
1527
  const client = this.getClient();
1297
1528
  const { sentinelPath, eventLogPath } = ensureRunPaths(runId);
1298
1529
  const spawnParams = {
@@ -1301,26 +1532,60 @@ var Supervisor = class Supervisor {
1301
1532
  sentinel_file: sentinelPath,
1302
1533
  on_event: eventLogPath
1303
1534
  };
1535
+ if (spawnParams.roster_entry) {
1536
+ for (const field of [
1537
+ "agent",
1538
+ "model",
1539
+ "backend"
1540
+ ]) if (spawnParams[field] === "") delete spawnParams[field];
1541
+ }
1304
1542
  const result = await client.spawn(spawnParams);
1543
+ let identityResult = result;
1544
+ if (spawnParams.roster_entry && result.runtime_id) {
1545
+ const hasBackend = [result.effective_backend, result.backend].some((value) => typeof value === "string" && value.length > 0);
1546
+ const hasAgentOrModel = [
1547
+ result.effective_agent,
1548
+ result.agent,
1549
+ result.effective_model,
1550
+ result.model
1551
+ ].some((value) => typeof value === "string" && value.length > 0);
1552
+ if (!(hasBackend && hasAgentOrModel)) try {
1553
+ identityResult = {
1554
+ ...result,
1555
+ ...await client.status(result.runtime_id)
1556
+ };
1557
+ } catch {}
1558
+ }
1559
+ const value = (candidate) => typeof candidate === "string" && candidate.length > 0 ? candidate : void 0;
1560
+ const effectiveAgent = metadata?.effectiveAgent ?? value(identityResult.effective_agent) ?? value(identityResult.agent) ?? value(spawnParams.agent);
1561
+ const effectiveModel = metadata?.effectiveModel ?? value(identityResult.effective_model) ?? value(identityResult.model) ?? value(spawnParams.model);
1562
+ const effectiveBackend = metadata?.effectiveBackend ?? value(identityResult.effective_backend) ?? value(identityResult.backend) ?? value(spawnParams.backend);
1563
+ const rosterFile = metadata?.rosterFile ?? value(identityResult.roster_file) ?? value(spawnParams.roster_file);
1564
+ const rosterEntry = metadata?.rosterEntry ?? value(identityResult.roster_entry) ?? value(spawnParams.roster_entry);
1305
1565
  const runInfo = {
1306
1566
  runId,
1307
1567
  label: spawnParams.label ?? runId,
1308
1568
  sentinelPath: result.sentinel_file ?? sentinelPath,
1309
1569
  eventLogPath: result.on_event ?? eventLogPath,
1310
- runtimeId: result.runtime_id,
1311
- sessionId: result.session_id,
1312
- agent: spawnParams.agent ?? params.agent,
1313
- agentProfile: spawnParams.agent_profile,
1314
- backend: spawnParams.backend,
1315
- model: spawnParams.model,
1570
+ runtimeId: value(identityResult.runtime_id),
1571
+ sessionId: value(identityResult.session_id),
1572
+ agent: effectiveAgent,
1573
+ agentProfile: value(identityResult.agent_profile) ?? spawnParams.agent_profile,
1574
+ backend: effectiveBackend,
1575
+ model: effectiveModel,
1576
+ effectiveAgent,
1577
+ effectiveModel,
1578
+ effectiveBackend,
1579
+ rosterFile,
1580
+ rosterEntry,
1316
1581
  thinking: spawnParams.thinking,
1317
1582
  dir: spawnParams.dir,
1318
1583
  brokerUrl: result.broker_url,
1319
1584
  parentToken: result.parent_token,
1320
1585
  autoApprove: spawnParams.auto_approve
1321
1586
  };
1322
- this.runs.set(runInfo.label, runInfo);
1323
1587
  this.runs.set(runId, runInfo);
1588
+ this.aliases.set(runInfo.label, runInfo);
1324
1589
  return runInfo;
1325
1590
  }
1326
1591
  async close(options = {}) {
@@ -1343,6 +1608,7 @@ var Supervisor = class Supervisor {
1343
1608
  this.crashed = false;
1344
1609
  this.crashCode = null;
1345
1610
  this.runs.clear();
1611
+ this.aliases.clear();
1346
1612
  Supervisor.instance = null;
1347
1613
  }
1348
1614
  };
@@ -1369,6 +1635,9 @@ function validateSupervisorSocketPath(supervisorId) {
1369
1635
  if (parent !== root || !/^avenor-mcp-[^/\\]+\.sock$/.test(basename)) throw new Error(`invalid supervisorId: ${supervisorId}`);
1370
1636
  return path.join(parent, basename);
1371
1637
  }
1638
+ function validateObservedAt(observedAt) {
1639
+ if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(observedAt)) throw new Error("observed_at must be an RFC3339 timestamp");
1640
+ }
1372
1641
  //#endregion
1373
1642
  //#region src/tools/get-supervisor-client.ts
1374
1643
  async function getSupervisorClient(supervisorId) {
@@ -1388,8 +1657,125 @@ async function getSupervisorClient(supervisorId) {
1388
1657
  };
1389
1658
  }
1390
1659
  //#endregion
1660
+ //#region src/tools/run-registry.ts
1661
+ const externalRuns = /* @__PURE__ */ new Map();
1662
+ function runsFor(supervisorId) {
1663
+ let runs = externalRuns.get(supervisorId);
1664
+ if (!runs) {
1665
+ runs = {
1666
+ byRunId: /* @__PURE__ */ new Map(),
1667
+ byAlias: /* @__PURE__ */ new Map()
1668
+ };
1669
+ externalRuns.set(supervisorId, runs);
1670
+ }
1671
+ return runs;
1672
+ }
1673
+ function indexRun(runInfo) {
1674
+ const runs = runsFor(runInfo.supervisorId);
1675
+ runs.byRunId.set(runInfo.runId, runInfo);
1676
+ runs.byAlias.set(runInfo.label, runInfo);
1677
+ if (runInfo.runtimeId) runs.byAlias.set(runInfo.runtimeId, runInfo);
1678
+ }
1679
+ function isSafeRunReference(reference) {
1680
+ return /^[a-zA-Z0-9_-]+$/.test(reference);
1681
+ }
1682
+ function externalRunMetadataPath(runId) {
1683
+ if (!isSafeRunReference(runId)) throw new Error(`unsafe external run reference: ${runId}`);
1684
+ return path.join(runsRoot(), runId, "run.json");
1685
+ }
1686
+ function optionalString(value) {
1687
+ return typeof value === "string" && value.length > 0 ? value : void 0;
1688
+ }
1689
+ function readPersistedRun(supervisorId, runId) {
1690
+ if (!isSafeRunReference(runId)) return void 0;
1691
+ try {
1692
+ const parsed = JSON.parse(fs.readFileSync(externalRunMetadataPath(runId), "utf-8"));
1693
+ if (parsed.schemaVersion !== 1 || parsed.runId !== runId || parsed.supervisorId !== supervisorId || typeof parsed.label !== "string") return;
1694
+ const runDir = path.join(runsRoot(), runId);
1695
+ const runInfo = {
1696
+ runId,
1697
+ label: parsed.label,
1698
+ supervisorId,
1699
+ sentinelPath: path.join(runDir, "sentinel.done"),
1700
+ eventLogPath: path.join(runDir, "events.log"),
1701
+ runtimeId: optionalString(parsed.runtimeId),
1702
+ sessionId: optionalString(parsed.sessionId),
1703
+ agent: optionalString(parsed.agent),
1704
+ agentProfile: optionalString(parsed.agentProfile),
1705
+ backend: optionalString(parsed.backend),
1706
+ model: optionalString(parsed.model),
1707
+ effectiveAgent: optionalString(parsed.effectiveAgent),
1708
+ effectiveModel: optionalString(parsed.effectiveModel),
1709
+ effectiveBackend: optionalString(parsed.effectiveBackend),
1710
+ rosterFile: optionalString(parsed.rosterFile),
1711
+ rosterEntry: optionalString(parsed.rosterEntry),
1712
+ thinking: parsed.thinking,
1713
+ dir: optionalString(parsed.dir),
1714
+ brokerUrl: optionalString(parsed.brokerUrl),
1715
+ parentToken: optionalString(parsed.parentToken),
1716
+ autoApprove: typeof parsed.autoApprove === "boolean" ? parsed.autoApprove : void 0
1717
+ };
1718
+ indexRun(runInfo);
1719
+ return runInfo;
1720
+ } catch {
1721
+ return;
1722
+ }
1723
+ }
1724
+ /** Persist the public-to-runtime identity for runs spawned through an external supervisor. */
1725
+ function registerExternalRun(runInfo) {
1726
+ const persisted = {
1727
+ schemaVersion: 1,
1728
+ ...runInfo,
1729
+ parentToken: void 0
1730
+ };
1731
+ const metadataPath = externalRunMetadataPath(runInfo.runId);
1732
+ const temporaryPath = `${metadataPath}.${process.pid}.${crypto.randomUUID()}.tmp`;
1733
+ fs.writeFileSync(temporaryPath, `${JSON.stringify(persisted, null, 2)}\n`, {
1734
+ mode: 384,
1735
+ flag: "wx"
1736
+ });
1737
+ try {
1738
+ fs.renameSync(temporaryPath, metadataPath);
1739
+ } catch (error) {
1740
+ try {
1741
+ fs.unlinkSync(temporaryPath);
1742
+ } catch {}
1743
+ throw error;
1744
+ }
1745
+ indexRun(runInfo);
1746
+ }
1747
+ function findExternalRun(supervisorId, reference) {
1748
+ const exact = externalRuns.get(supervisorId)?.byRunId.get(reference);
1749
+ if (exact) return exact;
1750
+ const persisted = readPersistedRun(supervisorId, reference);
1751
+ if (persisted) return persisted;
1752
+ listExternalRuns(supervisorId);
1753
+ return externalRuns.get(supervisorId)?.byAlias.get(reference);
1754
+ }
1755
+ function listExternalRuns(supervisorId) {
1756
+ try {
1757
+ for (const entry of fs.readdirSync(runsRoot(), { withFileTypes: true })) if (entry.isDirectory()) readPersistedRun(supervisorId, entry.name);
1758
+ } catch {}
1759
+ return [...externalRuns.get(supervisorId)?.byRunId.values() ?? []];
1760
+ }
1761
+ function forgetExternalRuns(supervisorId) {
1762
+ externalRuns.delete(supervisorId);
1763
+ }
1764
+ function clearExternalRuns(supervisorId) {
1765
+ const runs = listExternalRuns(supervisorId);
1766
+ forgetExternalRuns(supervisorId);
1767
+ return runs;
1768
+ }
1769
+ //#endregion
1391
1770
  //#region src/tools/spawn.ts
1392
- async function spawnTool(args) {
1771
+ async function executeSpawnTool(args, getSupervisorClient) {
1772
+ validateSpawnSelection({
1773
+ agent: args.agent,
1774
+ model: args.model,
1775
+ backend: args.backend,
1776
+ roster_file: args.rosterFile,
1777
+ roster_entry: args.rosterEntry
1778
+ });
1393
1779
  const runId = crypto.randomUUID();
1394
1780
  const label = args.label ?? runId;
1395
1781
  const baseParams = {
@@ -1405,10 +1791,23 @@ async function spawnTool(args) {
1405
1791
  session_id: args.sessionId,
1406
1792
  parent_run_id: args.parent_run_id
1407
1793
  };
1794
+ if (args.rosterFile) baseParams.roster_file = args.rosterFile;
1795
+ if (args.rosterEntry) baseParams.roster_entry = args.rosterEntry;
1408
1796
  if (args.agent) baseParams.agent = args.agent;
1409
1797
  if (args.thinking !== void 0) baseParams.thinking = args.thinking;
1410
1798
  if (args.supervisorId) {
1411
- const { client, isSingleton, supervisorId } = await getSupervisorClient(args.supervisorId);
1799
+ const { client, isSingleton, sup, supervisorId } = await getSupervisorClient(args.supervisorId);
1800
+ if (isSingleton && sup) {
1801
+ const runInfo = await sup.spawn(baseParams, runId);
1802
+ return {
1803
+ run_id: runInfo.runId,
1804
+ label: runInfo.label,
1805
+ supervisor_id: supervisorId,
1806
+ runtime_id: runInfo.runtimeId,
1807
+ broker_url: runInfo.brokerUrl,
1808
+ parent_token: runInfo.parentToken
1809
+ };
1810
+ }
1412
1811
  const { sentinelPath, eventLogPath } = ensureRunPaths(runId);
1413
1812
  try {
1414
1813
  const result = await client.spawn({
@@ -1416,13 +1815,37 @@ async function spawnTool(args) {
1416
1815
  sentinel_file: sentinelPath,
1417
1816
  on_event: eventLogPath
1418
1817
  });
1818
+ const stringValue = (candidate) => typeof candidate === "string" && candidate.length > 0 ? candidate : void 0;
1819
+ const runtimeId = stringValue(result.runtime_id);
1820
+ registerExternalRun({
1821
+ runId,
1822
+ label,
1823
+ supervisorId,
1824
+ sentinelPath,
1825
+ eventLogPath,
1826
+ runtimeId,
1827
+ sessionId: stringValue(result.session_id),
1828
+ agent: stringValue(result.effective_agent) ?? stringValue(result.agent) ?? baseParams.agent,
1829
+ model: stringValue(result.effective_model) ?? stringValue(result.model) ?? baseParams.model,
1830
+ backend: stringValue(result.effective_backend) ?? stringValue(result.backend) ?? baseParams.backend,
1831
+ effectiveAgent: stringValue(result.effective_agent) ?? stringValue(result.agent) ?? baseParams.agent,
1832
+ effectiveModel: stringValue(result.effective_model) ?? stringValue(result.model) ?? baseParams.model,
1833
+ effectiveBackend: stringValue(result.effective_backend) ?? stringValue(result.backend) ?? baseParams.backend,
1834
+ agentProfile: stringValue(result.agent_profile) ?? baseParams.agent_profile,
1835
+ rosterFile: stringValue(result.roster_file) ?? baseParams.roster_file,
1836
+ rosterEntry: stringValue(result.roster_entry) ?? baseParams.roster_entry,
1837
+ thinking: baseParams.thinking,
1838
+ dir: baseParams.dir,
1839
+ brokerUrl: stringValue(result.broker_url),
1840
+ parentToken: stringValue(result.parent_token)
1841
+ });
1419
1842
  return {
1420
- run_id: result.runtime_id ?? runId,
1843
+ run_id: runId,
1421
1844
  label,
1422
1845
  supervisor_id: supervisorId,
1423
- runtime_id: result.runtime_id,
1424
- broker_url: result.broker_url,
1425
- parent_token: result.parent_token
1846
+ runtime_id: runtimeId,
1847
+ broker_url: stringValue(result.broker_url),
1848
+ parent_token: stringValue(result.parent_token)
1426
1849
  };
1427
1850
  } finally {
1428
1851
  if (!isSingleton) client.close();
@@ -1439,14 +1862,251 @@ async function spawnTool(args) {
1439
1862
  parent_token: runInfo.parentToken
1440
1863
  };
1441
1864
  }
1865
+ function createSpawnTool(getSupervisorClient) {
1866
+ return (args) => executeSpawnTool(args, getSupervisorClient);
1867
+ }
1868
+ const spawnTool = createSpawnTool(getSupervisorClient);
1442
1869
  //#endregion
1443
- //#region src/tools/status.ts
1444
- function findRunByLabel$3(sup, runId) {
1445
- const runs = sup.runs;
1446
- const byKey = runs.get(runId);
1447
- if (byKey) return byKey;
1448
- for (const info of runs.values()) if (info.label === runId) return info;
1870
+ //#region ../../schemas/thinking_policy.umpire.json
1871
+ var thinking_policy_umpire_default = {
1872
+ version: 1,
1873
+ conditions: {
1874
+ "backend": {
1875
+ "type": "string",
1876
+ "description": "Backend identifier (e.g. codex-app-server, pi, claude)."
1877
+ },
1878
+ "resume": {
1879
+ "type": "boolean",
1880
+ "description": "Whether the value is being applied on an explicit resume rather than a fresh start."
1881
+ }
1882
+ },
1883
+ fields: { "thinking": {
1884
+ "isEmpty": "string",
1885
+ "description": "Canonical thinking level, or empty to use the backend default."
1886
+ } },
1887
+ rules: [{
1888
+ "type": "check",
1889
+ "field": "thinking",
1890
+ "check": {
1891
+ "op": "in",
1892
+ "field": "thinking",
1893
+ "value": [
1894
+ "off",
1895
+ "minimal",
1896
+ "low",
1897
+ "medium",
1898
+ "high",
1899
+ "xhigh",
1900
+ "max"
1901
+ ]
1902
+ },
1903
+ "reason": "not a canonical thinking value (off, minimal, low, medium, high, xhigh, max)"
1904
+ }, {
1905
+ "type": "eitherOf",
1906
+ "group": "thinkingPolicy",
1907
+ "field": "thinking",
1908
+ "branches": {
1909
+ "empty": [{
1910
+ "type": "fairWhen",
1911
+ "field": "thinking",
1912
+ "when": {
1913
+ "op": "absent",
1914
+ "field": "thinking"
1915
+ },
1916
+ "reason": "thinking is empty (backend default)"
1917
+ }],
1918
+ "fullSupport": [{
1919
+ "type": "fairWhen",
1920
+ "field": "thinking",
1921
+ "when": {
1922
+ "op": "and",
1923
+ "exprs": [{
1924
+ "op": "condIn",
1925
+ "condition": "backend",
1926
+ "values": ["codex-app-server", "pi"]
1927
+ }, {
1928
+ "op": "in",
1929
+ "field": "thinking",
1930
+ "values": [
1931
+ "off",
1932
+ "minimal",
1933
+ "low",
1934
+ "medium",
1935
+ "high",
1936
+ "xhigh",
1937
+ "max"
1938
+ ]
1939
+ }]
1940
+ },
1941
+ "reason": "codex-app-server and pi support all canonical thinking values on start and resume"
1942
+ }],
1943
+ "claudeStart": [{
1944
+ "type": "fairWhen",
1945
+ "field": "thinking",
1946
+ "when": {
1947
+ "op": "and",
1948
+ "exprs": [
1949
+ {
1950
+ "op": "condIn",
1951
+ "condition": "backend",
1952
+ "values": ["claude", "claude-channel"]
1953
+ },
1954
+ {
1955
+ "op": "condEq",
1956
+ "condition": "resume",
1957
+ "value": false
1958
+ },
1959
+ {
1960
+ "op": "in",
1961
+ "field": "thinking",
1962
+ "values": [
1963
+ "low",
1964
+ "medium",
1965
+ "high",
1966
+ "xhigh",
1967
+ "max"
1968
+ ]
1969
+ }
1970
+ ]
1971
+ },
1972
+ "reason": "claude and claude-channel support low through max only on a new session"
1973
+ }]
1974
+ }
1975
+ }]
1976
+ };
1977
+ //#endregion
1978
+ //#region src/thinking-policy.ts
1979
+ /**
1980
+ * Portable thinking-policy contract.
1981
+ *
1982
+ * THINKING_LEVELS is the single TypeScript source for the canonical tuple and
1983
+ * is exported for hosts (MCP, OpenCode, Pi) to derive their enums from. It is
1984
+ * verified against schemas/thinking_policy.umpire.json and the Go tuple by
1985
+ * introspection and conformance tests so all languages agree.
1986
+ *
1987
+ * The backend policy (which canonical values each backend supports, on start
1988
+ * vs. explicit resume) is modelled directly in the Umpire schema's eitherOf
1989
+ * branches with fairWhen rules and conditions (backend, resume). Both Go and
1990
+ * TypeScript derive the policy from the schema, eliminating hand-written
1991
+ * policy tables that could drift apart.
1992
+ */
1993
+ const THINKING_LEVELS = [
1994
+ "off",
1995
+ "minimal",
1996
+ "low",
1997
+ "medium",
1998
+ "high",
1999
+ "xhigh",
2000
+ "max"
2001
+ ];
2002
+ function isThinkingLevel(value) {
2003
+ return THINKING_LEVELS.includes(value);
2004
+ }
2005
+ /** Accepts an empty value (backend default) or a canonical thinking level. */
2006
+ function validateThinking(value) {
2007
+ if (value === void 0 || value === "") return;
2008
+ if (typeof value !== "string" || !isThinkingLevel(value)) throw new Error(`invalid thinking value ${JSON.stringify(value)} (allowed: ${THINKING_LEVELS.join(", ")})`);
2009
+ }
2010
+ const thinkingSchema = thinking_policy_umpire_default;
2011
+ thinkingSchema.rules.find((r) => r.check?.op === "in")?.check?.value;
2012
+ const policyBranches = (() => {
2013
+ const eitherOf = thinkingSchema.rules.find((r) => r.type === "eitherOf");
2014
+ if (!eitherOf?.branches) return [];
2015
+ const branches = [];
2016
+ for (const branchRules of Object.values(eitherOf.branches)) for (const rule of branchRules) if (rule.type === "fairWhen") branches.push({
2017
+ when: rule.when,
2018
+ reason: rule.reason
2019
+ });
2020
+ return branches;
2021
+ })();
2022
+ const supportedBackends = (() => {
2023
+ const backends = /* @__PURE__ */ new Set();
2024
+ for (const branch of policyBranches) collectBackends(branch.when, backends);
2025
+ return backends;
2026
+ })();
2027
+ function collectBackends(expr, backends) {
2028
+ if (expr.op === "condIn" && expr.condition === "backend") {
2029
+ for (const v of expr.values) backends.add(v);
2030
+ return;
2031
+ }
2032
+ if (expr.op === "and" || expr.op === "or") for (const sub of expr.exprs) collectBackends(sub, backends);
2033
+ }
2034
+ function evalExpr(expr, value, backend, resume) {
2035
+ switch (expr.op) {
2036
+ case "absent": return value === "";
2037
+ case "in": return expr.values.includes(value);
2038
+ case "condIn": return expr.condition === "backend" && expr.values.includes(backend);
2039
+ case "condEq":
2040
+ if (expr.condition === "backend") return backend === expr.value;
2041
+ if (expr.condition === "resume") return resume === expr.value;
2042
+ return false;
2043
+ case "and": return expr.exprs.every((e) => evalExpr(e, value, backend, resume));
2044
+ case "or": return expr.exprs.some((e) => evalExpr(e, value, backend, resume));
2045
+ default: return false;
2046
+ }
2047
+ }
2048
+ /** Evaluates the schema-derived backend policy for a (backend, value, resume) combination. */
2049
+ function evaluateThinkingPolicy(backend, value, resume = false) {
2050
+ if (value === "") return "ok";
2051
+ if (policyBranches.some((b) => evalExpr(b.when, value, backend, resume))) return "ok";
2052
+ if (!supportedBackends.has(backend)) return "unsupportedCapability";
2053
+ if (resume) {
2054
+ if (policyBranches.some((b) => evalExpr(b.when, value, backend, false))) return "startOnly";
2055
+ }
2056
+ return "unsupportedValue";
2057
+ }
2058
+ /** Returns the canonical values supported by backend on a fresh start. */
2059
+ function startValues(backend) {
2060
+ if (!supportedBackends.has(backend)) return [];
2061
+ return THINKING_LEVELS.filter((v) => policyBranches.some((b) => evalExpr(b.when, v, backend, false)));
2062
+ }
2063
+ /** Returns the canonical values supported by backend on an explicit resume. */
2064
+ function resumeValues(backend) {
2065
+ if (!supportedBackends.has(backend)) return [];
2066
+ return THINKING_LEVELS.filter((v) => policyBranches.some((b) => evalExpr(b.when, v, backend, true)));
2067
+ }
2068
+ /** Accepts a value iff it is valid under the static backend policy (start). */
2069
+ function validateThinkingForBackend(backend, value) {
2070
+ validateThinking(value);
2071
+ const v = value === void 0 ? "" : String(value);
2072
+ if (v === "") return;
2073
+ const outcome = evaluateThinkingPolicy(backend, v, false);
2074
+ if (outcome === "ok") return;
2075
+ if (outcome === "unsupportedValue") {
2076
+ const allowed = startValues(backend).join(", ");
2077
+ throw new Error(`backend ${JSON.stringify(backend)} does not support thinking value ${JSON.stringify(v)} (allowed: ${allowed})`);
2078
+ }
2079
+ throw new Error(`backend ${JSON.stringify(backend)} does not support parameter "thinking"`);
2080
+ }
2081
+ /** Accepts a value iff it is valid on an explicit resume for the backend. */
2082
+ function validateThinkingForBackendResume(backend, value) {
2083
+ validateThinking(value);
2084
+ const v = value === void 0 ? "" : String(value);
2085
+ if (v === "") return;
2086
+ const outcome = evaluateThinkingPolicy(backend, v, true);
2087
+ if (outcome === "ok") return;
2088
+ if (outcome === "startOnly") throw new Error(`backend ${JSON.stringify(backend)} supports parameter "thinking" only when starting a session`);
2089
+ if (outcome === "unsupportedValue") {
2090
+ const allowed = resumeValues(backend).join(", ");
2091
+ throw new Error(`backend ${JSON.stringify(backend)} does not support thinking value ${JSON.stringify(v)} (allowed: ${allowed})`);
2092
+ }
2093
+ throw new Error(`backend ${JSON.stringify(backend)} does not support parameter "thinking"`);
1449
2094
  }
2095
+ //#endregion
2096
+ //#region src/tools/run-resolution.ts
2097
+ /** Resolve a local public run ID before considering a label alias. */
2098
+ function findLocalRunByReference(sup, reference) {
2099
+ const { runs, aliases } = sup;
2100
+ return runs.get(reference) ?? aliases.get(reference);
2101
+ }
2102
+ /** Match a supervisor list entry to its stored runtime identity. */
2103
+ function findLocalRunByRuntimeId(sup, runtimeId) {
2104
+ if (!runtimeId) return void 0;
2105
+ const { runs } = sup;
2106
+ for (const runInfo of runs.values()) if (runInfo.runtimeId === runtimeId) return runInfo;
2107
+ }
2108
+ //#endregion
2109
+ //#region src/tools/status.ts
1450
2110
  async function parseSentinel$1(filePath) {
1451
2111
  try {
1452
2112
  const content = (await fs.promises.readFile(filePath, "utf-8")).trim();
@@ -1534,6 +2194,15 @@ function extractPendingPermission(liveStatus) {
1534
2194
  return normalizePermission(asRecord(pendingPermission));
1535
2195
  }
1536
2196
  function buildBaseStatus(source, fallback, translatedStatus) {
2197
+ const identity = fallback.identity;
2198
+ const authoritativeValue = (effectiveKey, directKey, fallbackEffective, fallbackDirect) => {
2199
+ if (typeof source[effectiveKey] === "string") return stringField(source, effectiveKey);
2200
+ if (typeof source[directKey] === "string") return stringField(source, directKey);
2201
+ return fallbackEffective ?? fallbackDirect;
2202
+ };
2203
+ const effectiveAgent = authoritativeValue("effective_agent", "agent", identity?.effective_agent, identity?.agent);
2204
+ const effectiveModel = authoritativeValue("effective_model", "model", identity?.effective_model, identity?.model);
2205
+ const effectiveBackend = authoritativeValue("effective_backend", "backend", identity?.effective_backend, identity?.backend);
1537
2206
  return {
1538
2207
  run_id: fallback.prefer_fallback_run_id ? fallback.run_id : stringField(source, "run_id") ?? fallback.run_id,
1539
2208
  label: stringField(source, "label") ?? fallback.label,
@@ -1544,9 +2213,15 @@ function buildBaseStatus(source, fallback, translatedStatus) {
1544
2213
  pending_permission: extractPendingPermission(source),
1545
2214
  session_id: stringField(source, "session_id") ?? fallback.session_id,
1546
2215
  stop_reason: stringField(source, "stop_reason") ?? fallback.stop_reason,
1547
- backend: stringField(source, "backend"),
1548
- agent: stringField(source, "agent"),
1549
- model: stringField(source, "model"),
2216
+ roster_file: typeof source.roster_file === "string" ? stringField(source, "roster_file") : identity?.roster_file,
2217
+ roster_entry: typeof source.roster_entry === "string" ? stringField(source, "roster_entry") : identity?.roster_entry,
2218
+ backend: effectiveBackend,
2219
+ agent: effectiveAgent,
2220
+ model: effectiveModel,
2221
+ agent_profile: typeof source.agent_profile === "string" ? stringField(source, "agent_profile") : identity?.agent_profile,
2222
+ effective_backend: effectiveBackend,
2223
+ effective_agent: effectiveAgent,
2224
+ effective_model: effectiveModel,
1550
2225
  dir: stringField(source, "dir"),
1551
2226
  parent_id: stringField(source, "parent_id", "parentId"),
1552
2227
  children: stringArrayField(source, "children"),
@@ -1573,6 +2248,7 @@ function shapeStatusResult(result, view = "full") {
1573
2248
  };
1574
2249
  }
1575
2250
  async function buildRunStatus(runInfo, liveStatus) {
2251
+ if (liveStatus) retainLiveIdentity(runInfo, liveStatus);
1576
2252
  let sentinel = null;
1577
2253
  if (await sentinelExists(runInfo.sentinelPath)) sentinel = await parseSentinel$1(runInfo.sentinelPath);
1578
2254
  const translated = translateStatus(rawStatusPhase(liveStatus), sentinel);
@@ -1582,7 +2258,18 @@ async function buildRunStatus(runInfo, liveStatus) {
1582
2258
  prefer_fallback_run_id: true,
1583
2259
  runtime_id: runInfo.runtimeId,
1584
2260
  session_id: sentinel?.SESSION ?? liveStatus?.session_id ?? runInfo.sessionId,
1585
- stop_reason: sentinel?._status
2261
+ stop_reason: sentinel?.STOP_REASON,
2262
+ identity: {
2263
+ roster_file: runInfo.rosterFile,
2264
+ roster_entry: runInfo.rosterEntry,
2265
+ agent: runInfo.agent,
2266
+ model: runInfo.model,
2267
+ backend: runInfo.backend,
2268
+ agent_profile: runInfo.agentProfile,
2269
+ effective_agent: runInfo.effectiveAgent,
2270
+ effective_model: runInfo.effectiveModel,
2271
+ effective_backend: runInfo.effectiveBackend
2272
+ }
1586
2273
  }, translated);
1587
2274
  }
1588
2275
  async function queryLiveStatus(client, runtimeId) {
@@ -1595,11 +2282,11 @@ async function queryLiveStatus(client, runtimeId) {
1595
2282
  }
1596
2283
  async function executeStatusTool(args, getSupervisorClient) {
1597
2284
  if (args.supervisorId) {
1598
- const { client, isSingleton, sup } = await getSupervisorClient(args.supervisorId);
2285
+ const { client, isSingleton, sup, supervisorId } = await getSupervisorClient(args.supervisorId);
1599
2286
  try {
1600
2287
  if (args.runId) {
1601
2288
  validateRunId(args.runId);
1602
- const runInfo = isSingleton && sup ? findRunByLabel$3(sup, args.runId) : void 0;
2289
+ const runInfo = isSingleton && sup ? findLocalRunByReference(sup, args.runId) : findExternalRun(supervisorId, args.runId);
1603
2290
  if (runInfo) return shapeStatusResult(await buildRunStatus(runInfo, await queryLiveStatus(client, runInfo.runtimeId)), args.view);
1604
2291
  let liveStatus = await queryLiveStatus(client, args.runId);
1605
2292
  if (!liveStatus) try {
@@ -1617,17 +2304,30 @@ async function executeStatusTool(args, getSupervisorClient) {
1617
2304
  return shapeStatusResult(buildBaseStatus(liveStatus ?? {}, {
1618
2305
  run_id: args.runId,
1619
2306
  label: args.runId,
2307
+ prefer_fallback_run_id: true,
1620
2308
  runtime_id: stringField(liveStatus ?? {}, "runtime_id", "id"),
1621
2309
  session_id: sentinel?.SESSION ?? liveStatus?.session_id,
1622
- stop_reason: sentinel?._status
2310
+ stop_reason: sentinel?.STOP_REASON
1623
2311
  }, translateStatus(rawStatusPhase(liveStatus), sentinel)), args.view);
1624
2312
  }
1625
- return (await client.list()).map((entry) => shapeStatusResult(buildBaseStatus(entry, {
1626
- run_id: String(entry.run_id ?? entry.runtime_id ?? entry.id ?? ""),
1627
- label: String(entry.label ?? entry.runtime_id ?? entry.id ?? ""),
1628
- runtime_id: String(entry.runtime_id ?? entry.id ?? ""),
1629
- session_id: entry.session_id
1630
- }, translateStatus(rawStatusPhase(entry), null)), args.view));
2313
+ const list = await client.list();
2314
+ if (!isSingleton) listExternalRuns(supervisorId);
2315
+ const results = [];
2316
+ for (const entry of list) {
2317
+ const entryId = String(entry.runtime_id ?? entry.id ?? "");
2318
+ const runInfo = !isSingleton ? findExternalRun(supervisorId, entryId) ?? findExternalRun(supervisorId, String(entry.run_id ?? "")) ?? findExternalRun(supervisorId, String(entry.label ?? "")) : void 0;
2319
+ if (runInfo) {
2320
+ results.push(shapeStatusResult(await buildRunStatus(runInfo, entry), args.view));
2321
+ continue;
2322
+ }
2323
+ results.push(shapeStatusResult(buildBaseStatus(entry, {
2324
+ run_id: String(entry.run_id ?? entryId),
2325
+ label: String(entry.label ?? entryId),
2326
+ runtime_id: entryId,
2327
+ session_id: entry.session_id
2328
+ }, translateStatus(rawStatusPhase(entry), null)), args.view));
2329
+ }
2330
+ return results;
1631
2331
  } finally {
1632
2332
  if (!isSingleton) client.close();
1633
2333
  }
@@ -1635,7 +2335,7 @@ async function executeStatusTool(args, getSupervisorClient) {
1635
2335
  const sup = await Supervisor.get();
1636
2336
  const client = sup.getClient();
1637
2337
  if (args.runId) {
1638
- const runInfo = findRunByLabel$3(sup, args.runId);
2338
+ const runInfo = findLocalRunByReference(sup, args.runId);
1639
2339
  if (runInfo) return shapeStatusResult(await buildRunStatus(runInfo, await queryLiveStatus(client, runInfo.runtimeId)), args.view);
1640
2340
  validateRunId(args.runId);
1641
2341
  const liveStatus = await queryLiveStatus(client, args.runId);
@@ -1647,7 +2347,7 @@ async function executeStatusTool(args, getSupervisorClient) {
1647
2347
  label: args.runId,
1648
2348
  runtime_id: stringField(liveStatus ?? {}, "runtime_id", "id"),
1649
2349
  session_id: sentinel?.SESSION ?? liveStatus?.session_id,
1650
- stop_reason: sentinel?._status
2350
+ stop_reason: sentinel?.STOP_REASON
1651
2351
  }, translateStatus(rawStatusPhase(liveStatus), sentinel)), args.view);
1652
2352
  }
1653
2353
  const list = await client.list();
@@ -1655,9 +2355,9 @@ async function executeStatusTool(args, getSupervisorClient) {
1655
2355
  for (const entry of list) {
1656
2356
  const entryId = String(entry.runtime_id ?? entry.id ?? "");
1657
2357
  const entryLabel = String(entry.label ?? entryId);
1658
- const runInfo = findRunByLabel$3(sup, entryId) ?? findRunByLabel$3(sup, entryLabel);
2358
+ const runInfo = findLocalRunByRuntimeId(sup, entryId) ?? findLocalRunByReference(sup, entryId) ?? findLocalRunByReference(sup, entryLabel);
1659
2359
  const liveStatus = runInfo ? await queryLiveStatus(client, runInfo.runtimeId) : null;
1660
- if (runInfo && liveStatus) results.push(shapeStatusResult(await buildRunStatus(runInfo, liveStatus), args.view));
2360
+ if (runInfo) results.push(shapeStatusResult(await buildRunStatus(runInfo, liveStatus), args.view));
1661
2361
  else results.push(shapeStatusResult(buildBaseStatus(entry, {
1662
2362
  run_id: String(entry.run_id ?? entryId),
1663
2363
  label: entryLabel,
@@ -1677,12 +2377,6 @@ const MAX_EVENT_LIMIT = 1e3;
1677
2377
  const MAX_STRING_CHARS = 4e3;
1678
2378
  const MAX_ARRAY_ITEMS = 32;
1679
2379
  const MAX_OBJECT_KEYS = 64;
1680
- function findRunByLabel$2(sup, runId) {
1681
- const runs = sup.runs;
1682
- const byKey = runs.get(runId);
1683
- if (byKey) return byKey;
1684
- for (const info of runs.values()) if (info.label === runId) return info;
1685
- }
1686
2380
  function normalizeLimit(limit) {
1687
2381
  return Math.max(1, Math.min(MAX_EVENT_LIMIT, Math.trunc(limit ?? 50)));
1688
2382
  }
@@ -1752,8 +2446,8 @@ async function readNdjsonEvents(filePath, args) {
1752
2446
  }
1753
2447
  async function resolveRun(args, getSupervisorClient) {
1754
2448
  if (args.supervisorId) {
1755
- const { client, isSingleton, sup } = await getSupervisorClient(args.supervisorId);
1756
- const runInfo = isSingleton && sup ? findRunByLabel$2(sup, args.runId) : void 0;
2449
+ const { client, isSingleton, sup, supervisorId } = await getSupervisorClient(args.supervisorId);
2450
+ const runInfo = isSingleton && sup ? findLocalRunByReference(sup, args.runId) : findExternalRun(supervisorId, args.runId);
1757
2451
  if (runInfo) return {
1758
2452
  client,
1759
2453
  isSingleton,
@@ -1796,7 +2490,7 @@ async function resolveRun(args, getSupervisorClient) {
1796
2490
  };
1797
2491
  }
1798
2492
  const sup = await Supervisor.get();
1799
- const runInfo = findRunByLabel$2(sup, args.runId);
2493
+ const runInfo = findLocalRunByReference(sup, args.runId);
1800
2494
  if (runInfo) return {
1801
2495
  client: sup.getClient(),
1802
2496
  isSingleton: true,
@@ -1822,6 +2516,7 @@ async function resolveRun(args, getSupervisorClient) {
1822
2516
  };
1823
2517
  }
1824
2518
  async function executeEventsTool(args, getSupervisorClient) {
2519
+ validateRunId(args.runId);
1825
2520
  const limit = normalizeLimit(args.limit);
1826
2521
  const resolved = await resolveRun(args, getSupervisorClient);
1827
2522
  try {
@@ -2030,12 +2725,6 @@ async function resultTool(args) {
2030
2725
  }
2031
2726
  //#endregion
2032
2727
  //#region src/tools/answer-permission.ts
2033
- function findRunByLabel$1(sup, runId) {
2034
- const runs = sup.runs;
2035
- const byKey = runs.get(runId);
2036
- if (byKey) return byKey;
2037
- for (const info of runs.values()) if (info.label === runId) return info;
2038
- }
2039
2728
  function pendingPermissionRequestID(liveStatus) {
2040
2729
  const status = asRecord(liveStatus);
2041
2730
  if (!status) return void 0;
@@ -2055,7 +2744,7 @@ async function answerPermissionTool(args) {
2055
2744
  }
2056
2745
  try {
2057
2746
  validateRunId(args.runId);
2058
- const runtimeId = (sup ? findRunByLabel$1(sup, args.runId) : void 0)?.runtimeId ?? args.runId;
2747
+ const runtimeId = (sup ? findLocalRunByReference(sup, args.runId) : supervisorId ? findExternalRun(supervisorId, args.runId) : void 0)?.runtimeId ?? args.runId;
2059
2748
  if (!requestId) {
2060
2749
  requestId = pendingPermissionRequestID(await client.status(runtimeId));
2061
2750
  if (!requestId) throw new Error("no pending permission request for this run");
@@ -2068,20 +2757,18 @@ async function answerPermissionTool(args) {
2068
2757
  }
2069
2758
  //#endregion
2070
2759
  //#region src/tools/follow-up.ts
2071
- function findRunByLabel(sup, runId) {
2072
- const runs = sup.runs;
2073
- const byKey = runs.get(runId);
2074
- if (byKey) return byKey;
2075
- for (const info of runs.values()) if (info.label === runId) return info;
2076
- }
2077
2760
  function resolveAutoApprove(liveStatus, runInfo) {
2078
2761
  const liveAutoApprove = liveStatus?.auto_approve;
2079
2762
  if (typeof liveAutoApprove === "boolean") return liveAutoApprove;
2080
2763
  return typeof runInfo?.autoApprove === "boolean" ? runInfo.autoApprove : void 0;
2081
2764
  }
2082
- function requireResumableAgent(agent) {
2083
- if (agent === void 0) throw new Error("run has no agent to resume");
2084
- return agent;
2765
+ function value(candidate) {
2766
+ return typeof candidate === "string" && candidate.length > 0 ? candidate : void 0;
2767
+ }
2768
+ function resolvedIdentity(liveStatus, effectiveKey, directKey, fallbackEffective, fallbackDirect) {
2769
+ if (liveStatus && typeof liveStatus[effectiveKey] === "string") return value(liveStatus[effectiveKey]);
2770
+ if (liveStatus && typeof liveStatus[directKey] === "string") return value(liveStatus[directKey]);
2771
+ return fallbackEffective ?? fallbackDirect;
2085
2772
  }
2086
2773
  async function parseSentinel(filePath) {
2087
2774
  try {
@@ -2101,85 +2788,144 @@ async function parseSentinel(filePath) {
2101
2788
  return null;
2102
2789
  }
2103
2790
  }
2791
+ function rejectSessionConflict(sentinel, liveStatus) {
2792
+ const sentinelStopReason = value(sentinel?.STOP_REASON);
2793
+ const liveStopReason = value(liveStatus?.stop_reason);
2794
+ if (sentinelStopReason === "session_id_conflict" || liveStopReason === "session_id_conflict") throw new Error("run is not resumable: session_id_conflict");
2795
+ }
2104
2796
  async function executeFollowUpTool(args, getSupervisorClient) {
2105
2797
  if (args.supervisorId) {
2106
- const { client, isSingleton, sup } = await getSupervisorClient(args.supervisorId);
2798
+ const { client, isSingleton, sup, supervisorId } = await getSupervisorClient(args.supervisorId);
2107
2799
  try {
2108
2800
  validateRunId(args.runId);
2109
2801
  let runInfo;
2110
- if (isSingleton && sup) runInfo = findRunByLabel(sup, args.runId);
2802
+ if (isSingleton && sup) runInfo = findLocalRunByReference(sup, args.runId);
2803
+ else runInfo = findExternalRun(supervisorId, args.runId);
2111
2804
  const fallbackSessionId = runInfo?.sessionId;
2112
2805
  let liveStatus = null;
2113
2806
  try {
2114
2807
  liveStatus = await client.status(runInfo?.runtimeId ?? args.runId);
2115
2808
  } catch {}
2116
- const sessionId = (await parseSentinel(liveStatus?.sentinel_file ?? path.join(runsRoot(), args.runId, "sentinel.done")))?.SESSION ?? liveStatus?.session_id ?? fallbackSessionId;
2809
+ const sentinel = await parseSentinel(liveStatus?.sentinel_file ?? runInfo?.sentinelPath ?? path.join(runsRoot(), args.runId, "sentinel.done"));
2810
+ rejectSessionConflict(sentinel, liveStatus);
2811
+ const sessionId = sentinel?.SESSION ?? liveStatus?.session_id ?? fallbackSessionId;
2117
2812
  if (!sessionId) throw new Error("run has no session to resume");
2118
- const agent = requireResumableAgent(liveStatus?.agent ?? runInfo?.agent);
2119
- const backend = liveStatus?.backend ?? runInfo?.backend;
2120
- const model = liveStatus?.model ?? runInfo?.model;
2813
+ const agent = resolvedIdentity(liveStatus, "effective_agent", "agent", runInfo?.effectiveAgent, runInfo?.agent);
2814
+ const model = resolvedIdentity(liveStatus, "effective_model", "model", runInfo?.effectiveModel, runInfo?.model);
2815
+ const backend = resolvedIdentity(liveStatus, "effective_backend", "backend", runInfo?.effectiveBackend, runInfo?.backend);
2816
+ if (runInfo && liveStatus) retainLiveIdentity(runInfo, liveStatus);
2121
2817
  const thinking = liveStatus?.thinking ?? runInfo?.thinking;
2122
2818
  const dir = liveStatus?.dir ?? runInfo?.dir;
2123
2819
  const agentProfile = liveStatus?.agent_profile ?? runInfo?.agentProfile;
2124
2820
  const autoApprove = resolveAutoApprove(liveStatus, runInfo);
2125
2821
  const followUpRunId = crypto.randomUUID();
2126
2822
  const followUpLabel = args.label ?? `${args.runId}-followup`;
2127
- const { sentinelPath: followUpSentinelPath, eventLogPath } = ensureRunPaths(followUpRunId);
2128
2823
  const spawnParams = {
2129
- agent,
2130
2824
  prompt: args.message,
2131
2825
  label: followUpLabel,
2132
- session_id: sessionId,
2133
- sentinel_file: followUpSentinelPath,
2134
- on_event: eventLogPath
2826
+ session_id: sessionId
2135
2827
  };
2828
+ if (agent) spawnParams.agent = agent;
2136
2829
  if (backend) spawnParams.backend = backend;
2137
2830
  if (model) spawnParams.model = model;
2138
2831
  if (thinking) spawnParams.thinking = thinking;
2139
2832
  if (dir) spawnParams.dir = dir;
2140
2833
  if (agentProfile) spawnParams.agent_profile = agentProfile;
2141
2834
  if (autoApprove === true) spawnParams.auto_approve = true;
2835
+ if (isSingleton && sup) {
2836
+ const followUpRun = await sup.spawn(spawnParams, followUpRunId, {
2837
+ rosterFile: runInfo?.rosterFile,
2838
+ rosterEntry: runInfo?.rosterEntry,
2839
+ effectiveAgent: agent,
2840
+ effectiveModel: model,
2841
+ effectiveBackend: backend
2842
+ });
2843
+ return {
2844
+ run_id: followUpRun.runId,
2845
+ label: followUpRun.label,
2846
+ runtime_id: followUpRun.runtimeId
2847
+ };
2848
+ }
2849
+ const { sentinelPath: followUpSentinelPath, eventLogPath } = ensureRunPaths(followUpRunId);
2850
+ spawnParams.sentinel_file = followUpSentinelPath;
2851
+ spawnParams.on_event = eventLogPath;
2852
+ const result = await client.spawn(spawnParams);
2853
+ const runtimeId = value(result.runtime_id);
2854
+ registerExternalRun({
2855
+ runId: followUpRunId,
2856
+ label: followUpLabel,
2857
+ supervisorId,
2858
+ sentinelPath: followUpSentinelPath,
2859
+ eventLogPath,
2860
+ runtimeId,
2861
+ sessionId: value(result.session_id) ?? sessionId,
2862
+ agent,
2863
+ model,
2864
+ backend,
2865
+ effectiveAgent: agent,
2866
+ effectiveModel: model,
2867
+ effectiveBackend: backend,
2868
+ rosterFile: runInfo?.rosterFile,
2869
+ rosterEntry: runInfo?.rosterEntry,
2870
+ thinking,
2871
+ dir,
2872
+ agentProfile,
2873
+ autoApprove
2874
+ });
2142
2875
  return {
2143
- run_id: (await client.spawn(spawnParams)).runtime_id ?? followUpRunId,
2144
- label: followUpLabel
2876
+ run_id: followUpRunId,
2877
+ label: followUpLabel,
2878
+ runtime_id: runtimeId
2145
2879
  };
2146
2880
  } finally {
2147
2881
  if (!isSingleton) client.close();
2148
2882
  }
2149
2883
  }
2150
2884
  const sup = await Supervisor.get();
2151
- const runInfo = findRunByLabel(sup, args.runId);
2885
+ const runInfo = findLocalRunByReference(sup, args.runId);
2152
2886
  if (!runInfo) throw new Error(`run not found: ${args.runId}`);
2153
- const sessionId = (await parseSentinel(runInfo.sentinelPath))?.SESSION ?? runInfo.sessionId;
2887
+ const sentinel = await parseSentinel(runInfo.sentinelPath);
2888
+ rejectSessionConflict(sentinel, null);
2889
+ const sessionId = sentinel?.SESSION ?? runInfo.sessionId;
2154
2890
  if (!sessionId) throw new Error("run has no session to resume");
2155
2891
  const client = sup.getClient();
2156
2892
  let liveStatus = null;
2157
2893
  if (runInfo.runtimeId) try {
2158
2894
  liveStatus = await client.status(runInfo.runtimeId);
2159
2895
  } catch {}
2160
- const agent = requireResumableAgent(liveStatus?.agent ?? runInfo.agent);
2161
- const backend = liveStatus?.backend ?? runInfo.backend;
2162
- const model = liveStatus?.model ?? runInfo.model;
2896
+ rejectSessionConflict(sentinel, liveStatus);
2897
+ const agent = resolvedIdentity(liveStatus, "effective_agent", "agent", runInfo.effectiveAgent, runInfo.agent);
2898
+ const model = resolvedIdentity(liveStatus, "effective_model", "model", runInfo.effectiveModel, runInfo.model);
2899
+ const backend = resolvedIdentity(liveStatus, "effective_backend", "backend", runInfo.effectiveBackend, runInfo.backend);
2900
+ if (liveStatus) retainLiveIdentity(runInfo, liveStatus);
2163
2901
  const thinking = liveStatus?.thinking ?? runInfo.thinking;
2164
2902
  const dir = liveStatus?.dir ?? runInfo.dir;
2165
2903
  const agentProfile = liveStatus?.agent_profile ?? runInfo.agentProfile;
2166
2904
  const autoApprove = resolveAutoApprove(liveStatus, runInfo);
2167
2905
  const followUpLabel = args.label ?? `${runInfo.label}-followup`;
2168
- const followUpRun = await sup.spawn({
2169
- agent,
2170
- backend,
2171
- model,
2172
- thinking,
2173
- dir,
2174
- agent_profile: agentProfile,
2906
+ const followUpParams = {
2907
+ ...agent ? { agent } : {},
2908
+ ...backend ? { backend } : {},
2909
+ ...model ? { model } : {},
2910
+ ...thinking ? { thinking } : {},
2911
+ ...dir ? { dir } : {},
2912
+ ...agentProfile ? { agent_profile: agentProfile } : {},
2175
2913
  prompt: args.message,
2176
2914
  session_id: sessionId,
2177
2915
  label: followUpLabel,
2178
2916
  ...autoApprove === true ? { auto_approve: true } : {}
2917
+ };
2918
+ const followUpRun = await sup.spawn(followUpParams, void 0, {
2919
+ rosterFile: runInfo.rosterFile,
2920
+ rosterEntry: runInfo.rosterEntry,
2921
+ effectiveAgent: agent,
2922
+ effectiveModel: model,
2923
+ effectiveBackend: backend
2179
2924
  });
2180
2925
  return {
2181
2926
  run_id: followUpRun.runId,
2182
- label: followUpRun.label
2927
+ label: followUpRun.label,
2928
+ runtime_id: followUpRun.runtimeId
2183
2929
  };
2184
2930
  }
2185
2931
  function createFollowUpTool(getSupervisorClient) {
@@ -2212,9 +2958,24 @@ async function shutdownTool(args) {
2212
2958
  const client = await dial(supervisorId);
2213
2959
  try {
2214
2960
  await client.shutdown(args.force ? "force" : "graceful");
2215
- } catch {} finally {
2961
+ } finally {
2216
2962
  client.close();
2217
2963
  }
2964
+ for (const info of clearExternalRuns(supervisorId)) {
2965
+ try {
2966
+ await fs.promises.unlink(info.sentinelPath);
2967
+ cleanedUp.push(info.sentinelPath);
2968
+ } catch {}
2969
+ try {
2970
+ await fs.promises.unlink(info.eventLogPath);
2971
+ cleanedUp.push(info.eventLogPath);
2972
+ } catch {}
2973
+ const metadataPath = externalRunMetadataPath(info.runId);
2974
+ try {
2975
+ await fs.promises.unlink(metadataPath);
2976
+ cleanedUp.push(metadataPath);
2977
+ } catch {}
2978
+ }
2218
2979
  }
2219
2980
  return {
2220
2981
  ok: true,
@@ -2243,4 +3004,184 @@ async function shutdownTool(args) {
2243
3004
  };
2244
3005
  }
2245
3006
  //#endregion
2246
- export { DEFAULT_RUN_REDUCER_LIMITS, RunReducer, Supervisor, answerPermissionTool, createRunSnapshot, dial, eventsTool, extractEventText, findAvenorBinary, followUpTool, getInstallDir, getVersion, inspectTool, installerBinaryPath, normalizeRunEvent, observeRun, reduceRunSnapshot, resultTool, shutdownTool, socketsRoot, spawnTool, statusTool };
3007
+ //#region src/tools/ask.ts
3008
+ function extractReplyMessage(result) {
3009
+ if (result.timeout === true) return "(timeout: no reply received)";
3010
+ if (result.cancelled === true) return "(cancelled)";
3011
+ const payload = result.payload;
3012
+ if (payload && typeof payload === "object") {
3013
+ const msg = payload.message;
3014
+ if (typeof msg === "string" && msg) return msg;
3015
+ }
3016
+ return JSON.stringify(result);
3017
+ }
3018
+ async function executeAskTool(args, getSupervisorClient) {
3019
+ const { client, isSingleton } = await getSupervisorClient(args.supervisorId);
3020
+ try {
3021
+ const result = await client.brokerAsk(args.toRunId, args.message);
3022
+ return {
3023
+ reply: extractReplyMessage(result),
3024
+ from_run_id: result.from_run_id ?? ""
3025
+ };
3026
+ } finally {
3027
+ if (!isSingleton) client.close();
3028
+ }
3029
+ }
3030
+ function createAskTool(getSupervisorClient) {
3031
+ return (args) => executeAskTool(args, getSupervisorClient);
3032
+ }
3033
+ const askTool = createAskTool(getSupervisorClient);
3034
+ //#endregion
3035
+ //#region src/tools/peers.ts
3036
+ async function executePeersTool(args, getSupervisorClient) {
3037
+ const { client, isSingleton } = await getSupervisorClient(args.supervisorId);
3038
+ try {
3039
+ return { peers: await client.brokerPeers() };
3040
+ } finally {
3041
+ if (!isSingleton) client.close();
3042
+ }
3043
+ }
3044
+ function createPeersTool(getSupervisorClient) {
3045
+ return (args) => executePeersTool(args, getSupervisorClient);
3046
+ }
3047
+ const peersTool = createPeersTool(getSupervisorClient);
3048
+ //#endregion
3049
+ //#region src/tools/cancel.ts
3050
+ async function executeCancelTool(args, getSupervisorClient) {
3051
+ const { client, isSingleton } = await getSupervisorClient(args.supervisorId);
3052
+ try {
3053
+ await client.brokerCancel(args.messageId);
3054
+ return { cancelled: true };
3055
+ } finally {
3056
+ if (!isSingleton) client.close();
3057
+ }
3058
+ }
3059
+ function createCancelTool(getSupervisorClient) {
3060
+ return (args) => executeCancelTool(args, getSupervisorClient);
3061
+ }
3062
+ const cancelTool = createCancelTool(getSupervisorClient);
3063
+ //#endregion
3064
+ //#region src/tools/broker-receive.ts
3065
+ async function executeBrokerReceive(args, getSupervisorClient) {
3066
+ const { client, isSingleton } = await getSupervisorClient(args.supervisorId);
3067
+ try {
3068
+ return { asks: await client.brokerReceive() };
3069
+ } finally {
3070
+ if (!isSingleton) client.close();
3071
+ }
3072
+ }
3073
+ function createBrokerReceiveTool(getSupervisorClient) {
3074
+ return (args) => executeBrokerReceive(args, getSupervisorClient);
3075
+ }
3076
+ const brokerReceiveTool = createBrokerReceiveTool(getSupervisorClient);
3077
+ //#endregion
3078
+ //#region src/tools/broker-reply.ts
3079
+ async function executeBrokerReply(args, getSupervisorClient) {
3080
+ const { client, isSingleton } = await getSupervisorClient(args.supervisorId);
3081
+ try {
3082
+ await client.brokerReply(args.toRunId, args.replyTo, args.message);
3083
+ return { ok: true };
3084
+ } finally {
3085
+ if (!isSingleton) client.close();
3086
+ }
3087
+ }
3088
+ function createBrokerReplyTool(getSupervisorClient) {
3089
+ return (args) => executeBrokerReply(args, getSupervisorClient);
3090
+ }
3091
+ const brokerReplyTool = createBrokerReplyTool(getSupervisorClient);
3092
+ //#endregion
3093
+ //#region src/tools/workflow-client.ts
3094
+ /**
3095
+ * Run `work` against the client for the given supervisor, or the singleton when none is
3096
+ * supplied. External supervisors are dialed on demand and closed again; the singleton client is
3097
+ * reused. Workflow host tools correlate by supervisor ID so runtime ids (e.g. rt_1) that collide
3098
+ * across supervisors never route to the wrong host.
3099
+ */
3100
+ async function withWorkflowClient(supervisorId, work, getSupervisorClient$1 = getSupervisorClient) {
3101
+ if (supervisorId) {
3102
+ const { client, isSingleton } = await getSupervisorClient$1(supervisorId);
3103
+ try {
3104
+ return await work(client);
3105
+ } finally {
3106
+ if (!isSingleton) client.close();
3107
+ }
3108
+ }
3109
+ return work((await Supervisor.get()).getClient());
3110
+ }
3111
+ //#endregion
3112
+ //#region src/tools/workflow-status.ts
3113
+ function createWorkflowStatusTool(getSupervisorClient) {
3114
+ return async (args) => {
3115
+ if (!args.workflowId) throw new Error("workflowId is required");
3116
+ return withWorkflowClient(args.supervisorId, (client) => client.workflowStatus(args.workflowId), getSupervisorClient);
3117
+ };
3118
+ }
3119
+ const workflowStatusTool = createWorkflowStatusTool(getSupervisorClient);
3120
+ //#endregion
3121
+ //#region src/tools/workflow-wait.ts
3122
+ function createWorkflowWaitTool(getSupervisorClient) {
3123
+ return async (args) => {
3124
+ if (!args.workflowId) throw new Error("workflowId is required");
3125
+ const timeoutMs = args.timeout ? validateTimeout(args.timeout) * 1e3 : 3e4;
3126
+ return withWorkflowClient(args.supervisorId, (client) => client.workflowWait(args.workflowId, timeoutMs), getSupervisorClient);
3127
+ };
3128
+ }
3129
+ const workflowWaitTool = createWorkflowWaitTool(getSupervisorClient);
3130
+ //#endregion
3131
+ //#region src/tools/workflow-inspect.ts
3132
+ function createWorkflowInspectTool(getSupervisorClient) {
3133
+ return async (args) => {
3134
+ if (!args.workflowId) throw new Error("workflowId is required");
3135
+ return withWorkflowClient(args.supervisorId, (client) => client.workflowInspect(args.workflowId), getSupervisorClient);
3136
+ };
3137
+ }
3138
+ const workflowInspectTool = createWorkflowInspectTool(getSupervisorClient);
3139
+ //#endregion
3140
+ //#region src/tools/workflow-events.ts
3141
+ function createWorkflowEventsTool(getSupervisorClient) {
3142
+ return async (args) => {
3143
+ if (!args.workflowId) throw new Error("workflowId is required");
3144
+ return withWorkflowClient(args.supervisorId, (client) => client.workflowEvents(args.workflowId, {
3145
+ afterSeq: args.afterSeq,
3146
+ limit: args.limit
3147
+ }), getSupervisorClient);
3148
+ };
3149
+ }
3150
+ const workflowEventsTool = createWorkflowEventsTool(getSupervisorClient);
3151
+ //#endregion
3152
+ //#region src/tools/workflow-complete.ts
3153
+ function createWorkflowCompleteTool(getSupervisorClient) {
3154
+ return async (args) => {
3155
+ if (!args.workflow_id) throw new Error("workflow_id is required");
3156
+ if (!args.node_id) throw new Error("node_id is required");
3157
+ if (!args.activation_id) throw new Error("activation_id is required");
3158
+ if (!args.attempt_id) throw new Error("attempt_id is required");
3159
+ if (!args.lease_id) throw new Error("lease_id is required");
3160
+ if (!args.owner_token) throw new Error("owner_token is required");
3161
+ if (!args.outcome) throw new Error("outcome is required");
3162
+ return withWorkflowClient(args.supervisorId, (client) => client.workflowComplete(args), getSupervisorClient);
3163
+ };
3164
+ }
3165
+ const workflowCompleteTool = createWorkflowCompleteTool(getSupervisorClient);
3166
+ //#endregion
3167
+ //#region src/tools/workflow-gate.ts
3168
+ function createWorkflowGateTool(getSupervisorClient) {
3169
+ return async (args) => {
3170
+ if (!args.workflow_id) throw new Error("workflow_id is required");
3171
+ if (!args.node_id) throw new Error("node_id is required");
3172
+ if (!args.gate_id) throw new Error("gate_id is required");
3173
+ if (!args.activation_id) throw new Error("activation_id is required");
3174
+ if (!args.operation) throw new Error("operation is required");
3175
+ if (![
3176
+ "satisfy",
3177
+ "reject",
3178
+ "waive",
3179
+ "external_result"
3180
+ ].includes(args.operation)) throw new Error(`unknown gate operation "${args.operation}" (allowed: satisfy, reject, waive, external_result)`);
3181
+ if (args.observed_at) validateObservedAt(args.observed_at);
3182
+ return withWorkflowClient(args.supervisorId, (client) => client.workflowGate(args), getSupervisorClient);
3183
+ };
3184
+ }
3185
+ const workflowGateTool = createWorkflowGateTool(getSupervisorClient);
3186
+ //#endregion
3187
+ export { DEFAULT_RUN_REDUCER_LIMITS, RunReducer, Supervisor, THINKING_LEVELS, answerPermissionTool, askTool, brokerReceiveTool, brokerReplyTool, cancelTool, createRunSnapshot, createWorkflowCompleteTool, createWorkflowEventsTool, createWorkflowGateTool, createWorkflowInspectTool, createWorkflowStatusTool, createWorkflowWaitTool, dial, evaluateThinkingPolicy, eventsTool, extractEventText, findAvenorBinary, followUpTool, getInstallDir, getVersion, inspectTool, installerBinaryPath, isThinkingLevel, normalizeRunEvent, observeRun, peersTool, reduceRunSnapshot, resultTool, shutdownTool, socketsRoot, spawnTool, statusTool, validateSpawnSelection, validateThinking, validateThinkingForBackend, validateThinkingForBackendResume, workflowCompleteTool, workflowEventsTool, workflowGateTool, workflowInspectTool, workflowStatusTool, workflowWaitTool };