@jr2/orchestrator 0.3.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (354) hide show
  1. package/README.md +20 -2
  2. package/bin/server.ts +7 -4
  3. package/console/canvas.ts +27 -14
  4. package/console/components/app.ts +12 -1
  5. package/console/components/fleet.ts +52 -6
  6. package/console/components/machine-pane.ts +128 -2
  7. package/console/components/nav.ts +1 -1
  8. package/console/components/schema-form.ts +12 -4
  9. package/console/json-field.ts +23 -0
  10. package/console/main.ts +122 -39
  11. package/console/store.ts +321 -21
  12. package/console/style.css +111 -0
  13. package/docs/agents-block.md +5 -0
  14. package/docs/concepts/agents-and-turns.md +442 -0
  15. package/docs/concepts/architecture.md +230 -0
  16. package/docs/concepts/charts.md +143 -0
  17. package/docs/concepts/composition.md +357 -0
  18. package/docs/concepts/converge.md +254 -0
  19. package/docs/concepts/durable-runs.md +189 -0
  20. package/docs/concepts/effects.md +603 -0
  21. package/docs/concepts/gates.md +208 -0
  22. package/docs/concepts/machines-and-workflows.md +289 -0
  23. package/docs/concepts/pools-and-sources.md +230 -0
  24. package/docs/concepts/secrets-and-trust.md +222 -0
  25. package/docs/concepts/the-menu.md +378 -0
  26. package/docs/concepts/workspaces.md +678 -0
  27. package/docs/get-started/a-model-decides.md +563 -0
  28. package/docs/get-started/an-agent-in-a-sandbox.md +381 -0
  29. package/docs/get-started/connect-a-model.md +248 -0
  30. package/docs/get-started/create-an-instance.md +194 -0
  31. package/docs/get-started/have-your-assistant-write-a-machine.md +922 -0
  32. package/docs/get-started/install.md +126 -0
  33. package/docs/guides/add-tools-to-the-sandbox.md +295 -0
  34. package/docs/guides/answer-a-gate.md +304 -0
  35. package/docs/guides/bind-open-parts.md +223 -0
  36. package/docs/guides/cancel-a-run.md +146 -0
  37. package/docs/guides/clean-up.md +206 -0
  38. package/docs/guides/fix-a-failed-converge.md +361 -0
  39. package/docs/guides/give-the-cluster-git-credentials.md +222 -0
  40. package/docs/guides/iterate-on-a-machine-on-kind.md +190 -0
  41. package/docs/guides/let-an-agent-write-a-chart.md +226 -0
  42. package/docs/guides/look-inside-a-sandbox.md +174 -0
  43. package/docs/guides/mirror-the-kit-images.md +176 -0
  44. package/docs/guides/move-from-kind-to-a-shared-cluster.md +302 -0
  45. package/docs/guides/open-the-console.md +91 -0
  46. package/docs/guides/publish-a-machine-as-an-npm-package.md +350 -0
  47. package/docs/guides/push-and-sign-a-branch.md +486 -0
  48. package/docs/guides/run-a-chart-by-name.md +270 -0
  49. package/docs/guides/run-a-command-or-call-a-url.md +544 -0
  50. package/docs/guides/run-one-worker-per-item.md +321 -0
  51. package/docs/guides/set-an-agents-access-and-dials.md +375 -0
  52. package/docs/guides/size-a-workspace.md +204 -0
  53. package/docs/guides/start-a-run-from-a-run.md +237 -0
  54. package/docs/guides/start-a-run.md +207 -0
  55. package/docs/guides/upgrade-the-kit-version.md +240 -0
  56. package/docs/guides/use-a-hosted-provider.md +208 -0
  57. package/docs/guides/use-a-local-model.md +313 -0
  58. package/docs/guides/watch-a-run.md +326 -0
  59. package/docs/guides/write-a-workflow.md +318 -0
  60. package/docs/index.md +44 -0
  61. package/docs/llms.txt +216 -0
  62. package/docs/reference/authoring-surface/agent.md +128 -0
  63. package/docs/reference/authoring-surface/answer.md +140 -0
  64. package/docs/reference/authoring-surface/arg.md +60 -0
  65. package/docs/reference/authoring-surface/basic.md +62 -0
  66. package/docs/reference/authoring-surface/bearer.md +53 -0
  67. package/docs/reference/authoring-surface/chartschema.md +23 -0
  68. package/docs/reference/authoring-surface/chartschemafor.md +48 -0
  69. package/docs/reference/authoring-surface/command.md +191 -0
  70. package/docs/reference/authoring-surface/customize.md +64 -0
  71. package/docs/reference/authoring-surface/defineconfig.md +41 -0
  72. package/docs/reference/authoring-surface/defineevent.md +114 -0
  73. package/docs/reference/authoring-surface/dispatch.md +126 -0
  74. package/docs/reference/authoring-surface/fail.md +83 -0
  75. package/docs/reference/authoring-surface/file.md +122 -0
  76. package/docs/reference/authoring-surface/frompick.md +277 -0
  77. package/docs/reference/authoring-surface/http.md +159 -0
  78. package/docs/reference/authoring-surface/isfailure.md +58 -0
  79. package/docs/reference/authoring-surface/jr2setup.md +237 -0
  80. package/docs/reference/authoring-surface/open.md +74 -0
  81. package/docs/reference/authoring-surface/openbody.md +86 -0
  82. package/docs/reference/authoring-surface/paramsof.md +125 -0
  83. package/docs/reference/authoring-surface/pool.md +186 -0
  84. package/docs/reference/authoring-surface/push.md +112 -0
  85. package/docs/reference/authoring-surface/secret.md +118 -0
  86. package/docs/reference/authoring-surface/sentby.md +83 -0
  87. package/docs/reference/authoring-surface/source.md +51 -0
  88. package/docs/reference/authoring-surface/text.md +62 -0
  89. package/docs/reference/authoring-surface/type-agentdeclaration.md +27 -0
  90. package/docs/reference/authoring-surface/type-agentdefinition.md +80 -0
  91. package/docs/reference/authoring-surface/type-agentlogic.md +15 -0
  92. package/docs/reference/authoring-surface/type-agentslots.md +12 -0
  93. package/docs/reference/authoring-surface/type-answeraction.md +12 -0
  94. package/docs/reference/authoring-surface/type-arg.md +19 -0
  95. package/docs/reference/authoring-surface/type-argnames.md +12 -0
  96. package/docs/reference/authoring-surface/type-binding.md +62 -0
  97. package/docs/reference/authoring-surface/type-bindingof.md +12 -0
  98. package/docs/reference/authoring-surface/type-bodyacceptsdoor.md +15 -0
  99. package/docs/reference/authoring-surface/type-bodyacceptsslots.md +17 -0
  100. package/docs/reference/authoring-surface/type-ceilings.md +41 -0
  101. package/docs/reference/authoring-surface/type-chainlink.md +19 -0
  102. package/docs/reference/authoring-surface/type-chartaction.md +46 -0
  103. package/docs/reference/authoring-surface/type-chartagent.md +28 -0
  104. package/docs/reference/authoring-surface/type-chartevent.md +20 -0
  105. package/docs/reference/authoring-surface/type-chartguard.md +20 -0
  106. package/docs/reference/authoring-surface/type-chartinvoke.md +28 -0
  107. package/docs/reference/authoring-surface/type-chartjson.md +14 -0
  108. package/docs/reference/authoring-surface/type-chartstate.md +46 -0
  109. package/docs/reference/authoring-surface/type-charttext.md +40 -0
  110. package/docs/reference/authoring-surface/type-charttransition.md +22 -0
  111. package/docs/reference/authoring-surface/type-charttransitions.md +14 -0
  112. package/docs/reference/authoring-surface/type-chartvalue.md +15 -0
  113. package/docs/reference/authoring-surface/type-childat.md +12 -0
  114. package/docs/reference/authoring-surface/type-childslots.md +12 -0
  115. package/docs/reference/authoring-surface/type-commandenv.md +15 -0
  116. package/docs/reference/authoring-surface/type-commandinput.md +19 -0
  117. package/docs/reference/authoring-surface/type-commandoptions.md +78 -0
  118. package/docs/reference/authoring-surface/type-commandoutput.md +104 -0
  119. package/docs/reference/authoring-surface/type-customize.md +109 -0
  120. package/docs/reference/authoring-surface/type-dispatchdefinition.md +39 -0
  121. package/docs/reference/authoring-surface/type-dispatchinput.md +14 -0
  122. package/docs/reference/authoring-surface/type-dispatchopenparts.md +14 -0
  123. package/docs/reference/authoring-surface/type-dispatchoutput.md +65 -0
  124. package/docs/reference/authoring-surface/type-duration.md +15 -0
  125. package/docs/reference/authoring-surface/type-effectbinding.md +93 -0
  126. package/docs/reference/authoring-surface/type-effectlogic.md +12 -0
  127. package/docs/reference/authoring-surface/type-envopenparts.md +14 -0
  128. package/docs/reference/authoring-surface/type-eventaudience.md +27 -0
  129. package/docs/reference/authoring-surface/type-eventdef.md +45 -0
  130. package/docs/reference/authoring-surface/type-eventfrom.md +16 -0
  131. package/docs/reference/authoring-surface/type-eventsemantics.md +26 -0
  132. package/docs/reference/authoring-surface/type-execoutcome.md +34 -0
  133. package/docs/reference/authoring-surface/type-failslot.md +14 -0
  134. package/docs/reference/authoring-surface/type-failure.md +19 -0
  135. package/docs/reference/authoring-surface/type-failureinput.md +20 -0
  136. package/docs/reference/authoring-surface/type-filebody.md +41 -0
  137. package/docs/reference/authoring-surface/type-frominput.md +14 -0
  138. package/docs/reference/authoring-surface/type-hostinjectedinput.md +58 -0
  139. package/docs/reference/authoring-surface/type-httpdefinition.md +101 -0
  140. package/docs/reference/authoring-surface/type-httpinput.md +12 -0
  141. package/docs/reference/authoring-surface/type-httpmethod.md +14 -0
  142. package/docs/reference/authoring-surface/type-httpopenparts.md +12 -0
  143. package/docs/reference/authoring-surface/type-httpoutput.md +89 -0
  144. package/docs/reference/authoring-surface/type-httprequestparts.md +33 -0
  145. package/docs/reference/authoring-surface/type-jr2config.md +126 -0
  146. package/docs/reference/authoring-surface/type-lends.md +38 -0
  147. package/docs/reference/authoring-surface/type-logicat.md +12 -0
  148. package/docs/reference/authoring-surface/type-markedif.md +12 -0
  149. package/docs/reference/authoring-surface/type-mergedactors.md +12 -0
  150. package/docs/reference/authoring-surface/type-namedsecret.md +25 -0
  151. package/docs/reference/authoring-surface/type-noslotspec.md +26 -0
  152. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +32 -0
  153. package/docs/reference/authoring-surface/type-openbodydeclaration.md +40 -0
  154. package/docs/reference/authoring-surface/type-openbodylogic.md +14 -0
  155. package/docs/reference/authoring-surface/type-openbodyoutput.md +42 -0
  156. package/docs/reference/authoring-surface/type-openbodyslot.md +18 -0
  157. package/docs/reference/authoring-surface/type-opencommandinput.md +17 -0
  158. package/docs/reference/authoring-surface/type-openeffect.md +12 -0
  159. package/docs/reference/authoring-surface/type-openeffectslots.md +12 -0
  160. package/docs/reference/authoring-surface/type-openof.md +12 -0
  161. package/docs/reference/authoring-surface/type-opensecret.md +23 -0
  162. package/docs/reference/authoring-surface/type-opensecretmark.md +12 -0
  163. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +31 -0
  164. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +32 -0
  165. package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
  166. package/docs/reference/authoring-surface/type-poolspec.md +60 -0
  167. package/docs/reference/authoring-surface/type-pushinput.md +15 -0
  168. package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
  169. package/docs/reference/authoring-surface/type-pushoutput.md +124 -0
  170. package/docs/reference/authoring-surface/type-pushwireoutput.md +12 -0
  171. package/docs/reference/authoring-surface/type-quantity.md +14 -0
  172. package/docs/reference/authoring-surface/type-reached.md +12 -0
  173. package/docs/reference/authoring-surface/type-reposlot.md +70 -0
  174. package/docs/reference/authoring-surface/type-reposlotsof.md +16 -0
  175. package/docs/reference/authoring-surface/type-sandboxoptions.md +77 -0
  176. package/docs/reference/authoring-surface/type-secret.md +25 -0
  177. package/docs/reference/authoring-surface/type-sender.md +19 -0
  178. package/docs/reference/authoring-surface/type-sentbyguards.md +12 -0
  179. package/docs/reference/authoring-surface/type-size.md +85 -0
  180. package/docs/reference/authoring-surface/type-slothandles.md +32 -0
  181. package/docs/reference/authoring-surface/type-sourcenextresult.md +17 -0
  182. package/docs/reference/authoring-surface/type-sourcespec.md +44 -0
  183. package/docs/reference/authoring-surface/type-syncstate.md +12 -0
  184. package/docs/reference/authoring-surface/type-text.md +19 -0
  185. package/docs/reference/authoring-surface/type-textpart.md +24 -0
  186. package/docs/reference/authoring-surface/type-thinkinglevel.md +39 -0
  187. package/docs/reference/authoring-surface/type-toparameterizedobject.md +14 -0
  188. package/docs/reference/authoring-surface/type-toprovidedactor.md +14 -0
  189. package/docs/reference/authoring-surface/type-unknown.md +21 -0
  190. package/docs/reference/authoring-surface/type-usercontainer.md +77 -0
  191. package/docs/reference/authoring-surface/type-withoutemptyindex.md +20 -0
  192. package/docs/reference/authoring-surface/type-workflowevent.md +46 -0
  193. package/docs/reference/authoring-surface/type-workspaceaccess.md +16 -0
  194. package/docs/reference/authoring-surface/type-workspaced.md +94 -0
  195. package/docs/reference/authoring-surface/type-workspacehandles.md +23 -0
  196. package/docs/reference/authoring-surface/type-workspacemachine.md +29 -0
  197. package/docs/reference/authoring-surface/type-workspaceof.md +13 -0
  198. package/docs/reference/authoring-surface/type-workspaceoptions.md +39 -0
  199. package/docs/reference/authoring-surface/type-workspacespec.md +37 -0
  200. package/docs/reference/authoring-surface/workspace.md +259 -0
  201. package/docs/reference/authoring-surface.md +197 -0
  202. package/docs/reference/cli.md +433 -0
  203. package/docs/reference/config.md +756 -0
  204. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +113 -0
  205. package/docs/reference/decisions/0002-duplex-streaming-actor.md +110 -0
  206. package/docs/reference/decisions/0004-worktree-provisioning-model.md +124 -0
  207. package/docs/reference/decisions/0005-sandbox-pod-composition.md +230 -0
  208. package/docs/reference/decisions/0006-agent-control-surface.md +47 -0
  209. package/docs/reference/decisions/0007-durable-machine-state.md +109 -0
  210. package/docs/reference/decisions/0008-orchestrator-as-deployed-app.md +60 -0
  211. package/docs/reference/decisions/0009-cli-and-instance-interface.md +275 -0
  212. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +160 -0
  213. package/docs/reference/decisions/0011-workflow-defined-events.md +141 -0
  214. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +103 -0
  215. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +157 -0
  216. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +95 -0
  217. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +265 -0
  218. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +124 -0
  219. package/docs/reference/decisions/0017-pool-over-a-source-port.md +69 -0
  220. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +111 -0
  221. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +275 -0
  222. package/docs/reference/decisions/0020-private-ca-trust-is-a-named-concept.md +60 -0
  223. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +100 -0
  224. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +103 -0
  225. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +176 -0
  226. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +439 -0
  227. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +65 -0
  228. package/docs/reference/decisions/0026-a-turn-that-is-over-is-never-prompted.md +62 -0
  229. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +237 -0
  230. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +89 -0
  231. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +154 -0
  232. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +100 -0
  233. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +190 -0
  234. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +61 -0
  235. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +127 -0
  236. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +57 -0
  237. package/docs/reference/decisions/0035-a-runaway-turn-is-ended-rerolled-once-then-a-fault.md +92 -0
  238. package/docs/reference/decisions/0036-a-turn-compacts-its-own-context-mid-flight.md +138 -0
  239. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +196 -0
  240. package/docs/reference/decisions/0038-jr2-up-builds-every-image-it-deploys.md +170 -0
  241. package/docs/reference/decisions/0039-image-garbage-collects-by-reachability.md +115 -0
  242. package/docs/reference/decisions/0041-a-build-the-host-already-holds-is-not-spent-again.md +64 -0
  243. package/docs/reference/decisions/0042-ready-is-not-routable-an-admission-retries-its-connection.md +142 -0
  244. package/docs/reference/decisions/0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md +170 -0
  245. package/docs/reference/decisions/0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md +85 -0
  246. package/docs/reference/decisions/0045-the-platform-joins-the-image-address-and-the-cluster-chooses-it.md +82 -0
  247. package/docs/reference/decisions/0046-a-converge-failure-carries-its-diagnosis.md +34 -0
  248. package/docs/reference/decisions/0047-the-git-ssh-key-source-is-the-users-choice.md +47 -0
  249. package/docs/reference/decisions/0048-the-orchestrator-boots-without-its-repos.md +36 -0
  250. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +126 -0
  251. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +73 -0
  252. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +227 -0
  253. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +81 -0
  254. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +111 -0
  255. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +126 -0
  256. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +107 -0
  257. package/docs/reference/decisions/0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md +62 -0
  258. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +152 -0
  259. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +106 -0
  260. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +384 -0
  261. package/docs/reference/decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md +111 -0
  262. package/docs/reference/decisions/0061-a-memory-kill-ends-the-agents-processes-not-the-harness.md +83 -0
  263. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +212 -0
  264. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +83 -0
  265. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +79 -0
  266. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +65 -0
  267. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +550 -0
  268. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +430 -0
  269. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +464 -0
  270. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +180 -0
  271. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +323 -0
  272. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +183 -0
  273. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +144 -0
  274. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +122 -0
  275. package/docs/reference/decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md +76 -0
  276. package/docs/reference/decisions.md +83 -0
  277. package/docs/reference/glossary.md +648 -0
  278. package/docs/reference/http-api.md +1348 -0
  279. package/docs/reference/machines.md +190 -0
  280. package/docs/reference/mechanism-events.md +72 -0
  281. package/docs/why.md +206 -0
  282. package/package.json +6 -4
  283. package/src/actor.ts +976 -61
  284. package/src/agent.ts +114 -38
  285. package/src/ambient.ts +4 -1
  286. package/src/assembly-checks.ts +347 -0
  287. package/src/assembly-tar.ts +494 -0
  288. package/src/assembly.ts +721 -0
  289. package/src/cause.ts +234 -0
  290. package/src/chart.ts +2182 -0
  291. package/src/charts.ts +748 -0
  292. package/src/config.ts +520 -162
  293. package/src/content-hash.ts +105 -0
  294. package/src/custodian.lua +6 -5
  295. package/src/custodian.ts +143 -39
  296. package/src/customize.ts +388 -55
  297. package/src/dispatch-effect.ts +945 -0
  298. package/src/door-compat.ts +217 -0
  299. package/src/durability.ts +11 -4
  300. package/src/effect-checks.ts +557 -0
  301. package/src/effect.ts +1202 -0
  302. package/src/effector-wire.ts +674 -0
  303. package/src/fail.ts +209 -0
  304. package/src/fingerprint.ts +4 -3
  305. package/src/gate.ts +8 -0
  306. package/src/git-clone.ts +65 -0
  307. package/src/git-push.ts +336 -0
  308. package/src/harness-client.ts +764 -48
  309. package/src/held-secrets.ts +82 -26
  310. package/src/holds.ts +156 -0
  311. package/src/http-effect.ts +1423 -0
  312. package/src/http.ts +916 -113
  313. package/src/images.ts +47 -21
  314. package/src/index.ts +62 -43
  315. package/src/instance.ts +505 -59
  316. package/src/internal.ts +84 -0
  317. package/src/kube-client.ts +333 -0
  318. package/src/machine-doc.ts +106 -6
  319. package/src/mechanism-routes.ts +168 -0
  320. package/src/model-keys.generated.ts +53 -0
  321. package/src/model-keys.ts +18 -58
  322. package/src/mount-digest.ts +53 -0
  323. package/src/names.ts +66 -8
  324. package/src/notices.ts +141 -0
  325. package/src/open-body.ts +1580 -0
  326. package/src/open-effect.ts +217 -0
  327. package/src/open.ts +59 -6
  328. package/src/parts.ts +709 -41
  329. package/src/party.ts +534 -0
  330. package/src/pick.ts +761 -0
  331. package/src/pool.ts +248 -37
  332. package/src/registration.ts +382 -19
  333. package/src/repo-fetch.ts +60 -85
  334. package/src/repos.ts +65 -75
  335. package/src/rest.ts +133 -0
  336. package/src/run-host.ts +2383 -115
  337. package/src/run-output.ts +38 -0
  338. package/src/sandbox-effect.ts +1126 -0
  339. package/src/sandbox-effector.ts +111 -0
  340. package/src/sandbox-kube.ts +1721 -0
  341. package/src/sandbox-watch.ts +260 -0
  342. package/src/secret-placement.ts +529 -0
  343. package/src/server.ts +284 -84
  344. package/src/setup.ts +819 -117
  345. package/src/size.ts +454 -0
  346. package/src/snapshot-store.ts +115 -20
  347. package/src/stub-harness.ts +49 -16
  348. package/src/sync-client.ts +54 -0
  349. package/src/timers.ts +191 -0
  350. package/src/tokens.ts +35 -4
  351. package/src/vocabulary.ts +135 -15
  352. package/src/wire.ts +238 -10
  353. package/src/workspace.ts +1195 -338
  354. package/src/sandbox-kubectl.ts +0 -1108
package/src/agent.ts CHANGED
@@ -22,57 +22,133 @@
22
22
 
23
23
  import { open } from "./open.ts";
24
24
 
25
- /** jr2's reasoning-effort scale (ADR-0027) — a strict subset of the runtime's, so every value
26
- * passes through unmapped; mirrored by `@jr2/harness`'s spec contract. */
25
+ // jr2's reasoning-effort scale (ADR-0027) — a strict subset of the runtime's, so every value
26
+ // passes through unmapped; mirrored by `@jr2/harness`'s spec contract.
27
+ /**
28
+ * How hard the model thinks before it answers: `"off"`, `"minimal"`, `"low"`, `"medium"`,
29
+ * `"high"` or `"xhigh"`. Set it on an Agent's definition (`thinkingLevel`), or for one Turn in the
30
+ * invoke's `input`.
31
+ *
32
+ * A model can lack a level. Claude Sonnet 5.5 has no `"off"`, and at `"off"` the Harness's request
33
+ * then sends effort `"high"`. The Harness does not refuse such a level: it runs the Turn, and the
34
+ * printed conversation names the level that the model ran at.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * import { agent, type ThinkingLevel } from "@jr2/orchestrator";
39
+ *
40
+ * const effort: ThinkingLevel = "high";
41
+ *
42
+ * export const reviewer = agent({
43
+ * model: "anthropic/claude-haiku-5-5",
44
+ * instructions: "You review diffs for correctness.",
45
+ * thinkingLevel: effort,
46
+ * });
47
+ * ```
48
+ *
49
+ * @see docs/guides/set-an-agents-access-and-dials.md
50
+ * @see docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md
51
+ */
27
52
  export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
28
53
 
29
- /** What an Agent may DO to the Workspace (ADR-0028) — and, through `"none"`, where its Turn runs
30
- * (ADR-0031): the value the Agent actor reads off its own slot's definition to place the Turn. */
54
+ // The value the Agent actor reads off its own slot's definition to place the Turn (ADR-0028,
55
+ // ADR-0031).
56
+ /** What an Agent may do to the Workspace: `"write"`, `"read"` or `"none"`. With `"none"`, its Turn
57
+ * runs on the Instance Harness, not in a Sandbox. The `workspace` field of {@link AgentDefinition}
58
+ * tells what each value gives. */
31
59
  export type WorkspaceAccess = "write" | "read" | "none";
32
60
 
33
- /** The plain-data Agent definition (ADR-0018): IDENTITY plus one default. A Turn's Frame — what
34
- * it is about and where it works — is not here and cannot be (ADR-0057): a Worktree path exists
35
- * only once a run has a branch, so no definition written before the run could name one.
61
+ // The plain-data Agent definition (ADR-0018): IDENTITY plus one default. A Turn's Frame — what it
62
+ // is about and where it works — is not here and cannot be (ADR-0057): a Worktree path exists only
63
+ // once a run has a branch, so no definition written before the run could name one.
64
+ //
65
+ // Must stay JSON-serializable: the definition rides the Turn to the Harness (ADR-0049), so
66
+ // anything non-serializable would be silently lost — grow this contract deliberately. ADR-0028
67
+ // added a restriction vocabulary (`workspace`), not an extension one: custom tool implementations
68
+ // stay out of the contract.
69
+ /**
70
+ * An Agent's definition: which model it runs, its instructions, and what it may do to the
71
+ * Workspace. Give it to `agent()` to get the Agent's actor slot. It is plain data that can be
72
+ * written as JSON.
73
+ *
74
+ * It holds who the Agent is, not what one Turn does. The prompt of a Turn and the directory it
75
+ * works in are the Frame. The state that invokes the Agent sets the Frame in the invoke's
76
+ * `input`. That `input` can also set `model` and `thinkingLevel` for one Turn: the Dials. It
77
+ * cannot change `instructions` or `workspace`.
36
78
  *
37
- * Must stay JSON-serializable: the definition rides
38
- * the Turn to the Harness (ADR-0049), so anything non-serializable would be silently lost — grow
39
- * this contract deliberately. ADR-0028 added a restriction vocabulary (`workspace`), not an
40
- * extension one: custom tool implementations stay out of the contract. */
79
+ * @example
80
+ * ```ts
81
+ * import { agent, type AgentDefinition } from "@jr2/orchestrator";
82
+ *
83
+ * const definition: AgentDefinition = {
84
+ * model: "anthropic/claude-haiku-5-5",
85
+ * instructions: "You fix the failing test, and you change nothing else.",
86
+ * description: "Fixes one failing test",
87
+ * thinkingLevel: "medium",
88
+ * workspace: "write",
89
+ * };
90
+ *
91
+ * export const coder = agent(definition);
92
+ * ```
93
+ *
94
+ * @see docs/concepts/agents-and-turns.md
95
+ * @see docs/guides/set-an-agents-access-and-dials.md
96
+ * @see docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md
97
+ * @see docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md
98
+ */
41
99
  export type AgentDefinition = {
42
- /** Model specifier, `<provider>/<modelId>`, e.g. `anthropic/claude-sonnet-4-6`. REQUIRED —
43
- * there is no instance-wide default (ADR-0018): `jr2.config.ts`'s `harness` section
44
- * declares which providers are REACHABLE, and the definition makes the choice. This is also the
45
- * only model `jr2 up` can preflight, since a workflow's is not statically recoverable. An
46
- * invocation may override it for one Turn (`AgentTurnInput.model`). */
100
+ // REQUIRED, with no instance-wide default (ADR-0018): `jr2.config.ts`'s `harness` section
101
+ // declares which providers are REACHABLE, and the definition makes the choice. This is also the
102
+ // only model `jr2 up` can preflight, since a workflow's is not statically recoverable. An
103
+ // invocation may override it for one Turn (`AgentTurnInput.model`).
104
+ /** The model, as `<provider>/<modelId>`: for example `anthropic/claude-haiku-5-5`. The
105
+ * provider must be one that `jr2.config.ts` can reach. Here it is always a string: an `open`
106
+ * model of `agent()` must be bound before a Turn starts. One Turn may set another model in the
107
+ * invoke's `input`. */
47
108
  model: string;
48
- /** The Agent's system prompt. */
109
+ /** The Agent's instructions: the text that tells the model who it is and how to work. The same
110
+ * on every Turn. */
49
111
  instructions: string;
50
- /** Optional static description — observability, never sent to the model. */
112
+ /** A short description, for people who read the Machine. The model never reads it. */
51
113
  description?: string;
52
- /** Reasoning effort. Omitted → the runtime's default. An invocation may override it for one
53
- * Turn (`AgentTurnInput.thinkingLevel`) — effort is a property of the task's difficulty, so the
54
- * same persona legitimately runs at different settings in different Machines. */
114
+ // Effort is a property of the task's difficulty, so the same persona legitimately runs at
115
+ // different settings in different Machines; an invocation may override it for one Turn
116
+ // (`AgentTurnInput.thinkingLevel`).
117
+ /** How hard the model thinks: one of {@link ThinkingLevel}. Absent: `"off"`. A model with no
118
+ * `"off"` runs at another level, and the printed conversation names it. One Turn may set another
119
+ * level in the invoke's `input`. */
55
120
  thinkingLevel?: ThinkingLevel;
56
- /** What this Agent may DO to the Workspace (ADR-0028) — the persona in one word, deliberately
57
- * not `tools` (that names the control-plane Menu, what it may SAY). `"read"` withholds the
58
- * write/edit Working tools; bash stays, so this states intent and stops the honest path — the
59
- * detached review worktree is the containment. `"none"` withholds the ENTIRE Working toolset:
60
- * the Menu-only Agent converses and picks from its Menu, nothing else (so its Turn frames no
61
- * directory — only Working tools consume one) — and places the Turn on the Instance Harness
62
- * (ADR-0031). Default `"write"`. */
121
+ // The persona in one word (ADR-0028), deliberately not `tools` (that names the control-plane
122
+ // Menu, what it may SAY). `"read"` states intent and stops the honest path: bash stays, and the
123
+ // detached review worktree is the containment. `"none"` frames no directory — only Working tools
124
+ // consume one — and places the Turn on the Instance Harness (ADR-0031).
125
+ /** What the Agent may do to the Workspace. Default: `"write"`.
126
+ * - `"write"`: all the Working tools (read, write, edit, bash, grep, glob).
127
+ * - `"read"`: no write or edit tool. The Agent keeps bash, so `"read"` does not stop a command
128
+ * that writes. To keep the Agent from changing the branch, frame its Turn in the review
129
+ * Worktree: `reviewSha` in the spec, then `cwd: context.workspace.review?.<slot>`.
130
+ * - `"none"`: no Working tools. The Agent only reads its prompt and picks from its Menu. Its Turn
131
+ * runs on the Instance Harness, not in a Sandbox, so it needs no `workspace()` around it. */
63
132
  workspace?: WorkspaceAccess;
64
133
  };
65
134
 
66
- /**
67
- * The Agent as an author DECLARES it (ADR-0054): the definition, except that `model` may be Open —
68
- * the one part a packaged Machine cannot honestly fill, since the package cannot pay for it. Every
69
- * other field is the wire's, because every other field a package CAN state.
70
- *
71
- * This is what `agent()` takes and what the slot's brand carries, so the walk and `customize()`
72
- * read the author's own answer, Open included. Nothing sends this: the wire takes an
73
- * {@link AgentDefinition}, and {@link requireBoundAgent} is the one door between the two.
74
- */
75
- export type AgentDeclaration = Omit<AgentDefinition, "model"> & { model: string | typeof open };
135
+ // The Agent as an author DECLARES it (ADR-0054): the definition, except that `model` may be Open —
136
+ // the one part a packaged Machine cannot honestly fill, since the package cannot pay for it. Every
137
+ // other field is the wire's, because every other field a package CAN state.
138
+ //
139
+ // This is what `agent()` takes and what the slot's brand carries, so the walk and `customize()`
140
+ // read the author's own answer, Open included. Nothing sends this: the wire takes an
141
+ // AgentDefinition, and `requireBoundAgent` is the one door between the two.
142
+ /** What `agent()` takes: an {@link AgentDefinition}, except that `model` can also be `open`. */
143
+ export type AgentDeclaration = Omit<AgentDefinition, "model"> & {
144
+ /** The model, as `<provider>/<modelId>`: for example `anthropic/claude-haiku-5-5`. Required:
145
+ * there is no default model. The provider must be one that `jr2.config.ts` can reach.
146
+ *
147
+ * Or `open`, in a Machine that you publish as a package. Then the Instance that registers the
148
+ * Machine binds the model with `customize()`. One Turn may set another model in the invoke's
149
+ * `input`. */
150
+ model: string | typeof open;
151
+ };
76
152
 
77
153
  /** The brand `agent(declaration)` stamps on its logic (ADR-0049): the declaration itself, readable
78
154
  * off the logic object. Everything that must recognize an Agent slot reads THIS — never a name
package/src/ambient.ts CHANGED
@@ -23,7 +23,10 @@ export type AmbientHandles = {
23
23
  sandbox: string;
24
24
  /** Every Repo Slot's branch-worktree path, by slot (ADR-0051). */
25
25
  repos: Record<string, string>;
26
- branch: string;
26
+ /** Every Repo Slot's Binding url, by slot (ADR-0051): where a push goes. */
27
+ bindings?: Record<string, string>;
28
+ /** The branch the Worktrees are on. Absent for a Workspace with no Repo Slot (ADR-0051). */
29
+ branch?: string;
27
30
  /** Detached review-worktree paths, when the spec carried `reviewSha` (ADR-0028). */
28
31
  review?: Record<string, string>;
29
32
  };
@@ -0,0 +1,347 @@
1
+ // The walk's refusals, as a LISTING (ADR-0069). The walk runs twice: `jr2 up` runs it on the host
2
+ // before any cluster work, and the door runs it again over the unpacked Assembly before the
3
+ // publish lands. Both ends call these functions, so the two walks cannot drift. They return the
4
+ // lines and print nothing: `jr2 up` prints them after `refusing: `, and the door returns them in
5
+ // its `422` report.
6
+
7
+ import { isAbsolute, relative, sep } from "node:path";
8
+ import type { AnyStateMachine } from "xstate";
9
+ import { z } from "zod";
10
+ import type { JR2Config } from "./config.ts";
11
+ import { imageContextDigest, type ImageRefs } from "./images.ts";
12
+ import { openPartsText } from "./open-effect.ts";
13
+ import { customizeLine, partsOf, placedEffectsOf, workspacePodsOf } from "./parts.ts";
14
+ import { refuseEffectSecrets, type PlacementWalk } from "./secret-placement.ts";
15
+ import {
16
+ chartFileRefusals,
17
+ chartName,
18
+ chartWrapperOf,
19
+ splitChartName,
20
+ wrapperFault,
21
+ type ChartFile,
22
+ } from "./charts.ts";
23
+ import { doorCompat } from "./door-compat.ts";
24
+
25
+ // The comparison is the same one `jr2 up`'s foreign preflight makes, so it is exported beside the
26
+ // walk the CLI already imports (ADR-0071).
27
+ export { doorCompat, type DoorCompat } from "./door-compat.ts";
28
+ import { inputSchemaOf, isZodSchema } from "./vocabulary.ts";
29
+
30
+ /** One registered Workflow, as `loadWorkflows` gives it. */
31
+ export type LoadedWorkflow = { name: string; machine: AnyStateMachine };
32
+
33
+ /**
34
+ * Every registered Machine that has a zod `input` on its `createMachine` config and that `jr2Setup`
35
+ * did not make (ADR-0033). The `setup()` of xstate keeps that `input` in the config but does not read it,
36
+ * so the Machine has no door: a start with any input passes, and the Console start form is only a
37
+ * field for JSON. TypeScript accepts the config, so only this walk finds it. One line per Workflow,
38
+ * naming the fix.
39
+ * A Machine with no `input`, or with an `input` that is not zod, can still use `setup()`.
40
+ *
41
+ * The line text follows `refusing: ` in `jr2 up`'s output; the prefix is the caller's.
42
+ */
43
+ export function doorRefusals(workflows: readonly LoadedWorkflow[]): string[] {
44
+ const lines: string[] = [];
45
+ for (const w of workflows) {
46
+ const declared = (w.machine.config as { input?: unknown }).input;
47
+ if (!isZodSchema(declared) || inputSchemaOf(w.machine)) continue;
48
+ lines.push(
49
+ `workflow "${w.name}": machine "${w.machine.id}" has a zod \`input\` on createMachine, but jr2Setup did not ` +
50
+ "make this Machine, so no door reads that input: a run starts with any input, and the Console start form " +
51
+ "is only a field for JSON — make the Machine with jr2Setup in place of setup() or createMachine() (leave " +
52
+ "out `events` if it takes no workflow event) (ADR-0033)",
53
+ );
54
+ }
55
+ return lines;
56
+ }
57
+
58
+ /**
59
+ * Every part a registered Machine leaves Open (ADR-0051, ADR-0054): a Repo Slot with no url, an
60
+ * Agent with no model, an Effect's url, argv or secret. One line per part, in walk order within
61
+ * each kind, so a composer fixes all of them in one edit. Each line names the Workflow and the
62
+ * `customize()` line that binds the part. A part on a Machine invoked inline has no fix line,
63
+ * because no `customize()` reaches an actor without a slot key.
64
+ *
65
+ * The line text follows `refusing: ` in `jr2 up`'s output; the prefix is the caller's.
66
+ */
67
+ export function openPartRefusals(workflows: readonly LoadedWorkflow[]): string[] {
68
+ const lines: string[] = [];
69
+ for (const w of workflows) {
70
+ const walked = partsOf([w.machine]);
71
+ const opened: Array<{
72
+ slot: string | undefined;
73
+ path: readonly string[] | undefined;
74
+ part: Parameters<typeof customizeLine>[3];
75
+ what: string;
76
+ adr: string;
77
+ }> = [
78
+ ...walked.openSlots.map((o) => ({
79
+ ...o,
80
+ part: "repo" as const,
81
+ // An Open MAP names no slot (the composer does), so it is refused as "the Repo Slots".
82
+ what: o.slot === undefined ? "its Repo Slots" : `Repo Slot "${o.slot}"`,
83
+ adr: "ADR-0051",
84
+ })),
85
+ ...walked.openAgents.map((o) => ({ ...o, part: "agent" as const, what: `Agent "${o.slot}"`, adr: "ADR-0054" })),
86
+ // An Effect's Open url, argv or secret (ADR-0054): one binding object for an Effect with two.
87
+ ...walked.openEffects.map(({ parts, ...o }) => ({
88
+ ...o,
89
+ part: parts,
90
+ what: `the ${openPartsText(parts)} of Effect "${o.slot}"`,
91
+ adr: "ADR-0054",
92
+ })),
93
+ ];
94
+ for (const { slot, path, part, what, adr } of opened) {
95
+ if (path === undefined) {
96
+ lines.push(
97
+ `workflow "${w.name}" leaves ${what} open on a Machine invoked inline — no ` +
98
+ "customize() reaches an actor without a slot key; declare that Machine under setup({ actors }) and " +
99
+ `bind it there (ADR-0049, ${adr})`,
100
+ );
101
+ continue;
102
+ }
103
+ const where = path.length ? ` (on the Machine composed as ${path.map((k) => `"${k}"`).join(" → ")})` : "";
104
+ lines.push(
105
+ `workflow "${w.name}" leaves ${what} open${where} — bind it where the Machine is ` +
106
+ `registered: export const machine = ${customizeLine("<import>", path, slot, part)} (${adr})`,
107
+ );
108
+ }
109
+ }
110
+ return lines;
111
+ }
112
+
113
+ /**
114
+ * What the registered Machines name for secret placement (ADR-0059, ADR-0068): every Effect and
115
+ * every Workspace's pod, per route, and the models the Instance Harness hosts: those of the `"none"`
116
+ * Agents and of every Open body that allows `"none"` (ADR-0070). `jr2 up` and the door build the same walk from the same Workflows.
117
+ */
118
+ export function placementWalkOf(workflows: readonly LoadedWorkflow[]): PlacementWalk {
119
+ // The walk's own `instanceHarness` (ADR-0068, ADR-0070): the models of the "none" Agents and of
120
+ // every Open body that allows "none", so a Chart's minted "none" Agent has its key placed, or the
121
+ // publish is refused, never a 401 mid-run.
122
+ const { instanceHarness } = partsOf(workflows.map((w) => w.machine));
123
+ return {
124
+ // Per route, not per Machine object: a Machine placed twice is checked where each copy runs.
125
+ effects: workflows.flatMap((w) => placedEffectsOf(w.machine).map((effect) => ({ workflow: w.name, effect }))),
126
+ workspaces: workflows.flatMap((w) => workspacePodsOf(w.machine).map((pod) => ({ workflow: w.name, pod }))),
127
+ ...(instanceHarness ? { instanceHarness: { models: [...instanceHarness.models] } } : {}),
128
+ };
129
+ }
130
+
131
+ /**
132
+ * What a `dispatch` names, checked against what the walk can see (ADR-0071). Each dispatch with no
133
+ * target — a same-Instance one; a foreign target is `jr2 up`'s preflight, a warning — must name a
134
+ * Workflow the Assembly has: a Machine (`workflows/<name>.ts`), or `<wrapper>/<name>`, a named Chart
135
+ * under a Chart wrapper the Assembly registers, from a Chart file or (when `runtime` is given) a
136
+ * runtime Chart. A miss is an error. Without `runtime` — on the host, which cannot see the
137
+ * Orchestrator's volume — a slashed name under a known wrapper with no Chart file is a warning.
138
+ * Then the slot's input schema, when it has one, is compared with the callee's door
139
+ * (`doorCompat`): a fault is an error, naming the field; a schema outside the compared subset is a
140
+ * warning, "not compared".
141
+ *
142
+ * A dispatch resolves at RUN time against the current Assembly (ADR-0069); this check is of the
143
+ * Assembly being published, so a later publish that drops the callee answers `refused` with the
144
+ * door's 404 then, and is not refused for it here.
145
+ */
146
+ export function dispatchRefusals(
147
+ workflows: readonly LoadedWorkflow[],
148
+ charts: readonly ChartFile[],
149
+ runtime?: readonly { name: string }[],
150
+ ): { errors: string[]; warnings: string[] } {
151
+ const errors: string[] = [];
152
+ const warnings: string[] = [];
153
+ const seen = new Set<string>();
154
+ for (const w of workflows) {
155
+ for (const effect of partsOf([w.machine]).effects) {
156
+ // A target, bound or Open, is another Orchestrator's: its door is not in this Assembly.
157
+ if (effect.kind !== "dispatch" || effect.target !== undefined || effect.open !== undefined) continue;
158
+ const name = effect.workflow;
159
+ const at = `workflow "${w.name}" dispatches "${name}"`;
160
+ const once = JSON.stringify([w.name, effect.name, name]);
161
+ if (seen.has(once)) continue;
162
+ seen.add(once);
163
+ const callee = calleeOf(workflows, charts, runtime, name);
164
+ if ("error" in callee) {
165
+ errors.push(`${at} ${callee.error} (ADR-0071)`);
166
+ continue;
167
+ }
168
+ // A slashed name with no Chart the host can see still has a known door: the wrapper's minus
169
+ // its Chart fields, the same for every Chart under it. So it is warned AND compared.
170
+ if (callee.warning) warnings.push(`${at}: ${callee.warning} (ADR-0071)`);
171
+ if (effect.input === undefined) continue;
172
+ if ("notDrawn" in callee.door) {
173
+ warnings.push(`${at}: its input schema and the door are not compared — ${callee.door.notDrawn} (ADR-0071)`);
174
+ continue;
175
+ }
176
+ const compared = doorCompat(effect.input, callee.door.schema);
177
+ if (compared.ok === "not-compared") {
178
+ warnings.push(`${at}: its input schema and the door are not compared — ${compared.why} (ADR-0071)`);
179
+ } else if (compared.ok === false) {
180
+ for (const fault of compared.faults) {
181
+ errors.push(
182
+ `${at} with input its door does not take: ${fault} — align the slot's \`input\` with the door ` +
183
+ "(derive it from the door's schema, `door.pick({ ... })`) (ADR-0071)",
184
+ );
185
+ }
186
+ }
187
+ }
188
+ }
189
+ return { errors, warnings };
190
+ }
191
+
192
+ /** The door a dispatch's name resolves to in these Workflows, or why it does not. */
193
+ function calleeOf(
194
+ workflows: readonly LoadedWorkflow[],
195
+ charts: readonly ChartFile[],
196
+ runtime: readonly { name: string }[] | undefined,
197
+ name: string,
198
+ ): { door: Door; warning?: string } | { error: string } {
199
+ const split = splitChartName(name);
200
+ if (!split) {
201
+ const found = workflows.find((w) => w.name === name);
202
+ if (!found) {
203
+ return {
204
+ error:
205
+ `which no Workflow in the Assembly has — add workflows/${name}.ts, or name one it registers` +
206
+ (workflows.length ? `: ${workflows.map((w) => `"${w.name}"`).join(", ")}` : ""),
207
+ };
208
+ }
209
+ return { door: doorOf(found.machine, []) };
210
+ }
211
+ const { wrapper, name: short } = split;
212
+ const fault = wrapperFault(workflows, wrapper);
213
+ if (fault) {
214
+ return {
215
+ error:
216
+ fault.status === 404
217
+ ? `which no Workflow in the Assembly has — no workflows/${wrapper}.ts registers the Chart wrapper "${wrapper}"`
218
+ : `which no Workflow in the Assembly can be: ${fault.error}`,
219
+ };
220
+ }
221
+ const machine = workflows.find((w) => w.name === wrapper)!.machine;
222
+ const door = doorOf(machine, chartWrapperOf(machine)?.chartFields ?? []);
223
+ const file = `workflows/${wrapper}/${short}.chart.json`;
224
+ if (charts.some((c) => chartName(c.wrapper, c.name) === name)) return { door };
225
+ if (runtime === undefined) {
226
+ return {
227
+ door,
228
+ warning:
229
+ `the Assembly has no Chart file ${file}, and a runtime Chart lives on the Orchestrator's volume alone, ` +
230
+ "which jr2 up cannot see — the publish door checks it, and a run of the dispatch is refused with the door's " +
231
+ "404 if it is not there",
232
+ };
233
+ }
234
+ if (runtime.some((c) => c.name === name)) return { door };
235
+ return {
236
+ error:
237
+ `which no Workflow in the Assembly has — add the Chart file ${file}; or, for a runtime Chart, PUT ` +
238
+ `/workflows/${wrapper}/charts/${short} before this publish (a PUT needs "${wrapper}" in the current ` +
239
+ `Assembly: when this publish adds it, publish "${wrapper}" first, then PUT the Chart, then publish the dispatch)`,
240
+ };
241
+ }
242
+
243
+ /** A door as the comparison reads it: its JSON Schema (null: it declares no input), or why zod
244
+ * could not draw one. */
245
+ type Door = { schema: Record<string, unknown> | null } | { notDrawn: string };
246
+
247
+ /** A Workflow's door as JSON Schema, exactly as `GET /workflows/<name>` serves it (run-host.ts
248
+ * `inputSchema`): null for a Machine that declares none; for a named Chart the wrapper's minus its
249
+ * Chart fields, which the Chart supplies (ADR-0070). A door zod cannot draw (`z.date()`,
250
+ * `z.custom()`) is not drawn as "any", which would guess "compatible": it is not compared. */
251
+ function doorOf(machine: AnyStateMachine, chartFields: readonly string[]): Door {
252
+ let schema = inputSchemaOf(machine);
253
+ if (!schema) return { schema: null };
254
+ if (chartFields.length) schema = schema.omit(Object.fromEntries(chartFields.map((f) => [f, true])) as never);
255
+ try {
256
+ return { schema: z.toJSONSchema(schema, { io: "input" }) as Record<string, unknown> };
257
+ } catch (err) {
258
+ return {
259
+ notDrawn: `the door has a part JSON Schema cannot state (${err instanceof Error ? err.message : String(err)})`,
260
+ };
261
+ }
262
+ }
263
+
264
+ export type AssemblyRefusalInput = {
265
+ /** The Assembly's `jr2.config.ts`, loaded; `undefined` when it ships none. */
266
+ config: JR2Config | undefined;
267
+ /** Every Workflow the Assembly registers, loaded. */
268
+ workflows: readonly LoadedWorkflow[];
269
+ /**
270
+ * The Assembly's Chart files (ADR-0070), read: `workflows/<wrapper>/<name>.chart.json`. Each is
271
+ * admitted against its wrapper's Open body by the same door a run's Chart passes. Absent, none.
272
+ */
273
+ charts?: readonly ChartFile[];
274
+ /**
275
+ * The runtime Charts on the Orchestrator's volume (ADR-0070), by Workflow name
276
+ * (`<wrapper>/<name>`): what a dispatch of a named Chart may resolve to beside the Chart files
277
+ * (ADR-0071). The door can see them; absent, none.
278
+ */
279
+ runtimeCharts?: readonly { name: string }[];
280
+ /**
281
+ * The image map the Orchestrator reads per provision (ADR-0038), or a reader of it. Present
282
+ * only when deployed: a host-booted process has no map, and no Sandbox to provision. When
283
+ * present, every `file:` context a Machine names must have its digest in the map.
284
+ */
285
+ images?: () => Promise<ImageRefs>;
286
+ /**
287
+ * The directory the Assembly was imported from. A refusal names a `file:` context relative to
288
+ * it (`images/box`), as the composer's own folder holds it: at the door that directory is a
289
+ * staging path, which is gone when the refusal arrives.
290
+ */
291
+ root?: string;
292
+ };
293
+
294
+ /**
295
+ * The door's walk (ADR-0069): the refusals the Orchestrator can see in an Assembly it has already
296
+ * imported. An import error, a missing `export const machine` and a Vocabulary error are the
297
+ * caller's: they throw at import, before this runs. Here: a zod `input` that no door reads, every
298
+ * Open part, a secret the config does not declare (or a literal one too short to scrub), a `file:`
299
+ * image context whose digest the mounted image map does not hold, and a Chart file that does not
300
+ * admit under its wrapper (ADR-0070), each line naming the file. The door runs this after `jr2 up`
301
+ * did its cluster work, so each check is true when the Assembly lands.
302
+ */
303
+ export async function assemblyRefusals(input: AssemblyRefusalInput): Promise<string[]> {
304
+ const lines = [...doorRefusals(input.workflows), ...openPartRefusals(input.workflows)];
305
+ // A Chart file is the Instance's own (ADR-0070): one that fails refuses the publish.
306
+ if (input.charts?.length) lines.push(...chartFileRefusals(input.charts, input.workflows));
307
+ // A same-Instance dispatch names a Workflow this Assembly has (ADR-0071). The door sees the
308
+ // runtime Charts, so a miss is an error here. Its warnings (a schema "not compared") have no
309
+ // channel in the publish's answer, and are dropped: `jr2 up` printed them before the publish.
310
+ lines.push(...dispatchRefusals(input.workflows, input.charts ?? [], input.runtimeCharts ?? []).errors);
311
+ try {
312
+ refuseEffectSecrets(input.config ?? {}, placementWalkOf(input.workflows));
313
+ } catch (err) {
314
+ lines.push(err instanceof Error ? err.message : String(err));
315
+ }
316
+ const images = partsOf(input.workflows.map((w) => w.machine)).images;
317
+ const where = (dir: string): string => {
318
+ const rel = input.root === undefined ? undefined : relative(input.root, dir);
319
+ return rel !== undefined && rel !== "" && !rel.startsWith("..") && !isAbsolute(rel)
320
+ ? rel.split(sep).join("/")
321
+ : dir;
322
+ };
323
+ if (input.images && images.length) {
324
+ let refs: ImageRefs | undefined;
325
+ try {
326
+ refs = await input.images();
327
+ } catch (err) {
328
+ lines.push(err instanceof Error ? err.message : String(err));
329
+ }
330
+ if (refs) {
331
+ for (const image of images) {
332
+ const digest = await imageContextDigest(image.dir);
333
+ if (refs.sandbox[digest] === undefined) {
334
+ const named = input.workflows
335
+ .filter((w) => partsOf([w.machine]).images.some((i) => i.dir === image.dir))
336
+ .map((w) => `"${w.name}"`);
337
+ const by = named.length ? ` that workflow ${named.join(", ")} names` : "";
338
+ lines.push(
339
+ `the Sandbox Image context ${where(image.dir)}${by} (digest ${digest}) is not in the image map — ` +
340
+ "`jr2 up` builds every `file:` context and writes the map before it publishes (ADR-0038, ADR-0069)",
341
+ );
342
+ }
343
+ }
344
+ }
345
+ }
346
+ return lines;
347
+ }