@tangle-network/agent-runtime 7.0.0 → 8.0.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.
Files changed (124) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.md +0 -3
  3. package/dist/{activation-Ba9IAgQ3.js → activation-8AdN-3r4.js} +4 -4
  4. package/dist/{activation-Ba9IAgQ3.js.map → activation-8AdN-3r4.js.map} +1 -1
  5. package/dist/{activation-DIkTEBWF.d.ts → activation-xxvLF1-1.d.ts} +2 -2
  6. package/dist/agent.d.ts +1 -1
  7. package/dist/agent.js +3 -3
  8. package/dist/{bundle-Cy5igJdv.js → bundle-CcpL2lfr.js} +2 -2
  9. package/dist/{bundle-Cy5igJdv.js.map → bundle-CcpL2lfr.js.map} +1 -1
  10. package/dist/candidate-execution/index.js +4 -4
  11. package/dist/continuation-CIyKnS1Q.js +451 -0
  12. package/dist/continuation-CIyKnS1Q.js.map +1 -0
  13. package/dist/{coordination-manager-DQZ2u6L-.js → coordination-manager-DAGPerJy.js} +194 -148
  14. package/dist/coordination-manager-DAGPerJy.js.map +1 -0
  15. package/dist/{coordination-map-routing-CKRNZSaV.js → coordination-map-routing-XZXTlCmn.js} +2 -4
  16. package/dist/coordination-map-routing-XZXTlCmn.js.map +1 -0
  17. package/dist/{delegate-DFLSVjFG.js → delegate-CoaNr0hO.js} +3 -3
  18. package/dist/{delegate-DFLSVjFG.js.map → delegate-CoaNr0hO.js.map} +1 -1
  19. package/dist/durable.d.ts +1 -1
  20. package/dist/durable.js +13 -12
  21. package/dist/durable.js.map +1 -1
  22. package/dist/{errors-AzSuGI23.js → errors-DG8yufqX.js} +8 -1
  23. package/dist/errors-DG8yufqX.js.map +1 -0
  24. package/dist/{failure-cause-rJTm_Tx8.js → failure-cause-CVB-YE88.js} +12 -8
  25. package/dist/{failure-cause-rJTm_Tx8.js.map → failure-cause-CVB-YE88.js.map} +1 -1
  26. package/dist/graph-pFTeNZ-G.js +1793 -0
  27. package/dist/graph-pFTeNZ-G.js.map +1 -0
  28. package/dist/{graph-registry-7X2nrreR.js → graph-registry-czAwYwNj.js} +133 -9
  29. package/dist/graph-registry-czAwYwNj.js.map +1 -0
  30. package/dist/{graph-registry-gYbq4L5Z.js → graph-registry-uTIyyy7A.js} +1 -1
  31. package/dist/{improve-BolbYegO.js → improve-I43jex8v.js} +19 -48
  32. package/dist/improve-I43jex8v.js.map +1 -0
  33. package/dist/index.d.ts +3 -3
  34. package/dist/index.js +16 -16
  35. package/dist/intelligence.d.ts +2 -2
  36. package/dist/intelligence.js +8 -8
  37. package/dist/intelligence.js.map +1 -1
  38. package/dist/kernel.d.ts +2 -2
  39. package/dist/kernel.js +16 -15
  40. package/dist/{loop-runner-Cs9IJ6Nk.d.ts → loop-runner-BGwhcn1p.d.ts} +2 -2
  41. package/dist/{loop-runner-C9W3T_3T.js → loop-runner-BN4KozR0.js} +3 -3
  42. package/dist/{loop-runner-C9W3T_3T.js.map → loop-runner-BN4KozR0.js.map} +1 -1
  43. package/dist/loop-runner-bin.d.ts +1 -1
  44. package/dist/loop-runner-bin.js +1 -1
  45. package/dist/mcp/bin.js +7 -5
  46. package/dist/mcp/bin.js.map +1 -1
  47. package/dist/mcp/index.d.ts +1 -1
  48. package/dist/mcp/index.js +9 -9
  49. package/dist/mcp/memory-bin.js +2 -2
  50. package/dist/{model-policy-JOcg3NRs.js → model-policy-CZwhXp8O.js} +15 -10
  51. package/dist/model-policy-CZwhXp8O.js.map +1 -0
  52. package/dist/{prepare-Dtfk_Wi_.js → prepare-BnF8nGIg.js} +4 -4
  53. package/dist/{prepare-Dtfk_Wi_.js.map → prepare-BnF8nGIg.js.map} +1 -1
  54. package/dist/{profile-CNSQjOes.js → profile-CD25rTJF.js} +4 -4
  55. package/dist/{profile-CNSQjOes.js.map → profile-CD25rTJF.js.map} +1 -1
  56. package/dist/profiles.d.ts +1 -1
  57. package/dist/profiles.js +2 -2
  58. package/dist/{provision-supervisor-BQFHjCtN.js → provision-supervisor-DvzayGpe.js} +8 -8
  59. package/dist/{provision-supervisor-BQFHjCtN.js.map → provision-supervisor-DvzayGpe.js.map} +1 -1
  60. package/dist/{run-cancellation-B84X8ZAP.js → run-cancellation-C5__aBKW.js} +4 -4
  61. package/dist/{run-cancellation-B84X8ZAP.js.map → run-cancellation-C5__aBKW.js.map} +1 -1
  62. package/dist/{run-state-Dh1bGERI.js → run-state-CJAaxqfp.js} +5 -5
  63. package/dist/{run-state-Dh1bGERI.js.map → run-state-CJAaxqfp.js.map} +1 -1
  64. package/dist/{runtime-C9F1PG2A.js → runtime-CuwHe8_K.js} +76 -987
  65. package/dist/runtime-CuwHe8_K.js.map +1 -0
  66. package/dist/{sandbox-profile-compat-oFGWESLn.js → sandbox-profile-compat-l-SLQnGb.js} +2 -2
  67. package/dist/{sandbox-profile-compat-oFGWESLn.js.map → sandbox-profile-compat-l-SLQnGb.js.map} +1 -1
  68. package/dist/{search-executor-CKR9_6NR.js → search-executor-DAjm-NlG.js} +17 -46
  69. package/dist/search-executor-DAjm-NlG.js.map +1 -0
  70. package/dist/{server-Dh3SQGQT.js → server-BUpNUn5D.js} +32 -8
  71. package/dist/{server-Dh3SQGQT.js.map → server-BUpNUn5D.js.map} +1 -1
  72. package/dist/{settle-record-C6LdVvok.js → settle-record-CeBdBuxP.js} +2 -2
  73. package/dist/{settle-record-C6LdVvok.js.map → settle-record-CeBdBuxP.js.map} +1 -1
  74. package/dist/{snapshot-Cgj2Wzi6.js → snapshot-BKI_w3JD.js} +19 -5
  75. package/dist/snapshot-BKI_w3JD.js.map +1 -0
  76. package/dist/{structural-rollout-Dapf2aZY.js → structural-rollout-CqxBKxKY.js} +18 -54
  77. package/dist/structural-rollout-CqxBKxKY.js.map +1 -0
  78. package/dist/{supervise-xwl2D6q3.js → supervise-Cdqj_oe4.js} +101 -111
  79. package/dist/supervise-Cdqj_oe4.js.map +1 -0
  80. package/dist/{supervision-discovery-B47U8TH5.js → supervision-discovery-CW-ildZ-.js} +4 -4
  81. package/dist/{supervision-discovery-B47U8TH5.js.map → supervision-discovery-CW-ildZ-.js.map} +1 -1
  82. package/dist/{supervisor-gC-u_PRk.js → supervisor-D-eT-6Tu.js} +206 -693
  83. package/dist/supervisor-D-eT-6Tu.js.map +1 -0
  84. package/dist/{supervisor-accounting-DxLQ_TQe.js → supervisor-accounting-Ow6CChpB.js} +434 -423
  85. package/dist/supervisor-accounting-Ow6CChpB.js.map +1 -0
  86. package/dist/testing.d.ts +1 -1
  87. package/dist/testing.js +61 -61
  88. package/dist/{tool-server-Dg350Bgz.js → tool-server-DYwRbi0I.js} +3 -3
  89. package/dist/{tool-server-Dg350Bgz.js.map → tool-server-DYwRbi0I.js.map} +1 -1
  90. package/dist/{top-web-BJLnFQdr.js → top-web-BuYMkJN4.js} +9 -7
  91. package/dist/{top-web-BJLnFQdr.js.map → top-web-BuYMkJN4.js.map} +1 -1
  92. package/dist/tui/bin.js +3 -3
  93. package/dist/tui/index.d.ts +1 -1
  94. package/dist/tui/index.js +2 -2
  95. package/dist/{types-Jg274chm.d.ts → types-C_qbnw9N.d.ts} +1335 -1413
  96. package/dist/{util-C_cdPNC4.js → util-fcckrkKp.js} +39 -113
  97. package/dist/util-fcckrkKp.js.map +1 -0
  98. package/dist/viewer/report-viewer.js +90 -90
  99. package/dist/viewer/runtime-viewer/AgentGraphCanvas-P_PFS2Ko-B7S6Fwwy.js +563 -0
  100. package/dist/viewer/runtime-viewer/latest-events-wofY6ifW-B8_bfLwI.js +17870 -0
  101. package/dist/viewer/styles.css +1592 -7027
  102. package/dist/viewer/workspace-viewer.js +10926 -10522
  103. package/dist/{workspace-streams-B7yNMahl.js → workspace-streams-DA9R3j-p.js} +2 -2
  104. package/dist/{workspace-streams-B7yNMahl.js.map → workspace-streams-DA9R3j-p.js.map} +1 -1
  105. package/package.json +9 -9
  106. package/dist/coordination-manager-DQZ2u6L-.js.map +0 -1
  107. package/dist/coordination-map-routing-CKRNZSaV.js.map +0 -1
  108. package/dist/errors-AzSuGI23.js.map +0 -1
  109. package/dist/graph-BjQF2syl.js +0 -579
  110. package/dist/graph-BjQF2syl.js.map +0 -1
  111. package/dist/graph-registry-7X2nrreR.js.map +0 -1
  112. package/dist/improve-BolbYegO.js.map +0 -1
  113. package/dist/model-policy-JOcg3NRs.js.map +0 -1
  114. package/dist/runtime-C9F1PG2A.js.map +0 -1
  115. package/dist/search-executor-CKR9_6NR.js.map +0 -1
  116. package/dist/snapshot-Cgj2Wzi6.js.map +0 -1
  117. package/dist/structural-rollout-Dapf2aZY.js.map +0 -1
  118. package/dist/supervise-xwl2D6q3.js.map +0 -1
  119. package/dist/supervisor-accounting-DxLQ_TQe.js.map +0 -1
  120. package/dist/supervisor-gC-u_PRk.js.map +0 -1
  121. package/dist/util-C_cdPNC4.js.map +0 -1
  122. package/dist/viewer/runtime-viewer/AgentGraphCanvas-DGq7_u8--C6yDfIUT.js +0 -8555
  123. package/dist/viewer/runtime-viewer/latest-events-CRq9sGkK-CfoBl1dP.js +0 -9592
  124. package/dist/viewer/runtime-viewer/sandbox-ui-BEziPi7D.css +0 -2
@@ -1,14 +1,16 @@
1
- import { C as writeWorkerCancellation, Ft as runtimeGraphMetadataKey, M as parseWorkerToolTraceArtifact, N as workerTraceAnalysisStore, T as writeWorkerSteerAcknowledgement, U as spawnJournalRecordEvents, V as FileSpawnJournal, _ as readWorkerSteerRequests, d as claimWorkerSteerDelivery, en as FileResultBlobStore, g as readWorkerSteerAcknowledgement, h as readWorkerCancellation, m as readWorkerCancelRequests, or as isLiveNodeStatus, sr as isTerminalNodeStatus, tt as meterRuntimeOwnedAccounting, yt as resolveSpawnResourcePaths } from "./supervisor-accounting-DxLQ_TQe.js";
2
- import { $ as assertValidBudget, M as contentAddress, o as settlementFailureCause } from "./failure-cause-rJTm_Tx8.js";
3
- import { a as errMessage, d as resolveRedactor, t as detachedFrozen } from "./snapshot-Cgj2Wzi6.js";
4
- import { Et as failedItems, Y as INBOX_RECEIPT_KEY, _ as withDriverExecutor, b as createExecutorRegistry, gt as CheckUnavailableError, nt as createPeerMailbox, v as InMemoryResultBlobStore, wt as checkVerdictOf, y as InMemorySpawnJournal } from "./supervisor-gC-u_PRk.js";
5
- import { c as writeRunCancellation, i as readRunCancellation, r as readRunCancelRequest, t as applyRunCancellation, v as FileCoordinationLog } from "./run-cancellation-B84X8ZAP.js";
6
- import { a as admitRuntimeSpawn, i as workerSettlement, n as resolveJsonPointer, o as safeJsonText, r as textBytes, t as createMapRouting } from "./coordination-map-routing-CKRNZSaV.js";
7
- import { agentProfileSchema, canonicalAgentProfileDigest, canonicalCandidateDigest } from "@tangle-network/agent-interface";
1
+ import { E as writeWorkerCancellation, F as parseWorkerToolTraceArtifact, I as workerTraceAnalysisStore, K as spawnJournalRecordEvents, O as writeWorkerSteerAcknowledgement, Rt as runtimeGraphMetadataKey, S as workerCancelRequestsFile, St as resolveSpawnResourcePaths, T as workerSteerRequestsDir, W as FileSpawnJournal, _ as readWorkerSteerAcknowledgement, d as claimWorkerSteerDelivery, dr as isLiveNodeStatus, fr as isTerminalNodeStatus, g as readWorkerCancellation, h as readWorkerCancelRequests, in as FileResultBlobStore, it as meterRuntimeOwnedAccounting, v as readWorkerSteerRequests } from "./supervisor-accounting-Ow6CChpB.js";
2
+ import { m as ValidationError } from "./errors-DG8yufqX.js";
3
+ import { M as contentAddress, et as assertValidBudget, o as settlementFailureCause } from "./failure-cause-CVB-YE88.js";
4
+ import { a as errMessage, f as resolveRedactor, t as detachedFrozen } from "./snapshot-BKI_w3JD.js";
5
+ import { Y as INBOX_RECEIPT_KEY, _ as withDriverExecutor, b as createExecutorRegistry, gt as CheckUnavailableError, nt as createPeerMailbox, v as InMemoryResultBlobStore, y as InMemorySpawnJournal } from "./supervisor-D-eT-6Tu.js";
6
+ import { i as checkVerdictOf, o as failedItems } from "./continuation-CIyKnS1Q.js";
7
+ import { c as writeRunCancellation, i as readRunCancellation, r as readRunCancelRequest, t as applyRunCancellation, v as FileCoordinationLog } from "./run-cancellation-C5__aBKW.js";
8
+ import { a as admitRuntimeSpawn, i as workerSettlement, n as resolveJsonPointer, o as safeJsonText, r as textBytes, t as createMapRouting } from "./coordination-map-routing-XZXTlCmn.js";
9
+ import { ResourceRequestSchema, agentProfileSchema, canonicalAgentProfileDigest, canonicalCandidateDigest } from "@tangle-network/agent-interface";
8
10
  import { argHash, errorStreakDetector, observeAll, repeatedActionDetector } from "@tangle-network/agent-eval";
9
11
  import { registry } from "zod";
10
12
  import { randomUUID } from "node:crypto";
11
- import { readFileSync } from "node:fs";
13
+ import { closeSync, openSync, readSync, statSync } from "node:fs";
12
14
  import { join } from "node:path";
13
15
  // src/runtime/supervise/run-context.ts
14
16
  /**
@@ -485,18 +487,62 @@ const BUDGET_FIELD = {
485
487
  usd: "maxUsd"
486
488
  };
487
489
  // src/runtime/supervise/driver-control-acknowledgers.ts
488
- /** The journal file `createFileRunContext` writes inside the run directory. The acknowledger
489
- * reads it as EVIDENCE for the terminated-descendants set — the nested trees of a cancelled
490
- * lead journal their terminal records there before the lead settles into this scope. */
491
490
  const SPAWN_JOURNAL_FILE = "spawn-journal.jsonl";
492
- /**
493
- * Apply externally admitted steers once through the owning manager's control path.
494
- *
495
- * The `unknown` acknowledgement lands before authorization or delivery. A crash after that write
496
- * can lose this steer, but a restarted manager never delivers it again. This is the same
497
- * at-most-once crash boundary as coordination instruction receipts: no duplicate instruction is
498
- * safer than replaying a mutation whose first delivery may already have succeeded.
499
- */
491
+ function appendedLines(tail) {
492
+ let size;
493
+ try {
494
+ size = statSync(tail.file).size;
495
+ } catch {
496
+ return;
497
+ }
498
+ if (size < tail.offset) {
499
+ tail.offset = 0;
500
+ tail.partial = "";
501
+ tail.at.clear();
502
+ }
503
+ if (size === tail.offset) return void 0;
504
+ const buf = Buffer.allocUnsafe(size - tail.offset);
505
+ const fd = openSync(tail.file, "r");
506
+ try {
507
+ const got = readSync(fd, buf, 0, buf.length, tail.offset);
508
+ tail.offset += got;
509
+ const text = tail.partial + buf.subarray(0, got).toString("utf8");
510
+ const parts = text.split("\n");
511
+ tail.partial = text.endsWith("\n") ? "" : parts.pop() ?? "";
512
+ if (text.endsWith("\n")) parts.pop();
513
+ return parts;
514
+ } finally {
515
+ closeSync(fd);
516
+ }
517
+ }
518
+ function indexDeaths(tail) {
519
+ const lines = appendedLines(tail);
520
+ if (lines === void 0) return false;
521
+ let grew = false;
522
+ for (const line of lines) {
523
+ const trimmed = line.trim();
524
+ if (!trimmed) continue;
525
+ try {
526
+ for (const event of spawnJournalRecordEvents(JSON.parse(trimmed))) {
527
+ if (event === void 0) continue;
528
+ if (!(event.kind === "settled" && event.status === "down" || event.kind === "cancelled") || typeof event.id !== "string" || typeof event.at !== "string") continue;
529
+ const prev = tail.at.get(event.id);
530
+ if (prev !== void 0 && prev >= event.at) continue;
531
+ tail.at.set(event.id, event.at);
532
+ grew = true;
533
+ }
534
+ } catch {}
535
+ }
536
+ return grew;
537
+ }
538
+ function descendantsSince(tail, nodeId, sinceIso) {
539
+ indexDeaths(tail);
540
+ const prefix = `${nodeId}:`;
541
+ const ids = [...tail.at.keys()].filter((id) => id.startsWith(prefix) && (tail.at.get(id) ?? "") >= sinceIso);
542
+ ids.sort();
543
+ return ids;
544
+ }
545
+ /** Unknown ack lands before delivery. A crash can lose the steer; a restart must not deliver it twice. */
500
546
  function createSteerAcknowledger(deps) {
501
547
  const iso = () => new Date(deps.now()).toISOString();
502
548
  const awaiting = /* @__PURE__ */ new Map();
@@ -521,9 +567,29 @@ function createSteerAcknowledger(deps) {
521
567
  worker: request.worker,
522
568
  requestedAt: request.at
523
569
  });
570
+ let steerStamp = "";
571
+ let steerIdle = false;
572
+ const steerChanged = (phase) => {
573
+ let stamp = "missing";
574
+ try {
575
+ const st = statSync(workerSteerRequestsDir(deps.dir), { bigint: true });
576
+ stamp = `${st.dev}:${st.ino}:${st.mtimeNs}:${st.size}`;
577
+ } catch {
578
+ stamp = "missing";
579
+ }
580
+ const changed = stamp !== steerStamp;
581
+ if (changed && phase !== "boundary") steerStamp = stamp;
582
+ return changed;
583
+ };
524
584
  return { async pass(phase) {
585
+ const changed = steerChanged(phase);
586
+ if (phase !== "final" && !changed && steerIdle) return;
587
+ let waiting = false;
525
588
  for (const request of readWorkerSteerRequests(deps.dir)) {
526
- if (phase !== "final" && deps.signal?.aborted) return;
589
+ if (phase !== "final" && deps.signal?.aborted) {
590
+ steerIdle = false;
591
+ return;
592
+ }
527
593
  const rootRequest = request.worker === deps.ownerId && deps.deliverRoot !== void 0;
528
594
  if (phase === "boundary" && !rootRequest) continue;
529
595
  if (!rootRequest && !isDirectChildNodeId(deps, request.worker)) continue;
@@ -547,7 +613,10 @@ function createSteerAcknowledger(deps) {
547
613
  });
548
614
  continue;
549
615
  }
550
- if (rootRequest && deps.deliverRootReady !== void 0 && !deps.deliverRootReady()) continue;
616
+ if (rootRequest && deps.deliverRootReady !== void 0 && !deps.deliverRootReady()) {
617
+ waiting = true;
618
+ continue;
619
+ }
551
620
  if (!claimWorkerSteerDelivery(deps.dir, {
552
621
  ...base(request),
553
622
  effect: "unknown",
@@ -596,17 +665,9 @@ function createSteerAcknowledger(deps) {
596
665
  });
597
666
  }
598
667
  }
668
+ if (phase === "turn") steerIdle = !waiting;
599
669
  } };
600
670
  }
601
- /**
602
- * The OPERATOR-facing sentence for a steer outcome, written into the durable acknowledgement file.
603
- *
604
- * Deliberately separate from `downMessageRefusalReasons`, which is the MANAGER-facing sentence: one
605
- * says what happened for someone reading the run afterwards, the other tells a model what to do
606
- * next, and merging them would make one audience read the other's copy. They must nonetheless cover
607
- * the SAME outcomes, so `tests/kernel/refusal-reasons.test.ts` holds both maps total over
608
- * `DownMessageDeliveryOutcome` — a new code cannot land in only one.
609
- */
610
671
  function steerAcknowledgementDetail(outcome) {
611
672
  switch (outcome.outcome) {
612
673
  case "delivered": return "the owning manager delivered the steer to the exact live worker";
@@ -617,42 +678,32 @@ function steerAcknowledgementDetail(outcome) {
617
678
  case "runtime-error": return "delivery outcome is unknown after a runtime error";
618
679
  }
619
680
  }
620
- /**
621
- * The worker-cancel ACKNOWLEDGER — the runtime-side half of `run-layout`'s `cancelWorker`
622
- * contract. Router managers check between turns; native managers observe throughout their invocation.
623
- * Every manager with a `controlDir` mounts one;
624
- * OWNERSHIP keeps them from colliding: a request naming a node id is owned by the manager whose
625
- * own id is that node's parent, and a label/profile-name reference is owned by the `'run'`-scoped
626
- * (root) manager only — so exactly one acknowledger can ever apply one operation.
627
- *
628
- * Two-phase, honestly reported: `cancel_requested` is written the moment a live worker's abort is
629
- * issued (through the per-child abort chain the scope already owns, so siblings are untouched);
630
- * `cancelled` is written only when that worker's settlement is DELIVERED on the settle path with
631
- * a terminal `down`, and then the record names every subtree node id proven terminated. A worker
632
- * that already settled — or that settles `done` despite the abort — records `not_live`; a
633
- * reference matching nothing this manager owns stays pending (`cancelWorker` reports it
634
- * `unknown`). No path reports success for a missing worker.
635
- *
636
- * Expiry is run end, not a clock: `finish()` (after the final post-drain pass) writes `not_live`
637
- * for every owned request never applied and `unknown` for an issued abort whose settle the run
638
- * ended too soon to observe. A pending request can therefore never outlive its run and abort a
639
- * future spawn that happens to reuse a label.
640
- *
641
- * Idempotency is a lookup, in-process and across processes: an operation with a durable
642
- * acknowledgement is returned as-is and never re-applied.
643
- */
644
681
  function createCancelAcknowledger(deps) {
645
682
  const tracked = /* @__PURE__ */ new Map();
646
683
  let runTracked;
647
684
  const abortIssuedAt = /* @__PURE__ */ new Map();
648
685
  const iso = () => new Date(deps.now()).toISOString();
686
+ const waiting = /* @__PURE__ */ new Map();
687
+ const reconciling = /* @__PURE__ */ new Map();
688
+ const regrowing = /* @__PURE__ */ new Set();
689
+ let inboxBytes;
690
+ const deaths = {
691
+ file: join(deps.dir, SPAWN_JOURNAL_FILE),
692
+ offset: 0,
693
+ partial: "",
694
+ at: /* @__PURE__ */ new Map()
695
+ };
696
+ const watch = (record) => {
697
+ if (record.effect === "cancel_requested") reconciling.set(record.operationId, record);
698
+ else reconciling.delete(record.operationId);
699
+ if (record.effect === "cancelled" && abortIssuedAt.has(record.operationId)) regrowing.add(record.operationId);
700
+ else regrowing.delete(record.operationId);
701
+ };
649
702
  const write = (record) => {
650
703
  writeWorkerCancellation(deps.dir, record);
651
704
  tracked.set(record.operationId, record);
705
+ watch(record);
652
706
  };
653
- /** Whether this acknowledger owns `ref`. A node id deeper in this subtree belongs to the nested
654
- * manager that parents it; anything that is not a node id under this manager is a
655
- * label/profile-name reference, owned by the `'run'`-scoped manager alone. */
656
707
  const owned = (ref) => {
657
708
  if (isDirectChildNodeId(deps, ref)) return true;
658
709
  if (deps.controlScope !== "run") return false;
@@ -695,15 +746,13 @@ function createCancelAcknowledger(deps) {
695
746
  terminated: []
696
747
  });
697
748
  };
698
- /** The proven-terminated set for one record: the worker plus every subtree id with a terminal
699
- * journal record at/after the abort was issued. Union with what the record already names, so
700
- * the set only ever grows (a late teardown journal adds; nothing removes). */
749
+ /** Grows only: the worker, the record's ids, and subtree deaths at or after the abort. */
701
750
  const provenTerminated = (record, workerId) => {
702
751
  const since = abortIssuedAt.get(record.operationId) ?? record.observedAt;
703
752
  return [.../* @__PURE__ */ new Set([
704
753
  ...record.terminated,
705
754
  workerId,
706
- ...terminatedDescendants(deps.dir, workerId, since)
755
+ ...descendantsSince(deaths, workerId, since)
707
756
  ])].sort();
708
757
  };
709
758
  const reconcile = (record) => {
@@ -729,10 +778,6 @@ function createCancelAcknowledger(deps) {
729
778
  detail: `worker '${workerId}' settled done despite the abort request; nothing was terminated`
730
779
  });
731
780
  };
732
- /** Re-scan a `cancelled` record while the manager still turns: a descendant whose teardown
733
- * journals after the lead's settle joins the set on a later pass instead of being lost. Only
734
- * a grown set is re-written; the window needs the in-process abort instant, so a record a
735
- * PRIOR process closed stays as that process proved it. */
736
781
  const regrow = (record) => {
737
782
  const workerId = record.workerId;
738
783
  if (workerId === void 0 || !abortIssuedAt.has(record.operationId)) return;
@@ -743,36 +788,49 @@ function createCancelAcknowledger(deps) {
743
788
  terminated
744
789
  });
745
790
  };
746
- /**
747
- * The RUN-scoped request: seen once, `cancel_requested` written the moment the run's cascading
748
- * abort is issued through the one controller the run already has. The `supervise()` settle path
749
- * records what the run then actually did — this manager cannot observe its own tree's terminal
750
- * state from inside `act`.
751
- *
752
- * Applied only at a TURN boundary, never on the final post-drain pass: by then the driver has
753
- * finished and drained, so a root abort could only void work that is already delivered. A
754
- * request that arrives that late expires in `finish()` instead — it terminated nothing.
755
- */
756
791
  const passRun = () => {
757
792
  if (deps.controlScope !== "run" || deps.abortRun === void 0) return;
758
793
  if (runTracked !== void 0) return;
759
794
  runTracked = applyRunCancellation(deps.dir, deps.abortRun, iso);
760
795
  };
761
- const pass = (phase) => {
762
- if (phase === "turn") passRun();
796
+ const ingest = () => {
797
+ const file = workerCancelRequestsFile(deps.dir);
798
+ let size;
799
+ try {
800
+ size = statSync(file).size;
801
+ } catch {
802
+ size = null;
803
+ }
804
+ if (size === inboxBytes) return;
805
+ inboxBytes = size;
763
806
  for (const request of readWorkerCancelRequests(deps.dir)) {
764
- if (!owned(request.worker)) continue;
765
- let record = tracked.get(request.operationId);
766
- if (record === void 0) {
767
- record = readWorkerCancellation(deps.dir, request.operationId);
768
- if (record !== void 0) tracked.set(request.operationId, record);
769
- }
770
- if (record === void 0) {
771
- if (phase === "turn") apply(request);
772
- continue;
807
+ if (!owned(request.worker) || tracked.has(request.operationId) || waiting.has(request.operationId)) continue;
808
+ const record = readWorkerCancellation(deps.dir, request.operationId);
809
+ if (record === void 0) waiting.set(request.operationId, request);
810
+ else {
811
+ tracked.set(record.operationId, record);
812
+ watch(record);
773
813
  }
774
- if (record.effect === "cancel_requested") reconcile(record);
775
- else if (record.effect === "cancelled") regrow(record);
814
+ }
815
+ };
816
+ const pass = (phase) => {
817
+ if (phase === "turn") passRun();
818
+ ingest();
819
+ const pending = [...reconciling.values()];
820
+ if (phase === "turn") for (const request of [...waiting.values()]) {
821
+ waiting.delete(request.operationId);
822
+ apply(request);
823
+ if (!tracked.has(request.operationId)) waiting.set(request.operationId, request);
824
+ }
825
+ const grew = indexDeaths(deaths);
826
+ for (const record of pending) {
827
+ const current = tracked.get(record.operationId);
828
+ if (current?.effect === "cancel_requested") reconcile(current);
829
+ }
830
+ if (!grew) return;
831
+ for (const id of [...regrowing]) {
832
+ const record = tracked.get(id);
833
+ if (record !== void 0) regrow(record);
776
834
  }
777
835
  };
778
836
  return {
@@ -814,43 +872,6 @@ function createCancelAcknowledger(deps) {
814
872
  }
815
873
  };
816
874
  }
817
- /**
818
- * Subtree node ids with a terminal `down`/`cancelled` journal record at or after `sinceIso` —
819
- * the abort-issue instant (the acknowledger's own `observedAt` on the `cancel_requested` record,
820
- * runtime clock), never the client's `requestedAt` — read from the durable spawn journal beside
821
- * the run layout. The set is proven at acknowledgement time and is approximate about post-abort
822
- * causation: a descendant that died of its OWN cause after the abort was issued is
823
- * indistinguishable from the cascade and may be included; one whose teardown journals late joins
824
- * on a later acknowledger pass; a teardown journal still absent when the run ends is absent from
825
- * the set. Ids are hierarchical (`parent:sN`), so `${nodeId}:` prefixes exactly the subtree.
826
- * Tolerant of a missing or partially-written journal: evidence that cannot be read names fewer
827
- * nodes, never wrong ones.
828
- */
829
- function terminatedDescendants(dir, nodeId, sinceIso) {
830
- let raw;
831
- try {
832
- raw = readFileSync(join(dir, SPAWN_JOURNAL_FILE), "utf8");
833
- } catch {
834
- return [];
835
- }
836
- const prefix = `${nodeId}:`;
837
- const ids = /* @__PURE__ */ new Set();
838
- for (const line of raw.split("\n")) {
839
- const trimmed = line.trim();
840
- if (!trimmed) continue;
841
- try {
842
- for (const event of spawnJournalRecordEvents(JSON.parse(trimmed))) {
843
- if (event === void 0) continue;
844
- if (!(event.kind === "settled" && event.status === "down" || event.kind === "cancelled")) continue;
845
- if (typeof event.id !== "string" || !event.id.startsWith(prefix)) continue;
846
- if (typeof event.at !== "string" || event.at < sinceIso) continue;
847
- ids.add(event.id);
848
- }
849
- } catch {}
850
- }
851
- return [...ids].sort();
852
- }
853
- /** A direct child has exactly one generated spawn segment below its owner. */
854
875
  function isDirectChildNodeId(owner, ref) {
855
876
  return ref.startsWith(`${owner.ownerId}:s`) && /^s\d+$/.test(ref.slice(owner.ownerId.length + 1));
856
877
  }
@@ -1867,15 +1888,13 @@ function mountGraphsTools({ opts, tools, admitSpawn }) {
1867
1888
  error: "invalid-graph",
1868
1889
  reason: prepared.reason
1869
1890
  };
1870
- const profile = detachedFrozen(prepared.profile);
1871
1891
  return admitSpawn({
1872
- profile,
1892
+ profile: detachedFrozen(prepared.profile),
1873
1893
  args: a,
1874
1894
  task: deliverable,
1875
1895
  key,
1876
1896
  continuity: { continuity: "fresh" },
1877
1897
  successorOf: void 0,
1878
- preflightProfile: () => opts.resolveSpawnProfile ? detachedFrozen(opts.resolveSpawnProfile(profile)) : profile,
1879
1898
  resourcesFromPath: [],
1880
1899
  defaultLabel: `graph:${prepared.root}`,
1881
1900
  accepted: { graph: {
@@ -3025,7 +3044,6 @@ async function spawnNode(state, marker, profile, task, key, continuity, traversa
3025
3044
  key,
3026
3045
  continuity,
3027
3046
  successorOf: state.settledWorker.get(marker.node),
3028
- preflightProfile: () => state.opts.resolveSpawnProfile ? detachedFrozen(state.opts.resolveSpawnProfile(profile)) : profile,
3029
3047
  resourcesFromPath: [],
3030
3048
  defaultLabel: marker.node,
3031
3049
  route: Object.freeze({ ...marker })
@@ -3685,14 +3703,21 @@ function resolveContinuity(state, requested, profileName, key) {
3685
3703
  * assignment, budget reservation, `Scope.spawn` — is synchronous and commits state.
3686
3704
  * Returns the refusal body, or `undefined` when the spawn may proceed.
3687
3705
  */
3688
- async function preflightRefusal(state, input, label, task) {
3706
+ /** `spawn_worker`'s optional machine request, as the canonical resource request. */
3707
+ function spawnResources(raw) {
3708
+ if (raw === void 0) return void 0;
3709
+ const parsed = ResourceRequestSchema.omit({ providerOptions: true }).strict().safeParse(raw);
3710
+ if (!parsed.success) throw new ValidationError(`spawn_worker resources: ${parsed.error.message}`);
3711
+ return detachedFrozen(parsed.data);
3712
+ }
3713
+ async function preflightRefusal(state, effective, spawn) {
3689
3714
  const { opts } = state;
3690
3715
  if (!opts.preflightSpawn) return void 0;
3691
- const { key } = input;
3692
- const refusal = await opts.preflightSpawn(input.preflightProfile(), {
3693
- label,
3716
+ const { key, resources, ...rest } = spawn;
3717
+ const refusal = await opts.preflightSpawn(effective, {
3718
+ ...rest,
3694
3719
  ...key !== void 0 ? { key } : {},
3695
- task
3720
+ ...resources !== void 0 ? { resources } : {}
3696
3721
  });
3697
3722
  if (!refusal) return void 0;
3698
3723
  state.preflightCounts[refusal.cause] += 1;
@@ -3711,7 +3736,14 @@ async function admitSpawn(state, input) {
3711
3736
  const a = input.args;
3712
3737
  const task = detachedFrozen(input.task);
3713
3738
  const label = typeof a.label === "string" ? a.label : input.defaultLabel;
3714
- const refused = await preflightRefusal(state, input, label, task);
3739
+ const effective = opts.resolveSpawnProfile ? detachedFrozen(opts.resolveSpawnProfile(profile)) : profile;
3740
+ const resources = spawnResources(a.resources);
3741
+ const refused = await preflightRefusal(state, effective, {
3742
+ label,
3743
+ task,
3744
+ key,
3745
+ resources
3746
+ });
3715
3747
  if (refused !== void 0) return refused;
3716
3748
  const budget = Object.freeze(a.budget === void 0 ? opts.perWorker : mergeBudget(opts.perWorker, a.budget));
3717
3749
  const assignmentId = key !== void 0 ? `key:${key}` : `ordinal:${state.unkeyedAssignment.next++}`;
@@ -3723,6 +3755,7 @@ async function admitSpawn(state, input) {
3723
3755
  task,
3724
3756
  label,
3725
3757
  ...key !== void 0 ? { key } : {},
3758
+ ...resources !== void 0 ? { resources } : {},
3726
3759
  continuity: continuity.continuity,
3727
3760
  ...continuity.continuity === "resume" ? { resume: continuity.resume } : {},
3728
3761
  ...successorOf !== void 0 ? { successorOf } : {},
@@ -3730,7 +3763,7 @@ async function admitSpawn(state, input) {
3730
3763
  ...input.map !== void 0 ? { map: input.map } : {},
3731
3764
  ...input.route !== void 0 ? { route: input.route } : {}
3732
3765
  });
3733
- const res = opts.scope.spawn(() => opts.makeWorkerAgent(profile, context), task, {
3766
+ const res = opts.scope.spawn(() => opts.makeWorkerAgent(effective, context), task, {
3734
3767
  budget,
3735
3768
  label,
3736
3769
  assignmentId,
@@ -4373,7 +4406,7 @@ function spawnWorkerTool(ctx) {
4373
4406
  const { profileTable } = ctx;
4374
4407
  return {
4375
4408
  name: "spawn_worker",
4376
- description: "Start a worker the driver will drive. `profile` is the worker or another driver; `task` is what it should do. Reserves budget from the conserved pool and fails closed. Pass an optional `budget` (per-field) to give a sub-task more or less than the default — it merges over the per-worker default; the conserved pool is still the hard fence. Each worker reserves only the channels capped by the pool. workerSlots sets concurrency. A funded worker waits with `status: \"queued\"` when every slot is busy and starts when one frees. A new worker whose budget cannot fit is refused immediately. Wait for settlements or request a smaller allocation before trying again. End your turn to be woken with results. Pass a `key` naming the assignment to make it run-once ACROSS restarts: a key that already completed returns the finished result (`resumed: \"completed\"` — no work re-runs, nothing is spent). A keyed worker that settled with a `budgetStop` keeps its session for a while: spawn the same key with a larger `budget` and a new worker continues that same session where it stopped (`resumed: \"continued\"`, `priorWorkerId`), or cancel_worker the stopped worker to release it. A key whose prior attempt failed (`down`) spawns fresh and says so (`resumed: \"retried\"`), and a key the process died with IN FLIGHT retries under the same key when the executor provably died with the process (an inline worker — the same `resumed: \"retried\"`, naming the interruption). A key with no terminal receipt whose execution may still exist elsewhere (sandbox, CLI bridge, router) is refused (`error: \"in-doubt\"`) until its exact prior execution is recovered. A key still running is refused (`error: \"duplicate-key\"`). Returns `freeSlots`: how many MORE workers start at once (`null` = no slot bound), and `queued`: how many of your workers wait for a slot. Parallel workers finish the run sooner than one at a time." + (profileTable === void 0 ? "" : " This run has a profiles table: pass `profile` as one of the table's names to run that exact profile, or write a new profile under a name the table does not hold."),
4409
+ description: "Start a worker the driver will drive. `profile` is the worker or another driver; `task` is what it should do. Reserves budget from the conserved pool and fails closed. Pass an optional `budget` (per-field) to give a sub-task more or less than the default — it merges over the per-worker default; the conserved pool is still the hard fence. Each worker reserves only the channels capped by the pool. workerSlots sets concurrency. A funded worker waits with `status: \"queued\"` when every slot is busy and starts when one frees. A new worker whose budget cannot fit is refused immediately. Wait for settlements or request a smaller allocation before trying again. End your turn to be woken with results. Pass a `key` naming the assignment to make it run-once ACROSS restarts: a key that already completed returns the finished result (`resumed: \"completed\"` — no work re-runs, nothing is spent). A keyed worker that settled with a `budgetStop` keeps its session for a while: spawn the same key with a larger `budget` and a new worker continues that same session where it stopped (`resumed: \"continued\"`, `priorWorkerId`), or cancel_worker the stopped worker to release it. A key whose prior attempt failed (`down`) spawns fresh and says so (`resumed: \"retried\"`), and a key the process died with IN FLIGHT retries under the same key when the executor provably died with the process (an inline worker — the same `resumed: \"retried\"`, naming the interruption). A key with no terminal receipt whose execution may still exist elsewhere (sandbox, CLI bridge, router) is refused (`error: \"in-doubt\"`) until its exact prior execution is recovered. A key still running is refused (`error: \"duplicate-key\"`). Returns `freeSlots`: how many MORE workers start at once (`null` = no slot bound), and `queued`: how many of your workers wait for a slot. Parallel workers finish the run sooner than one at a time. An authored profile must be fully executable before it spawns: `harness`, `model.provider`, a concrete `model.default`, and no legacy `systemPrompt` — one refusal names every field still missing." + (profileTable === void 0 ? "" : " This run has a profiles table: pass `profile` as one of the table's names to run that exact profile, or write a new profile under a name the table does not hold."),
4377
4410
  inputSchema: {
4378
4411
  type: "object",
4379
4412
  properties: {
@@ -4394,9 +4427,29 @@ function spawnWorkerTool(ctx) {
4394
4427
  },
4395
4428
  successorOf: {
4396
4429
  type: "string",
4397
- description: "The workerId of a SETTLED worker you spawned that this new worker replaces — for example one that failed, stalled, or finished with a weak result, restarted with a changed profile or task. The run record then names the predecessor on the new worker. Refused for an id you did not spawn (`error: \"successor-unknown\"`) and for a worker still live (`error: \"successor-live\"` — end your turn until it settles). Put what the successor needs from its predecessor in `task`."
4430
+ description: "The workerId of a worker you spawned that this new worker revises or replaces — for example one that failed, stalled, or finished with a weak result, restarted with a changed profile or task. The predecessor may still be live; cancel_worker it if the successor supersedes it. The run record names the predecessor on the new worker. Refused for an id you did not spawn (`error: \"successor-unknown\"`). Put what the successor needs from its predecessor in `task`."
4398
4431
  },
4399
- budget: spawnBudgetArg
4432
+ budget: spawnBudgetArg,
4433
+ resources: {
4434
+ type: "object",
4435
+ description: "Optional machine for this worker: `cpu`, `memoryMb`, `diskMb`, `gpu`. Omit for the run's default. Refused, naming the ceiling, above the backend's `maxWorkerResources`.",
4436
+ properties: {
4437
+ cpu: {
4438
+ type: "number",
4439
+ exclusiveMinimum: 0
4440
+ },
4441
+ memoryMb: {
4442
+ type: "number",
4443
+ exclusiveMinimum: 0
4444
+ },
4445
+ diskMb: {
4446
+ type: "number",
4447
+ exclusiveMinimum: 0
4448
+ },
4449
+ gpu: { type: "string" }
4450
+ },
4451
+ additionalProperties: false
4452
+ }
4400
4453
  },
4401
4454
  required: ["profile", "task"]
4402
4455
  },
@@ -4451,15 +4504,9 @@ function successorRefusal({ opts, spawn, workers }, successorOf) {
4451
4504
  live: workers.liveWorkerCount(),
4452
4505
  freeSlots: workers.freeWorkerSlots()
4453
4506
  };
4454
- if (isLiveNodeStatus(predecessor.status)) return {
4455
- error: "successor-live",
4456
- reason: `worker '${successorOf}' has not settled — end your turn until it does, then spawn its successor (steer_agent redirects a live worker instead)`,
4457
- live: workers.liveWorkerCount(),
4458
- freeSlots: workers.freeWorkerSlots()
4459
- };
4460
4507
  }
4461
4508
  async function spawnWorkerHandler(ctx, raw) {
4462
- const { opts, spawn, workers } = ctx;
4509
+ const { spawn, workers } = ctx;
4463
4510
  const a = obj(raw);
4464
4511
  const key = a.key === void 0 ? void 0 : str(a.key, "key");
4465
4512
  const resolvedProfile = await resolveSpawnedProfile(ctx, a.profile);
@@ -4485,7 +4532,6 @@ async function spawnWorkerHandler(ctx, raw) {
4485
4532
  key,
4486
4533
  continuity,
4487
4534
  successorOf,
4488
- preflightProfile: () => opts.resolveSpawnProfile ? detachedFrozen(opts.resolveSpawnProfile(profile)) : profile,
4489
4535
  resourcesFromPath: resolved,
4490
4536
  defaultLabel: typeof profile.name === "string" && profile.name.length > 0 ? profile.name : "worker"
4491
4537
  });
@@ -5187,4 +5233,4 @@ function createResumeReplay({ opts, bus, ledger, analysis, projectSettled }) {
5187
5233
  }
5188
5234
  export { downMessageRefusalReasons as a, createSteerAcknowledger as c, coordinationVerbNames as d, createFileRunContext as f, watchTrace as i, budgetOverspendText as l, withRunContext as m, createCoordinationToolsForManager as n, createEventBus as o, createInMemoryRunContext as p, defaultToolDetectors as r, createCancelAcknowledger as s, createCoordinationTools as t, normalizeAnalyzeOnSettle as u };
5189
5235
 
5190
- //# sourceMappingURL=coordination-manager-DQZ2u6L-.js.map
5236
+ //# sourceMappingURL=coordination-manager-DAGPerJy.js.map