@jr2/orchestrator 0.4.1 → 0.6.0

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 (361) 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 +478 -0
  10. package/docs/concepts/architecture.md +243 -0
  11. package/docs/concepts/charts.md +146 -0
  12. package/docs/concepts/composition.md +357 -0
  13. package/docs/concepts/converge.md +257 -0
  14. package/docs/concepts/durable-runs.md +189 -0
  15. package/docs/concepts/effects.md +658 -0
  16. package/docs/concepts/gates.md +208 -0
  17. package/docs/concepts/machines-and-workflows.md +298 -0
  18. package/docs/concepts/pools-and-sources.md +230 -0
  19. package/docs/concepts/secrets-and-trust.md +306 -0
  20. package/docs/concepts/the-menu.md +381 -0
  21. package/docs/concepts/workspaces.md +756 -0
  22. package/docs/get-started/a-model-decides.md +563 -0
  23. package/docs/get-started/an-agent-in-a-sandbox.md +383 -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 +993 -0
  27. package/docs/get-started/install.md +130 -0
  28. package/docs/guides/add-tools-to-the-sandbox.md +308 -0
  29. package/docs/guides/answer-a-gate.md +304 -0
  30. package/docs/guides/bind-open-parts.md +224 -0
  31. package/docs/guides/cancel-a-run.md +146 -0
  32. package/docs/guides/clean-up.md +206 -0
  33. package/docs/guides/cut-a-worktree.md +560 -0
  34. package/docs/guides/fix-a-failed-converge.md +361 -0
  35. package/docs/guides/give-the-cluster-git-credentials.md +222 -0
  36. package/docs/guides/iterate-on-a-machine-on-kind.md +190 -0
  37. package/docs/guides/let-an-agent-write-a-chart.md +230 -0
  38. package/docs/guides/look-inside-a-sandbox.md +177 -0
  39. package/docs/guides/mirror-the-kit-images.md +176 -0
  40. package/docs/guides/move-from-kind-to-a-shared-cluster.md +318 -0
  41. package/docs/guides/open-the-console.md +91 -0
  42. package/docs/guides/publish-a-machine-as-an-npm-package.md +350 -0
  43. package/docs/guides/push-and-sign-a-branch.md +532 -0
  44. package/docs/guides/run-a-chart-by-name.md +270 -0
  45. package/docs/guides/run-a-command-or-call-a-url.md +587 -0
  46. package/docs/guides/run-one-worker-per-item.md +322 -0
  47. package/docs/guides/set-an-agents-access-and-dials.md +420 -0
  48. package/docs/guides/size-a-workspace.md +217 -0
  49. package/docs/guides/start-a-run-from-a-run.md +237 -0
  50. package/docs/guides/start-a-run.md +207 -0
  51. package/docs/guides/upgrade-the-kit-version.md +240 -0
  52. package/docs/guides/use-a-hosted-provider.md +214 -0
  53. package/docs/guides/use-a-local-model.md +313 -0
  54. package/docs/guides/watch-a-run.md +349 -0
  55. package/docs/guides/write-a-workflow.md +318 -0
  56. package/docs/index.md +44 -0
  57. package/docs/llms.txt +237 -0
  58. package/docs/reference/authoring-surface/agent.md +151 -0
  59. package/docs/reference/authoring-surface/answer.md +156 -0
  60. package/docs/reference/authoring-surface/arg.md +82 -0
  61. package/docs/reference/authoring-surface/basic.md +62 -0
  62. package/docs/reference/authoring-surface/bearer.md +53 -0
  63. package/docs/reference/authoring-surface/branchname.md +15 -0
  64. package/docs/reference/authoring-surface/chartschema.md +23 -0
  65. package/docs/reference/authoring-surface/chartschemafor.md +48 -0
  66. package/docs/reference/authoring-surface/command.md +206 -0
  67. package/docs/reference/authoring-surface/customize.md +64 -0
  68. package/docs/reference/authoring-surface/defineconfig.md +41 -0
  69. package/docs/reference/authoring-surface/defineevent.md +114 -0
  70. package/docs/reference/authoring-surface/dispatch.md +126 -0
  71. package/docs/reference/authoring-surface/fail.md +96 -0
  72. package/docs/reference/authoring-surface/file.md +142 -0
  73. package/docs/reference/authoring-surface/frompick.md +277 -0
  74. package/docs/reference/authoring-surface/http.md +259 -0
  75. package/docs/reference/authoring-surface/isfailure.md +58 -0
  76. package/docs/reference/authoring-surface/isready.md +14 -0
  77. package/docs/reference/authoring-surface/jr2setup.md +237 -0
  78. package/docs/reference/authoring-surface/open.md +105 -0
  79. package/docs/reference/authoring-surface/openbody.md +86 -0
  80. package/docs/reference/authoring-surface/paramsof.md +143 -0
  81. package/docs/reference/authoring-surface/pool.md +186 -0
  82. package/docs/reference/authoring-surface/push.md +130 -0
  83. package/docs/reference/authoring-surface/readyof.md +21 -0
  84. package/docs/reference/authoring-surface/secret.md +139 -0
  85. package/docs/reference/authoring-surface/sentby.md +83 -0
  86. package/docs/reference/authoring-surface/source.md +51 -0
  87. package/docs/reference/authoring-surface/text.md +62 -0
  88. package/docs/reference/authoring-surface/type-agentdeclaration.md +27 -0
  89. package/docs/reference/authoring-surface/type-agentdefinition.md +80 -0
  90. package/docs/reference/authoring-surface/type-agentlogic.md +15 -0
  91. package/docs/reference/authoring-surface/type-agentslots.md +15 -0
  92. package/docs/reference/authoring-surface/type-answeraction.md +12 -0
  93. package/docs/reference/authoring-surface/type-anyframepath.md +14 -0
  94. package/docs/reference/authoring-surface/type-anyworktreepath.md +15 -0
  95. package/docs/reference/authoring-surface/type-arg.md +19 -0
  96. package/docs/reference/authoring-surface/type-argnames.md +12 -0
  97. package/docs/reference/authoring-surface/type-bindactors.md +14 -0
  98. package/docs/reference/authoring-surface/type-binding.md +61 -0
  99. package/docs/reference/authoring-surface/type-bindingof.md +12 -0
  100. package/docs/reference/authoring-surface/type-bodyacceptsdoor.md +15 -0
  101. package/docs/reference/authoring-surface/type-bodyacceptsslots.md +17 -0
  102. package/docs/reference/authoring-surface/type-ceilings.md +41 -0
  103. package/docs/reference/authoring-surface/type-chainlink.md +19 -0
  104. package/docs/reference/authoring-surface/type-chartaction.md +46 -0
  105. package/docs/reference/authoring-surface/type-chartagent.md +28 -0
  106. package/docs/reference/authoring-surface/type-chartevent.md +20 -0
  107. package/docs/reference/authoring-surface/type-chartguard.md +20 -0
  108. package/docs/reference/authoring-surface/type-chartinvoke.md +28 -0
  109. package/docs/reference/authoring-surface/type-chartjson.md +14 -0
  110. package/docs/reference/authoring-surface/type-chartstate.md +46 -0
  111. package/docs/reference/authoring-surface/type-charttext.md +40 -0
  112. package/docs/reference/authoring-surface/type-charttransition.md +22 -0
  113. package/docs/reference/authoring-surface/type-charttransitions.md +14 -0
  114. package/docs/reference/authoring-surface/type-chartvalue.md +15 -0
  115. package/docs/reference/authoring-surface/type-childat.md +12 -0
  116. package/docs/reference/authoring-surface/type-childslots.md +12 -0
  117. package/docs/reference/authoring-surface/type-commandenv.md +15 -0
  118. package/docs/reference/authoring-surface/type-commandinput.md +20 -0
  119. package/docs/reference/authoring-surface/type-commandoptions.md +56 -0
  120. package/docs/reference/authoring-surface/type-commandoutput.md +122 -0
  121. package/docs/reference/authoring-surface/type-customize.md +111 -0
  122. package/docs/reference/authoring-surface/type-dispatchdefinition.md +39 -0
  123. package/docs/reference/authoring-surface/type-dispatchinput.md +14 -0
  124. package/docs/reference/authoring-surface/type-dispatchopenparts.md +14 -0
  125. package/docs/reference/authoring-surface/type-dispatchoutput.md +72 -0
  126. package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
  127. package/docs/reference/authoring-surface/type-duration.md +15 -0
  128. package/docs/reference/authoring-surface/type-effectbinding.md +93 -0
  129. package/docs/reference/authoring-surface/type-effectlogic.md +12 -0
  130. package/docs/reference/authoring-surface/type-envopenparts.md +14 -0
  131. package/docs/reference/authoring-surface/type-eventaudience.md +27 -0
  132. package/docs/reference/authoring-surface/type-eventdef.md +45 -0
  133. package/docs/reference/authoring-surface/type-eventfrom.md +16 -0
  134. package/docs/reference/authoring-surface/type-eventsemantics.md +26 -0
  135. package/docs/reference/authoring-surface/type-execoutcome.md +34 -0
  136. package/docs/reference/authoring-surface/type-failslot.md +14 -0
  137. package/docs/reference/authoring-surface/type-failure.md +19 -0
  138. package/docs/reference/authoring-surface/type-failureinput.md +20 -0
  139. package/docs/reference/authoring-surface/type-filebody.md +37 -0
  140. package/docs/reference/authoring-surface/type-framepath.md +14 -0
  141. package/docs/reference/authoring-surface/type-frominput.md +14 -0
  142. package/docs/reference/authoring-surface/type-hostinjectedinput.md +56 -0
  143. package/docs/reference/authoring-surface/type-httpdefinition.md +104 -0
  144. package/docs/reference/authoring-surface/type-httpfileinput.md +20 -0
  145. package/docs/reference/authoring-surface/type-httpinput.md +12 -0
  146. package/docs/reference/authoring-surface/type-httpmethod.md +14 -0
  147. package/docs/reference/authoring-surface/type-httpopenparts.md +12 -0
  148. package/docs/reference/authoring-surface/type-httpoutput.md +95 -0
  149. package/docs/reference/authoring-surface/type-httprequestparts.md +33 -0
  150. package/docs/reference/authoring-surface/type-jr2config.md +126 -0
  151. package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
  152. package/docs/reference/authoring-surface/type-lends.md +38 -0
  153. package/docs/reference/authoring-surface/type-logicat.md +12 -0
  154. package/docs/reference/authoring-surface/type-markedif.md +12 -0
  155. package/docs/reference/authoring-surface/type-mergedactors.md +12 -0
  156. package/docs/reference/authoring-surface/type-namedsecret.md +25 -0
  157. package/docs/reference/authoring-surface/type-namestart.md +77 -0
  158. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +27 -0
  159. package/docs/reference/authoring-surface/type-notworktreekeys.md +12 -0
  160. package/docs/reference/authoring-surface/type-openbodydeclaration.md +40 -0
  161. package/docs/reference/authoring-surface/type-openbodylogic.md +14 -0
  162. package/docs/reference/authoring-surface/type-openbodyoutput.md +42 -0
  163. package/docs/reference/authoring-surface/type-openbodyslot.md +18 -0
  164. package/docs/reference/authoring-surface/type-opencommandinput.md +22 -0
  165. package/docs/reference/authoring-surface/type-openeffect.md +12 -0
  166. package/docs/reference/authoring-surface/type-openeffectslots.md +12 -0
  167. package/docs/reference/authoring-surface/type-openof.md +12 -0
  168. package/docs/reference/authoring-surface/type-opensecret.md +23 -0
  169. package/docs/reference/authoring-surface/type-opensecretmark.md +12 -0
  170. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +26 -0
  171. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +27 -0
  172. package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
  173. package/docs/reference/authoring-surface/type-poolnextinput.md +22 -0
  174. package/docs/reference/authoring-surface/type-poolspec.md +60 -0
  175. package/docs/reference/authoring-surface/type-pushinput.md +34 -0
  176. package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
  177. package/docs/reference/authoring-surface/type-pushoutput.md +149 -0
  178. package/docs/reference/authoring-surface/type-pushwireoutput.md +12 -0
  179. package/docs/reference/authoring-surface/type-quantity.md +14 -0
  180. package/docs/reference/authoring-surface/type-reached.md +12 -0
  181. package/docs/reference/authoring-surface/type-reposlot.md +70 -0
  182. package/docs/reference/authoring-surface/type-reposlotsof.md +16 -0
  183. package/docs/reference/authoring-surface/type-sandboxoptions.md +84 -0
  184. package/docs/reference/authoring-surface/type-secret.md +25 -0
  185. package/docs/reference/authoring-surface/type-sender.md +19 -0
  186. package/docs/reference/authoring-surface/type-sentbyguards.md +12 -0
  187. package/docs/reference/authoring-surface/type-size.md +85 -0
  188. package/docs/reference/authoring-surface/type-slothandles.md +24 -0
  189. package/docs/reference/authoring-surface/type-sourcenextresult.md +17 -0
  190. package/docs/reference/authoring-surface/type-sourcespec.md +44 -0
  191. package/docs/reference/authoring-surface/type-syncstate.md +12 -0
  192. package/docs/reference/authoring-surface/type-text.md +19 -0
  193. package/docs/reference/authoring-surface/type-textpart.md +24 -0
  194. package/docs/reference/authoring-surface/type-thinkinglevel.md +39 -0
  195. package/docs/reference/authoring-surface/type-toparameterizedobject.md +14 -0
  196. package/docs/reference/authoring-surface/type-toprovidedactor.md +14 -0
  197. package/docs/reference/authoring-surface/type-unknown.md +21 -0
  198. package/docs/reference/authoring-surface/type-usercontainer.md +77 -0
  199. package/docs/reference/authoring-surface/type-withoutemptyindex.md +20 -0
  200. package/docs/reference/authoring-surface/type-workflowevent.md +46 -0
  201. package/docs/reference/authoring-surface/type-workspaceaccess.md +16 -0
  202. package/docs/reference/authoring-surface/type-workspaced.md +115 -0
  203. package/docs/reference/authoring-surface/type-workspacehandles.md +22 -0
  204. package/docs/reference/authoring-surface/type-workspacemachine.md +29 -0
  205. package/docs/reference/authoring-surface/type-workspaceof.md +13 -0
  206. package/docs/reference/authoring-surface/type-workspaceoptions.md +27 -0
  207. package/docs/reference/authoring-surface/type-worktreefound.md +16 -0
  208. package/docs/reference/authoring-surface/type-worktreeinput.md +56 -0
  209. package/docs/reference/authoring-surface/type-worktreekey.md +14 -0
  210. package/docs/reference/authoring-surface/type-worktreeoptions.md +29 -0
  211. package/docs/reference/authoring-surface/type-worktreeoutput.md +45 -0
  212. package/docs/reference/authoring-surface/type-worktreepath.md +16 -0
  213. package/docs/reference/authoring-surface/type-worktreeready.md +37 -0
  214. package/docs/reference/authoring-surface/type-worktreerefused.md +21 -0
  215. package/docs/reference/authoring-surface/workspace.md +287 -0
  216. package/docs/reference/authoring-surface/worktree.md +71 -0
  217. package/docs/reference/authoring-surface.md +217 -0
  218. package/docs/reference/cli.md +433 -0
  219. package/docs/reference/config.md +765 -0
  220. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +114 -0
  221. package/docs/reference/decisions/0002-duplex-streaming-actor.md +117 -0
  222. package/docs/reference/decisions/0004-worktree-provisioning-model.md +130 -0
  223. package/docs/reference/decisions/0005-sandbox-pod-composition.md +240 -0
  224. package/docs/reference/decisions/0006-agent-control-surface.md +50 -0
  225. package/docs/reference/decisions/0007-durable-machine-state.md +109 -0
  226. package/docs/reference/decisions/0008-orchestrator-as-deployed-app.md +60 -0
  227. package/docs/reference/decisions/0009-cli-and-instance-interface.md +275 -0
  228. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +160 -0
  229. package/docs/reference/decisions/0011-workflow-defined-events.md +141 -0
  230. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +103 -0
  231. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +175 -0
  232. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +95 -0
  233. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +271 -0
  234. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +126 -0
  235. package/docs/reference/decisions/0017-pool-over-a-source-port.md +71 -0
  236. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +111 -0
  237. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +287 -0
  238. package/docs/reference/decisions/0020-private-ca-trust-is-a-named-concept.md +60 -0
  239. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +101 -0
  240. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +103 -0
  241. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +175 -0
  242. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +436 -0
  243. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +65 -0
  244. package/docs/reference/decisions/0026-a-turn-that-is-over-is-never-prompted.md +62 -0
  245. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +245 -0
  246. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +90 -0
  247. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +154 -0
  248. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +100 -0
  249. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +193 -0
  250. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +61 -0
  251. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +126 -0
  252. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +57 -0
  253. package/docs/reference/decisions/0035-a-runaway-turn-is-ended-rerolled-once-then-a-fault.md +92 -0
  254. package/docs/reference/decisions/0036-a-turn-compacts-its-own-context-mid-flight.md +138 -0
  255. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +196 -0
  256. package/docs/reference/decisions/0038-jr2-up-builds-every-image-it-deploys.md +170 -0
  257. package/docs/reference/decisions/0039-image-garbage-collects-by-reachability.md +115 -0
  258. package/docs/reference/decisions/0041-a-build-the-host-already-holds-is-not-spent-again.md +64 -0
  259. package/docs/reference/decisions/0042-ready-is-not-routable-an-admission-retries-its-connection.md +142 -0
  260. package/docs/reference/decisions/0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md +170 -0
  261. package/docs/reference/decisions/0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md +85 -0
  262. package/docs/reference/decisions/0045-the-platform-joins-the-image-address-and-the-cluster-chooses-it.md +82 -0
  263. package/docs/reference/decisions/0046-a-converge-failure-carries-its-diagnosis.md +34 -0
  264. package/docs/reference/decisions/0047-the-git-ssh-key-source-is-the-users-choice.md +47 -0
  265. package/docs/reference/decisions/0048-the-orchestrator-boots-without-its-repos.md +36 -0
  266. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +126 -0
  267. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +73 -0
  268. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +238 -0
  269. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +86 -0
  270. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +123 -0
  271. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +126 -0
  272. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +107 -0
  273. package/docs/reference/decisions/0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md +62 -0
  274. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +152 -0
  275. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +108 -0
  276. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +427 -0
  277. package/docs/reference/decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md +111 -0
  278. package/docs/reference/decisions/0061-a-memory-kill-ends-the-agents-processes-not-the-harness.md +83 -0
  279. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +239 -0
  280. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +82 -0
  281. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +79 -0
  282. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +65 -0
  283. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +552 -0
  284. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +458 -0
  285. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +475 -0
  286. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +180 -0
  287. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +334 -0
  288. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +183 -0
  289. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +144 -0
  290. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +122 -0
  291. package/docs/reference/decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md +76 -0
  292. package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +295 -0
  293. package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +320 -0
  294. package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
  295. package/docs/reference/decisions.md +86 -0
  296. package/docs/reference/glossary.md +687 -0
  297. package/docs/reference/http-api.md +1386 -0
  298. package/docs/reference/machines.md +189 -0
  299. package/docs/reference/mechanism-events.md +72 -0
  300. package/docs/why.md +207 -0
  301. package/package.json +5 -3
  302. package/src/actor.ts +694 -106
  303. package/src/agent.ts +114 -38
  304. package/src/ambient.ts +3 -4
  305. package/src/assembly-checks.ts +189 -7
  306. package/src/cause.ts +234 -0
  307. package/src/chart.ts +345 -46
  308. package/src/charts.ts +43 -8
  309. package/src/config.ts +440 -178
  310. package/src/custodian.lua +17 -5
  311. package/src/custodian.ts +325 -99
  312. package/src/customize.ts +199 -49
  313. package/src/dispatch-effect.ts +945 -0
  314. package/src/door-compat.ts +217 -0
  315. package/src/durability.ts +11 -4
  316. package/src/effect-checks.ts +50 -3
  317. package/src/effect.ts +641 -128
  318. package/src/effector-wire.ts +165 -13
  319. package/src/fail.ts +222 -0
  320. package/src/harness-client.ts +587 -50
  321. package/src/harness-env.ts +33 -0
  322. package/src/held-secrets.ts +125 -16
  323. package/src/holds.ts +3 -0
  324. package/src/http-effect.ts +556 -58
  325. package/src/http.ts +480 -30
  326. package/src/index.ts +74 -82
  327. package/src/instance.ts +68 -8
  328. package/src/internal.ts +85 -0
  329. package/src/kube-client.ts +6 -3
  330. package/src/machine-doc.ts +24 -7
  331. package/src/mechanism-routes.ts +168 -0
  332. package/src/model-keys.generated.ts +53 -0
  333. package/src/model-keys.ts +42 -58
  334. package/src/names.ts +19 -6
  335. package/src/notices.ts +483 -41
  336. package/src/open-body.ts +242 -40
  337. package/src/open-effect.ts +81 -8
  338. package/src/open.ts +90 -6
  339. package/src/parts.ts +213 -30
  340. package/src/party.ts +101 -2
  341. package/src/pick.ts +497 -11
  342. package/src/pool.ts +282 -50
  343. package/src/registration.ts +133 -12
  344. package/src/repo-identity.ts +7 -3
  345. package/src/repos.ts +22 -3
  346. package/src/run-host.ts +665 -45
  347. package/src/run-output.ts +38 -0
  348. package/src/sandbox-effect.ts +457 -71
  349. package/src/sandbox-kube.ts +66 -17
  350. package/src/secret-placement.ts +59 -17
  351. package/src/server.ts +3 -0
  352. package/src/setup.ts +325 -62
  353. package/src/size.ts +121 -9
  354. package/src/snapshot-store.ts +54 -10
  355. package/src/stub-harness.ts +44 -16
  356. package/src/timers.ts +14 -1
  357. package/src/tokens.ts +107 -32
  358. package/src/vocabulary.ts +88 -14
  359. package/src/wire.ts +100 -26
  360. package/src/workspace.ts +771 -238
  361. package/src/worktree.ts +249 -0
package/src/open-body.ts CHANGED
@@ -24,6 +24,7 @@ import {
24
24
  type UnknownActorLogic,
25
25
  } from "xstate";
26
26
  import { isAgent, type AgentDefinition, type WorkspaceAccess } from "./agent.ts";
27
+ import { ambientHandlesFor } from "./ambient.ts";
27
28
  import {
28
29
  admitChart,
29
30
  chartDigest,
@@ -31,8 +32,10 @@ import {
31
32
  ChartRefusal,
32
33
  GENERIC_ACTIONS,
33
34
  pathSegments,
35
+ READY_GUARD,
34
36
  resolveSource,
35
37
  resolveReferences,
38
+ slotFolderFault,
36
39
  type AdmittedChart,
37
40
  type ChartAction,
38
41
  type ChartGuard,
@@ -43,28 +46,33 @@ import {
43
46
  type ChartTransition,
44
47
  type ChartTransitions,
45
48
  } from "./chart.ts";
46
- import { inputKeysOf, isEffect, type Duration } from "./effect.ts";
49
+ import { inputShapeOf, isEffect, type Duration, type InputShape } from "./effect.ts";
50
+ import { fail, FAILURE_GUARD, failureGuard } from "./fail.ts";
47
51
  import { agent } from "./harness-client.ts";
48
52
  import { SENT_BY } from "./party.ts";
49
- import { wrapperBodyOf } from "./parts.ts";
53
+ import { composesSandbox, sandboxPartsOf, wrapperBodyOf } from "./parts.ts";
54
+ import { open } from "./open.ts";
50
55
  import { actorPath, boundRun } from "./registration.ts";
51
- import { jr2Setup } from "./setup.ts";
56
+ import { COMPILED_CHART, jr2Setup } from "./setup.ts";
52
57
  import { attachBoundMachine, boundMachineFor } from "./vocabulary.ts";
53
58
 
59
+ // ADR-0070: "counters are the Open body's".
54
60
  /**
55
- * The ceilings an Open body sets on the Chart that binds it (ADR-0070: "counters are the Open
56
- * body's"). Each is a positive bound; reaching one settles the body with a `ceiling` output.
61
+ * The limits that an Open body sets on its Chart. Each one is a positive bound. When the Chart gets
62
+ * to one, the Open body stops it and ends with `{ outcome: "ceiling", ceiling, detail }`.
57
63
  */
58
64
  export type Ceilings = {
59
- /** The Agent Turns started under the body, at any depth: a lent Machine's Turns count too. */
65
+ /** The Agent Turns that start under the Open body, at all levels. The Turns of a lent Machine
66
+ * count too. */
60
67
  turns: number;
61
- /** The wall clock from the body's start. */
68
+ /** The time from the start of the Open body, such as `"30m"`. At most about 596 hours. */
62
69
  wallClock: Duration;
63
- /** The microsteps the body takes, over the whole run: a budget, since xstate has no loop guard. */
70
+ /** The transitions of the Chart in the whole run. xstate has no guard against a loop, so this is
71
+ * the guard. */
64
72
  transitions: number;
65
- /** The children live under the body at once. */
73
+ /** The children that run under the Open body at one time. */
66
74
  fanOut: number;
67
- /** The Chart's canonical JSON, in bytes: the door's own limit. At most {@link MAX_CHART_SIZE}. */
75
+ /** The size of the Chart, as JSON, in bytes. At most 1048576 (1 MiB). */
68
76
  size: number;
69
77
  };
70
78
 
@@ -106,8 +114,11 @@ export function jsonOver(value: unknown, cap: number): boolean {
106
114
  return false;
107
115
  }
108
116
 
109
- /** The kinds of part an Open body lends, each as a list of the wrapper's own slot names; and the
110
- * kit's Gate, lent whole (`gate: true`), since there is one and it has no name to list. */
117
+ /** The parts of the Chart wrapper that a Chart can use, by their keys in `actors`, `guards`,
118
+ * `actions` and `delays`. `gate: true` lends the Gate, so that a Chart can wait for an answer from
119
+ * outside the run. `fail: true` lends the Part that fails the run, so that a Chart can end its run
120
+ * as a Failure (ADR-0073): the Machine decides which ends a Chart can choose. A Chart can use no
121
+ * other part. */
111
122
  export type Lends = {
112
123
  agents?: string[];
113
124
  effects?: string[];
@@ -116,13 +127,23 @@ export type Lends = {
116
127
  actions?: string[];
117
128
  delays?: string[];
118
129
  gate?: boolean;
130
+ fail?: boolean;
119
131
  };
120
132
 
121
- /** What an Open body states (ADR-0070): the parts it lends, what a Chart may define, the ceilings. */
133
+ /**
134
+ * What an Open body declares: the parts that it lends a Chart, what the Agents of a Chart can
135
+ * have, and the limits of a Chart. Give it to {@link openBody}, and to `chartSchemaFor` for the
136
+ * pick of a planner.
137
+ *
138
+ * @see docs/concepts/charts.md
139
+ */
122
140
  export type OpenBodyDeclaration = {
141
+ /** The parts that a Chart can use. */
123
142
  lends: Lends;
124
- /** What a Chart's own Agents may have: a model from `models`, a Workspace access from `workspace`. */
143
+ /** What the Agents that a Chart adds can have: a model from `models`, and a Workspace access
144
+ * from `workspace`. With no model, a Chart cannot add an Agent. */
125
145
  allows: { models: string[]; workspace: WorkspaceAccess[] };
146
+ /** The limits of one Chart. */
126
147
  ceilings: Ceilings;
127
148
  };
128
149
 
@@ -151,6 +172,8 @@ const reservedName = (kind: (typeof LEND_KINDS)[number], v: string): string | un
151
172
  if (v.includes(".")) return `"${v}" holds a dot; a lent name is a slot key, and dotted names are jr2's and xstate's`;
152
173
  if ((kind === "agents" || kind === "effects" || kind === "machines") && v === "gate")
153
174
  return `"gate" is the kit's Gate; lend a slot of another name`;
175
+ if ((kind === "agents" || kind === "effects" || kind === "machines") && v === "fail")
176
+ return `"fail" is the kit's Part that fails the run, lent as \`fail: true\`; lend a slot of another name`;
154
177
  if (kind === "actions" && (GENERIC_ACTIONS as readonly string[]).includes(v))
155
178
  return `"${v}" is a generic action every Chart has; lend an action of another name`;
156
179
  if (kind === "delays" && /^(0|[1-9][0-9]*)$/.test(v))
@@ -201,8 +224,12 @@ export function declarationFaults(decl: unknown): string[] {
201
224
  if (typeof (lends as { gate?: unknown }).gate !== "boolean") {
202
225
  faults.push("lends.gate: the kit's Gate is lent whole: true or false");
203
226
  }
227
+ } else if (key === "fail") {
228
+ if (typeof (lends as { fail?: unknown }).fail !== "boolean") {
229
+ faults.push("lends.fail: the kit's Part that fails the run is lent whole: true or false");
230
+ }
204
231
  } else if (!(LEND_KINDS as readonly string[]).includes(key)) {
205
- faults.push(`lends.${key}: an Open body lends ${LEND_KINDS.join(", ")} and gate only`);
232
+ faults.push(`lends.${key}: an Open body lends ${LEND_KINDS.join(", ")}, gate and fail only`);
206
233
  }
207
234
  }
208
235
  for (const kind of LEND_KINDS) {
@@ -261,6 +288,7 @@ function frozen(decl: OpenBodyDeclaration): OpenBodyDeclaration {
261
288
  if (v !== undefined) lends[kind] = Object.freeze([...v]) as string[];
262
289
  }
263
290
  if (decl.lends.gate !== undefined) lends.gate = decl.lends.gate;
291
+ if (decl.lends.fail !== undefined) lends.fail = decl.lends.fail;
264
292
  return Object.freeze({
265
293
  lends: Object.freeze(lends),
266
294
  allows: Object.freeze({
@@ -271,11 +299,78 @@ function frozen(decl: OpenBodyDeclaration): OpenBodyDeclaration {
271
299
  });
272
300
  }
273
301
 
302
+ // It validates the declaration now, so a bad one fails where the Machine is written, and returns
303
+ // actor logic branded with it. `jr2Setup` resolves every lent name against its own `actors`,
304
+ // `guards`, `actions` and `delays` at createMachine, and stamps the result with `attachLent`
305
+ // (ADR-0070).
274
306
  /**
275
- * Declare an Open body (ADR-0070). It validates the declaration now, so a bad one fails where the
276
- * Machine is written, and returns actor logic branded with it. `jr2Setup` resolves every lent name
277
- * against its own `actors`, `guards`, `actions` and `delays` at createMachine, and stamps the
278
- * result with {@link attachLent}.
307
+ * Make an Open body: the actor slot that a Chart binds. Put it in `actors`, and invoke it from a
308
+ * state with `{ chart, ...input }`. `chart` is the text of a Chart, or `{ name }` for a named
309
+ * Chart of this wrapper. The other keys are the input of the Chart, which it reads as
310
+ * `{{input.<key>}}`. A Machine with an Open body is a Chart wrapper.
311
+ *
312
+ * Each Agent Turn of the Chart runs where the Open body runs: in the Sandbox of its `workspace()`,
313
+ * or on the Instance Harness. The Open body ends with an {@link OpenBodyOutput}.
314
+ *
315
+ * It refuses a declaration that is not correct: `openBody: <problem>; <problem>`, for example
316
+ * `ceilings.turns: missing; an Open body states every ceiling`. `createMachine` refuses a lent
317
+ * name that the Machine does not have: `machine "<id>": Open body "<slot>": lends.agents: "<name>"
318
+ * is no actor slot of this Machine; its actors: <slots>`.
319
+ *
320
+ * @param decl - What a Chart can use, and its limits (see {@link OpenBodyDeclaration}).
321
+ * @returns The actor logic of the slot.
322
+ *
323
+ * @example
324
+ * ```ts
325
+ * import { assign } from "xstate";
326
+ * import { z } from "zod";
327
+ * import { agent, chartSchema, fail, jr2Setup, openBody, type OpenBodyOutput } from "@jr2/orchestrator";
328
+ *
329
+ * // `chart` is a Chart, so a run input can carry one, and Charts can have names.
330
+ * const door = z.object({ topic: z.string(), chart: chartSchema });
331
+ * type Input = z.infer<typeof door>;
332
+ * type Context = Input & { result?: OpenBodyOutput };
333
+ *
334
+ * export const machine = jr2Setup({
335
+ * types: {} as { context: Context; input: Input },
336
+ * actors: {
337
+ * writer: agent({
338
+ * model: "anthropic/claude-haiku-5-5",
339
+ * workspace: "none",
340
+ * instructions: "You write clear, short text for engineers.",
341
+ * }),
342
+ * body: openBody({
343
+ * lends: { agents: ["writer"] },
344
+ * allows: { models: [], workspace: [] },
345
+ * ceilings: { turns: 6, wallClock: "15m", transitions: 200, fanOut: 1, size: 65536 },
346
+ * }),
347
+ * fail: fail(),
348
+ * },
349
+ * }).createMachine({
350
+ * id: "notes",
351
+ * input: door,
352
+ * context: ({ input }) => ({ ...input }),
353
+ * // The fault of `writer` when a Chart does not route it: the Open body sends it here.
354
+ * on: { "agent.fault": ".failed" },
355
+ * initial: "running",
356
+ * states: {
357
+ * running: {
358
+ * invoke: {
359
+ * src: "body",
360
+ * input: ({ context }: { context: Context }) => ({ chart: context.chart, topic: context.topic }),
361
+ * onDone: { target: "done", actions: assign({ result: ({ event }) => event.output as OpenBodyOutput }) },
362
+ * },
363
+ * },
364
+ * done: { type: "final" },
365
+ * failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
366
+ * },
367
+ * });
368
+ * ```
369
+ *
370
+ * @see docs/concepts/charts.md
371
+ * @see docs/guides/run-a-chart-by-name.md
372
+ * @see docs/guides/let-an-agent-write-a-chart.md
373
+ * @see docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md
279
374
  */
280
375
  export function openBody(decl: OpenBodyDeclaration): OpenBodyLogic {
281
376
  return openBodyWith(decl);
@@ -358,7 +453,16 @@ export function lentOf(logic: OpenBodyLogic): LentParts | undefined {
358
453
  /** What an Open body is invoked with: the Chart, by text or by name, and its `input` (the rest). */
359
454
  export type OpenBodyInput = { chart: ChartText | { name: string } } & Record<string, unknown>;
360
455
 
361
- /** How an Open body settles: the Chart's output, the door's refusal, or the ceiling it reached. */
456
+ /**
457
+ * The output of an Open body. Route on `outcome` in the `onDone` of the state that invokes it:
458
+ *
459
+ * - `done`: the Chart got to a final state. `output` is the output of the Chart.
460
+ * - `refused`: the door refused the Chart. `report` has one line for each problem. Give it to the
461
+ * Agent that wrote the Chart, so that it can send a corrected one.
462
+ * - `ceiling`: the Chart got to a ceiling, and the Open body stopped it. `ceiling` names it.
463
+ *
464
+ * @see docs/concepts/charts.md
465
+ */
362
466
  export type OpenBodyOutput =
363
467
  | { outcome: "done"; output: unknown }
364
468
  | { outcome: "refused"; report: string[] }
@@ -473,9 +577,12 @@ export function admitBodyChart(
473
577
  text: unknown,
474
578
  decl: OpenBodyDeclaration,
475
579
  lent: LentParts,
476
- options: { inputKeys?: readonly string[] } = {},
580
+ options: { inputKeys?: readonly string[]; repoSlots?: readonly string[] } = {},
477
581
  ): AdmittedChart {
478
- const admitted = admitChart(text, decl, lent, options.inputKeys ? { inputKeys: options.inputKeys } : {});
582
+ const admitted = admitChart(text, decl, lent, {
583
+ ...(options.inputKeys ? { inputKeys: options.inputKeys } : {}),
584
+ ...(options.repoSlots ? { repoSlots: options.repoSlots } : {}),
585
+ });
479
586
  try {
480
587
  compileChart(admitted, lent, decl);
481
588
  } catch (err) {
@@ -527,11 +634,23 @@ export function admitOpenBodyChart(
527
634
  const logic = (inner.implementations.actors as Record<string, unknown>)[slot];
528
635
  if (!isOpenBody(logic)) throw new Error(`machine "${machine.id}" has no Open body "${slot}"`);
529
636
  const decl = openBodyDeclarationOf(logic);
637
+ const repoSlots = repoSlotsAround(machine);
530
638
  return admitBodyChart(text, decl, lentIn(inner.implementations as Implementations, decl), {
531
639
  ...(inputKeys ? { inputKeys } : {}),
640
+ ...(repoSlots ? { repoSlots } : {}),
532
641
  });
533
642
  }
534
643
 
644
+ /** The Repo Slots of the `workspace()` around a Chart wrapper, where the door can see one: the
645
+ * wrapper IS the `workspace()`. Undefined: an Open map, or a Workspace that a parent state invokes,
646
+ * whose slots this door cannot read. Then the Open body's own door reads them off the running
647
+ * Workspace, and a Turn's or an Effect's start refuses what no door could see. */
648
+ export function repoSlotsAround(machine: AnyStateMachine): readonly string[] | undefined {
649
+ if (!composesSandbox(machine)) return undefined;
650
+ const { repos } = sandboxPartsOf(machine);
651
+ return repos === open ? undefined : Object.keys(repos);
652
+ }
653
+
535
654
  /**
536
655
  * Admit a Chart field of a Chart wrapper's door (ADR-0070), the door a run's start runs before the
537
656
  * run exists. The door cannot tell which Open body a field feeds, so the Chart is admitted against
@@ -548,7 +667,10 @@ export function admitWrapperChart(machine: AnyStateMachine, text: unknown): Admi
548
667
  for (const { slot, logic } of bodies) {
549
668
  const decl = openBodyDeclarationOf(logic);
550
669
  try {
551
- return admitBodyChart(text, decl, lentIn(inner.implementations as Implementations, decl));
670
+ const repoSlots = repoSlotsAround(machine);
671
+ return admitBodyChart(text, decl, lentIn(inner.implementations as Implementations, decl), {
672
+ ...(repoSlots ? { repoSlots } : {}),
673
+ });
552
674
  } catch (err) {
553
675
  if (!(err instanceof ChartRefusal)) throw err;
554
676
  report.push(...err.report.map((line) => (bodies.length > 1 ? `body "${slot}": ${line}` : line)));
@@ -575,7 +697,7 @@ const scopeOf = (args: Args) => ({
575
697
  input: chartInputOf((args.context?.[CHART_INPUT_KEY] as Record<string, unknown> | undefined) ?? {}),
576
698
  });
577
699
 
578
- /** An invoke's error whose message jr2 wrote for a Chart to read: a lent Effect's input fault. */
700
+ /** An invoke's error whose message jr2 wrote for a Chart to read: a fault in an input the door could not see. */
579
701
  class ChartInvokeFault extends Error {}
580
702
 
581
703
  /**
@@ -739,6 +861,10 @@ function transitionsOf(ts: ChartTransitions): unknown {
739
861
  return Array.isArray(ts) ? ts.map(transitionOf) : transitionOf(ts);
740
862
  }
741
863
 
864
+ /** `jr2.ready`: is this cut's output `ready`? On an `onDone`. */
865
+ const readyGuard = ({ event }: { event: unknown }): boolean =>
866
+ (event as { output?: { outcome?: unknown } } | undefined)?.output?.outcome === "ready";
867
+
742
868
  const mapValues = <T, U>(o: Record<string, T>, fn: (v: T) => U): Record<string, U> =>
743
869
  Object.fromEntries(Object.entries(o).map(([k, v]) => [k, fn(v)]));
744
870
 
@@ -747,15 +873,17 @@ const mapValues = <T, U>(o: Record<string, T>, fn: (v: T) => U): Record<string,
747
873
  * resolved object again at invoke time"), or undefined when it is. The door checked the literal
748
874
  * parts; a reference may still resolve to a value that is no string.
749
875
  */
750
- function effectInputFault(keys: readonly string[], value: unknown): string | undefined {
876
+ function effectInputFault(shape: InputShape, value: unknown): string | undefined {
751
877
  const given = typeof value === "object" && value !== null && !Array.isArray(value) ? Object.keys(value) : undefined;
878
+ const known = [...shape.required, ...shape.optional, ...(shape.oneOf ?? [])];
752
879
  const ok =
753
880
  given !== undefined &&
754
- given.length === keys.length &&
755
- keys.every((k) => typeof (value as Record<string, unknown>)[k] === "string");
881
+ shape.required.every((k) => given.includes(k)) &&
882
+ given.every((k) => known.includes(k) && typeof (value as Record<string, unknown>)[k] === "string") &&
883
+ (shape.oneOf === undefined || shape.oneOf.filter((k) => given.includes(k)).length === 1);
756
884
  return ok
757
885
  ? undefined
758
- : `its input resolved to ${JSON.stringify(value)}; it takes exactly ${keys.join(", ") || "nothing"}, each a string (ADR-0070)`;
886
+ : `its input resolved to ${JSON.stringify(value)}; it takes ${known.join(", ") || "nothing"}, each a string (ADR-0070)`;
759
887
  }
760
888
 
761
889
  const checkedEffects = new WeakMap<object, UnknownActorLogic>();
@@ -767,13 +895,15 @@ const checkedEffects = new WeakMap<object, UnknownActorLogic>();
767
895
  */
768
896
  function checkedEffect(slot: string, logic: UnknownActorLogic): UnknownActorLogic {
769
897
  if (!isEffect(logic)) return logic;
770
- const keys = inputKeysOf(logic.effect);
771
- if (keys === undefined) return logic;
898
+ const shape = inputShapeOf(logic.effect);
899
+ if (shape === undefined) return logic;
772
900
  const hit = checkedEffects.get(logic);
773
901
  if (hit) return hit;
774
902
  const checked = Object.create(logic) as UnknownActorLogic;
775
903
  checked.getInitialSnapshot = (scope, input) => {
776
- const fault = effectInputFault(keys, input);
904
+ // An Effect that takes nothing, invoked with no input, is a whole input: the door let it through.
905
+ const fault =
906
+ input === undefined && shape.required.length === 0 && !shape.oneOf ? undefined : effectInputFault(shape, input);
777
907
  if (fault === undefined) return logic.getInitialSnapshot(scope, input);
778
908
  return {
779
909
  status: "error",
@@ -785,9 +915,38 @@ function checkedEffect(slot: string, logic: UnknownActorLogic): UnknownActorLogi
785
915
  return checked;
786
916
  }
787
917
 
788
- /** The lent actors as a Chart invokes them: each Effect checked at its start (see {@link checkedEffect}). */
918
+ const checkedAgents = new WeakMap<object, UnknownActorLogic>();
919
+
920
+ /**
921
+ * An Agent as a Chart invokes it: the Agent itself, whose start refuses a resolved `cwd` that is a
922
+ * Repo Slot's folder. A reference hides that value from the door. The check is in `start`, because
923
+ * the Workspace's handles are registered at a start, and a Chart's first invoke resolves before
924
+ * that. The refusal is THAT invoke's error, as in {@link checkedEffect}.
925
+ */
926
+ function checkedAgent(slot: string, logic: UnknownActorLogic): UnknownActorLogic {
927
+ const hit = checkedAgents.get(logic);
928
+ if (hit) return hit;
929
+ const checked = Object.create(logic) as UnknownActorLogic;
930
+ checked.start = (snapshot, scope) => {
931
+ const cwd = ((snapshot as { input?: unknown }).input as { cwd?: unknown } | undefined)?.cwd;
932
+ const reads = (logic as { definition?: { workspace?: unknown } }).definition?.workspace !== "none";
933
+ const repos = reads && typeof cwd === "string" ? ambientHandlesFor(scope.self)?.repos : undefined;
934
+ const fault = repos ? slotFolderFault(cwd as string, Object.keys(repos)) : undefined;
935
+ if (fault !== undefined) throw new ChartInvokeFault(`the Agent "${slot}": ${fault}`);
936
+ logic.start?.(snapshot, scope);
937
+ };
938
+ checkedAgents.set(logic, checked);
939
+ return checked;
940
+ }
941
+
942
+ /** The lent actors as a Chart invokes them: each Effect and Agent checked at its start. */
789
943
  function chartActors(actors: Record<string, UnknownActorLogic>): Record<string, UnknownActorLogic> {
790
- return Object.fromEntries(Object.entries(actors).map(([slot, logic]) => [slot, checkedEffect(slot, logic)]));
944
+ return Object.fromEntries(
945
+ Object.entries(actors).map(([slot, logic]) => [
946
+ slot,
947
+ isAgent(logic) ? checkedAgent(slot, logic) : checkedEffect(slot, logic),
948
+ ]),
949
+ );
791
950
  }
792
951
 
793
952
  /** An Agent Turn's input with `prompt` and `cwd` as strings: a whole placeholder may resolve to
@@ -863,13 +1022,16 @@ export type CompileOptions = OpenBodyOptions & {
863
1022
 
864
1023
  /**
865
1024
  * Build the Machine a Chart describes (ADR-0070), with `jr2Setup` over: the lent actors by their
866
- * slot names, `agent(def)` for each Agent the Chart mints, and the kit's `gate`; the lent guards
867
- * and `jr2.sentBy`; the lent actions and the four generic ones; the lent delays. Every reference
868
- * resolves at invoke time, so the Machine holds the text's shape and no run's data. The Chart's
1025
+ * slot names, `agent(def)` for each Agent the Chart mints, the kit's `gate`, and `fail()` where the
1026
+ * body lends it; the lent guards, `jr2.sentBy`, `jr2.failure` and `jr2.ready`; the lent actions and the four
1027
+ * generic ones; the lent delays. Every reference resolves at invoke time, so the Machine holds the
1028
+ * text's shape and no run's data. The Chart's
869
1029
  * input is the context key {@link CHART_INPUT_KEY}; a done Chart outputs `{ outcome: "done",
870
1030
  * output }`, its `output` resolved (or, without one, its final state's). The transitions ceiling
871
1031
  * is xstate's `maxIterations` as well, so a guarded `always` loop that never ends in one macrostep
872
- * still ends. `jr2Setup`'s own refusals (a parallel state below an Agent's) throw here.
1032
+ * still ends. `jr2Setup`'s own refusals (a parallel state below an Agent's) throw here, but not its
1033
+ * `agent.fault` refusal: in a Chart that route is optional, and the body sends an `agent.fault` that
1034
+ * no transition of the Chart takes to the Machine that invoked it (ADR-0074).
873
1035
  */
874
1036
  export function compileChart(
875
1037
  admitted: AdmittedChart,
@@ -881,7 +1043,9 @@ export function compileChart(
881
1043
  const mint = options.mintAgent ?? ((_slot: string, definition: AgentDefinition) => agent(definition));
882
1044
  const minted = mapValues(admitted.agents, (def) => def);
883
1045
  const actors: Record<string, UnknownActorLogic> = chartActors(lent.actors);
884
- for (const [slot, def] of Object.entries(minted)) actors[slot] = mint(slot, def);
1046
+ for (const [slot, def] of Object.entries(minted)) actors[slot] = checkedAgent(slot, mint(slot, def));
1047
+ // The kit's Part that fails the run, where the body lends it (ADR-0073): no slot of the wrapper's.
1048
+ if (decl.lends.fail === true) actors.fail = fail();
885
1049
  // The slots an invoke runs an Agent Turn in: the lent Agents and the Chart's own.
886
1050
  const agentSlots = new Set([
887
1051
  ...Object.keys(lent.actors).filter((slot) => isAgent(lent.actors[slot])),
@@ -905,11 +1069,19 @@ export function compileChart(
905
1069
  : (event as { output?: unknown } | undefined)?.output,
906
1070
  }) satisfies OpenBodyOutput,
907
1071
  options: { maxIterations: decl.ceilings.transitions },
1072
+ // In a Chart the `agent.fault` route is optional (ADR-0070): the Open body sends an `agent.fault`
1073
+ // that no transition takes to its parent, so `createMachine` does not refuse a state with none.
1074
+ [COMPILED_CHART]: true,
908
1075
  };
909
1076
  return jr2Setup({
910
1077
  events: admitted.events,
911
1078
  actors,
912
- guards: lent.guards as never,
1079
+ // `jr2.failure` is in every Chart, as `jr2.sentBy` is: a predicate, with no capability (ADR-0073).
1080
+ guards: {
1081
+ ...(lent.guards as Record<string, unknown>),
1082
+ [FAILURE_GUARD]: failureGuard,
1083
+ [READY_GUARD]: readyGuard,
1084
+ } as never,
913
1085
  actions: { ...(lent.actions as Record<string, unknown>), ...GENERIC } as never,
914
1086
  delays: lent.delays as never,
915
1087
  }).createMachine(config as never) as unknown as AnyStateMachine;
@@ -1097,6 +1269,20 @@ function watchTurns(self: AnyActorRef, turns: number, ceiling: number): TurnWatc
1097
1269
  return watch;
1098
1270
  }
1099
1271
 
1272
+ /**
1273
+ * Does a transition of the Chart take this event now? xstate's own selection, guards and all. A
1274
+ * guard that throws (a reference that resolved to nothing) counts as a take: the step then runs
1275
+ * the guard again, and settles the body `refused` with the reference named.
1276
+ */
1277
+ function takes(machine: AnyStateMachine, snapshot: BodySnapshot, event: AnyEventObject): boolean {
1278
+ try {
1279
+ return machine.getTransitionData(snapshot as never, event as never).length > 0;
1280
+ } catch {
1281
+ runFault = undefined;
1282
+ return true;
1283
+ }
1284
+ }
1285
+
1100
1286
  /** The actor logic of an Open body: a custom ActorLogic that delegates to the compiled Chart. */
1101
1287
  export function openBodyLogic(decl: OpenBodyDeclaration, options: OpenBodyOptions = {}): UnknownActorLogic {
1102
1288
  const wallMs = durationMs(decl.ceilings.wallClock) ?? Number.POSITIVE_INFINITY;
@@ -1259,7 +1445,10 @@ export function openBodyLogic(decl: OpenBodyDeclaration, options: OpenBodyOption
1259
1445
  /** Admit and compile a Chart text, and record the Machine the actor runs. Throws ChartRefusal. */
1260
1446
  const bind = (self: AnyActorRef, text: unknown, inputKeys: readonly string[]) => {
1261
1447
  const lent = liveLentOf(self, logic);
1262
- const admitted = admitChart(text, decl, lent, { inputKeys });
1448
+ // A Chart that comes after the Workspace started finds its slots here. A Chart in the step that
1449
+ // starts the Workspace comes before them, and the wrapper's door or the Turn's start saw it.
1450
+ const repos = ambientHandlesFor(self)?.repos;
1451
+ const admitted = admitChart(text, decl, lent, { inputKeys, ...(repos ? { repoSlots: Object.keys(repos) } : {}) });
1263
1452
  const src = (self as { src?: unknown }).src;
1264
1453
  const machine = compiledFor(admitted, lent, decl, {
1265
1454
  ...options,
@@ -1392,6 +1581,19 @@ export function openBodyLogic(decl: OpenBodyDeclaration, options: OpenBodyOption
1392
1581
  ceiling("transitions", transitionsDetail(fields.counters.transitions), snapshot, fields, scope),
1393
1582
  );
1394
1583
  }
1584
+ // An `agent.fault` that no transition of the Chart takes goes to the Machine that invoked the
1585
+ // body, unchanged (ADR-0074): in a Chart the route is optional, and the parent's `agent.fault`
1586
+ // route is the route for a Chart's Agents too. The body does not step the Chart: the parent's
1587
+ // route decides, and its exit stops the body. With no parent route that takes it, the run
1588
+ // host faults the run, as for any `agent.fault` that no transition took.
1589
+ if (event.type === "agent.fault" && !takes(machine, snapshot, event)) {
1590
+ // Relayed from the body, as `sendBack` relays from an Agent: the run host reads a relayed
1591
+ // event's source, so a pick held in the parent is answered as for an Agent of its own.
1592
+ const parent = scope.self._parent;
1593
+ const system = scope.system as unknown as { _relay(from: unknown, to: unknown, e: AnyEventObject): void };
1594
+ if (parent) scope.defer(() => system._relay(scope.self, parent, event));
1595
+ return snapshot;
1596
+ }
1395
1597
  return release(
1396
1598
  scope.self,
1397
1599
  step(
@@ -25,18 +25,82 @@
25
25
  import type { ActorLogic, AnyActorRef, EventObject } from "xstate";
26
26
  import { open } from "./open.ts";
27
27
  import type { Arg, EffectLogic, EffectParts, EffectSnapshot } from "./effect.ts";
28
- import type { Text } from "./http-effect.ts";
28
+ import type { Secret, Text } from "./http-effect.ts";
29
29
 
30
- /** The parts of an Effect a package may leave Open: an http url, a command's argv, a secret's name. */
31
- export type OpenEffectPart = "url" | "argv" | "secret";
30
+ /** The parts of an Effect a package may leave Open: an http url, a command's argv, a secret's name,
31
+ * a dispatch's target (its url and token together, ADR-0071). */
32
+ export type OpenEffectPart = "url" | "argv" | "secret" | "target";
32
33
 
33
- /** What a composer binds an Effect's Open parts to, each key one part: a url (a string, or a `text`
34
- * with a secret in its path), an argv (each entry one argument, never shell text), a secret's name
35
- * (its file in the Effector's mount, ADR-0068). One object for an Effect with two Open parts. */
34
+ // What a composer binds an Effect's Open parts to, each key one part (ADR-0054); a dispatch's
35
+ // target is its url and token, bound as one (ADR-0071). One object for an
36
+ // Effect with two Open parts. `customize()` reads each Effect's own binding type off its logic
37
+ // (`BindingOf`, customize.ts), so this loose shape is what the implementation walks.
38
+ /**
39
+ * The values that bind the Open parts of one Effect. A packaged Machine leaves a part of an Effect
40
+ * Open when it cannot know it. That part is the url of an `http` Effect, the argv of a `command`,
41
+ * the name of a secret, written `secret(open)`, or the target of a `dispatch`: the url of another
42
+ * Orchestrator and the secret that holds its Instance token, bound as one. You bind it once, where you register the
43
+ * Machine, with `customize(machine, { effects: { <slot>: binding } })`. Each key binds one Open
44
+ * part. For an Effect with two Open parts, one object holds both keys.
45
+ *
46
+ * The binding is a value, never a function of the run: no run chooses an Effect's url, argv or
47
+ * secret. `customize()` builds the Effect again with the bound parts, so a bound part gets every
48
+ * check that a part written in the definition gets.
49
+ *
50
+ * `customize()` refuses:
51
+ * - an empty binding, or a key for a part that is not Open: `customize(): the <kind> Effect
52
+ * "<slot>" leaves its <parts> Open — bind it with <binding> (ADR-0054)`
53
+ * - an Effect with no Open part: ``customize(): the <kind> Effect "<slot>" of machine "<id>"
54
+ * leaves nothing Open — its parts are fixed in its definition, and only a part a package left
55
+ * `open` is bound, once (ADR-0054).``
56
+ * - `open` as a value: ``customize(): the <kind> Effect "<slot>"'s <part> is bound once, to a
57
+ * value — not `open` again: no run chooses an Effect's <part> (ADR-0054)``. A function as a
58
+ * value gives the same text, with `a function of the run` for `` `open` again ``.
59
+ * - a secret that is not a string: `customize(): the <kind> Effect "<slot>"'s secret is bound to
60
+ * a secret's name, a string — the file the Effector reads it from: { secret: "<name>" }
61
+ * (ADR-0054, ADR-0068)`
62
+ *
63
+ * `jr2 up` refuses a registered Machine with an Open part that nobody bound, and prints the
64
+ * `customize` line that binds it.
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * import { customize, http, jr2Setup, open, type EffectBinding } from "@jr2/orchestrator";
69
+ *
70
+ * const notifier = jr2Setup({
71
+ * actors: { notify: http({ method: "POST", url: open }) },
72
+ * }).createMachine({
73
+ * id: "notifier",
74
+ * initial: "sending",
75
+ * states: {
76
+ * sending: { invoke: { src: "notify", onDone: "done" } },
77
+ * done: { type: "final" },
78
+ * },
79
+ * });
80
+ *
81
+ * const notify = { url: "https://hooks.example.com/build" } satisfies EffectBinding;
82
+ *
83
+ * export const machine = customize(notifier, { effects: { notify } });
84
+ * ```
85
+ *
86
+ * @see docs/guides/bind-open-parts.md
87
+ * @see docs/concepts/effects.md
88
+ * @see docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md
89
+ * @see docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md
90
+ */
36
91
  export type EffectBinding = {
92
+ /** The url of an `http` Effect whose url is Open: a string, or a `text` template with a secret
93
+ * in its path, such as `` text`https://hooks.example.com/services/${secret("hook")}` ``. Its
94
+ * scheme and host must be literal. */
37
95
  readonly url?: string | Text;
96
+ /** The argv of a `command` whose argv is Open: one entry for each argument, never shell text.
97
+ * An entry is a string or an `arg("<param>")` that the invoke's input fills. It must not be
98
+ * empty. */
38
99
  readonly argv?: readonly (string | Arg)[];
100
+ /** The name of the secret that `secret(open)` stands for: an entry of `secrets` in
101
+ * `jr2.config.ts`. The Effector reads it from a file of that name. */
39
102
  readonly secret?: string;
103
+ readonly target?: { readonly url: string; readonly token: Secret };
40
104
  };
41
105
 
42
106
  declare const openPart: unique symbol;
@@ -46,10 +110,16 @@ declare const openPart: unique symbol;
46
110
  * `effects` offers exactly the Open slots, each with the binding its parts take — the intersection
47
111
  * of each part's, for an Effect with two. Phantom, like `JR2Wrapper`: the property exists in the
48
112
  * type alone.
113
+ *
114
+ * @internal
49
115
  */
50
116
  export type OpenEffect<P extends OpenEffectPart> = { readonly [openPart]: P };
51
117
 
52
- /** The phantom for these Open parts, or nothing for none: what a constructor's type adds. */
118
+ /**
119
+ * The phantom for these Open parts, or nothing for none: what a constructor's type adds.
120
+ *
121
+ * @internal
122
+ */
53
123
  export type OpenOf<P extends OpenEffectPart> = [P] extends [never] ? unknown : OpenEffect<P>;
54
124
 
55
125
  /** The binder an Open Effect carries, under a `Symbol.for` key so a second copy of this module (an
@@ -58,11 +128,13 @@ const BIND: unique symbol = Symbol.for("jr2.effect.bind");
58
128
 
59
129
  type Binder = (binding: EffectBinding) => EffectLogic<unknown, unknown>;
60
130
 
61
- /** Which parts of these parts are Open, in binding order: the url or the argv, then the secret. */
131
+ /** Which parts of these parts are Open, in binding order: the url, the argv or the target, then the
132
+ * secret. A dispatch's target is one part: its token is never `secret(open)` (ADR-0071). */
62
133
  export function openPartsOf(parts: EffectParts): OpenEffectPart[] {
63
134
  const out: OpenEffectPart[] = [];
64
135
  if (parts.kind === "http" && parts.url === open) out.push("url");
65
136
  if (parts.kind === "command" && parts.argv === open) out.push("argv");
137
+ if (parts.kind === "dispatch" && parts.target === open) out.push("target");
66
138
  if (parts.kind !== "push" && parts.openSecret) out.push("secret");
67
139
  return out;
68
140
  }
@@ -78,6 +150,7 @@ const BINDING_SAMPLE: Record<OpenEffectPart, string> = {
78
150
  url: 'url: "https://<host>/<path>"',
79
151
  argv: 'argv: ["<program>", "<arg>"]',
80
152
  secret: 'secret: "<name>"',
153
+ target: 'target: { url: "https://<host>", token: secret("<name>") }',
81
154
  };
82
155
 
83
156
  /** One binding object for these Open parts — `{ url: "…", secret: "<name>" }` for two. */