@jr2/orchestrator 0.3.0 → 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 (354) hide show
  1. package/README.md +20 -2
  2. package/bin/server.ts +7 -4
  3. package/console/canvas.ts +27 -14
  4. package/console/components/app.ts +12 -1
  5. package/console/components/fleet.ts +52 -6
  6. package/console/components/machine-pane.ts +128 -2
  7. package/console/components/nav.ts +1 -1
  8. package/console/components/schema-form.ts +12 -4
  9. package/console/json-field.ts +23 -0
  10. package/console/main.ts +122 -39
  11. package/console/store.ts +321 -21
  12. package/console/style.css +111 -0
  13. package/docs/agents-block.md +5 -0
  14. package/docs/concepts/agents-and-turns.md +442 -0
  15. package/docs/concepts/architecture.md +230 -0
  16. package/docs/concepts/charts.md +143 -0
  17. package/docs/concepts/composition.md +357 -0
  18. package/docs/concepts/converge.md +254 -0
  19. package/docs/concepts/durable-runs.md +189 -0
  20. package/docs/concepts/effects.md +603 -0
  21. package/docs/concepts/gates.md +208 -0
  22. package/docs/concepts/machines-and-workflows.md +289 -0
  23. package/docs/concepts/pools-and-sources.md +230 -0
  24. package/docs/concepts/secrets-and-trust.md +222 -0
  25. package/docs/concepts/the-menu.md +378 -0
  26. package/docs/concepts/workspaces.md +678 -0
  27. package/docs/get-started/a-model-decides.md +563 -0
  28. package/docs/get-started/an-agent-in-a-sandbox.md +381 -0
  29. package/docs/get-started/connect-a-model.md +248 -0
  30. package/docs/get-started/create-an-instance.md +194 -0
  31. package/docs/get-started/have-your-assistant-write-a-machine.md +922 -0
  32. package/docs/get-started/install.md +126 -0
  33. package/docs/guides/add-tools-to-the-sandbox.md +295 -0
  34. package/docs/guides/answer-a-gate.md +304 -0
  35. package/docs/guides/bind-open-parts.md +223 -0
  36. package/docs/guides/cancel-a-run.md +146 -0
  37. package/docs/guides/clean-up.md +206 -0
  38. package/docs/guides/fix-a-failed-converge.md +361 -0
  39. package/docs/guides/give-the-cluster-git-credentials.md +222 -0
  40. package/docs/guides/iterate-on-a-machine-on-kind.md +190 -0
  41. package/docs/guides/let-an-agent-write-a-chart.md +226 -0
  42. package/docs/guides/look-inside-a-sandbox.md +174 -0
  43. package/docs/guides/mirror-the-kit-images.md +176 -0
  44. package/docs/guides/move-from-kind-to-a-shared-cluster.md +302 -0
  45. package/docs/guides/open-the-console.md +91 -0
  46. package/docs/guides/publish-a-machine-as-an-npm-package.md +350 -0
  47. package/docs/guides/push-and-sign-a-branch.md +486 -0
  48. package/docs/guides/run-a-chart-by-name.md +270 -0
  49. package/docs/guides/run-a-command-or-call-a-url.md +544 -0
  50. package/docs/guides/run-one-worker-per-item.md +321 -0
  51. package/docs/guides/set-an-agents-access-and-dials.md +375 -0
  52. package/docs/guides/size-a-workspace.md +204 -0
  53. package/docs/guides/start-a-run-from-a-run.md +237 -0
  54. package/docs/guides/start-a-run.md +207 -0
  55. package/docs/guides/upgrade-the-kit-version.md +240 -0
  56. package/docs/guides/use-a-hosted-provider.md +208 -0
  57. package/docs/guides/use-a-local-model.md +313 -0
  58. package/docs/guides/watch-a-run.md +326 -0
  59. package/docs/guides/write-a-workflow.md +318 -0
  60. package/docs/index.md +44 -0
  61. package/docs/llms.txt +216 -0
  62. package/docs/reference/authoring-surface/agent.md +128 -0
  63. package/docs/reference/authoring-surface/answer.md +140 -0
  64. package/docs/reference/authoring-surface/arg.md +60 -0
  65. package/docs/reference/authoring-surface/basic.md +62 -0
  66. package/docs/reference/authoring-surface/bearer.md +53 -0
  67. package/docs/reference/authoring-surface/chartschema.md +23 -0
  68. package/docs/reference/authoring-surface/chartschemafor.md +48 -0
  69. package/docs/reference/authoring-surface/command.md +191 -0
  70. package/docs/reference/authoring-surface/customize.md +64 -0
  71. package/docs/reference/authoring-surface/defineconfig.md +41 -0
  72. package/docs/reference/authoring-surface/defineevent.md +114 -0
  73. package/docs/reference/authoring-surface/dispatch.md +126 -0
  74. package/docs/reference/authoring-surface/fail.md +83 -0
  75. package/docs/reference/authoring-surface/file.md +122 -0
  76. package/docs/reference/authoring-surface/frompick.md +277 -0
  77. package/docs/reference/authoring-surface/http.md +159 -0
  78. package/docs/reference/authoring-surface/isfailure.md +58 -0
  79. package/docs/reference/authoring-surface/jr2setup.md +237 -0
  80. package/docs/reference/authoring-surface/open.md +74 -0
  81. package/docs/reference/authoring-surface/openbody.md +86 -0
  82. package/docs/reference/authoring-surface/paramsof.md +125 -0
  83. package/docs/reference/authoring-surface/pool.md +186 -0
  84. package/docs/reference/authoring-surface/push.md +112 -0
  85. package/docs/reference/authoring-surface/secret.md +118 -0
  86. package/docs/reference/authoring-surface/sentby.md +83 -0
  87. package/docs/reference/authoring-surface/source.md +51 -0
  88. package/docs/reference/authoring-surface/text.md +62 -0
  89. package/docs/reference/authoring-surface/type-agentdeclaration.md +27 -0
  90. package/docs/reference/authoring-surface/type-agentdefinition.md +80 -0
  91. package/docs/reference/authoring-surface/type-agentlogic.md +15 -0
  92. package/docs/reference/authoring-surface/type-agentslots.md +12 -0
  93. package/docs/reference/authoring-surface/type-answeraction.md +12 -0
  94. package/docs/reference/authoring-surface/type-arg.md +19 -0
  95. package/docs/reference/authoring-surface/type-argnames.md +12 -0
  96. package/docs/reference/authoring-surface/type-binding.md +62 -0
  97. package/docs/reference/authoring-surface/type-bindingof.md +12 -0
  98. package/docs/reference/authoring-surface/type-bodyacceptsdoor.md +15 -0
  99. package/docs/reference/authoring-surface/type-bodyacceptsslots.md +17 -0
  100. package/docs/reference/authoring-surface/type-ceilings.md +41 -0
  101. package/docs/reference/authoring-surface/type-chainlink.md +19 -0
  102. package/docs/reference/authoring-surface/type-chartaction.md +46 -0
  103. package/docs/reference/authoring-surface/type-chartagent.md +28 -0
  104. package/docs/reference/authoring-surface/type-chartevent.md +20 -0
  105. package/docs/reference/authoring-surface/type-chartguard.md +20 -0
  106. package/docs/reference/authoring-surface/type-chartinvoke.md +28 -0
  107. package/docs/reference/authoring-surface/type-chartjson.md +14 -0
  108. package/docs/reference/authoring-surface/type-chartstate.md +46 -0
  109. package/docs/reference/authoring-surface/type-charttext.md +40 -0
  110. package/docs/reference/authoring-surface/type-charttransition.md +22 -0
  111. package/docs/reference/authoring-surface/type-charttransitions.md +14 -0
  112. package/docs/reference/authoring-surface/type-chartvalue.md +15 -0
  113. package/docs/reference/authoring-surface/type-childat.md +12 -0
  114. package/docs/reference/authoring-surface/type-childslots.md +12 -0
  115. package/docs/reference/authoring-surface/type-commandenv.md +15 -0
  116. package/docs/reference/authoring-surface/type-commandinput.md +19 -0
  117. package/docs/reference/authoring-surface/type-commandoptions.md +78 -0
  118. package/docs/reference/authoring-surface/type-commandoutput.md +104 -0
  119. package/docs/reference/authoring-surface/type-customize.md +109 -0
  120. package/docs/reference/authoring-surface/type-dispatchdefinition.md +39 -0
  121. package/docs/reference/authoring-surface/type-dispatchinput.md +14 -0
  122. package/docs/reference/authoring-surface/type-dispatchopenparts.md +14 -0
  123. package/docs/reference/authoring-surface/type-dispatchoutput.md +65 -0
  124. package/docs/reference/authoring-surface/type-duration.md +15 -0
  125. package/docs/reference/authoring-surface/type-effectbinding.md +93 -0
  126. package/docs/reference/authoring-surface/type-effectlogic.md +12 -0
  127. package/docs/reference/authoring-surface/type-envopenparts.md +14 -0
  128. package/docs/reference/authoring-surface/type-eventaudience.md +27 -0
  129. package/docs/reference/authoring-surface/type-eventdef.md +45 -0
  130. package/docs/reference/authoring-surface/type-eventfrom.md +16 -0
  131. package/docs/reference/authoring-surface/type-eventsemantics.md +26 -0
  132. package/docs/reference/authoring-surface/type-execoutcome.md +34 -0
  133. package/docs/reference/authoring-surface/type-failslot.md +14 -0
  134. package/docs/reference/authoring-surface/type-failure.md +19 -0
  135. package/docs/reference/authoring-surface/type-failureinput.md +20 -0
  136. package/docs/reference/authoring-surface/type-filebody.md +41 -0
  137. package/docs/reference/authoring-surface/type-frominput.md +14 -0
  138. package/docs/reference/authoring-surface/type-hostinjectedinput.md +58 -0
  139. package/docs/reference/authoring-surface/type-httpdefinition.md +101 -0
  140. package/docs/reference/authoring-surface/type-httpinput.md +12 -0
  141. package/docs/reference/authoring-surface/type-httpmethod.md +14 -0
  142. package/docs/reference/authoring-surface/type-httpopenparts.md +12 -0
  143. package/docs/reference/authoring-surface/type-httpoutput.md +89 -0
  144. package/docs/reference/authoring-surface/type-httprequestparts.md +33 -0
  145. package/docs/reference/authoring-surface/type-jr2config.md +126 -0
  146. package/docs/reference/authoring-surface/type-lends.md +38 -0
  147. package/docs/reference/authoring-surface/type-logicat.md +12 -0
  148. package/docs/reference/authoring-surface/type-markedif.md +12 -0
  149. package/docs/reference/authoring-surface/type-mergedactors.md +12 -0
  150. package/docs/reference/authoring-surface/type-namedsecret.md +25 -0
  151. package/docs/reference/authoring-surface/type-noslotspec.md +26 -0
  152. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +32 -0
  153. package/docs/reference/authoring-surface/type-openbodydeclaration.md +40 -0
  154. package/docs/reference/authoring-surface/type-openbodylogic.md +14 -0
  155. package/docs/reference/authoring-surface/type-openbodyoutput.md +42 -0
  156. package/docs/reference/authoring-surface/type-openbodyslot.md +18 -0
  157. package/docs/reference/authoring-surface/type-opencommandinput.md +17 -0
  158. package/docs/reference/authoring-surface/type-openeffect.md +12 -0
  159. package/docs/reference/authoring-surface/type-openeffectslots.md +12 -0
  160. package/docs/reference/authoring-surface/type-openof.md +12 -0
  161. package/docs/reference/authoring-surface/type-opensecret.md +23 -0
  162. package/docs/reference/authoring-surface/type-opensecretmark.md +12 -0
  163. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +31 -0
  164. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +32 -0
  165. package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
  166. package/docs/reference/authoring-surface/type-poolspec.md +60 -0
  167. package/docs/reference/authoring-surface/type-pushinput.md +15 -0
  168. package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
  169. package/docs/reference/authoring-surface/type-pushoutput.md +124 -0
  170. package/docs/reference/authoring-surface/type-pushwireoutput.md +12 -0
  171. package/docs/reference/authoring-surface/type-quantity.md +14 -0
  172. package/docs/reference/authoring-surface/type-reached.md +12 -0
  173. package/docs/reference/authoring-surface/type-reposlot.md +70 -0
  174. package/docs/reference/authoring-surface/type-reposlotsof.md +16 -0
  175. package/docs/reference/authoring-surface/type-sandboxoptions.md +77 -0
  176. package/docs/reference/authoring-surface/type-secret.md +25 -0
  177. package/docs/reference/authoring-surface/type-sender.md +19 -0
  178. package/docs/reference/authoring-surface/type-sentbyguards.md +12 -0
  179. package/docs/reference/authoring-surface/type-size.md +85 -0
  180. package/docs/reference/authoring-surface/type-slothandles.md +32 -0
  181. package/docs/reference/authoring-surface/type-sourcenextresult.md +17 -0
  182. package/docs/reference/authoring-surface/type-sourcespec.md +44 -0
  183. package/docs/reference/authoring-surface/type-syncstate.md +12 -0
  184. package/docs/reference/authoring-surface/type-text.md +19 -0
  185. package/docs/reference/authoring-surface/type-textpart.md +24 -0
  186. package/docs/reference/authoring-surface/type-thinkinglevel.md +39 -0
  187. package/docs/reference/authoring-surface/type-toparameterizedobject.md +14 -0
  188. package/docs/reference/authoring-surface/type-toprovidedactor.md +14 -0
  189. package/docs/reference/authoring-surface/type-unknown.md +21 -0
  190. package/docs/reference/authoring-surface/type-usercontainer.md +77 -0
  191. package/docs/reference/authoring-surface/type-withoutemptyindex.md +20 -0
  192. package/docs/reference/authoring-surface/type-workflowevent.md +46 -0
  193. package/docs/reference/authoring-surface/type-workspaceaccess.md +16 -0
  194. package/docs/reference/authoring-surface/type-workspaced.md +94 -0
  195. package/docs/reference/authoring-surface/type-workspacehandles.md +23 -0
  196. package/docs/reference/authoring-surface/type-workspacemachine.md +29 -0
  197. package/docs/reference/authoring-surface/type-workspaceof.md +13 -0
  198. package/docs/reference/authoring-surface/type-workspaceoptions.md +39 -0
  199. package/docs/reference/authoring-surface/type-workspacespec.md +37 -0
  200. package/docs/reference/authoring-surface/workspace.md +259 -0
  201. package/docs/reference/authoring-surface.md +197 -0
  202. package/docs/reference/cli.md +433 -0
  203. package/docs/reference/config.md +756 -0
  204. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +113 -0
  205. package/docs/reference/decisions/0002-duplex-streaming-actor.md +110 -0
  206. package/docs/reference/decisions/0004-worktree-provisioning-model.md +124 -0
  207. package/docs/reference/decisions/0005-sandbox-pod-composition.md +230 -0
  208. package/docs/reference/decisions/0006-agent-control-surface.md +47 -0
  209. package/docs/reference/decisions/0007-durable-machine-state.md +109 -0
  210. package/docs/reference/decisions/0008-orchestrator-as-deployed-app.md +60 -0
  211. package/docs/reference/decisions/0009-cli-and-instance-interface.md +275 -0
  212. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +160 -0
  213. package/docs/reference/decisions/0011-workflow-defined-events.md +141 -0
  214. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +103 -0
  215. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +157 -0
  216. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +95 -0
  217. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +265 -0
  218. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +124 -0
  219. package/docs/reference/decisions/0017-pool-over-a-source-port.md +69 -0
  220. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +111 -0
  221. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +275 -0
  222. package/docs/reference/decisions/0020-private-ca-trust-is-a-named-concept.md +60 -0
  223. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +100 -0
  224. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +103 -0
  225. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +176 -0
  226. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +439 -0
  227. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +65 -0
  228. package/docs/reference/decisions/0026-a-turn-that-is-over-is-never-prompted.md +62 -0
  229. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +237 -0
  230. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +89 -0
  231. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +154 -0
  232. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +100 -0
  233. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +190 -0
  234. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +61 -0
  235. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +127 -0
  236. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +57 -0
  237. package/docs/reference/decisions/0035-a-runaway-turn-is-ended-rerolled-once-then-a-fault.md +92 -0
  238. package/docs/reference/decisions/0036-a-turn-compacts-its-own-context-mid-flight.md +138 -0
  239. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +196 -0
  240. package/docs/reference/decisions/0038-jr2-up-builds-every-image-it-deploys.md +170 -0
  241. package/docs/reference/decisions/0039-image-garbage-collects-by-reachability.md +115 -0
  242. package/docs/reference/decisions/0041-a-build-the-host-already-holds-is-not-spent-again.md +64 -0
  243. package/docs/reference/decisions/0042-ready-is-not-routable-an-admission-retries-its-connection.md +142 -0
  244. package/docs/reference/decisions/0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md +170 -0
  245. package/docs/reference/decisions/0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md +85 -0
  246. package/docs/reference/decisions/0045-the-platform-joins-the-image-address-and-the-cluster-chooses-it.md +82 -0
  247. package/docs/reference/decisions/0046-a-converge-failure-carries-its-diagnosis.md +34 -0
  248. package/docs/reference/decisions/0047-the-git-ssh-key-source-is-the-users-choice.md +47 -0
  249. package/docs/reference/decisions/0048-the-orchestrator-boots-without-its-repos.md +36 -0
  250. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +126 -0
  251. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +73 -0
  252. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +227 -0
  253. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +81 -0
  254. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +111 -0
  255. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +126 -0
  256. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +107 -0
  257. package/docs/reference/decisions/0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md +62 -0
  258. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +152 -0
  259. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +106 -0
  260. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +384 -0
  261. package/docs/reference/decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md +111 -0
  262. package/docs/reference/decisions/0061-a-memory-kill-ends-the-agents-processes-not-the-harness.md +83 -0
  263. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +212 -0
  264. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +83 -0
  265. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +79 -0
  266. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +65 -0
  267. 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
  268. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +430 -0
  269. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +464 -0
  270. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +180 -0
  271. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +323 -0
  272. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +183 -0
  273. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +144 -0
  274. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +122 -0
  275. package/docs/reference/decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md +76 -0
  276. package/docs/reference/decisions.md +83 -0
  277. package/docs/reference/glossary.md +648 -0
  278. package/docs/reference/http-api.md +1348 -0
  279. package/docs/reference/machines.md +190 -0
  280. package/docs/reference/mechanism-events.md +72 -0
  281. package/docs/why.md +206 -0
  282. package/package.json +6 -4
  283. package/src/actor.ts +976 -61
  284. package/src/agent.ts +114 -38
  285. package/src/ambient.ts +4 -1
  286. package/src/assembly-checks.ts +347 -0
  287. package/src/assembly-tar.ts +494 -0
  288. package/src/assembly.ts +721 -0
  289. package/src/cause.ts +234 -0
  290. package/src/chart.ts +2182 -0
  291. package/src/charts.ts +748 -0
  292. package/src/config.ts +520 -162
  293. package/src/content-hash.ts +105 -0
  294. package/src/custodian.lua +6 -5
  295. package/src/custodian.ts +143 -39
  296. package/src/customize.ts +388 -55
  297. package/src/dispatch-effect.ts +945 -0
  298. package/src/door-compat.ts +217 -0
  299. package/src/durability.ts +11 -4
  300. package/src/effect-checks.ts +557 -0
  301. package/src/effect.ts +1202 -0
  302. package/src/effector-wire.ts +674 -0
  303. package/src/fail.ts +209 -0
  304. package/src/fingerprint.ts +4 -3
  305. package/src/gate.ts +8 -0
  306. package/src/git-clone.ts +65 -0
  307. package/src/git-push.ts +336 -0
  308. package/src/harness-client.ts +764 -48
  309. package/src/held-secrets.ts +82 -26
  310. package/src/holds.ts +156 -0
  311. package/src/http-effect.ts +1423 -0
  312. package/src/http.ts +916 -113
  313. package/src/images.ts +47 -21
  314. package/src/index.ts +62 -43
  315. package/src/instance.ts +505 -59
  316. package/src/internal.ts +84 -0
  317. package/src/kube-client.ts +333 -0
  318. package/src/machine-doc.ts +106 -6
  319. package/src/mechanism-routes.ts +168 -0
  320. package/src/model-keys.generated.ts +53 -0
  321. package/src/model-keys.ts +18 -58
  322. package/src/mount-digest.ts +53 -0
  323. package/src/names.ts +66 -8
  324. package/src/notices.ts +141 -0
  325. package/src/open-body.ts +1580 -0
  326. package/src/open-effect.ts +217 -0
  327. package/src/open.ts +59 -6
  328. package/src/parts.ts +709 -41
  329. package/src/party.ts +534 -0
  330. package/src/pick.ts +761 -0
  331. package/src/pool.ts +248 -37
  332. package/src/registration.ts +382 -19
  333. package/src/repo-fetch.ts +60 -85
  334. package/src/repos.ts +65 -75
  335. package/src/rest.ts +133 -0
  336. package/src/run-host.ts +2383 -115
  337. package/src/run-output.ts +38 -0
  338. package/src/sandbox-effect.ts +1126 -0
  339. package/src/sandbox-effector.ts +111 -0
  340. package/src/sandbox-kube.ts +1721 -0
  341. package/src/sandbox-watch.ts +260 -0
  342. package/src/secret-placement.ts +529 -0
  343. package/src/server.ts +284 -84
  344. package/src/setup.ts +819 -117
  345. package/src/size.ts +454 -0
  346. package/src/snapshot-store.ts +115 -20
  347. package/src/stub-harness.ts +49 -16
  348. package/src/sync-client.ts +54 -0
  349. package/src/timers.ts +191 -0
  350. package/src/tokens.ts +35 -4
  351. package/src/vocabulary.ts +135 -15
  352. package/src/wire.ts +238 -10
  353. package/src/workspace.ts +1195 -338
  354. package/src/sandbox-kubectl.ts +0 -1108
@@ -1,7 +1,8 @@
1
1
  // The internal registration table (ADR-0011): one structure behind both delivery dialects.
2
- // An Agent slot and `gate` register `address → { accepted event defs, deliver closure, meta }`;
3
- // the two HTTP surfaces (`/agents/:iid/*` for the Agent's Harness, `/runs/:id/gates/*` for humans and
4
- // webhooks) are adapters over it — lookup, schema validation, delivery, discovery, and lifecycle
2
+ // An Agent slot and `gate` register `address → { accepted event defs, deliver closure, meta,
3
+ // sender }` — the sender written on every event it delivers (ADR-0015); the two HTTP surfaces
4
+ // (`/agents/:iid/*` for the Agent's Harness, `/runs/:id/gates/*` for humans and webhooks) are
5
+ // adapters over it — lookup, schema validation, delivery, discovery, and lifecycle
5
6
  // are implemented ONCE. The table is implementation structure, not vocabulary: workflows speak
6
7
  // only `defineEvent` / `agent` / `gate`.
7
8
  //
@@ -23,11 +24,21 @@
23
24
  // `resolveAccepts` reads them off the invoking Machine below and a nested Machine's names are
24
25
  // never the root's problem.
25
26
 
26
- import type { ActorSystem, AnyActorRef, AnyEventObject } from "xstate";
27
- import type { EventDef } from "@jr2/agent-protocol";
27
+ import { getStateNodes } from "xstate";
28
+ import type { ActorSystem, AnyActorRef, AnyEventObject, AnyMachineSnapshot, AnyStateMachine } from "xstate";
29
+ import { SENDER_KEY, type EventDef, type Sender } from "@jr2/agent-protocol";
28
30
  import type { AgentAdmission } from "./actor.ts";
31
+ import type { EffectMarker, EffectRow } from "./effect.ts";
32
+ import type { EffectorPlacement, EffectorTarget } from "./http-effect.ts";
33
+ import type { NoticeScope, TakenNotices } from "./notices.ts";
34
+ import type { AnswerSeat } from "./pick.ts";
35
+ import type { Notice, PickAnswer } from "./wire.ts";
29
36
  import type { SandboxPort } from "./workspace.ts";
30
37
  import { invokingMachine, vocabularyOf } from "./vocabulary.ts";
38
+ import type { ChartText } from "./chart.ts";
39
+ import { notTaken, senderText, takesNow, type NotTakenWriting } from "./party.ts";
40
+ import { restOf } from "./rest.ts";
41
+ import type { RunStatus } from "./run-host.ts";
31
42
 
32
43
  /** xstate doesn't export its internal AnyActorSystem; this matches what actors receive. */
33
44
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -45,8 +56,17 @@ export type Registration = {
45
56
  kind: "gate" | "agent";
46
57
  /** The caller-facing id within its dialect (the gate id, or the agent's instance id). */
47
58
  id: string;
48
- /** Accepted events, name→def — the validation scope AND the discovery listing. */
59
+ /** Accepted events, name→def — the validation scope AND the discovery listing. For an Agent,
60
+ * the invoking state's derived set: what its Allowed picks are narrowed from (ADR-0029). */
49
61
  defs: Map<string, EventDef>;
62
+ /**
63
+ * An Agent's MENU (ADR-0029), in its fixed order: the union of the derived sets of every state
64
+ * that invokes an Agent of this name, and the tools block the Harness presents — byte-stable for
65
+ * the conversation, so it never breaks the provider's prompt cache. A superset of {@link defs};
66
+ * a pick on it that {@link defs} lacks is refused at delivery, naming the Allowed picks. Absent
67
+ * on gates, and on an Agent registered with no Menu, which serves its `defs` as the Menu.
68
+ */
69
+ menu?: Map<string, EventDef>;
50
70
  /** Serializable caller/integration context (PR URL, title …) — rides the discovery listing. */
51
71
  meta?: Record<string, unknown>;
52
72
  /**
@@ -64,6 +84,9 @@ export type Registration = {
64
84
  sandbox?: string;
65
85
  /** Close over the invoking state's `sendBack`; delivery lands where the actor was invoked. */
66
86
  deliver: (event: DeliveredEvent) => void;
87
+ /** An Agent's: the Harness read this surface (`GET /agents/:iid/surface`), so it has the Turn in
88
+ * hand and prompts it. Until then the Turn may end with no model ever asked (ADR-0026). */
89
+ served?: () => void;
67
90
  /**
68
91
  * The Machine that invoked this actor — `self._parent`, captured at registration. The menu was
69
92
  * derived from THIS machine's transitions (ADR-0015), so it is the only snapshot whose guards
@@ -71,6 +94,22 @@ export type Registration = {
71
94
  * that have no parent to name, which is why every read of it fails open.
72
95
  */
73
96
  invoker?: AnyActorRef;
97
+ /**
98
+ * Who this registration delivers as (ADR-0015): an Agent by its slot, a Gate by its invoke id.
99
+ * The table writes it on every event it delivers (`event.sender`), never taken from the caller.
100
+ * Absent on a registration no party made (a bare unit-test entry): its events carry none.
101
+ */
102
+ sender?: Sender;
103
+ /** The invoke id of the actor that registered: what finds its state — its seat — in the
104
+ * Machine, for the party questions (`party.ts`). */
105
+ invokeId?: string;
106
+ /**
107
+ * An Agent's: the continued conversation ({@link continuedIid}) this Turn is part of — set only
108
+ * when the Turn continues one (`continue: true`). A pick whose route leaves the Agent's state is
109
+ * answered by a notice to it (ADR-0024); a fresh Turn's conversation is never heard again, so its
110
+ * answer has nowhere to go.
111
+ */
112
+ conversation?: string;
74
113
  };
75
114
 
76
115
  /**
@@ -98,6 +137,41 @@ export function wouldMove(invoker: AnyActorRef | undefined, event: AnyEventObjec
98
137
  }
99
138
  }
100
139
 
140
+ /**
141
+ * The states this exact event's transitions name as targets, by id — empty for a pick with no
142
+ * target. Read before the send and checked after it with `leftAgain`: what tells a pick that entered
143
+ * a state and left it again inside its own send (an `always` back to where it was) from one that
144
+ * stayed where it went or went nowhere (ADR-0024). Fails CLOSED: a read that cannot be made reads
145
+ * as no target, the plain receipt.
146
+ */
147
+ export function targetsOf(invoker: AnyActorRef | undefined, event: AnyEventObject): string[] {
148
+ if (!invoker) return [];
149
+ try {
150
+ const snapshot = invoker.getSnapshot() as AnyMachineSnapshot;
151
+ return (snapshot.machine as AnyStateMachine)
152
+ .getTransitionData(snapshot, event)
153
+ .flatMap((t) => (t.target ?? []).map((node) => node.id));
154
+ } catch {
155
+ return [];
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Is some state of `targets` no longer active — entered by the send and left again inside it? A
161
+ * target to the state the pick was taken in (`drafting: { on: { note: "drafting" } }`) is still
162
+ * active, so it is not. Fails CLOSED: false, the plain receipt.
163
+ */
164
+ export function leftAgain(invoker: AnyActorRef | undefined, targets: readonly string[]): boolean {
165
+ if (!invoker || targets.length === 0) return false;
166
+ try {
167
+ const snapshot = invoker.getSnapshot() as AnyMachineSnapshot;
168
+ const active = new Set(getStateNodes(snapshot.machine.root, snapshot.value).map((node) => node.id));
169
+ return targets.some((id) => !active.has(id));
170
+ } catch {
171
+ return false;
172
+ }
173
+ }
174
+
101
175
  /**
102
176
  * COULD this event move the invoking Machine, judged before the Agent has picked its arguments?
103
177
  * What the surface build can ask (ADR-0029) — there is no payload yet, so a guard reading one
@@ -138,10 +212,82 @@ export function mayMove(invoker: AnyActorRef | undefined, type: string): boolean
138
212
  return readPayload;
139
213
  }
140
214
 
215
+ /** Would the run take `type` from this registration now, payload-blind? Asked AS its sender
216
+ * ({@link takesNow}); a registration with no seat to ask from (a plain `setup()` actor tree, a bare
217
+ * test entry) falls back to {@link mayMove}. */
218
+ function takenNow(reg: Pick<Registration, "invoker" | "sender" | "invokeId">, type: string): boolean {
219
+ return takesNow(reg, type) ?? mayMove(reg.invoker, type);
220
+ }
221
+
222
+ /**
223
+ * An Agent's Allowed picks now (ADR-0029): the invoking state's derived set, narrowed to what the
224
+ * invoking Machine would take from this Agent ({@link takenNow}), in Menu order — so the list reads
225
+ * in the order the tools block does. The ONE reading of the list: the surface, every receipt, the
226
+ * nudge and the wake all ask here, so no two of them can disagree about what the Turn may pick
227
+ * (ADR-0016).
228
+ *
229
+ * EMPTY while the Agent's state is not at Rest (rest.ts, ADR-0024): something below it runs, or a
230
+ * timer holds, and every pick is refused until it is back at Rest — so the Rest rule and the
231
+ * payload-blind selection give one list, and a Turn that starts busy is not asked into a refusal.
232
+ */
233
+ export function allowedPicks(reg: Pick<Registration, "defs" | "menu" | "invoker" | "sender" | "invokeId">): string[] {
234
+ if (restOf(reg) === "busy") return [];
235
+ const ordered = new Set([...(reg.menu ?? reg.defs).keys()].filter((name) => reg.defs.has(name)));
236
+ for (const name of reg.defs.keys()) ordered.add(name);
237
+ return [...ordered].filter((name) => takenNow(reg, name));
238
+ }
239
+
240
+ /** A Gate's `acceptsNow` (ADR-0011): its accepted set, narrowed the same way as an Agent's Allowed
241
+ * picks — the names the run would take from this Gate now. */
242
+ export function acceptsNow(reg: Pick<Registration, "defs" | "invoker" | "sender" | "invokeId">): string[] {
243
+ return [...reg.defs.keys()].filter((name) => takenNow(reg, name));
244
+ }
245
+
141
246
  /** Delivery target absent (unknown address, settled run, exited state) — the one catch point. */
142
247
  export class UnknownAddressError extends Error {}
143
- /** Delivery body rejected (unaccepted name, or payload failing the named schema). */
144
- export class EventValidationError extends Error {}
248
+ /** Delivery body rejected (unaccepted name, or payload failing the named schema). An Agent's
249
+ * refusal carries its Allowed picks, read after the refusal (ADR-0029), so the Harness can say
250
+ * what the Turn may pick instead. */
251
+ export class EventValidationError extends Error {
252
+ allowed?: string[];
253
+ }
254
+
255
+ /**
256
+ * A pick that arrived while the Agent's state is not at Rest (ADR-0024): refused, `finish` included,
257
+ * and nothing is delivered — xstate would take it, and a pick its state handles would exit the child
258
+ * state and stop the work running there. `outstanding` names this Turn's pick still out, when there
259
+ * is one; without it, the state is busy with work the Turn did not pick (it started busy).
260
+ */
261
+ export class OutstandingError extends Error {
262
+ readonly outstanding?: string;
263
+ constructor(message: string, outstanding?: string) {
264
+ super(message);
265
+ this.outstanding = outstanding;
266
+ }
267
+ }
268
+
269
+ /** Refuse a caller's body that carries the sender (ADR-0013, ADR-0015): only the Orchestrator writes
270
+ * it, from the registration, so neither the Agent's wire nor the gates route takes it. */
271
+ export function refuseSenderField(payload: unknown): void {
272
+ if (payload && typeof payload === "object" && SENDER_KEY in payload) {
273
+ throw new EventValidationError(
274
+ `\`${SENDER_KEY}\` is written by the Orchestrator on every delivered event; a caller cannot send it`,
275
+ );
276
+ }
277
+ }
278
+
279
+ /**
280
+ * The run would not take this event from this sender (ADR-0015, ADR-0029): xstate's selection holds
281
+ * a writing that is another party's, or — at a Gate — nothing would move. Refused BEFORE the Machine
282
+ * sees it. `writings` names the other party's writings, empty when nothing would move.
283
+ */
284
+ export class NotTakenError extends Error {
285
+ readonly writings: readonly NotTakenWriting[];
286
+ constructor(message: string, writings: readonly NotTakenWriting[] = []) {
287
+ super(message);
288
+ this.writings = writings;
289
+ }
290
+ }
145
291
 
146
292
  /**
147
293
  * The actor path below the run's root: every id from the root's children down to `ref` itself,
@@ -164,7 +310,7 @@ export function actorPath(ref: AnyActorRef): string[] {
164
310
  * instances (two Pool children, whose ids are the items') can never meet.
165
311
  *
166
312
  * One derivation, two readers, so they cannot drift: `mintIid` builds every iid on it (a fresh
167
- * Turn adds a random suffix, a burned conversation's successor its epoch), and the Agent actor
313
+ * Turn adds a random suffix, a burned conversation's successor its epoch), and the Agent's Actor
168
314
  * names the conversation it burns with it.
169
315
  */
170
316
  export function continuedIid(machine: AnyActorRef | undefined, runId: string, agentName: string): string {
@@ -184,6 +330,9 @@ export function agentAddress(instanceId: string): string {
184
330
 
185
331
  export class RegistrationTable {
186
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>>();
187
336
 
188
337
  /** Register a live surface; returns its disposer (actors call it from their stop cleanup). */
189
338
  register(reg: Registration): () => void {
@@ -193,10 +342,16 @@ export class RegistrationTable {
193
342
  );
194
343
  }
195
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);
196
348
  return () => {
197
349
  // Dispose only our own entry — a re-registration under the same address must not be
198
350
  // clobbered by a stale disposer running late (dev reload, restore races).
199
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);
200
355
  };
201
356
  }
202
357
 
@@ -204,20 +359,24 @@ export class RegistrationTable {
204
359
  return this.byAddress.get(address);
205
360
  }
206
361
 
207
- /** 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. */
208
364
  byRun(runId: string): Registration[] {
209
- return [...this.byAddress.values()].filter((r) => r.runId === runId);
365
+ return [...(this.ofRun.get(runId) ?? [])];
210
366
  }
211
367
 
212
368
  /**
213
369
  * Validate and deliver one event to an address: the shared behavior both dialect adapters
214
370
  * call. Unknown address / unaccepted name / bad payload throw typed errors the adapters map
215
371
  * to their wire (404 / 400, MCP tool errors). The payload is parsed by the named def's
216
- * schema, so what lands in the Machine is exactly the validated shape.
372
+ * schema, so what lands in the Machine is exactly the validated shape — plus the registration's
373
+ * sender, written here and nowhere else (ADR-0015). A delivery that would run another party's
374
+ * writing throws {@link NotTakenError}.
217
375
  */
218
376
  deliver(address: string, type: string, payload: unknown): void {
219
377
  const reg = this.byAddress.get(address);
220
378
  if (!reg) throw new UnknownAddressError(`no live registration at "${address}"`);
379
+ refuseSenderField(payload);
221
380
  const def = reg.defs.get(type);
222
381
  if (!def) {
223
382
  throw new EventValidationError(
@@ -228,14 +387,80 @@ export class RegistrationTable {
228
387
  if (!parsed.success) {
229
388
  throw new EventValidationError(`invalid "${type}" payload: ${parsed.error.message}`);
230
389
  }
231
- reg.deliver({ type: def.name, ...parsed.data });
390
+ // `type` after the payload: a payload never names the event the Machine receives (ADR-0011).
391
+ const event: DeliveredEvent = {
392
+ ...parsed.data,
393
+ type: def.name,
394
+ ...(reg.sender ? { [SENDER_KEY]: reg.sender } : {}),
395
+ };
396
+ // Exact, with the real payload (ADR-0015): a delivery whose selection holds a writing that is
397
+ // not this sender's never reaches the Machine.
398
+ const others = reg.sender ? notTaken(reg, event) : [];
399
+ if (others.length) {
400
+ const owned = (w: NotTakenWriting): string =>
401
+ w.owners.length ? `, which is ${w.owners.map(senderText).join(" and ")}'s` : "";
402
+ throw new NotTakenError(
403
+ `"${type}" from ${senderText(reg.sender!)} would run the writing on ` +
404
+ `${others.map((w) => `"${w.state}"${owned(w)}`).join(" and ")}`,
405
+ others,
406
+ );
407
+ }
408
+ reg.deliver(event);
232
409
  }
233
410
  }
234
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
+
235
437
  /** What a callback actor needs from its host: run identity, the table, and the host's ports. */
236
438
  export type RunBinding = {
237
439
  runId: string;
238
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
+ };
461
+ /** The hash of the Assembly the run holds (ADR-0069): the one it started under, through
462
+ * restarts. Absent for a run no Assembly started. */
463
+ assembly?: string;
239
464
  table: RegistrationTable;
240
465
  /** The host's Sandbox backend, when it has a cluster (`workspace()` resolves it here —
241
466
  * one cluster per orchestrator instance, so it is host infrastructure like the table). */
@@ -248,18 +473,30 @@ export type RunBinding = {
248
473
  instanceHarness?: string;
249
474
  /**
250
475
  * The Harness bearer for a placement (ADR-0058): `harnessToken(signingKey, placement)`, closed
251
- * over the key by the host. The Agent actor asks it for the pod its Turn resolved to; the echo
476
+ * over the key by the host. The Agent's Actor asks it for the pod its Turn resolved to; the echo
252
477
  * attach asks it for the Workspace's. Absent (bare unit-test bindings, the stub path), no bearer
253
478
  * is sent — which only a Harness that checks nothing accepts.
254
479
  */
255
480
  harnessBearer?: (placement: string) => string;
481
+ /**
482
+ * Where an Effector placement is, and the key its wire is signed with (ADR-0068): the Instance
483
+ * Effector's Service and `effectorKey(signingKey, "instance")`, closed over by the host. An http
484
+ * Effect asks it; absent (bare unit-test bindings, an instance with no Effector), it faults.
485
+ */
486
+ effector?: (placement: EffectorPlacement) => EffectorTarget | undefined;
256
487
  /**
257
488
  * Record an Agent invocation's durable admission in the host ledger (ADR-0016): persisted
258
489
  * beside the snapshot in the same RunBlob save, keyed by iid (globally unique, so the map is
259
490
  * flat). Optional so a bare unit-test binding can omit it — then admissions simply are not
260
491
  * durable.
261
492
  */
262
- recordAdmission?: (instanceId: string, admission: AgentAdmission) => void;
493
+ recordAdmission?: (instanceId: string, admission: AgentAdmission, delivered?: readonly string[]) => void;
494
+ /**
495
+ * The admission ledger's READ half (ADR-0016): the last admission ledgered under this iid. A
496
+ * continued Turn asks it whether its conversation was ever admitted — only then can the Harness
497
+ * have lost it (ADR-0062).
498
+ */
499
+ admissionOf?: (instanceId: string) => AgentAdmission | undefined;
263
500
  /**
264
501
  * The epoch ledger's READ half (ADR-0057): the current epoch of a continued conversation,
265
502
  * keyed by its base Instance ID ({@link continuedIid}). The input mapper reads it AT MINT TIME,
@@ -273,9 +510,48 @@ export type RunBinding = {
273
510
  * it ended, and the next `continue` on that Agent mints `<conversation>/<epoch>` — a virgin
274
511
  * conversation. No fault leaves a conversation worth continuing (ADR-0035): a runaway either
275
512
  * had its reroll or holds a poisoned context, and infra and no-signal mean the Harness that
276
- * held it is unreachable. Persisted beside the snapshot with the admission ledger.
513
+ * held it is unreachable. Persisted beside the snapshot with the admission ledger. `reason` is
514
+ * the fault's: the host raises it as the next `continue`'s `conversation-new` notice (ADR-0062).
515
+ */
516
+ bumpEpoch?: (conversation: string, reason: string) => void;
517
+ /**
518
+ * The notices' write half (ADR-0062): keep a notice for the next admission in scope — `to` is a
519
+ * continued conversation's base id or a Sandbox name, as the notice's `scope` says. Persisted at
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`).
522
+ * A burned conversation's notice is raised by `bumpEpoch` itself, in the same save.
523
+ */
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;
528
+ /** Reserve the pending notices in one admission's scope. They are delivered by the
529
+ * `recordAdmission` that ledgers it (its `delivered` ids), or released if the admission fails. */
530
+ takeNotices?: (scope: NoticeScope) => TakenNotices;
531
+ releaseNotices?: (ids: readonly string[]) => void;
532
+ /**
533
+ * The pick the Turn now at this address has outstanding, or answered and not yet read (ADR-0024):
534
+ * what the Agent's Actor asks when its Harness restarted under a Turn — a Turn whose pick is still
535
+ * out, or whose answer never reached the model, is cut, not faulted (ADR-0031). Read after the host
536
+ * settles its holds, so `answered` is the Rest rule's verdict now. An earlier Turn's pick on the
537
+ * same continued address is not this Turn's: its answer went as a notice. Absent outside a RunHost.
538
+ */
539
+ outstandingOf?: (instanceId: string) => OutstandingPick | undefined;
540
+ /** The held answer reached the model by another road — a re-admission's notice — so the poll's
541
+ * copy is spent (ADR-0031). */
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.
277
553
  */
278
- bumpEpoch?: (conversation: string) => void;
554
+ turnEndedByPick?: (instanceId: string) => boolean;
279
555
  /**
280
556
  * Surface absorbed-retry telemetry on the run feed (ADR-0016): attempts are observable, but
281
557
  * as `{ child, attempt }` — state-key-class data, never iids (ADR-0014's line holds on the
@@ -287,7 +563,7 @@ export type RunBinding = {
287
563
  * settlement pick, so the run's narrative can name what a Turn hosted elsewhere decided.
288
564
  * Host-supplied; absent (bare unit-test bindings), turns leave no markers.
289
565
  */
290
- marker?: (event: TurnMarker) => void;
566
+ marker?: (event: RunMarker) => void;
291
567
  /**
292
568
  * Attach the run-narrative echo to a Workspace's Harness (ADR-0023): the host replays the
293
569
  * run's feed-so-far to `endpoint` — the Harness in `placement`'s pod, pushed with that
@@ -306,13 +582,70 @@ export type RunBinding = {
306
582
  */
307
583
  hostStopping?: boolean;
308
584
  /**
309
- * iid → the abort still in flight for it (ADR-0024). The Agent actor fills this on the way out and
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>;
591
+ /**
592
+ * Every reply since the pick that `machine`'s Agent has outstanding, or is delivering now, oldest
593
+ * first (ADR-0024) — what an `answer` action reads as its `chain` (ADR-0067). `seat` is the place
594
+ * the action runs from: an Agent's state, and then only that Agent's pick counts; or `null`, a
595
+ * transition below no Agent's state, and then `event` — the event whose transition runs the action
596
+ * — names the Agent: the sender of a pick, or the Agent whose pick started the child whose done
597
+ * or error event it is. Undefined when no such pick is being answered, when the event names an
598
+ * Agent with none, or when the event names no Agent outside a pick's own send while two Agents'
599
+ * states are active or more than one pick is outstanding: then the action cannot tell whose
600
+ * answer it shapes, and sets none.
601
+ */
602
+ chainOf?: (machine: AnyActorRef, seat: AnswerSeat, event?: AnyEventObject) => PickAnswer[] | undefined;
603
+ /** Set what that pick's answer is (ADR-0067): it replaces the default, and the last call wins.
604
+ * Kept with the hold, so a restore answers the same. */
605
+ setAnswer?: (machine: AnyActorRef, seat: AnswerSeat, value: unknown, event?: AnyEventObject) => void;
606
+ /**
607
+ * The Effect ledger's READ half (ADR-0067): one Effect's row, by its id — what a restored Effect
608
+ * reads to deliver its output, re-attach, or settle `unknown`, and never to run twice.
609
+ */
610
+ effectRow?: (effectId: string) => EffectRow | undefined;
611
+ /** Every row of the run's Effect ledger, by Effect id — what a push reads its pairs from (each
612
+ * push of its Workspace and Repo Slot that published, ADR-0067). */
613
+ effectRows?: () => Readonly<Record<string, EffectRow>>;
614
+ /**
615
+ * The Effect ledger's WRITE half (ADR-0067), in the same RunBlob as the snapshot. `"now"` (the
616
+ * default) saves AT THE WRITE — so an intent row is on disk, with the snapshot that holds its
617
+ * Effect, before the call leaves. `"with-transition"` is for a row written INSIDE a transition (an
618
+ * abort, D1): it is kept in memory for the save that carries that transition — a save now would
619
+ * read the Machine before its route — and saved after the transition if nothing else saves it.
620
+ * Optional: a bare binding keeps no ledger, and nothing it runs is durable.
621
+ */
622
+ recordEffect?: (effectId: string, row: EffectRow, save?: "now" | "with-transition") => void;
623
+ /**
624
+ * iid → the abort still in flight for it (ADR-0024). The Agent's Actor fills this on the way out and
310
625
  * waits on it before admitting, so an abort can never overtake the next turn on the same
311
626
  * instance — flue QUEUES per instance, and an abort that lost that race would settle the new
312
627
  * submission before it ran. Created on demand: the ordering must hold for ANY binding, not only
313
628
  * one the host remembered to equip.
314
629
  */
315
630
  pendingAborts?: Map<string, Promise<void>>;
631
+ /**
632
+ * A named Chart of the run's own Workflow, by its short name (ADR-0070, D-C): what an Open body
633
+ * whose input is `{ chart: { name } }` binds. The host resolves it among the Charts of the
634
+ * wrapper that is the run's Workflow (`<wrapper>` of `<wrapper>/<name>`, or the Workflow itself),
635
+ * and only for a body that wrapper invokes itself. Absent outside a RunHost: no name resolves.
636
+ */
637
+ chartNamed?: (name: string) => ChartText | undefined;
638
+ };
639
+
640
+ /** A conversation's latest outstanding pick, as the Agent's Actor reads it (ADR-0024). `answered`: the Machine
641
+ * came back to Rest, or left the Agent's state (`turnComplete`); `fetched`: the answer reached the
642
+ * model. */
643
+ export type OutstandingPick = {
644
+ pick: string;
645
+ answered: boolean;
646
+ turnComplete: boolean;
647
+ answer?: PickAnswer;
648
+ fetched: boolean;
316
649
  };
317
650
 
318
651
  /** One absorbed-retry attempt (a no-signal nudge), as the run feed carries it. */
@@ -330,6 +663,30 @@ export type TurnMarker =
330
663
  | { kind: "admission"; agent: string; endpoint: string; prompt: string }
331
664
  | { kind: "pick"; agent: string; endpoint: string; event: string; payload?: Record<string, unknown> };
332
665
 
666
+ /**
667
+ * ADR-0064's two feed lines per wait for capacity: a Workspace's Sandbox has no node — `placing`,
668
+ * with the reason (`on`) and the scheduler's or the quota's own words — and then it has one —
669
+ * `placed`, `after` milliseconds since `since`. A changed reason adds no line: `RunStatus.waiting`
670
+ * carries the latest. `child` is the Workspace's actor path below the run root, its ids joined by
671
+ * `/` (`""` for a run whose root IS the Workspace). Instance-token class like the Turn markers: the
672
+ * message can name nodes and taints, so these ride the per-run feed alone.
673
+ */
674
+ export type PlacingMarker =
675
+ | { kind: "placing"; child: string; on: "node" | "quota"; message: string; since: string }
676
+ | { kind: "placed"; child: string; since: string; after: number };
677
+
678
+ /**
679
+ * A child below an Agent's state failed while a pick of that Agent was outstanding (ADR-0024): the
680
+ * pick's answer carries no reply for it — the Agent is not told — so the operator reads it here: the
681
+ * Agent, its pick, the child (its slot, or its invoke id) and the error's message. Instance-token
682
+ * class like the Turn markers: an error message is mechanism text, as `fault` is.
683
+ */
684
+ export type ChildErrorMarker = { kind: "child-error"; agent: string; pick: string; child: string; error: string };
685
+
686
+ /** Every marker a run's mechanism puts on its per-run feed — an Effect's lines among them
687
+ * (ADR-0067): its start, its stream, its output, its abort. */
688
+ export type RunMarker = TurnMarker | PlacingMarker | ChildErrorMarker | EffectMarker;
689
+
333
690
  const bindings = new WeakMap<AnyActorSystem, RunBinding>();
334
691
 
335
692
  /** Host-side: bind a run's actor system to its identity, before the actor starts. */
@@ -349,6 +706,12 @@ export function boundEpoch(system: AnyActorSystem, conversation: string): number
349
706
  return bindings.get(system)?.epochOf?.(conversation) ?? 0;
350
707
  }
351
708
 
709
+ /** The bound run, or undefined outside a jr2 host — the SOFT read an Effect uses: with no host it
710
+ * keeps no ledger and writes no feed, and still runs. */
711
+ export function boundRun(system: AnyActorSystem): RunBinding | undefined {
712
+ return bindings.get(system);
713
+ }
714
+
352
715
  /** Actor-side: resolve the run this actor tree belongs to. Throws outside a jr2 host. */
353
716
  export function runBindingOf(system: AnyActorSystem): RunBinding {
354
717
  const binding = bindings.get(system);