@jr2/orchestrator 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (327) hide show
  1. package/README.md +20 -2
  2. package/console/components/app.ts +2 -0
  3. package/console/components/fleet.ts +14 -1
  4. package/console/components/machine-pane.ts +41 -2
  5. package/console/main.ts +13 -1
  6. package/console/store.ts +102 -6
  7. package/console/style.css +21 -0
  8. package/docs/agents-block.md +5 -0
  9. package/docs/concepts/agents-and-turns.md +442 -0
  10. package/docs/concepts/architecture.md +230 -0
  11. package/docs/concepts/charts.md +143 -0
  12. package/docs/concepts/composition.md +357 -0
  13. package/docs/concepts/converge.md +254 -0
  14. package/docs/concepts/durable-runs.md +189 -0
  15. package/docs/concepts/effects.md +603 -0
  16. package/docs/concepts/gates.md +208 -0
  17. package/docs/concepts/machines-and-workflows.md +289 -0
  18. package/docs/concepts/pools-and-sources.md +230 -0
  19. package/docs/concepts/secrets-and-trust.md +222 -0
  20. package/docs/concepts/the-menu.md +378 -0
  21. package/docs/concepts/workspaces.md +678 -0
  22. package/docs/get-started/a-model-decides.md +563 -0
  23. package/docs/get-started/an-agent-in-a-sandbox.md +381 -0
  24. package/docs/get-started/connect-a-model.md +248 -0
  25. package/docs/get-started/create-an-instance.md +194 -0
  26. package/docs/get-started/have-your-assistant-write-a-machine.md +922 -0
  27. package/docs/get-started/install.md +126 -0
  28. package/docs/guides/add-tools-to-the-sandbox.md +295 -0
  29. package/docs/guides/answer-a-gate.md +304 -0
  30. package/docs/guides/bind-open-parts.md +223 -0
  31. package/docs/guides/cancel-a-run.md +146 -0
  32. package/docs/guides/clean-up.md +206 -0
  33. package/docs/guides/fix-a-failed-converge.md +361 -0
  34. package/docs/guides/give-the-cluster-git-credentials.md +222 -0
  35. package/docs/guides/iterate-on-a-machine-on-kind.md +190 -0
  36. package/docs/guides/let-an-agent-write-a-chart.md +226 -0
  37. package/docs/guides/look-inside-a-sandbox.md +174 -0
  38. package/docs/guides/mirror-the-kit-images.md +176 -0
  39. package/docs/guides/move-from-kind-to-a-shared-cluster.md +302 -0
  40. package/docs/guides/open-the-console.md +91 -0
  41. package/docs/guides/publish-a-machine-as-an-npm-package.md +350 -0
  42. package/docs/guides/push-and-sign-a-branch.md +486 -0
  43. package/docs/guides/run-a-chart-by-name.md +270 -0
  44. package/docs/guides/run-a-command-or-call-a-url.md +544 -0
  45. package/docs/guides/run-one-worker-per-item.md +321 -0
  46. package/docs/guides/set-an-agents-access-and-dials.md +375 -0
  47. package/docs/guides/size-a-workspace.md +204 -0
  48. package/docs/guides/start-a-run-from-a-run.md +237 -0
  49. package/docs/guides/start-a-run.md +207 -0
  50. package/docs/guides/upgrade-the-kit-version.md +240 -0
  51. package/docs/guides/use-a-hosted-provider.md +208 -0
  52. package/docs/guides/use-a-local-model.md +313 -0
  53. package/docs/guides/watch-a-run.md +326 -0
  54. package/docs/guides/write-a-workflow.md +318 -0
  55. package/docs/index.md +44 -0
  56. package/docs/llms.txt +216 -0
  57. package/docs/reference/authoring-surface/agent.md +128 -0
  58. package/docs/reference/authoring-surface/answer.md +140 -0
  59. package/docs/reference/authoring-surface/arg.md +60 -0
  60. package/docs/reference/authoring-surface/basic.md +62 -0
  61. package/docs/reference/authoring-surface/bearer.md +53 -0
  62. package/docs/reference/authoring-surface/chartschema.md +23 -0
  63. package/docs/reference/authoring-surface/chartschemafor.md +48 -0
  64. package/docs/reference/authoring-surface/command.md +191 -0
  65. package/docs/reference/authoring-surface/customize.md +64 -0
  66. package/docs/reference/authoring-surface/defineconfig.md +41 -0
  67. package/docs/reference/authoring-surface/defineevent.md +114 -0
  68. package/docs/reference/authoring-surface/dispatch.md +126 -0
  69. package/docs/reference/authoring-surface/fail.md +83 -0
  70. package/docs/reference/authoring-surface/file.md +122 -0
  71. package/docs/reference/authoring-surface/frompick.md +277 -0
  72. package/docs/reference/authoring-surface/http.md +159 -0
  73. package/docs/reference/authoring-surface/isfailure.md +58 -0
  74. package/docs/reference/authoring-surface/jr2setup.md +237 -0
  75. package/docs/reference/authoring-surface/open.md +74 -0
  76. package/docs/reference/authoring-surface/openbody.md +86 -0
  77. package/docs/reference/authoring-surface/paramsof.md +125 -0
  78. package/docs/reference/authoring-surface/pool.md +186 -0
  79. package/docs/reference/authoring-surface/push.md +112 -0
  80. package/docs/reference/authoring-surface/secret.md +118 -0
  81. package/docs/reference/authoring-surface/sentby.md +83 -0
  82. package/docs/reference/authoring-surface/source.md +51 -0
  83. package/docs/reference/authoring-surface/text.md +62 -0
  84. package/docs/reference/authoring-surface/type-agentdeclaration.md +27 -0
  85. package/docs/reference/authoring-surface/type-agentdefinition.md +80 -0
  86. package/docs/reference/authoring-surface/type-agentlogic.md +15 -0
  87. package/docs/reference/authoring-surface/type-agentslots.md +12 -0
  88. package/docs/reference/authoring-surface/type-answeraction.md +12 -0
  89. package/docs/reference/authoring-surface/type-arg.md +19 -0
  90. package/docs/reference/authoring-surface/type-argnames.md +12 -0
  91. package/docs/reference/authoring-surface/type-binding.md +62 -0
  92. package/docs/reference/authoring-surface/type-bindingof.md +12 -0
  93. package/docs/reference/authoring-surface/type-bodyacceptsdoor.md +15 -0
  94. package/docs/reference/authoring-surface/type-bodyacceptsslots.md +17 -0
  95. package/docs/reference/authoring-surface/type-ceilings.md +41 -0
  96. package/docs/reference/authoring-surface/type-chainlink.md +19 -0
  97. package/docs/reference/authoring-surface/type-chartaction.md +46 -0
  98. package/docs/reference/authoring-surface/type-chartagent.md +28 -0
  99. package/docs/reference/authoring-surface/type-chartevent.md +20 -0
  100. package/docs/reference/authoring-surface/type-chartguard.md +20 -0
  101. package/docs/reference/authoring-surface/type-chartinvoke.md +28 -0
  102. package/docs/reference/authoring-surface/type-chartjson.md +14 -0
  103. package/docs/reference/authoring-surface/type-chartstate.md +46 -0
  104. package/docs/reference/authoring-surface/type-charttext.md +40 -0
  105. package/docs/reference/authoring-surface/type-charttransition.md +22 -0
  106. package/docs/reference/authoring-surface/type-charttransitions.md +14 -0
  107. package/docs/reference/authoring-surface/type-chartvalue.md +15 -0
  108. package/docs/reference/authoring-surface/type-childat.md +12 -0
  109. package/docs/reference/authoring-surface/type-childslots.md +12 -0
  110. package/docs/reference/authoring-surface/type-commandenv.md +15 -0
  111. package/docs/reference/authoring-surface/type-commandinput.md +19 -0
  112. package/docs/reference/authoring-surface/type-commandoptions.md +78 -0
  113. package/docs/reference/authoring-surface/type-commandoutput.md +104 -0
  114. package/docs/reference/authoring-surface/type-customize.md +109 -0
  115. package/docs/reference/authoring-surface/type-dispatchdefinition.md +39 -0
  116. package/docs/reference/authoring-surface/type-dispatchinput.md +14 -0
  117. package/docs/reference/authoring-surface/type-dispatchopenparts.md +14 -0
  118. package/docs/reference/authoring-surface/type-dispatchoutput.md +65 -0
  119. package/docs/reference/authoring-surface/type-duration.md +15 -0
  120. package/docs/reference/authoring-surface/type-effectbinding.md +93 -0
  121. package/docs/reference/authoring-surface/type-effectlogic.md +12 -0
  122. package/docs/reference/authoring-surface/type-envopenparts.md +14 -0
  123. package/docs/reference/authoring-surface/type-eventaudience.md +27 -0
  124. package/docs/reference/authoring-surface/type-eventdef.md +45 -0
  125. package/docs/reference/authoring-surface/type-eventfrom.md +16 -0
  126. package/docs/reference/authoring-surface/type-eventsemantics.md +26 -0
  127. package/docs/reference/authoring-surface/type-execoutcome.md +34 -0
  128. package/docs/reference/authoring-surface/type-failslot.md +14 -0
  129. package/docs/reference/authoring-surface/type-failure.md +19 -0
  130. package/docs/reference/authoring-surface/type-failureinput.md +20 -0
  131. package/docs/reference/authoring-surface/type-filebody.md +41 -0
  132. package/docs/reference/authoring-surface/type-frominput.md +14 -0
  133. package/docs/reference/authoring-surface/type-hostinjectedinput.md +58 -0
  134. package/docs/reference/authoring-surface/type-httpdefinition.md +101 -0
  135. package/docs/reference/authoring-surface/type-httpinput.md +12 -0
  136. package/docs/reference/authoring-surface/type-httpmethod.md +14 -0
  137. package/docs/reference/authoring-surface/type-httpopenparts.md +12 -0
  138. package/docs/reference/authoring-surface/type-httpoutput.md +89 -0
  139. package/docs/reference/authoring-surface/type-httprequestparts.md +33 -0
  140. package/docs/reference/authoring-surface/type-jr2config.md +126 -0
  141. package/docs/reference/authoring-surface/type-lends.md +38 -0
  142. package/docs/reference/authoring-surface/type-logicat.md +12 -0
  143. package/docs/reference/authoring-surface/type-markedif.md +12 -0
  144. package/docs/reference/authoring-surface/type-mergedactors.md +12 -0
  145. package/docs/reference/authoring-surface/type-namedsecret.md +25 -0
  146. package/docs/reference/authoring-surface/type-noslotspec.md +26 -0
  147. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +32 -0
  148. package/docs/reference/authoring-surface/type-openbodydeclaration.md +40 -0
  149. package/docs/reference/authoring-surface/type-openbodylogic.md +14 -0
  150. package/docs/reference/authoring-surface/type-openbodyoutput.md +42 -0
  151. package/docs/reference/authoring-surface/type-openbodyslot.md +18 -0
  152. package/docs/reference/authoring-surface/type-opencommandinput.md +17 -0
  153. package/docs/reference/authoring-surface/type-openeffect.md +12 -0
  154. package/docs/reference/authoring-surface/type-openeffectslots.md +12 -0
  155. package/docs/reference/authoring-surface/type-openof.md +12 -0
  156. package/docs/reference/authoring-surface/type-opensecret.md +23 -0
  157. package/docs/reference/authoring-surface/type-opensecretmark.md +12 -0
  158. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +31 -0
  159. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +32 -0
  160. package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
  161. package/docs/reference/authoring-surface/type-poolspec.md +60 -0
  162. package/docs/reference/authoring-surface/type-pushinput.md +15 -0
  163. package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
  164. package/docs/reference/authoring-surface/type-pushoutput.md +124 -0
  165. package/docs/reference/authoring-surface/type-pushwireoutput.md +12 -0
  166. package/docs/reference/authoring-surface/type-quantity.md +14 -0
  167. package/docs/reference/authoring-surface/type-reached.md +12 -0
  168. package/docs/reference/authoring-surface/type-reposlot.md +70 -0
  169. package/docs/reference/authoring-surface/type-reposlotsof.md +16 -0
  170. package/docs/reference/authoring-surface/type-sandboxoptions.md +77 -0
  171. package/docs/reference/authoring-surface/type-secret.md +25 -0
  172. package/docs/reference/authoring-surface/type-sender.md +19 -0
  173. package/docs/reference/authoring-surface/type-sentbyguards.md +12 -0
  174. package/docs/reference/authoring-surface/type-size.md +85 -0
  175. package/docs/reference/authoring-surface/type-slothandles.md +32 -0
  176. package/docs/reference/authoring-surface/type-sourcenextresult.md +17 -0
  177. package/docs/reference/authoring-surface/type-sourcespec.md +44 -0
  178. package/docs/reference/authoring-surface/type-syncstate.md +12 -0
  179. package/docs/reference/authoring-surface/type-text.md +19 -0
  180. package/docs/reference/authoring-surface/type-textpart.md +24 -0
  181. package/docs/reference/authoring-surface/type-thinkinglevel.md +39 -0
  182. package/docs/reference/authoring-surface/type-toparameterizedobject.md +14 -0
  183. package/docs/reference/authoring-surface/type-toprovidedactor.md +14 -0
  184. package/docs/reference/authoring-surface/type-unknown.md +21 -0
  185. package/docs/reference/authoring-surface/type-usercontainer.md +77 -0
  186. package/docs/reference/authoring-surface/type-withoutemptyindex.md +20 -0
  187. package/docs/reference/authoring-surface/type-workflowevent.md +46 -0
  188. package/docs/reference/authoring-surface/type-workspaceaccess.md +16 -0
  189. package/docs/reference/authoring-surface/type-workspaced.md +94 -0
  190. package/docs/reference/authoring-surface/type-workspacehandles.md +23 -0
  191. package/docs/reference/authoring-surface/type-workspacemachine.md +29 -0
  192. package/docs/reference/authoring-surface/type-workspaceof.md +13 -0
  193. package/docs/reference/authoring-surface/type-workspaceoptions.md +39 -0
  194. package/docs/reference/authoring-surface/type-workspacespec.md +37 -0
  195. package/docs/reference/authoring-surface/workspace.md +259 -0
  196. package/docs/reference/authoring-surface.md +197 -0
  197. package/docs/reference/cli.md +433 -0
  198. package/docs/reference/config.md +756 -0
  199. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +113 -0
  200. package/docs/reference/decisions/0002-duplex-streaming-actor.md +110 -0
  201. package/docs/reference/decisions/0004-worktree-provisioning-model.md +124 -0
  202. package/docs/reference/decisions/0005-sandbox-pod-composition.md +230 -0
  203. package/docs/reference/decisions/0006-agent-control-surface.md +47 -0
  204. package/docs/reference/decisions/0007-durable-machine-state.md +109 -0
  205. package/docs/reference/decisions/0008-orchestrator-as-deployed-app.md +60 -0
  206. package/docs/reference/decisions/0009-cli-and-instance-interface.md +275 -0
  207. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +160 -0
  208. package/docs/reference/decisions/0011-workflow-defined-events.md +141 -0
  209. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +103 -0
  210. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +157 -0
  211. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +95 -0
  212. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +265 -0
  213. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +124 -0
  214. package/docs/reference/decisions/0017-pool-over-a-source-port.md +69 -0
  215. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +111 -0
  216. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +275 -0
  217. package/docs/reference/decisions/0020-private-ca-trust-is-a-named-concept.md +60 -0
  218. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +100 -0
  219. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +103 -0
  220. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +176 -0
  221. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +439 -0
  222. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +65 -0
  223. package/docs/reference/decisions/0026-a-turn-that-is-over-is-never-prompted.md +62 -0
  224. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +237 -0
  225. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +89 -0
  226. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +154 -0
  227. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +100 -0
  228. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +190 -0
  229. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +61 -0
  230. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +127 -0
  231. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +57 -0
  232. package/docs/reference/decisions/0035-a-runaway-turn-is-ended-rerolled-once-then-a-fault.md +92 -0
  233. package/docs/reference/decisions/0036-a-turn-compacts-its-own-context-mid-flight.md +138 -0
  234. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +196 -0
  235. package/docs/reference/decisions/0038-jr2-up-builds-every-image-it-deploys.md +170 -0
  236. package/docs/reference/decisions/0039-image-garbage-collects-by-reachability.md +115 -0
  237. package/docs/reference/decisions/0041-a-build-the-host-already-holds-is-not-spent-again.md +64 -0
  238. package/docs/reference/decisions/0042-ready-is-not-routable-an-admission-retries-its-connection.md +142 -0
  239. package/docs/reference/decisions/0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md +170 -0
  240. package/docs/reference/decisions/0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md +85 -0
  241. package/docs/reference/decisions/0045-the-platform-joins-the-image-address-and-the-cluster-chooses-it.md +82 -0
  242. package/docs/reference/decisions/0046-a-converge-failure-carries-its-diagnosis.md +34 -0
  243. package/docs/reference/decisions/0047-the-git-ssh-key-source-is-the-users-choice.md +47 -0
  244. package/docs/reference/decisions/0048-the-orchestrator-boots-without-its-repos.md +36 -0
  245. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +126 -0
  246. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +73 -0
  247. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +227 -0
  248. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +81 -0
  249. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +111 -0
  250. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +126 -0
  251. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +107 -0
  252. package/docs/reference/decisions/0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md +62 -0
  253. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +152 -0
  254. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +106 -0
  255. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +384 -0
  256. package/docs/reference/decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md +111 -0
  257. package/docs/reference/decisions/0061-a-memory-kill-ends-the-agents-processes-not-the-harness.md +83 -0
  258. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +212 -0
  259. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +83 -0
  260. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +79 -0
  261. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +65 -0
  262. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +550 -0
  263. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +430 -0
  264. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +464 -0
  265. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +180 -0
  266. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +323 -0
  267. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +183 -0
  268. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +144 -0
  269. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +122 -0
  270. package/docs/reference/decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md +76 -0
  271. package/docs/reference/decisions.md +83 -0
  272. package/docs/reference/glossary.md +648 -0
  273. package/docs/reference/http-api.md +1348 -0
  274. package/docs/reference/machines.md +190 -0
  275. package/docs/reference/mechanism-events.md +72 -0
  276. package/docs/why.md +206 -0
  277. package/package.json +5 -3
  278. package/src/actor.ts +541 -79
  279. package/src/agent.ts +114 -38
  280. package/src/ambient.ts +2 -1
  281. package/src/assembly-checks.ts +189 -7
  282. package/src/cause.ts +234 -0
  283. package/src/chart.ts +169 -37
  284. package/src/charts.ts +34 -6
  285. package/src/config.ts +430 -178
  286. package/src/custodian.ts +74 -22
  287. package/src/customize.ts +188 -46
  288. package/src/dispatch-effect.ts +945 -0
  289. package/src/door-compat.ts +217 -0
  290. package/src/durability.ts +11 -4
  291. package/src/effect.ts +341 -44
  292. package/src/effector-wire.ts +160 -11
  293. package/src/fail.ts +209 -0
  294. package/src/harness-client.ts +541 -47
  295. package/src/http-effect.ts +495 -53
  296. package/src/http.ts +427 -16
  297. package/src/index.ts +60 -82
  298. package/src/instance.ts +68 -8
  299. package/src/internal.ts +84 -0
  300. package/src/machine-doc.ts +21 -3
  301. package/src/mechanism-routes.ts +168 -0
  302. package/src/model-keys.generated.ts +53 -0
  303. package/src/model-keys.ts +18 -58
  304. package/src/notices.ts +66 -13
  305. package/src/open-body.ts +159 -23
  306. package/src/open-effect.ts +81 -8
  307. package/src/open.ts +59 -6
  308. package/src/parts.ts +184 -13
  309. package/src/party.ts +68 -2
  310. package/src/pick.ts +463 -11
  311. package/src/pool.ts +247 -36
  312. package/src/registration.ts +83 -4
  313. package/src/repos.ts +2 -1
  314. package/src/run-host.ts +565 -31
  315. package/src/run-output.ts +38 -0
  316. package/src/sandbox-effect.ts +273 -38
  317. package/src/sandbox-kube.ts +3 -1
  318. package/src/secret-placement.ts +52 -17
  319. package/src/server.ts +3 -0
  320. package/src/setup.ts +239 -56
  321. package/src/size.ts +121 -9
  322. package/src/snapshot-store.ts +54 -10
  323. package/src/stub-harness.ts +44 -16
  324. package/src/timers.ts +14 -1
  325. package/src/vocabulary.ts +69 -14
  326. package/src/wire.ts +90 -16
  327. package/src/workspace.ts +697 -175
package/src/cause.ts ADDED
@@ -0,0 +1,234 @@
1
+ // What took the Machine out of an Agent's state (ADR-0024): the Turn's own pick, or something else.
2
+ // The Agent's Actor asks it as its invocation stops, and its sweep says `cause: "pick"` only for
3
+ // the first, so the Harness settles that Submission `ended` and every other end `aborted`. The pick
4
+ // may be from an earlier Submission of the same Turn: a nudge or a wake starts a new Submission in
5
+ // the same Turn.
6
+ //
7
+ // The answer comes from the events the Machines took, never from the pick's answer: "was the pick's
8
+ // work stopped" (`Hold.cutShort`, the answer's `stopped`) is another question. Two facts are kept,
9
+ // read off xstate's inspection stream and the actor system's scheduler and clock:
10
+ //
11
+ // - THE CAUSE OF EACH RELAY: the set of Agents' picks it comes from. ADR-0024 lists the four ways
12
+ // a relay comes from a pick: the pick itself, the work below the Agent's state, a send of an
13
+ // actor that takes a caused relay, and a timer armed while its Machine took one. The cause rides
14
+ // the RELAY, never the event object: xstate relays one object each time a config action with an
15
+ // object literal runs. A timer's cause rides its own `setTimeout` on the run's clock, so two
16
+ // timers of one id each keep their own.
17
+ // - THE EXIT. On each microstep of a Machine, the first microstep that stops the Agent's
18
+ // invocation names the cause of the macrostep it is part of. After it, the Agent's actor is no
19
+ // longer the child of the Machine that invokes it, a child Machine on the way up is no longer a
20
+ // child of the one above, or a Machine on the way up is done. A microstep that throws is never
21
+ // inspected, so a fault is never the exit. When a Machine above catches a fault on the way up,
22
+ // that catch is the exit, and it has no cause: a fault is never the pick's, even when the pick's
23
+ // route caused it. The stop of a run is no event of the Machine's.
24
+ // - A FAILURE (ADR-0073) is not a fault: it is the end that a state chose. When a Machine on the
25
+ // way up from the Agent ends with a Failure, its own error event is the exit, with the cause of
26
+ // that relay. So a pick whose route invokes `fail` ends its Turn, whether a Machine above
27
+ // catches the Failure or the run ends `failed`.
28
+ //
29
+ // Which relay an actor takes now is read from the relay order: xstate relays an event (inspected as
30
+ // `@xstate.event`) to the actor's mailbox. The mailbox takes events one at a time, in order, and
31
+ // xstate inspects `@xstate.snapshot` with that event when the actor has taken it. An actor's own done
32
+ // or error event (`xstate.done.actor.<id>`, `xstate.error.actor.<id>`) is the last relay it makes
33
+ // with a cause. It takes the cause of the relay the actor took then; after it, the actor is
34
+ // finished. A finished actor has no cause, and a relay to it, or to a stopped actor, is not queued:
35
+ // xstate drops it. Status alone does not say it: a done Machine makes its last deferred sends with
36
+ // status `done`, and a Machine done at start is `done` before its init relay. And an errored callback
37
+ // is never stopped: its own later sends still reach its parent.
38
+
39
+ import type { AnyActorRef, AnyEventObject, AnyMachineSnapshot } from "xstate";
40
+ import { isFailure } from "./fail.ts";
41
+ import type { Registration } from "./registration.ts";
42
+
43
+ const XSTATE_STOP = "xstate.stop";
44
+
45
+ /** The Agents' picks a relay comes from; undefined for none. */
46
+ type Cause = ReadonlySet<Registration> | undefined;
47
+
48
+ export class PickCauses {
49
+ /** Each actor's relays that it has not taken yet, in order, with their causes: the first is the
50
+ * one it takes now, or next. */
51
+ private readonly pending = new WeakMap<AnyActorRef, Array<{ event: AnyEventObject; cause: Cause }>>();
52
+ /** For each registration, the cause of the microstep that took the Machine out of its Agent's
53
+ * state, or `null` for none. Set once, at the first such microstep. */
54
+ private readonly exits = new WeakMap<Registration, Cause | null>();
55
+ /** Each registration's Agent actor, read off the invoking Machine's committed snapshot. */
56
+ private readonly actors = new WeakMap<Registration, AnyActorRef>();
57
+ /** Each actor that relayed its own done or error event: it takes nothing more, and has no cause. */
58
+ private readonly finished = new WeakSet<AnyActorRef>();
59
+ /** The cause of the timer the scheduler arms now: the next `setTimeout` on the clock keeps it. */
60
+ private arming: Cause;
61
+ /** Set while a timer's callback runs: its cause, for the relay the scheduler makes next. */
62
+ private firing: { cause: Cause } | undefined;
63
+
64
+ /** The scheduler arms a timer whose source is `source`: its cause is the relay `source` takes now. */
65
+ armingFrom(source: AnyActorRef): void {
66
+ this.arming = this.causeOfCurrent(source);
67
+ }
68
+
69
+ /**
70
+ * The run's clock, over `base`: each `setTimeout` keeps the cause of the timer the scheduler arms
71
+ * with it, and gives it to the relay its callback makes. So each timer's cause is its own, even
72
+ * for two timers of one id. A clear needs nothing: the callback never runs.
73
+ */
74
+ clockOver<T>(base: { setTimeout: (fn: () => void, ms: number) => T; clearTimeout: (timer: T) => void }) {
75
+ return {
76
+ setTimeout: (fn: () => void, ms: number): T => {
77
+ const cause = this.arming;
78
+ this.arming = undefined;
79
+ return base.setTimeout(() => {
80
+ this.firing = { cause };
81
+ try {
82
+ fn();
83
+ } finally {
84
+ this.firing = undefined;
85
+ }
86
+ }, ms);
87
+ },
88
+ clearTimeout: (timer: T) => base.clearTimeout(timer),
89
+ };
90
+ }
91
+
92
+ /**
93
+ * `@xstate.event`: `event` is relayed from `source` to `target`. `pickOf` names the registrations
94
+ * whose pick it is, or whose work below it comes from. With them: the cause of the timer whose
95
+ * callback makes this relay, or else of the relay that `source` takes now.
96
+ *
97
+ * When the relay is the error event of a Machine that a Failure ended, it is the exit of each of
98
+ * `registrations` whose Agent that Machine invokes, or is above: the Failure took the Agent's
99
+ * state away, and the pick that chose it is the cause.
100
+ */
101
+ relayed(
102
+ source: AnyActorRef | undefined,
103
+ target: AnyActorRef,
104
+ event: AnyEventObject,
105
+ pickOf: () => Cause,
106
+ registrations: () => Iterable<Registration> = () => [],
107
+ ): void {
108
+ const firing = this.firing;
109
+ this.firing = undefined;
110
+ const by = firing ? firing.cause : source !== undefined ? this.causeOfCurrent(source) : undefined;
111
+ const cause = union(pickOf(), by);
112
+ // The source's own done or error event: the last relay it makes with a cause.
113
+ if (source !== undefined && isOwnEnd(source, event)) {
114
+ this.finished.add(source);
115
+ this.pending.delete(source);
116
+ if (isFailure((event as { error?: unknown }).error)) this.failedAt(source, cause, registrations());
117
+ }
118
+ if (this.finished.has(target) || statusOf(target) === "stopped") return;
119
+ const queue = this.pending.get(target);
120
+ if (queue) queue.push({ event, cause });
121
+ else this.pending.set(target, [{ event, cause }]);
122
+ }
123
+
124
+ /** A Failure ended `machine`: record the exit of each registration whose invoker is `machine` or
125
+ * is below it, with the cause of the Failure's relay. */
126
+ private failedAt(machine: AnyActorRef, cause: Cause, registrations: Iterable<Registration>): void {
127
+ for (const reg of registrations) {
128
+ if (this.exits.has(reg)) continue;
129
+ for (let at: AnyActorRef | undefined = reg.invoker; at !== undefined; at = at._parent) {
130
+ if (at !== machine) continue;
131
+ this.exits.set(reg, cause ?? null);
132
+ break;
133
+ }
134
+ }
135
+ }
136
+
137
+ /** `@xstate.snapshot`: `actor` has taken `event`. An event that was not relayed (a stop) leaves
138
+ * the order as it is; a stop clears it, since a stopped actor takes nothing more. */
139
+ taken(actor: AnyActorRef, event: AnyEventObject): void {
140
+ const queue = this.pending.get(actor);
141
+ if (!queue) return;
142
+ if (event.type === XSTATE_STOP) {
143
+ queue.length = 0;
144
+ return;
145
+ }
146
+ const at = queue.findIndex((relay) => relay.event === event);
147
+ if (at !== -1) queue.splice(0, at + 1);
148
+ }
149
+
150
+ /**
151
+ * `@xstate.microstep` of `machine`, with the snapshot after it: record the exit of each of
152
+ * `registrations` that this microstep takes out of the Machine.
153
+ */
154
+ microstep(
155
+ machine: AnyActorRef,
156
+ snapshot: AnyMachineSnapshot,
157
+ event: AnyEventObject,
158
+ registrations: Iterable<Registration>,
159
+ ): void {
160
+ for (const reg of registrations) {
161
+ if (this.exits.has(reg) || !reg.invoker || !reg.invokeId) continue;
162
+ if (!this.actors.has(reg)) {
163
+ // The committed snapshot: the invoking Machine's own microstep is not committed until its
164
+ // macrostep ends, so this is the actor the registration was made for.
165
+ const ref = childrenOf(reg.invoker.getSnapshot())[reg.invokeId];
166
+ if (ref === undefined) continue;
167
+ this.actors.set(reg, ref);
168
+ }
169
+ const exit = leaves(reg, this.actors.get(reg)!, machine, snapshot);
170
+ if (exit === false) continue;
171
+ // A fault on the way up took the Agent's state away when the Machine above caught it: that is
172
+ // the exit, and a fault is never the pick's, even when the pick's route caused it.
173
+ const none = event.type === XSTATE_STOP || exit === "fault";
174
+ this.exits.set(reg, none ? null : (this.causeOfCurrent(machine) ?? null));
175
+ }
176
+ }
177
+
178
+ /** Did this registration's own pick take the Machine out of its Agent's state? */
179
+ endedByPick(reg: Registration): boolean {
180
+ return this.exits.get(reg)?.has(reg) === true;
181
+ }
182
+
183
+ /** The cause of the relay `actor` takes now. A done or errored actor relays its own done or error
184
+ * event while it takes its last relay, so that event keeps the cause. A finished or stopped actor
185
+ * has none. */
186
+ private causeOfCurrent(actor: AnyActorRef): Cause {
187
+ const status = statusOf(actor);
188
+ if (status === undefined || status === "stopped" || this.finished.has(actor)) return undefined;
189
+ return this.pending.get(actor)?.[0]?.cause;
190
+ }
191
+ }
192
+
193
+ function statusOf(actor: AnyActorRef): string | undefined {
194
+ return (actor.getSnapshot() as { status?: string } | undefined)?.status;
195
+ }
196
+
197
+ /** Is `event` the actor's own done or error event? */
198
+ function isOwnEnd(actor: AnyActorRef, event: AnyEventObject): boolean {
199
+ return event.type === `xstate.done.actor.${actor.id}` || event.type === `xstate.error.actor.${actor.id}`;
200
+ }
201
+
202
+ function union(a: Cause, b: Cause): Cause {
203
+ if (a === undefined) return b;
204
+ if (b === undefined) return a;
205
+ return new Set([...a, ...b]);
206
+ }
207
+
208
+ /** Does this microstep of `machine` stop `reg`'s invocation: `machine` is on the way up from the
209
+ * Agent and is done, or the actor (or child Machine) on the way down is no longer its child. It is
210
+ * `"fault"` when a Machine on the way up below `machine` has status `error`: `machine` caught that
211
+ * fault. A Failure is no fault (ADR-0073), but it never gets here: its relay records the exit
212
+ * (`failedAt`) before a Machine above takes it. The invoker's own microstep has no Machine below it
213
+ * on the way up, so the error of a child of the invoker is never a fault here. */
214
+ function leaves(
215
+ reg: Registration,
216
+ agent: AnyActorRef,
217
+ machine: AnyActorRef,
218
+ snapshot: AnyMachineSnapshot,
219
+ ): "exit" | "fault" | false {
220
+ let below = agent;
221
+ let faulted = false;
222
+ for (let at: AnyActorRef | undefined = reg.invoker; at !== undefined; below = at, at = at._parent) {
223
+ if (at === machine) {
224
+ if (snapshot.status !== "done" && childrenOf(snapshot)[below.id] === below) return false;
225
+ return faulted ? "fault" : "exit";
226
+ }
227
+ if (statusOf(at) === "error") faulted = true;
228
+ }
229
+ return false;
230
+ }
231
+
232
+ function childrenOf(snapshot: unknown): Record<string, AnyActorRef | undefined> {
233
+ return (snapshot as { children?: Record<string, AnyActorRef | undefined> } | undefined)?.children ?? {};
234
+ }
package/src/chart.ts CHANGED
@@ -23,6 +23,7 @@ import { z } from "zod";
23
23
  import { defineEvent, type EventAudience, type EventDef } from "@jr2/agent-protocol";
24
24
  import type { AgentDefinition, ThinkingLevel, WorkspaceAccess } from "./agent.ts";
25
25
  import { inputKeysOf, isEffect } from "./effect.ts";
26
+ import { FAILURE_GUARD } from "./fail.ts";
26
27
  import { compileChart, durationMs, type LentParts, type OpenBodyDeclaration } from "./open-body.ts";
27
28
  import { SENT_BY } from "./party.ts";
28
29
  import { loadWarningsOf } from "./vocabulary.ts";
@@ -38,7 +39,8 @@ export type ChartJson = string | number | boolean | null | ChartJson[] | { [key:
38
39
  */
39
40
  export type ChartValue = ChartJson;
40
41
 
41
- /** A guard: a lent guard by name, with or without params, `jr2.sentBy`, or `not`/`and`/`or`. */
42
+ /** A guard: a lent guard by name, with or without params, `jr2.sentBy`, `jr2.failure`, or
43
+ * `not`/`and`/`or`. */
42
44
  export type ChartGuard =
43
45
  | string
44
46
  | { type: string; params?: ChartValue }
@@ -71,7 +73,7 @@ export type ChartTransition =
71
73
  /** One transition or a list, in xstate's order. */
72
74
  export type ChartTransitions = ChartTransition | ChartTransition[];
73
75
 
74
- /** An invoke: a lent Agent, Effect or Machine, a minted Agent, or the kit's `gate`. */
76
+ /** An invoke: a lent Agent, Effect or Machine, a minted Agent, or the kit's `gate` or `fail`. */
75
77
  export type ChartInvoke = {
76
78
  src: string;
77
79
  id?: string;
@@ -158,6 +160,10 @@ export function chartBriefing(narrow?: Narrow): string {
158
160
  ? ' `src: "gate"` is the kit\'s Gate: the Chart waits there for an outside answer (a human, a webhook), ' +
159
161
  'one of its `events` with `audience: "external"`, not an Agent\'s Turn.'
160
162
  : "";
163
+ const fail = narrow?.fail
164
+ ? ' `src: "fail"` with `input: { reason }` ends the run as a Failure: work that did not succeed, with ' +
165
+ `the reason for a person; an \`onError\` with the guard \`${FAILURE_GUARD}\` catches it. A final state means done.`
166
+ : "";
161
167
  const agents = mints
162
168
  ? `A state runs an Agent with \`invoke: { src, input: { prompt } }\`: \`src\` is a lent Agent's name${lent}. ` +
163
169
  "This Chart mints no Agent of its own: there is no `agents`."
@@ -165,7 +171,7 @@ export function chartBriefing(narrow?: Narrow): string {
165
171
  "a key of `agents`; a lent Agent needs no entry in `agents`, which mints new ones.";
166
172
  return (
167
173
  `A Chart is a workflow written as data: states, the events its Agents pick${mints ? "" : ", and the Agents it mints"}. ` +
168
- `${agents}${gate} An Agent's Turn ends only when it ` +
174
+ `${agents}${gate}${fail} An Agent's Turn ends only when it ` +
169
175
  "picks one of the Chart's `events`: declare each pick in `events` with an `input` schema " +
170
176
  '(`{ "type": "object", "properties": { "<field>": { "type": "string" } }, "required": ["<field>"] }`; a field not in ' +
171
177
  "`required` may be absent), and " +
@@ -233,6 +239,7 @@ type Narrow = {
233
239
  srcs: string[];
234
240
  lentAgents: string[];
235
241
  gate: boolean;
242
+ fail: boolean;
236
243
  guards: string[];
237
244
  actions: string[];
238
245
  delays: string[];
@@ -353,16 +360,25 @@ function grammar(narrow?: Narrow): z.ZodType<ChartText> {
353
360
  })
354
361
  .describe('A context path without the "context." root, like "round" or "ticket.notes".');
355
362
 
356
- // `jr2.sentBy` is the kit's routing guard (ADR-0015), and the one guard that is never lent.
363
+ // `jr2.sentBy` is the kit's routing guard (ADR-0015), and `jr2.failure` its test for a Failure on
364
+ // an `onError` (ADR-0073): every Chart has both, and neither is lent.
357
365
  const sentByArm = z.strictObject({ type: z.literal(SENT_BY), params: z.strictObject({ id: z.string() }) });
366
+ const failureArm = z.strictObject({ type: z.literal(FAILURE_GUARD) });
358
367
  const lentGuardName = narrow
359
- ? enumOf(narrow.guards)
368
+ ? enumOf([...narrow.guards, FAILURE_GUARD])
360
369
  : z.string().refine((t) => t !== SENT_BY, { message: `"${SENT_BY}" takes params { id }` });
370
+ const paramGuardName = narrow
371
+ ? enumOf(narrow.guards)
372
+ : z
373
+ .string()
374
+ .refine((t) => t !== SENT_BY, { message: `"${SENT_BY}" takes params { id }` })
375
+ .refine((t) => t !== FAILURE_GUARD, { message: `"${FAILURE_GUARD}" takes no params` });
361
376
  const guard: z.ZodType<ChartGuard> = z
362
377
  .union([
363
378
  lentGuardName,
364
379
  sentByArm,
365
- z.strictObject({ type: lentGuardName, params: template.optional() }),
380
+ failureArm,
381
+ z.strictObject({ type: paramGuardName, params: template.optional() }),
366
382
  z.strictObject({
367
383
  get not() {
368
384
  return guard;
@@ -381,7 +397,7 @@ function grammar(narrow?: Narrow): z.ZodType<ChartText> {
381
397
  ])
382
398
  .meta({
383
399
  id: `${ID_PREFIX}ChartGuard`,
384
- description: "A lent guard by name, `jr2.sentBy`, or not/and/or of guards.",
400
+ description: `A lent guard by name, \`jr2.sentBy\`, \`${FAILURE_GUARD}\`, or not/and/or of guards.`,
385
401
  }) as never;
386
402
 
387
403
  const lentActionName = narrow
@@ -425,9 +441,14 @@ function grammar(narrow?: Narrow): z.ZodType<ChartText> {
425
441
  : z.string();
426
442
  const invoke = z
427
443
  .strictObject({
444
+ // A narrowed grammar names `fail` only where the body lends it: a model reads only what it can use.
428
445
  src: src.describe(
429
- "A lent Agent, Effect or Machine by its name; the kit's `gate` (waits for an outside answer: a human, a " +
430
- "webhook) where the body lends it; or a key of this Chart's `agents`.",
446
+ narrow && !narrow.fail
447
+ ? "A lent Agent, Effect or Machine by its name; the kit's `gate` (waits for an outside answer: a human, a " +
448
+ "webhook) where the body lends it; or a key of this Chart's `agents`."
449
+ : "A lent Agent, Effect or Machine by its name; the kit's `gate` (waits for an outside answer: a human, a " +
450
+ "webhook) or `fail` (ends the run as a Failure, with `input: { reason }`) where the body lends it; or a " +
451
+ "key of this Chart's `agents`.",
431
452
  ),
432
453
  id: name("invoke id").optional(),
433
454
  input: template.optional().describe("An Agent takes `{ prompt }`; an Effect or a Machine takes its own input."),
@@ -526,9 +547,17 @@ function grammar(narrow?: Narrow): z.ZodType<ChartText> {
526
547
  .describe(chartBriefing(narrow)) as unknown as z.ZodType<ChartText>;
527
548
  }
528
549
 
550
+ // A door field or a pick param that IS this schema is a Chart field: `isChartSchema` tells, and
551
+ // jr2 reads a Chart wrapper's one-shot door off it (ADR-0070).
529
552
  /**
530
- * The Chart grammar (ADR-0070), as zod. A door field or a pick param that IS this schema is a
531
- * Chart field: `isChartSchema` tells, and jr2 reads a Chart wrapper's one-shot door off it.
553
+ * The Chart grammar, as a zod schema. Make a field of the run input of a Chart wrapper this schema,
554
+ * as in `z.object({ topic: z.string(), chart: chartSchema })`. Then a run input can carry a Chart,
555
+ * and the wrapper can keep Charts under names: Chart files in `workflows/<wrapper>/`, and runtime
556
+ * Charts. For the pick of a planner, use `chartSchemaFor`, which knows the names that the Open body
557
+ * lends.
558
+ *
559
+ * @see docs/guides/run-a-chart-by-name.md
560
+ * @see docs/concepts/charts.md
532
561
  */
533
562
  export const chartSchema: z.ZodType<ChartText> = guarded(grammar());
534
563
 
@@ -578,13 +607,38 @@ export function chartBriefingOf(schema: unknown): string | undefined {
578
607
 
579
608
  const narrowedSchemas = new WeakMap<OpenBodyDeclaration, z.ZodType<ChartText>>();
580
609
 
610
+ // Either way it is a Chart field (`isChartSchema`), and the door still admits the Chart against
611
+ // the lent set the body has at its start. One schema per declaration (ADR-0070).
581
612
  /**
582
- * The Chart grammar as zod, narrowed to one Open body (ADR-0070): the lent `src` names, guard,
583
- * action and delay names, and the allowed models and Workspace accesses are enums. A planner's
584
- * pick whose field is this schema gets the narrowed grammar as its tool parameters, so the model
585
- * is constrained when it writes the Chart; a Chart wrapper's door may take it too. Either way it
586
- * is a Chart field (`isChartSchema`), and the door still admits the Chart against the lent set the
587
- * body has at its start. One schema per declaration.
613
+ * The Chart grammar for one Open body, as a zod schema. The names that the Open body lends, and the
614
+ * models and the Workspace accesses that it allows, are the only values that the schema takes.
615
+ * Make it the parameter of the pick of a planner. The model then gets the grammar as the
616
+ * parameters of its tool, and the Briefing tells it how a Chart runs. The door still checks each
617
+ * Chart before it runs.
618
+ *
619
+ * @param decl - The declaration of the Open body that the Chart binds.
620
+ * @returns The schema of a Chart for that Open body.
621
+ *
622
+ * @example
623
+ * ```ts
624
+ * import { z } from "zod";
625
+ * import { chartSchemaFor, defineEvent, type OpenBodyDeclaration } from "@jr2/orchestrator";
626
+ *
627
+ * const declaration: OpenBodyDeclaration = {
628
+ * lends: { agents: ["writer"], gate: true },
629
+ * allows: { models: ["anthropic/claude-haiku-5-5"], workspace: ["none"] },
630
+ * ceilings: { turns: 12, wallClock: "30m", transitions: 500, fanOut: 2, size: 65536 },
631
+ * };
632
+ *
633
+ * export const runChart = defineEvent({
634
+ * name: "run_chart",
635
+ * description: "Run this Chart. Its outcome is the answer to this call.",
636
+ * input: z.object({ chart: chartSchemaFor(declaration) }),
637
+ * });
638
+ * ```
639
+ *
640
+ * @see docs/guides/let-an-agent-write-a-chart.md
641
+ * @see docs/concepts/charts.md
588
642
  */
589
643
  export function chartSchemaFor(decl: OpenBodyDeclaration): z.ZodType<ChartText> {
590
644
  const hit = narrowedSchemas.get(decl);
@@ -607,6 +661,7 @@ function lendsOf(decl: OpenBodyDeclaration) {
607
661
  actions: decl.lends.actions ?? [],
608
662
  delays: decl.lends.delays ?? [],
609
663
  gate: decl.lends.gate === true,
664
+ fail: decl.lends.fail === true,
610
665
  };
611
666
  }
612
667
 
@@ -624,9 +679,10 @@ export function narrowedChartSchema(decl: OpenBodyDeclaration): Record<string, u
624
679
  function narrowOf(decl: OpenBodyDeclaration): Narrow {
625
680
  const l = lendsOf(decl);
626
681
  return {
627
- srcs: [...l.agents, ...l.effects, ...l.machines, ...(l.gate ? ["gate"] : [])],
682
+ srcs: [...l.agents, ...l.effects, ...l.machines, ...(l.gate ? ["gate"] : []), ...(l.fail ? ["fail"] : [])],
628
683
  lentAgents: l.agents,
629
684
  gate: l.gate,
685
+ fail: l.fail,
630
686
  guards: l.guards,
631
687
  actions: l.actions,
632
688
  delays: l.delays,
@@ -867,6 +923,9 @@ function issueLines(
867
923
 
868
924
  const pathText = (path: readonly PropertyKey[]): string => (path.length ? path.map(String).join(".") : "(chart)");
869
925
 
926
+ /** What an invoke's `src` names (rule 3). */
927
+ type InvokeKind = "agent" | "minted" | "effect" | "machine" | "gate" | "fail";
928
+
870
929
  /** One state of the Chart, indexed for the checks. */
871
930
  type Node = {
872
931
  key: string;
@@ -1064,11 +1123,12 @@ class Door {
1064
1123
  private lentText(): string {
1065
1124
  const actors = this.lentActors().map((a) => `${a.name} (${a.kind})`);
1066
1125
  const minted = Object.keys(this.chart.agents ?? {});
1067
- return `lent: ${names(actors)}; kit: ${this.lends.gate ? "gate" : "none"}; minted: ${names(minted)}`;
1126
+ const kit = [...(this.lends.gate ? ["gate"] : []), ...(this.lends.fail ? ["fail"] : [])];
1127
+ return `lent: ${names(actors)}; kit: ${kit.length ? kit.join(", ") : "none"}; minted: ${names(minted)}`;
1068
1128
  }
1069
1129
 
1070
1130
  /** What an invoke's `src` names, or undefined after a refusal line. */
1071
- private srcKind(src: string, path: string): "agent" | "minted" | "effect" | "machine" | "gate" | undefined {
1131
+ private srcKind(src: string, path: string): InvokeKind | undefined {
1072
1132
  const lentNow = (name: string) => name in this.lent.actors;
1073
1133
  const declared = [...this.lends.agents, ...this.lends.effects, ...this.lends.machines];
1074
1134
  if (declared.includes(src) && !lentNow(src)) {
@@ -1089,6 +1149,14 @@ class Door {
1089
1149
  );
1090
1150
  return undefined;
1091
1151
  }
1152
+ if (src === "fail") {
1153
+ if (this.lends.fail) return "fail";
1154
+ this.fail(
1155
+ 3,
1156
+ `${path}: "fail" is the kit's Part that ends the run as a Failure, and this Open body does not lend it (\`lends.fail: true\` would); ${this.lentText()}`,
1157
+ );
1158
+ return undefined;
1159
+ }
1092
1160
  if (Object.hasOwn(this.chart.agents ?? {}, src)) return "minted";
1093
1161
  this.fail(3, `${path}: "${src}" is not a lent actor or an Agent of this Chart; ${this.lentText()}`);
1094
1162
  return undefined;
@@ -1103,11 +1171,16 @@ class Door {
1103
1171
  this.sentBys.push({ id: (guard.params as { id: string }).id, path: `${path}.params.id` });
1104
1172
  return;
1105
1173
  }
1174
+ if (guard.type === FAILURE_GUARD) {
1175
+ if (guard.params !== undefined) this.fail(3, `${path}.params: "${FAILURE_GUARD}" takes no params`);
1176
+ return;
1177
+ }
1106
1178
  this.checkGuardName(guard.type, `${path}.type`);
1107
1179
  if (guard.params !== undefined) this.checkRefs(guard.params, `${path}.params`, scope);
1108
1180
  }
1109
1181
 
1110
1182
  private checkGuardName(name: string, path: string): void {
1183
+ if (name === FAILURE_GUARD) return;
1111
1184
  const lent = this.lends.guards.filter((g) => g in this.lent.guards);
1112
1185
  if (this.lends.guards.includes(name) && !(name in this.lent.guards)) {
1113
1186
  this.fail(
@@ -1117,7 +1190,7 @@ class Door {
1117
1190
  } else if (!this.lends.guards.includes(name)) {
1118
1191
  this.fail(
1119
1192
  3,
1120
- `${path}: "${name}" is not a lent guard; lent guards: ${names(lent)} (and ${SENT_BY} with params { id })`,
1193
+ `${path}: "${name}" is not a lent guard; lent guards: ${names(lent)} (and ${SENT_BY} with params { id }, and ${FAILURE_GUARD})`,
1121
1194
  );
1122
1195
  }
1123
1196
  }
@@ -1262,7 +1335,7 @@ class Door {
1262
1335
  // --- rule 5: minted Agents ------------------------------------------------------------------------
1263
1336
 
1264
1337
  private checkAgents(): void {
1265
- const lentNames = new Set([...this.lends.agents, ...this.lends.effects, ...this.lends.machines, "gate"]);
1338
+ const lentNames = new Set([...this.lends.agents, ...this.lends.effects, ...this.lends.machines, "gate", "fail"]);
1266
1339
  for (const [slot, a] of Object.entries(this.chart.agents ?? {})) {
1267
1340
  const path = `agents.${slot}`;
1268
1341
  if (lentNames.has(slot)) {
@@ -1835,15 +1908,12 @@ class Door {
1835
1908
 
1836
1909
  // --- rule 8: lent input shapes --------------------------------------------------------------------
1837
1910
 
1838
- private checkInvokeInput(
1839
- inv: ChartInvoke,
1840
- kind: "agent" | "minted" | "effect" | "machine" | "gate",
1841
- path: string,
1842
- ): void {
1911
+ private checkInvokeInput(inv: ChartInvoke, kind: InvokeKind, path: string): void {
1843
1912
  const ipath = `${path}.input`;
1844
1913
  const input = inv.input;
1845
1914
  const isObject = typeof input === "object" && input !== null && !Array.isArray(input) && !("$ref" in input);
1846
1915
  if (kind === "machine") return;
1916
+ if (kind === "fail") return this.checkFailInput(input, isObject, ipath);
1847
1917
  if (kind === "gate") {
1848
1918
  if (input === undefined) return;
1849
1919
  if (!isObject) return this.fail(8, `${ipath}: a Gate's input is an object of gate (a name) and meta`);
@@ -1864,6 +1934,23 @@ class Door {
1864
1934
  if (kind === "effect") {
1865
1935
  const logic = this.lent.actors[inv.src];
1866
1936
  const keys = isEffect(logic) ? inputKeysOf(logic.effect) : undefined;
1937
+ if (keys === undefined && isEffect(logic) && logic.effect.kind === "dispatch") {
1938
+ // A dispatch with no input schema has no keys to hold the Chart to: its input is the run's
1939
+ // body, and the callee's door is the check (ADR-0071). Its values are what any Effect's
1940
+ // input slot takes.
1941
+ if (input === undefined) return;
1942
+ if (!isObject) {
1943
+ return this.fail(
1944
+ 8,
1945
+ `${ipath}: the lent dispatch "${inv.src}" takes an object, the run's body — the callee's door checks it`,
1946
+ );
1947
+ }
1948
+ for (const [k, v] of Object.entries(input as Record<string, unknown>)) {
1949
+ if (typeof v !== "string" && !isRef(v))
1950
+ this.fail(8, `${ipath}.${k}: an Effect's input slot takes a string or a reference`);
1951
+ }
1952
+ return;
1953
+ }
1867
1954
  if (keys === undefined) {
1868
1955
  return this.fail(
1869
1956
  8,
@@ -1895,6 +1982,24 @@ class Door {
1895
1982
  this.checkAgentInput(inv.src, kind, input, isObject, ipath);
1896
1983
  }
1897
1984
 
1985
+ /** The input of `fail` (ADR-0073): `{ reason, detail? }`, `reason` a text for a person (a string
1986
+ * or a reference), `detail` any value. */
1987
+ private checkFailInput(input: unknown, isObject: boolean, ipath: string): void {
1988
+ if (!isObject) {
1989
+ return this.fail(8, `${ipath}: \`fail\` takes { reason, detail? }: reason is a text for a person`);
1990
+ }
1991
+ for (const key of Object.keys(input as Record<string, unknown>)) {
1992
+ if (key !== "reason" && key !== "detail") {
1993
+ this.fail(8, `${ipath}.${key}: \`fail\` takes reason and detail only`);
1994
+ }
1995
+ }
1996
+ const reason = (input as Record<string, unknown>).reason;
1997
+ if (reason === undefined) this.fail(8, `${ipath}.reason: \`fail\` needs a reason, a text for a person`);
1998
+ else if ((typeof reason !== "string" || reason === "") && !isRef(reason)) {
1999
+ this.fail(8, `${ipath}.reason: a reason is a string that is not empty, or a reference`);
2000
+ }
2001
+ }
2002
+
1898
2003
  /**
1899
2004
  * An Agent invoke's input is a Turn's Frame and Dials (ADR-0057): `prompt` (a string), and
1900
2005
  * optionally `cwd`, `continue: true`, a `model` the Open body allows, and a `thinkingLevel`.
@@ -1953,9 +2058,10 @@ class Door {
1953
2058
  * The JSON Schema keywords a Chart event's payload may use (ADR-0070, rule 4). The Orchestrator
1954
2059
  * checks every pick against this schema in its own process, before the Allowed check, so a keyword
1955
2060
  * whose cost the door cannot bound is refused by name: `pattern` (a backtracking regex),
1956
- * `patternProperties`, `$ref`, the combinators and the conditionals.
2061
+ * `patternProperties`, `$ref`, the combinators and the conditionals. It is also the subset a
2062
+ * dispatch's slot schema and its callee's door are compared within (ADR-0071, door-compat.ts).
1957
2063
  */
1958
- const PAYLOAD_KEYWORDS = new Set([
2064
+ export const PAYLOAD_KEYWORDS: ReadonlySet<string> = new Set([
1959
2065
  "type",
1960
2066
  "properties",
1961
2067
  "required",
@@ -1981,6 +2087,39 @@ const PAYLOAD_KEYWORDS = new Set([
1981
2087
  * the payload name the event a Chart receives (registration.ts builds `{ type, ...payload }`).
1982
2088
  */
1983
2089
  function payloadSchemaFaults(schema: unknown): Array<{ path: PropertyKey[]; message: string }> {
2090
+ const out = subsetFaults(
2091
+ schema,
2092
+ (key) =>
2093
+ `"${key}" is not a keyword a Chart event's payload may use; it may use ${[...PAYLOAD_KEYWORDS].join(", ")}`,
2094
+ );
2095
+ const props = (schema as { properties?: unknown } | null)?.properties;
2096
+ if (typeof props === "object" && props !== null && Object.hasOwn(props, "type")) {
2097
+ out.push({
2098
+ path: ["properties", "type"],
2099
+ message: "a payload field named `type` is refused: jr2 writes the event's type, and the payload never names it",
2100
+ });
2101
+ }
2102
+ return out;
2103
+ }
2104
+
2105
+ /**
2106
+ * The faults of a door's JSON Schema, or of a dispatch slot's (ADR-0071): every keyword not in
2107
+ * {@link PAYLOAD_KEYWORDS} and a list-form `items` — the payload rules without the Chart's own one
2108
+ * (a field named `type` is a door's to have). A schema with a fault is reported "not compared":
2109
+ * outside the subset, a comparison would be a guess.
2110
+ */
2111
+ export function doorSchemaFaults(schema: unknown): Array<{ path: PropertyKey[]; message: string }> {
2112
+ return subsetFaults(
2113
+ schema,
2114
+ (key) => `"${key}" is outside the JSON Schema subset jr2 compares (${[...PAYLOAD_KEYWORDS].join(", ")})`,
2115
+ );
2116
+ }
2117
+
2118
+ /** The subset's faults: `outside` words the fault of a keyword not in it, for whose rule it is. */
2119
+ function subsetFaults(
2120
+ schema: unknown,
2121
+ outside: (key: string) => string,
2122
+ ): Array<{ path: PropertyKey[]; message: string }> {
1984
2123
  const out: Array<{ path: PropertyKey[]; message: string }> = [];
1985
2124
  const walk = (node: unknown, path: PropertyKey[]): void => {
1986
2125
  if (typeof node === "boolean" && path.length && path[path.length - 1] === "additionalProperties") return;
@@ -1993,7 +2132,7 @@ function payloadSchemaFaults(schema: unknown): Array<{ path: PropertyKey[]; mess
1993
2132
  if (!PAYLOAD_KEYWORDS.has(key)) {
1994
2133
  out.push({
1995
2134
  path: [...path, key],
1996
- message: `"${key}" is not a keyword a Chart event's payload may use; it may use ${[...PAYLOAD_KEYWORDS].join(", ")}`,
2135
+ message: outside(key),
1997
2136
  });
1998
2137
  continue;
1999
2138
  }
@@ -2012,13 +2151,6 @@ function payloadSchemaFaults(schema: unknown): Array<{ path: PropertyKey[]; mess
2012
2151
  }
2013
2152
  };
2014
2153
  walk(schema, []);
2015
- const props = (schema as { properties?: unknown } | null)?.properties;
2016
- if (typeof props === "object" && props !== null && Object.hasOwn(props, "type")) {
2017
- out.push({
2018
- path: ["properties", "type"],
2019
- message: "a payload field named `type` is refused: jr2 writes the event's type, and the payload never names it",
2020
- });
2021
- }
2022
2154
  return out;
2023
2155
  }
2024
2156