@jr2/orchestrator 0.4.1 → 0.5.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 (327) hide show
  1. package/README.md +20 -2
  2. package/console/components/app.ts +2 -0
  3. package/console/components/fleet.ts +14 -1
  4. package/console/components/machine-pane.ts +41 -2
  5. package/console/main.ts +13 -1
  6. package/console/store.ts +102 -6
  7. package/console/style.css +21 -0
  8. package/docs/agents-block.md +5 -0
  9. package/docs/concepts/agents-and-turns.md +442 -0
  10. package/docs/concepts/architecture.md +230 -0
  11. package/docs/concepts/charts.md +143 -0
  12. package/docs/concepts/composition.md +357 -0
  13. package/docs/concepts/converge.md +254 -0
  14. package/docs/concepts/durable-runs.md +189 -0
  15. package/docs/concepts/effects.md +603 -0
  16. package/docs/concepts/gates.md +208 -0
  17. package/docs/concepts/machines-and-workflows.md +289 -0
  18. package/docs/concepts/pools-and-sources.md +230 -0
  19. package/docs/concepts/secrets-and-trust.md +222 -0
  20. package/docs/concepts/the-menu.md +378 -0
  21. package/docs/concepts/workspaces.md +678 -0
  22. package/docs/get-started/a-model-decides.md +563 -0
  23. package/docs/get-started/an-agent-in-a-sandbox.md +381 -0
  24. package/docs/get-started/connect-a-model.md +248 -0
  25. package/docs/get-started/create-an-instance.md +194 -0
  26. package/docs/get-started/have-your-assistant-write-a-machine.md +922 -0
  27. package/docs/get-started/install.md +126 -0
  28. package/docs/guides/add-tools-to-the-sandbox.md +295 -0
  29. package/docs/guides/answer-a-gate.md +304 -0
  30. package/docs/guides/bind-open-parts.md +223 -0
  31. package/docs/guides/cancel-a-run.md +146 -0
  32. package/docs/guides/clean-up.md +206 -0
  33. package/docs/guides/fix-a-failed-converge.md +361 -0
  34. package/docs/guides/give-the-cluster-git-credentials.md +222 -0
  35. package/docs/guides/iterate-on-a-machine-on-kind.md +190 -0
  36. package/docs/guides/let-an-agent-write-a-chart.md +226 -0
  37. package/docs/guides/look-inside-a-sandbox.md +174 -0
  38. package/docs/guides/mirror-the-kit-images.md +176 -0
  39. package/docs/guides/move-from-kind-to-a-shared-cluster.md +302 -0
  40. package/docs/guides/open-the-console.md +91 -0
  41. package/docs/guides/publish-a-machine-as-an-npm-package.md +350 -0
  42. package/docs/guides/push-and-sign-a-branch.md +486 -0
  43. package/docs/guides/run-a-chart-by-name.md +270 -0
  44. package/docs/guides/run-a-command-or-call-a-url.md +544 -0
  45. package/docs/guides/run-one-worker-per-item.md +321 -0
  46. package/docs/guides/set-an-agents-access-and-dials.md +375 -0
  47. package/docs/guides/size-a-workspace.md +204 -0
  48. package/docs/guides/start-a-run-from-a-run.md +237 -0
  49. package/docs/guides/start-a-run.md +207 -0
  50. package/docs/guides/upgrade-the-kit-version.md +240 -0
  51. package/docs/guides/use-a-hosted-provider.md +208 -0
  52. package/docs/guides/use-a-local-model.md +313 -0
  53. package/docs/guides/watch-a-run.md +326 -0
  54. package/docs/guides/write-a-workflow.md +318 -0
  55. package/docs/index.md +44 -0
  56. package/docs/llms.txt +216 -0
  57. package/docs/reference/authoring-surface/agent.md +128 -0
  58. package/docs/reference/authoring-surface/answer.md +140 -0
  59. package/docs/reference/authoring-surface/arg.md +60 -0
  60. package/docs/reference/authoring-surface/basic.md +62 -0
  61. package/docs/reference/authoring-surface/bearer.md +53 -0
  62. package/docs/reference/authoring-surface/chartschema.md +23 -0
  63. package/docs/reference/authoring-surface/chartschemafor.md +48 -0
  64. package/docs/reference/authoring-surface/command.md +191 -0
  65. package/docs/reference/authoring-surface/customize.md +64 -0
  66. package/docs/reference/authoring-surface/defineconfig.md +41 -0
  67. package/docs/reference/authoring-surface/defineevent.md +114 -0
  68. package/docs/reference/authoring-surface/dispatch.md +126 -0
  69. package/docs/reference/authoring-surface/fail.md +83 -0
  70. package/docs/reference/authoring-surface/file.md +122 -0
  71. package/docs/reference/authoring-surface/frompick.md +277 -0
  72. package/docs/reference/authoring-surface/http.md +159 -0
  73. package/docs/reference/authoring-surface/isfailure.md +58 -0
  74. package/docs/reference/authoring-surface/jr2setup.md +237 -0
  75. package/docs/reference/authoring-surface/open.md +74 -0
  76. package/docs/reference/authoring-surface/openbody.md +86 -0
  77. package/docs/reference/authoring-surface/paramsof.md +125 -0
  78. package/docs/reference/authoring-surface/pool.md +186 -0
  79. package/docs/reference/authoring-surface/push.md +112 -0
  80. package/docs/reference/authoring-surface/secret.md +118 -0
  81. package/docs/reference/authoring-surface/sentby.md +83 -0
  82. package/docs/reference/authoring-surface/source.md +51 -0
  83. package/docs/reference/authoring-surface/text.md +62 -0
  84. package/docs/reference/authoring-surface/type-agentdeclaration.md +27 -0
  85. package/docs/reference/authoring-surface/type-agentdefinition.md +80 -0
  86. package/docs/reference/authoring-surface/type-agentlogic.md +15 -0
  87. package/docs/reference/authoring-surface/type-agentslots.md +12 -0
  88. package/docs/reference/authoring-surface/type-answeraction.md +12 -0
  89. package/docs/reference/authoring-surface/type-arg.md +19 -0
  90. package/docs/reference/authoring-surface/type-argnames.md +12 -0
  91. package/docs/reference/authoring-surface/type-binding.md +62 -0
  92. package/docs/reference/authoring-surface/type-bindingof.md +12 -0
  93. package/docs/reference/authoring-surface/type-bodyacceptsdoor.md +15 -0
  94. package/docs/reference/authoring-surface/type-bodyacceptsslots.md +17 -0
  95. package/docs/reference/authoring-surface/type-ceilings.md +41 -0
  96. package/docs/reference/authoring-surface/type-chainlink.md +19 -0
  97. package/docs/reference/authoring-surface/type-chartaction.md +46 -0
  98. package/docs/reference/authoring-surface/type-chartagent.md +28 -0
  99. package/docs/reference/authoring-surface/type-chartevent.md +20 -0
  100. package/docs/reference/authoring-surface/type-chartguard.md +20 -0
  101. package/docs/reference/authoring-surface/type-chartinvoke.md +28 -0
  102. package/docs/reference/authoring-surface/type-chartjson.md +14 -0
  103. package/docs/reference/authoring-surface/type-chartstate.md +46 -0
  104. package/docs/reference/authoring-surface/type-charttext.md +40 -0
  105. package/docs/reference/authoring-surface/type-charttransition.md +22 -0
  106. package/docs/reference/authoring-surface/type-charttransitions.md +14 -0
  107. package/docs/reference/authoring-surface/type-chartvalue.md +15 -0
  108. package/docs/reference/authoring-surface/type-childat.md +12 -0
  109. package/docs/reference/authoring-surface/type-childslots.md +12 -0
  110. package/docs/reference/authoring-surface/type-commandenv.md +15 -0
  111. package/docs/reference/authoring-surface/type-commandinput.md +19 -0
  112. package/docs/reference/authoring-surface/type-commandoptions.md +78 -0
  113. package/docs/reference/authoring-surface/type-commandoutput.md +104 -0
  114. package/docs/reference/authoring-surface/type-customize.md +109 -0
  115. package/docs/reference/authoring-surface/type-dispatchdefinition.md +39 -0
  116. package/docs/reference/authoring-surface/type-dispatchinput.md +14 -0
  117. package/docs/reference/authoring-surface/type-dispatchopenparts.md +14 -0
  118. package/docs/reference/authoring-surface/type-dispatchoutput.md +65 -0
  119. package/docs/reference/authoring-surface/type-duration.md +15 -0
  120. package/docs/reference/authoring-surface/type-effectbinding.md +93 -0
  121. package/docs/reference/authoring-surface/type-effectlogic.md +12 -0
  122. package/docs/reference/authoring-surface/type-envopenparts.md +14 -0
  123. package/docs/reference/authoring-surface/type-eventaudience.md +27 -0
  124. package/docs/reference/authoring-surface/type-eventdef.md +45 -0
  125. package/docs/reference/authoring-surface/type-eventfrom.md +16 -0
  126. package/docs/reference/authoring-surface/type-eventsemantics.md +26 -0
  127. package/docs/reference/authoring-surface/type-execoutcome.md +34 -0
  128. package/docs/reference/authoring-surface/type-failslot.md +14 -0
  129. package/docs/reference/authoring-surface/type-failure.md +19 -0
  130. package/docs/reference/authoring-surface/type-failureinput.md +20 -0
  131. package/docs/reference/authoring-surface/type-filebody.md +41 -0
  132. package/docs/reference/authoring-surface/type-frominput.md +14 -0
  133. package/docs/reference/authoring-surface/type-hostinjectedinput.md +58 -0
  134. package/docs/reference/authoring-surface/type-httpdefinition.md +101 -0
  135. package/docs/reference/authoring-surface/type-httpinput.md +12 -0
  136. package/docs/reference/authoring-surface/type-httpmethod.md +14 -0
  137. package/docs/reference/authoring-surface/type-httpopenparts.md +12 -0
  138. package/docs/reference/authoring-surface/type-httpoutput.md +89 -0
  139. package/docs/reference/authoring-surface/type-httprequestparts.md +33 -0
  140. package/docs/reference/authoring-surface/type-jr2config.md +126 -0
  141. package/docs/reference/authoring-surface/type-lends.md +38 -0
  142. package/docs/reference/authoring-surface/type-logicat.md +12 -0
  143. package/docs/reference/authoring-surface/type-markedif.md +12 -0
  144. package/docs/reference/authoring-surface/type-mergedactors.md +12 -0
  145. package/docs/reference/authoring-surface/type-namedsecret.md +25 -0
  146. package/docs/reference/authoring-surface/type-noslotspec.md +26 -0
  147. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +32 -0
  148. package/docs/reference/authoring-surface/type-openbodydeclaration.md +40 -0
  149. package/docs/reference/authoring-surface/type-openbodylogic.md +14 -0
  150. package/docs/reference/authoring-surface/type-openbodyoutput.md +42 -0
  151. package/docs/reference/authoring-surface/type-openbodyslot.md +18 -0
  152. package/docs/reference/authoring-surface/type-opencommandinput.md +17 -0
  153. package/docs/reference/authoring-surface/type-openeffect.md +12 -0
  154. package/docs/reference/authoring-surface/type-openeffectslots.md +12 -0
  155. package/docs/reference/authoring-surface/type-openof.md +12 -0
  156. package/docs/reference/authoring-surface/type-opensecret.md +23 -0
  157. package/docs/reference/authoring-surface/type-opensecretmark.md +12 -0
  158. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +31 -0
  159. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +32 -0
  160. package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
  161. package/docs/reference/authoring-surface/type-poolspec.md +60 -0
  162. package/docs/reference/authoring-surface/type-pushinput.md +15 -0
  163. package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
  164. package/docs/reference/authoring-surface/type-pushoutput.md +124 -0
  165. package/docs/reference/authoring-surface/type-pushwireoutput.md +12 -0
  166. package/docs/reference/authoring-surface/type-quantity.md +14 -0
  167. package/docs/reference/authoring-surface/type-reached.md +12 -0
  168. package/docs/reference/authoring-surface/type-reposlot.md +70 -0
  169. package/docs/reference/authoring-surface/type-reposlotsof.md +16 -0
  170. package/docs/reference/authoring-surface/type-sandboxoptions.md +77 -0
  171. package/docs/reference/authoring-surface/type-secret.md +25 -0
  172. package/docs/reference/authoring-surface/type-sender.md +19 -0
  173. package/docs/reference/authoring-surface/type-sentbyguards.md +12 -0
  174. package/docs/reference/authoring-surface/type-size.md +85 -0
  175. package/docs/reference/authoring-surface/type-slothandles.md +32 -0
  176. package/docs/reference/authoring-surface/type-sourcenextresult.md +17 -0
  177. package/docs/reference/authoring-surface/type-sourcespec.md +44 -0
  178. package/docs/reference/authoring-surface/type-syncstate.md +12 -0
  179. package/docs/reference/authoring-surface/type-text.md +19 -0
  180. package/docs/reference/authoring-surface/type-textpart.md +24 -0
  181. package/docs/reference/authoring-surface/type-thinkinglevel.md +39 -0
  182. package/docs/reference/authoring-surface/type-toparameterizedobject.md +14 -0
  183. package/docs/reference/authoring-surface/type-toprovidedactor.md +14 -0
  184. package/docs/reference/authoring-surface/type-unknown.md +21 -0
  185. package/docs/reference/authoring-surface/type-usercontainer.md +77 -0
  186. package/docs/reference/authoring-surface/type-withoutemptyindex.md +20 -0
  187. package/docs/reference/authoring-surface/type-workflowevent.md +46 -0
  188. package/docs/reference/authoring-surface/type-workspaceaccess.md +16 -0
  189. package/docs/reference/authoring-surface/type-workspaced.md +94 -0
  190. package/docs/reference/authoring-surface/type-workspacehandles.md +23 -0
  191. package/docs/reference/authoring-surface/type-workspacemachine.md +29 -0
  192. package/docs/reference/authoring-surface/type-workspaceof.md +13 -0
  193. package/docs/reference/authoring-surface/type-workspaceoptions.md +39 -0
  194. package/docs/reference/authoring-surface/type-workspacespec.md +37 -0
  195. package/docs/reference/authoring-surface/workspace.md +259 -0
  196. package/docs/reference/authoring-surface.md +197 -0
  197. package/docs/reference/cli.md +433 -0
  198. package/docs/reference/config.md +756 -0
  199. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +113 -0
  200. package/docs/reference/decisions/0002-duplex-streaming-actor.md +110 -0
  201. package/docs/reference/decisions/0004-worktree-provisioning-model.md +124 -0
  202. package/docs/reference/decisions/0005-sandbox-pod-composition.md +230 -0
  203. package/docs/reference/decisions/0006-agent-control-surface.md +47 -0
  204. package/docs/reference/decisions/0007-durable-machine-state.md +109 -0
  205. package/docs/reference/decisions/0008-orchestrator-as-deployed-app.md +60 -0
  206. package/docs/reference/decisions/0009-cli-and-instance-interface.md +275 -0
  207. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +160 -0
  208. package/docs/reference/decisions/0011-workflow-defined-events.md +141 -0
  209. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +103 -0
  210. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +157 -0
  211. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +95 -0
  212. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +265 -0
  213. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +124 -0
  214. package/docs/reference/decisions/0017-pool-over-a-source-port.md +69 -0
  215. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +111 -0
  216. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +275 -0
  217. package/docs/reference/decisions/0020-private-ca-trust-is-a-named-concept.md +60 -0
  218. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +100 -0
  219. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +103 -0
  220. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +176 -0
  221. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +439 -0
  222. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +65 -0
  223. package/docs/reference/decisions/0026-a-turn-that-is-over-is-never-prompted.md +62 -0
  224. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +237 -0
  225. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +89 -0
  226. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +154 -0
  227. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +100 -0
  228. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +190 -0
  229. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +61 -0
  230. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +127 -0
  231. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +57 -0
  232. package/docs/reference/decisions/0035-a-runaway-turn-is-ended-rerolled-once-then-a-fault.md +92 -0
  233. package/docs/reference/decisions/0036-a-turn-compacts-its-own-context-mid-flight.md +138 -0
  234. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +196 -0
  235. package/docs/reference/decisions/0038-jr2-up-builds-every-image-it-deploys.md +170 -0
  236. package/docs/reference/decisions/0039-image-garbage-collects-by-reachability.md +115 -0
  237. package/docs/reference/decisions/0041-a-build-the-host-already-holds-is-not-spent-again.md +64 -0
  238. package/docs/reference/decisions/0042-ready-is-not-routable-an-admission-retries-its-connection.md +142 -0
  239. package/docs/reference/decisions/0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md +170 -0
  240. package/docs/reference/decisions/0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md +85 -0
  241. package/docs/reference/decisions/0045-the-platform-joins-the-image-address-and-the-cluster-chooses-it.md +82 -0
  242. package/docs/reference/decisions/0046-a-converge-failure-carries-its-diagnosis.md +34 -0
  243. package/docs/reference/decisions/0047-the-git-ssh-key-source-is-the-users-choice.md +47 -0
  244. package/docs/reference/decisions/0048-the-orchestrator-boots-without-its-repos.md +36 -0
  245. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +126 -0
  246. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +73 -0
  247. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +227 -0
  248. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +81 -0
  249. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +111 -0
  250. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +126 -0
  251. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +107 -0
  252. package/docs/reference/decisions/0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md +62 -0
  253. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +152 -0
  254. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +106 -0
  255. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +384 -0
  256. package/docs/reference/decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md +111 -0
  257. package/docs/reference/decisions/0061-a-memory-kill-ends-the-agents-processes-not-the-harness.md +83 -0
  258. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +212 -0
  259. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +83 -0
  260. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +79 -0
  261. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +65 -0
  262. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +550 -0
  263. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +430 -0
  264. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +464 -0
  265. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +180 -0
  266. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +323 -0
  267. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +183 -0
  268. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +144 -0
  269. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +122 -0
  270. package/docs/reference/decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md +76 -0
  271. package/docs/reference/decisions.md +83 -0
  272. package/docs/reference/glossary.md +648 -0
  273. package/docs/reference/http-api.md +1348 -0
  274. package/docs/reference/machines.md +190 -0
  275. package/docs/reference/mechanism-events.md +72 -0
  276. package/docs/why.md +206 -0
  277. package/package.json +5 -3
  278. package/src/actor.ts +541 -79
  279. package/src/agent.ts +114 -38
  280. package/src/ambient.ts +2 -1
  281. package/src/assembly-checks.ts +189 -7
  282. package/src/cause.ts +234 -0
  283. package/src/chart.ts +169 -37
  284. package/src/charts.ts +34 -6
  285. package/src/config.ts +430 -178
  286. package/src/custodian.ts +74 -22
  287. package/src/customize.ts +188 -46
  288. package/src/dispatch-effect.ts +945 -0
  289. package/src/door-compat.ts +217 -0
  290. package/src/durability.ts +11 -4
  291. package/src/effect.ts +341 -44
  292. package/src/effector-wire.ts +160 -11
  293. package/src/fail.ts +209 -0
  294. package/src/harness-client.ts +541 -47
  295. package/src/http-effect.ts +495 -53
  296. package/src/http.ts +427 -16
  297. package/src/index.ts +60 -82
  298. package/src/instance.ts +68 -8
  299. package/src/internal.ts +84 -0
  300. package/src/machine-doc.ts +21 -3
  301. package/src/mechanism-routes.ts +168 -0
  302. package/src/model-keys.generated.ts +53 -0
  303. package/src/model-keys.ts +18 -58
  304. package/src/notices.ts +66 -13
  305. package/src/open-body.ts +159 -23
  306. package/src/open-effect.ts +81 -8
  307. package/src/open.ts +59 -6
  308. package/src/parts.ts +184 -13
  309. package/src/party.ts +68 -2
  310. package/src/pick.ts +463 -11
  311. package/src/pool.ts +247 -36
  312. package/src/registration.ts +83 -4
  313. package/src/repos.ts +2 -1
  314. package/src/run-host.ts +565 -31
  315. package/src/run-output.ts +38 -0
  316. package/src/sandbox-effect.ts +273 -38
  317. package/src/sandbox-kube.ts +3 -1
  318. package/src/secret-placement.ts +52 -17
  319. package/src/server.ts +3 -0
  320. package/src/setup.ts +239 -56
  321. package/src/size.ts +121 -9
  322. package/src/snapshot-store.ts +54 -10
  323. package/src/stub-harness.ts +44 -16
  324. package/src/timers.ts +14 -1
  325. package/src/vocabulary.ts +69 -14
  326. package/src/wire.ts +90 -16
  327. package/src/workspace.ts +697 -175
package/src/pool.ts CHANGED
@@ -18,6 +18,8 @@
18
18
  // final, distinct status (jr exit 2), never an indistinguishable idle poll;
19
19
  // - waiting — children parked (gates, agents) → the run stays open; the gates list is the
20
20
  // "what needs me" surface (jr exit 3).
21
+ // With failures and `failures: "fail"` (the default), a drained or deadlocked pool fails the run instead
22
+ // (ADR-0073), and the detail of the Failure carries the status.
21
23
  // The pool can make this call because it owns both the source and the children. Distinguishing
22
24
  // drained from deadlocked needs one more bit than `null` carries, so a Source's `next` MAY
23
25
  // resolve the richer `{ item: null, open: n }` shape; a bare `null` means drained-when-idle.
@@ -33,67 +35,135 @@ import {
33
35
  } from "xstate";
34
36
  import type { z } from "zod";
35
37
  import type { EventDef } from "@jr2/agent-protocol";
38
+ import { fail, failurePayload, isFailure, type FailureInput } from "./fail.ts";
36
39
  import { gate } from "./gate.ts";
37
40
  import { attachWrapperBody, type JR2Wrapper, type WrapperActors } from "./parts.ts";
38
41
  import { attachInputSchema, attachVocabulary } from "./vocabulary.ts";
39
42
 
43
+ // The rich shape's `open` is the one bit that separates DRAINED from DEADLOCKED when the pool goes
44
+ // idle (ADR-0017); a bare `null` means drained-when-idle.
40
45
  /**
41
- * What `next` resolves. A plain item claims it; `null` says "nothing ready now" (drained, when
42
- * nothing is running); the rich shape adds `open` — how many items exist but are not ready —
43
- * which is the one bit that separates DRAINED from DEADLOCKED when the pool goes idle.
46
+ * What the `next` of a Source resolves to: an item, to claim it; `null`, when no item is ready
47
+ * now; or `{ item, open }`, where `open` is the number of items that exist but are not ready.
48
+ * When no worker runs and `next` gives no item, the Pool ends: `drained` when `open` is 0 or
49
+ * absent, `deadlocked` when `open` is more than 0.
44
50
  */
45
51
  export type SourceNextResult<T> = T | null | { item: T | null; open: number };
46
52
 
47
- /** The Source port (ADR-0017): where a pool draws work from. Build one with {@link source}. */
53
+ // The Source port (ADR-0017).
54
+ /** Where a Pool gets its work items: the Source. Make one with {@link source}. */
48
55
  export type SourceSpec<T> = {
49
- /** Claim the next ready item, excluding the ones already being worked (`input.active`).
50
- * A union of the accepted output shapes because xstate's actor-logic type is invariant in its
51
- * output — a `fromPromise` inferred as `T | null` must still assign. */
56
+ // A union of the accepted output shapes because xstate's actor-logic type is invariant in its
57
+ // output — a `fromPromise` inferred as `T | null` must still assign.
58
+ /** Claim the next ready item. Write it as an xstate promise actor,
59
+ * `fromPromise(async ({ input }) => …)`. Its input is `{ active }`: the ids of the items that
60
+ * workers have now, which it must not give again. It must also not give again an item that a
61
+ * worker finished. It resolves to a {@link SourceNextResult}. The Pool calls it when the run
62
+ * starts, after each claim, and when a worker settles. While workers run and no item is ready, it
63
+ * also calls it on `wake` and every `pollEvery`. */
52
64
  next:
53
65
  | PromiseActorLogic<SourceNextResult<T>, { active: string[] }>
54
66
  | PromiseActorLogic<{ item: T | null; open: number }, { active: string[] }>
55
67
  | PromiseActorLogic<T | null, { active: string[] }>
56
68
  | PromiseActorLogic<null, { active: string[] }>;
57
- /** An external event def that wakes discovery early (webhook / `jr2 send` push seam). The pool
58
- * serves it as a standing gate named "source"; the def IS the pool machine's vocabulary. */
69
+ // The webhook / `jr2 send` push seam. The def IS the pool machine's vocabulary (ADR-0011).
70
+ /** An external event, made with `defineEvent`, that tells the Pool that new work is ready, for
71
+ * example from a webhook or `jr2 send`. While workers run and no item is ready, it makes the Pool
72
+ * call `next` at once. The Pool serves it as a Gate named `source` for the whole run
73
+ * (`<path>.source` when the Pool is composed in another Machine). Default: none. */
59
74
  wake?: EventDef;
60
- /** Re-query cadence while parked, in ms — for sets that mutate underneath us. */
75
+ // For sets that mutate underneath us.
76
+ /** While workers run and no item is ready, call `next` again after this many milliseconds. Use
77
+ * it for a Source whose items change while the run waits. Default: none, so the Pool calls
78
+ * `next` again only when a worker settles or `wake` comes. */
61
79
  pollEvery?: number;
62
80
  };
63
81
 
64
- /** Declare a Source. Identity by design — the value IS the spec; this is the naming/typing seam. */
82
+ // Identity by design — the value IS the spec; this is the naming/typing seam.
83
+ /**
84
+ * Declare a Source: where a `pool()` gets its work items, one at a time. It gives back the spec
85
+ * that you pass. The item type `T` types what `next` gives. `pool()` does not carry `T` to
86
+ * `itemId` and `itemInput`, so write the item type on their parameter, as the `pool()` example does.
87
+ *
88
+ * @param spec - How to claim the next item, and when to look again. See {@link SourceSpec}.
89
+ * @returns The same spec.
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * import { fromPromise } from "xstate";
94
+ * import { source } from "@jr2/orchestrator";
95
+ *
96
+ * type Issue = { id: string; title: string };
97
+ *
98
+ * const backlog: Issue[] = [
99
+ * { id: "ISSUE-1", title: "Fix the greeting." },
100
+ * { id: "ISSUE-2", title: "Add a --name flag." },
101
+ * ];
102
+ *
103
+ * export const issues = source<Issue>({
104
+ * next: fromPromise(async ({ input }: { input: { active: string[] } }) => {
105
+ * const issue = backlog.find((i) => !input.active.includes(i.id)) ?? null;
106
+ * if (issue) backlog.splice(backlog.indexOf(issue), 1);
107
+ * return issue;
108
+ * }),
109
+ * pollEvery: 60_000,
110
+ * });
111
+ * ```
112
+ *
113
+ * @see docs/guides/run-one-worker-per-item.md
114
+ * @see docs/concepts/pools-and-sources.md
115
+ * @see docs/reference/decisions/0017-pool-over-a-source-port.md
116
+ */
65
117
  export function source<T>(spec: SourceSpec<T>): SourceSpec<T> {
66
118
  return spec;
67
119
  }
68
120
 
69
- /** How a pool run ends (its machine output). `items` maps itemId → the worker's output. */
70
- export type PoolOutput = { status: "drained" | "deadlocked"; items: Record<string, unknown> };
121
+ /** How a pool run ends (its machine output). `items` maps itemId → the output of a worker that got
122
+ * to a final state. `failures` maps itemId → the Failure of a worker that failed (ADR-0073). */
123
+ export type PoolOutput = {
124
+ status: "drained" | "deadlocked";
125
+ items: Record<string, unknown>;
126
+ failures: Record<string, FailureInput>;
127
+ };
71
128
 
72
- /**
73
- * How a pool is configured (ADR-0017, ADR-0033). `TInput` is what came through the pool's door:
74
- * the PARSED schema when `input` declares one, and `unknown` when it does not — absence is
75
- * permissive, so jr2 knows nothing and says so, exactly as `workspace()`'s door-less path does.
76
- * A pool that is fed by something other than a caller may state what it is fed by annotating the
77
- * mapper's parameter; for anything a caller starts, the honest fix is to declare `input`.
78
- */
129
+ // How a pool is configured (ADR-0017, ADR-0033). `TInput` is what came through the pool's door:
130
+ // the PARSED schema when `input` declares one, and `unknown` when it does not — absence is
131
+ // permissive, so jr2 knows nothing and says so, exactly as `workspace()`'s door-less path does.
132
+ // A pool that is fed by something other than a caller may state what it is fed by annotating the
133
+ // mapper's parameter; for anything a caller starts, the honest fix is to declare `input`.
134
+ /** What you pass to `pool()`: where the items come from, how many workers run at once, and what
135
+ * each worker gets. */
79
136
  export type PoolSpec<T, TInput = unknown> = {
80
- /** The machine id (the workflow's name in the Console). Default "pool". */
137
+ /** The Machine id: the name of the Pool in the Console. Default: `"pool"`. */
81
138
  id?: string;
82
- /** The pool's OWN declared run input (ADR-0033) — what `cap` and `itemInput` read off the door,
83
- * and what types their `input`. The WORKER's schema is deliberately not the door: workers are
84
- * fed per-ITEM (the source item, or `itemInput`'s result), never the run body, so it describes
85
- * nothing a starter sends. */
139
+ // The WORKER's schema is deliberately not the door (ADR-0033): workers are fed per-ITEM (the
140
+ // source item, or `itemInput`'s result), never the run body, so it describes nothing a starter
141
+ // sends.
142
+ /** The run input of the Pool, as a zod object: what a caller sends to start a run. `cap` and
143
+ * `itemInput` read it, typed by it. A worker never gets it: each worker gets its item. Default:
144
+ * none, and then their `input` is `unknown`. */
86
145
  input?: z.ZodObject;
146
+ /** Where the items come from. Make it with {@link source}. */
87
147
  source: SourceSpec<T>;
88
- /** Stable child identity: the spawn id, the active-set entry, the outcome key. */
148
+ // Stable child identity: the spawn id, the active-set entry, the outcome key.
149
+ /** The id of an item. The Pool uses it as the id of the item's worker and in the `active` list
150
+ * that `next` gets. It is also the key of that worker's output in `items`. Give each item an id
151
+ * of its own, and the same id every time. */
89
152
  itemId: (item: T) => string;
90
- /** Max concurrent workers — a number, or derived from run input. Default 1. */
153
+ /** The most workers that run at once: a number, or a function of the run input. Default: `1`. */
91
154
  cap?: number | ((args: { input: TInput }) => number);
92
- /** Map an item (+ run input) to the worker's input. Default: the item itself. */
155
+ /** Make a worker's input from its item and the run input. Default: the item itself. */
93
156
  itemInput?: (item: T, args: { input: TInput }) => unknown;
94
- /** Terminal policy when the source runs dry and children settle. Only "final" exists today
95
- * (jr semantics: runs END); a parking policy can be added when a workflow needs one. */
157
+ // jr semantics: runs END; a parking policy can be added when a workflow needs one.
158
+ /** What the Pool does when the Source has no more items and every worker has settled. Only
159
+ * `"final"`: the run ends. Default: `"final"`. */
96
160
  onDrained?: "final";
161
+ /** What the Pool does at the end when one or more workers failed. The Pool catches each worker's
162
+ * Failure either way, so one failed item does not stop the other workers. `"fail"`: when the Pool
163
+ * ends with failures, `drained` or `deadlocked`, it fails the run with the reason
164
+ * `"<n> of <m> items failed"` and the detail `{ status, items, failures }`. `"report"`: the Pool
165
+ * ends `done`, with the failures in its output. Default: `"fail"`. */
166
+ failures?: "fail" | "report";
97
167
  };
98
168
 
99
169
  /**
@@ -102,12 +172,14 @@ export type PoolSpec<T, TInput = unknown> = {
102
172
  * TRANSPARENT to its worker (ADR-0049), so `customize(machine, { agents })` on a pool-rooted
103
173
  * workflow offers the WORKER's Agents and the composer never spells `worker`. {@link JR2Wrapper} is
104
174
  * what marks it a jr2 wrapper: the transparency follows the marker, not the slot's spelling.
175
+ *
176
+ * @internal
105
177
  */
106
178
  export type PoolMachine<TWorker extends AnyStateMachine, TInput> = StateMachine<
107
179
  any,
108
180
  any,
109
181
  any,
110
- WrapperActors<"worker", TWorker, "next" | "gate">,
182
+ WrapperActors<"worker", TWorker, "next" | "gate" | "fail">,
111
183
  any,
112
184
  any,
113
185
  any,
@@ -128,6 +200,8 @@ type PoolCtx = {
128
200
  active: string[];
129
201
  /** Settled workers' outputs, by item id — the pool's output collects them. */
130
202
  items: Record<string, unknown>;
203
+ /** Failed workers' payloads, by item id (ADR-0073). */
204
+ failures: Record<string, FailureInput>;
131
205
  status?: "drained" | "deadlocked";
132
206
  };
133
207
 
@@ -146,15 +220,90 @@ function normalizeClaim(output: unknown): Claim {
146
220
  return { item: output, open: 0 };
147
221
  }
148
222
 
223
+ // Build the pool machine (ADR-0017). Returns a plain machine — the usual workflow ROOT, but
224
+ // nestable as a child like any other. Its vocabulary is the wake def alone: the worker keeps its
225
+ // own (ADR-0011, ADR-0049).
149
226
  /**
150
- * Build the pool machine (ADR-0017). Returns a plain machine — the usual workflow ROOT
151
- * (`export const machine = pool(...)`), but nestable as a child like any other. Its vocabulary is
152
- * the wake def alone: the worker keeps its own (ADR-0011, ADR-0049).
227
+ * Make a Pool: a Machine that runs one worker Machine for each item that a Source gives, with at
228
+ * most `cap` workers at once. Use it as the root of a Workflow (`export const machine = pool(…)`),
229
+ * or compose it in another Machine.
230
+ *
231
+ * The run ends when the Source has no item ready and no worker runs. Its output is
232
+ * `{ status, items, failures }`: `status` is `"drained"` when no item is left, or `"deadlocked"`
233
+ * when items exist but none can start; `items` holds the output of each worker that got to a final
234
+ * state, by item id; `failures` holds the Failure of each worker that failed, by item id. A worker's
235
+ * Failure stops only that worker: the other workers and their Sandboxes stay. When the Pool ends
236
+ * with one or more failures, `drained` or `deadlocked`, it fails the run by default: the reason is
237
+ * `"<n> of <m> items failed"`, and the detail is `{ status, items, failures }`. With
238
+ * `failures: "report"`, it ends `done` with that output. A worker's defect ends the whole run
239
+ * `error`. While workers wait on Gates, the run stays open.
240
+ *
241
+ * `customize()` reaches through a Pool to its worker: `customize(pool, { agents })` retunes the
242
+ * worker's Agents. The events that the Pool accepts are its `wake` event only; each worker keeps
243
+ * its own.
244
+ *
245
+ * @param worker - The Machine to run for each item. It gets the item, or what `itemInput` makes
246
+ * of it, as its input. It does not get the run input.
247
+ * @param spec - The Source, the item id, the cap, and the worker's input. See {@link PoolSpec}.
248
+ * @returns The Pool, as a Machine.
249
+ *
250
+ * @example
251
+ * ```ts
252
+ * import { fromPromise } from "xstate";
253
+ * import { customize, pool, source } from "@jr2/orchestrator";
254
+ * import { task } from "@jr2/machines";
255
+ *
256
+ * type Issue = { id: string; title: string };
257
+ *
258
+ * const backlog: Issue[] = [
259
+ * { id: "ISSUE-1", title: "Fix the greeting." },
260
+ * { id: "ISSUE-2", title: "Add a --name flag." },
261
+ * ];
262
+ *
263
+ * const issues = source<Issue>({
264
+ * next: fromPromise(async ({ input }: { input: { active: string[] } }) => {
265
+ * const issue = backlog.find((i) => !input.active.includes(i.id)) ?? null;
266
+ * if (issue) backlog.splice(backlog.indexOf(issue), 1);
267
+ * return issue;
268
+ * }),
269
+ * });
270
+ *
271
+ * const fixer = customize(task, {
272
+ * repos: { app: { url: "https://github.com/you/app.git" } },
273
+ * agents: { coder: { model: "anthropic/claude-haiku-5-5" } },
274
+ * });
275
+ *
276
+ * export const machine = pool(fixer, {
277
+ * id: "issues",
278
+ * source: issues,
279
+ * itemId: (issue: Issue) => issue.id,
280
+ * cap: 2,
281
+ * itemInput: (issue: Issue) => ({ prompt: issue.title, branch: `fix/${issue.id}` }),
282
+ * });
283
+ * ```
284
+ *
285
+ * @see docs/guides/run-one-worker-per-item.md
286
+ * @see docs/concepts/pools-and-sources.md
287
+ * @see docs/reference/decisions/0017-pool-over-a-source-port.md
153
288
  */
154
289
  export function pool<TWorker extends AnyStateMachine, TSchema extends z.ZodObject>(
155
290
  worker: TWorker,
156
291
  spec: PoolSpec<any, z.infer<TSchema>> & { input: TSchema },
157
292
  ): PoolMachine<TWorker, z.infer<TSchema>>;
293
+ /**
294
+ * Make a Pool with no `input` schema. A Pool is a Machine that runs one worker Machine for each
295
+ * item that a Source gives, with at most `cap` workers at once. See {@link pool} for the full description and
296
+ * an example.
297
+ *
298
+ * @param worker - The Machine to run for each item. It gets the item, or what `itemInput` makes
299
+ * of it, as its input. It does not get the run input.
300
+ * @param spec - The Source, the item id, the cap, and the worker's input. See {@link PoolSpec}.
301
+ * @returns The Pool, as a Machine.
302
+ *
303
+ * @see docs/guides/run-one-worker-per-item.md
304
+ * @see docs/concepts/pools-and-sources.md
305
+ * @see docs/reference/decisions/0017-pool-over-a-source-port.md
306
+ */
158
307
  export function pool<TWorker extends AnyStateMachine, TInput = unknown>(
159
308
  worker: TWorker,
160
309
  spec: PoolSpec<any, TInput> & { input?: never },
@@ -166,9 +315,14 @@ export function pool(worker: AnyStateMachine, spec: PoolSpec<any, any>): AnyStat
166
315
  /** `xstate.done.actor.<childId>` → the settled worker's item id. */
167
316
  const doneChildOf = (event: unknown): string =>
168
317
  ((event as { type: string }).type.match(/^xstate\.done\.actor\.(.+)$/) as RegExpMatchArray)[1]!;
318
+ /** `xstate.error.actor.<childId>` → the failed worker's item id. */
319
+ const errorChildOf = (event: unknown): string =>
320
+ ((event as { type: string }).type.match(/^xstate\.error\.actor\.(.+)$/) as RegExpMatchArray)[1]!;
169
321
 
322
+ const report = spec.failures === "report";
323
+ const failuresOf = (context: unknown) => (context as PoolCtx).failures;
170
324
  const machine = setup({
171
- actors: { worker, next: spec.source.next, gate },
325
+ actors: { worker, next: spec.source.next, gate, fail: fail() },
172
326
  }).createMachine({
173
327
  id: spec.id ?? "pool",
174
328
  context: ({ input }): PoolCtx => ({
@@ -176,6 +330,7 @@ export function pool(worker: AnyStateMachine, spec: PoolSpec<any, any>): AnyStat
176
330
  cap: typeof spec.cap === "function" ? spec.cap({ input }) : (spec.cap ?? 1),
177
331
  active: [],
178
332
  items: {},
333
+ failures: {},
179
334
  }),
180
335
  // The wake seam: a standing gate for the whole run's life (root invokes stop only at final).
181
336
  // The invoke id IS the gate's name (the id derives from the actor path — ADR-0011):
@@ -201,6 +356,28 @@ export function pool(worker: AnyStateMachine, spec: PoolSpec<any, any>): AnyStat
201
356
  stopChild(({ event }) => doneChildOf(event)),
202
357
  ],
203
358
  },
359
+ // A worker failed (ADR-0073): keep its Failure, free its slot, stop the ref, and go look for
360
+ // more work. The Pool is the parent that catches it, so one failed item does not end the run
361
+ // and the host does not delete the Sandboxes of the other workers — a worker parked at a Gate
362
+ // keeps its work (parking is retention, ADR-0012). Only a Failure of a live worker: a defect,
363
+ // or an error of `next`, takes no transition here, so it goes up and ends the run `error`.
364
+ "xstate.error.actor.*": {
365
+ guard: ({ context, event }) =>
366
+ isFailure((event as { error?: unknown }).error) &&
367
+ (context as unknown as PoolCtx).active.includes(errorChildOf(event)),
368
+ target: ".discovering",
369
+ actions: [
370
+ assign({
371
+ failures: ({ context, event }) => ({
372
+ ...(context as unknown as PoolCtx).failures,
373
+ [errorChildOf(event)]: failurePayload((event as unknown as { error: never }).error),
374
+ }),
375
+ active: ({ context, event }) =>
376
+ (context as unknown as PoolCtx).active.filter((id) => id !== errorChildOf(event)),
377
+ }),
378
+ stopChild(({ event }) => errorChildOf(event)),
379
+ ],
380
+ },
204
381
  },
205
382
  states: {
206
383
  discovering: {
@@ -234,6 +411,22 @@ export function pool(worker: AnyStateMachine, spec: PoolSpec<any, any>): AnyStat
234
411
  // Nothing ready, workers live → healthy parking: their settling re-queries, as do
235
412
  // the wake gate and the poll timer. This is jr's exit-3 "waiting" as a state.
236
413
  { guard: ({ context }) => (context as PoolCtx).active.length > 0, target: "parked" },
414
+ // Idle with failures: the run fails (ADR-0017, ADR-0073), at a drain and at a deadlock.
415
+ // No worker is live, so the Failure deletes no sibling's Sandbox. `"report"` has no
416
+ // `failing` state and no branch to it, so the walk lists no state that fails the run. The
417
+ // branch is spread in, so xstate cannot infer the actor type of its action: `as never`
418
+ // lets the check pass.
419
+ ...(report
420
+ ? []
421
+ : [
422
+ {
423
+ guard: ({ context }: { context: unknown }) => Object.keys(failuresOf(context)).length > 0,
424
+ target: "failing",
425
+ actions: assign({
426
+ status: ({ event }) => (claimOf(event).open > 0 ? ("deadlocked" as const) : ("drained" as const)),
427
+ }) as never,
428
+ },
429
+ ]),
237
430
  // Idle + items exist that will never become ready by themselves → jr's exit 2.
238
431
  {
239
432
  guard: ({ event }) => claimOf(event).open > 0,
@@ -246,13 +439,30 @@ export function pool(worker: AnyStateMachine, spec: PoolSpec<any, any>): AnyStat
246
439
  },
247
440
  },
248
441
  saturated: {
249
- // Every slot full: only a settling worker (the root `xstate.done.actor.*`) moves us.
442
+ // Every slot full: only a settling worker (the root `xstate.done.actor.*` or a caught
443
+ // Failure) moves us.
250
444
  },
251
445
  parked: {
252
446
  // Waiting on the world: wake early on push, or re-query on the poll cadence.
253
447
  on: wake ? { [wake.name]: "discovering" } : {},
254
448
  after: spec.source.pollEvery ? { [spec.source.pollEvery]: { target: "discovering" } } : {},
255
449
  },
450
+ // Not a final state: the run ends `failed`, and the Failure says which items failed.
451
+ ...(report
452
+ ? {}
453
+ : {
454
+ failing: {
455
+ invoke: {
456
+ src: "fail" as const,
457
+ input: ({ context }: { context: unknown }) => {
458
+ const { status, items, failures } = context as PoolCtx;
459
+ const n = Object.keys(failures).length;
460
+ const m = n + Object.keys(items).length;
461
+ return { reason: `${n} of ${m} items failed`, detail: { status, items, failures } };
462
+ },
463
+ },
464
+ },
465
+ }),
256
466
  drained: { type: "final" },
257
467
  deadlocked: { type: "final" },
258
468
  },
@@ -260,6 +470,7 @@ export function pool(worker: AnyStateMachine, spec: PoolSpec<any, any>): AnyStat
260
470
  output: ({ context }): PoolOutput => ({
261
471
  status: (context as unknown as PoolCtx).status ?? "drained",
262
472
  items: (context as unknown as PoolCtx).items,
473
+ failures: (context as unknown as PoolCtx).failures,
263
474
  }),
264
475
  });
265
476
 
@@ -38,6 +38,7 @@ import { invokingMachine, vocabularyOf } from "./vocabulary.ts";
38
38
  import type { ChartText } from "./chart.ts";
39
39
  import { notTaken, senderText, takesNow, type NotTakenWriting } from "./party.ts";
40
40
  import { restOf } from "./rest.ts";
41
+ import type { RunStatus } from "./run-host.ts";
41
42
 
42
43
  /** xstate doesn't export its internal AnyActorSystem; this matches what actors receive. */
43
44
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -329,6 +330,9 @@ export function agentAddress(instanceId: string): string {
329
330
 
330
331
  export class RegistrationTable {
331
332
  private readonly byAddress = new Map<string, Registration>();
333
+ /** The same registrations by run, so a run's are read without a scan of every run's: the run host
334
+ * reads them on every microstep of the run (`cause.ts`). */
335
+ private readonly ofRun = new Map<string, Set<Registration>>();
332
336
 
333
337
  /** Register a live surface; returns its disposer (actors call it from their stop cleanup). */
334
338
  register(reg: Registration): () => void {
@@ -338,10 +342,16 @@ export class RegistrationTable {
338
342
  );
339
343
  }
340
344
  this.byAddress.set(reg.address, reg);
345
+ const ofRun = this.ofRun.get(reg.runId) ?? new Set<Registration>();
346
+ ofRun.add(reg);
347
+ this.ofRun.set(reg.runId, ofRun);
341
348
  return () => {
342
349
  // Dispose only our own entry — a re-registration under the same address must not be
343
350
  // clobbered by a stale disposer running late (dev reload, restore races).
344
351
  if (this.byAddress.get(reg.address) === reg) this.byAddress.delete(reg.address);
352
+ const runRegs = this.ofRun.get(reg.runId);
353
+ runRegs?.delete(reg);
354
+ if (runRegs?.size === 0) this.ofRun.delete(reg.runId);
345
355
  };
346
356
  }
347
357
 
@@ -349,9 +359,10 @@ export class RegistrationTable {
349
359
  return this.byAddress.get(address);
350
360
  }
351
361
 
352
- /** All live registrations for one run — the `GET /runs/:id` discovery listing. */
362
+ /** All live registrations for one run, in the order they registered — the `GET /runs/:id`
363
+ * discovery listing. */
353
364
  byRun(runId: string): Registration[] {
354
- return [...this.byAddress.values()].filter((r) => r.runId === runId);
365
+ return [...(this.ofRun.get(runId) ?? [])];
355
366
  }
356
367
 
357
368
  /**
@@ -398,10 +409,55 @@ export class RegistrationTable {
398
409
  }
399
410
  }
400
411
 
412
+ /**
413
+ * Which run dispatched a run (ADR-0071): the caller's run id, the slot of its `dispatch` Effect, and
414
+ * that Effect's ledger row id, which is the idempotency key — a start that carries a key some run
415
+ * already carries answers that run. `depth` is the dispatched run's depth in the chain, one below
416
+ * its caller, stated by the caller and recorded as sent: one meaning on the wire, on the binding
417
+ * and on the row. `instance` is set for a foreign caller, whose Orchestrator this one cannot
418
+ * verify: opaque text, never resolved.
419
+ */
420
+ export type DispatchedBy = { run: string; slot: string; effect: string; depth: number; instance?: string };
421
+
422
+ /** The header a foreign `dispatch` carries its {@link DispatchedBy} in, as JSON (ADR-0071): out of
423
+ * band, so the callee's door reserves no body field for it. */
424
+ export const DISPATCHED_BY_HEADER = "jr2-dispatched-by";
425
+
426
+ /**
427
+ * A dispatch's start as the run host answers it in-process (ADR-0071): the run, and whether the key
428
+ * found one that already existed; or the door's refusal, with the status `POST /workflows/:name/runs`
429
+ * would answer — 400 input the door refuses, 404 a name the current Assembly lacks, 409 a named
430
+ * Chart that cannot run now, 422 a Chart the door refuses — and its report. A refusal is the
431
+ * Effect's output, never thrown.
432
+ */
433
+ export type DispatchStart =
434
+ | { ok: true; runId: string; existing: boolean }
435
+ | { ok: false; status: 400 | 404 | 409 | 422; report: string[] };
436
+
401
437
  /** What a callback actor needs from its host: run identity, the table, and the host's ports. */
402
438
  export type RunBinding = {
403
439
  runId: string;
404
440
  workflow: string;
441
+ /** The name this run's Orchestrator gives itself at another Orchestrator's door (ADR-0071): the
442
+ * `instance` of a foreign dispatch's `dispatchedBy` — stable across its runs and restarts, and not
443
+ * secret, since the callee serves it on its open band. Deployed, its namespace. Never an Instance
444
+ * ID, which names an Agent's conversation and stays behind the token. Absent on a bare unit-test
445
+ * binding and an undeployed Orchestrator. */
446
+ orchestratorName?: string;
447
+ /** The run's depth in a chain of dispatches (ADR-0071): 0 for a run nothing dispatched. Absent
448
+ * on a bare unit-test binding, which reads 0. */
449
+ depth?: number;
450
+ /**
451
+ * The in-process half of a `dispatch` Effect (ADR-0071): the run host, reached directly, with no
452
+ * wire and no token — the Orchestrator is the single writer of runs. `start` answers a refusal as
453
+ * {@link DispatchStart}, never by throwing; `settled` resolves with the run's status once it is no
454
+ * longer live — at once for a run already settled, and across a restore in either order — and
455
+ * rejects when `signal` aborts. Absent outside a RunHost: a dispatch with no target faults.
456
+ */
457
+ dispatch?: {
458
+ start(workflow: string, input: Record<string, unknown>, by: DispatchedBy): Promise<DispatchStart>;
459
+ settled(runId: string, signal: AbortSignal): Promise<RunStatus>;
460
+ };
405
461
  /** The hash of the Assembly the run holds (ADR-0069): the one it started under, through
406
462
  * restarts. Absent for a run no Assembly started. */
407
463
  assembly?: string;
@@ -461,10 +517,14 @@ export type RunBinding = {
461
517
  /**
462
518
  * The notices' write half (ADR-0062): keep a notice for the next admission in scope — `to` is a
463
519
  * continued conversation's base id or a Sandbox name, as the notice's `scope` says. Persisted at
464
- * the write, like the ledgers. `source` names the event, so one event raised twice is one notice.
520
+ * the write, like the ledgers. `source` names the event, so one event raised twice is one notice;
521
+ * `older` names earlier events this one tells the same as, marked raised with it (`NoticeLedger`).
465
522
  * A burned conversation's notice is raised by `bumpEpoch` itself, in the same save.
466
523
  */
467
- raiseNotice?: (notice: Notice, to: string, source?: string) => void;
524
+ raiseNotice?: (notice: Notice, to: string, source?: string, older?: readonly string[]) => void;
525
+ /** Put back a notice an admission carried that no model heard, WITH its source (`NoticeLedger.restore`):
526
+ * a raise would drop it, its source being seen. Persisted at the write. */
527
+ restoreNotice?: (notice: Notice, to: string, source?: string) => void;
468
528
  /** Reserve the pending notices in one admission's scope. They are delivered by the
469
529
  * `recordAdmission` that ledgers it (its `delivered` ids), or released if the admission fails. */
470
530
  takeNotices?: (scope: NoticeScope) => TakenNotices;
@@ -480,6 +540,18 @@ export type RunBinding = {
480
540
  /** The held answer reached the model by another road — a re-admission's notice — so the poll's
481
541
  * copy is spent (ADR-0031). */
482
542
  heldAnswerDelivered?: (instanceId: string) => void;
543
+ /**
544
+ * Did this Turn's own pick end it (ADR-0024)? Asked by the Agent's Actor as its invocation stops,
545
+ * before its registration goes, so its sweep can say so (`cause: "pick"`) and the Harness settles
546
+ * the Submission `ended`. True exactly when the transition that stopped the invocation came from
547
+ * the Turn's own pick, by the four rules of ADR-0024 (`cause.ts`): the pick in its own send; the
548
+ * work below the Agent's state while the pick is out; a send of an actor that takes a relay from
549
+ * the pick (rule 3); and a timer armed while its Machine took such a relay (rule 4) — so a timer
550
+ * that the pick's route armed gives true. False for every other end — a timer armed before the
551
+ * pick, an outside event, another party's event, the run's CANCEL, a fault. Absent outside a RunHost: the
552
+ * sweep then says no cause.
553
+ */
554
+ turnEndedByPick?: (instanceId: string) => boolean;
483
555
  /**
484
556
  * Surface absorbed-retry telemetry on the run feed (ADR-0016): attempts are observable, but
485
557
  * as `{ child, attempt }` — state-key-class data, never iids (ADR-0014's line holds on the
@@ -509,6 +581,13 @@ export type RunBinding = {
509
581
  * process), so it says so here, before it stops the actor.
510
582
  */
511
583
  hostStopping?: boolean;
584
+ /**
585
+ * The Sandbox names that a live Workspace of this run holds now, a Workspace in `placing` too
586
+ * (ADR-0064, ADR-0073). A delete that runs after a stop leaves these alone: a parent that invokes
587
+ * the same Machine again gives its new Workspace the same invoke id, and so the same name. Absent
588
+ * outside a RunHost: no name is held.
589
+ */
590
+ heldSandboxes?: () => ReadonlySet<string>;
512
591
  /**
513
592
  * Every reply since the pick that `machine`'s Agent has outstanding, or is delivering now, oldest
514
593
  * first (ADR-0024) — what an `answer` action reads as its `chain` (ADR-0067). `seat` is the place
package/src/repos.ts CHANGED
@@ -54,7 +54,8 @@ export type RepoNodeState = {
54
54
 
55
55
  /** One `Repo` resource as the instance reports it (`GET /repos`). */
56
56
  export type RepoStatus = {
57
- /** The cache key (repo-identity.ts): the resource's name, the hostPath leaf, `/repos/<key>`. */
57
+ // repo-identity.ts makes the key.
58
+ /** The cache key: the resource's name, the hostPath leaf, `/repos/<key>`. */
58
59
  key: string;
59
60
  /** The Binding's own spelling — what the cache clones. */
60
61
  url: string;