@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
@@ -0,0 +1,84 @@
1
+ // @jr2/orchestrator/internal — NOT for workflow authors. Everything the kit's own packages share
2
+ // that is not on the Authoring surface (index.ts): the run host, the HTTP API, the Harness wire,
3
+ // the Custodian, the Effector wire, the cluster names, the load walk. The CLI, the e2e suite,
4
+ // and any other kit package that imports this one use it. No stability promise: any
5
+ // name here can change or go in any release, so a workflow or a `jr2.config.ts` that imports it
6
+ // is wrong, whatever compiles.
7
+ //
8
+ // It is the whole of index.ts plus the rest, so a kit file imports one specifier. The Orchestrator
9
+ // does NOT speak MCP (ADR-0013): the Harness presents the Menu to its model itself, and reaches the
10
+ // surface here through the Custodian in its pod.
11
+
12
+ export * from "./index.ts";
13
+
14
+ // The wire contract (ADR-0011): `defineEvent` is on the surface; the rest is the kit's.
15
+ export * from "@jr2/agent-protocol";
16
+ export * from "./agent.ts";
17
+ export * from "./effect.ts";
18
+ export * from "./http-effect.ts";
19
+ export * from "./sandbox-effect.ts";
20
+ export * from "./dispatch-effect.ts";
21
+ export type { EffectBinding, OpenEffect, OpenEffectPart } from "./open-effect.ts";
22
+ export { openPartsText } from "./open-effect.ts";
23
+ export * from "./effector-wire.ts";
24
+ export * from "./pick.ts";
25
+ export * from "./chart.ts";
26
+ export * from "./open-body.ts";
27
+ export * from "./charts.ts";
28
+ export * from "./parts.ts";
29
+ export * from "./size.ts";
30
+ export * from "./customize.ts";
31
+ export * from "./ambient.ts";
32
+ export * from "./config.ts";
33
+ export * from "./repo-identity.ts";
34
+ export * from "./pool.ts";
35
+ export * from "./setup.ts";
36
+ export { sentBy } from "./party.ts";
37
+ export * from "./vocabulary.ts";
38
+ export * from "./tokens.ts";
39
+ export * from "./durability.ts";
40
+ export * from "./snapshot-store.ts";
41
+ export * from "./actor.ts";
42
+ // The `gate` actor: jr2Setup pre-registers it and a state invokes it as `src: "gate"`, so no
43
+ // author writes the value (ADR-0015).
44
+ export * from "./gate.ts";
45
+ // The Failure's brand stays in fail.ts, and so does the logic that throws it: the kit makes a Failure
46
+ // only through `fail()` and `workspace()`, which imports that logic from fail.ts (ADR-0073).
47
+ export { failurePayload, isFailSlot } from "./fail.ts";
48
+ export type { Failure, FailSlot, FailureInput } from "./fail.ts";
49
+ export * from "./workspace.ts";
50
+ export * from "./images.ts";
51
+ export * from "./content-hash.ts";
52
+ export * from "./assembly-tar.ts";
53
+ export * from "./assembly.ts";
54
+ export * from "./assembly-checks.ts";
55
+ export * from "./mount-digest.ts";
56
+ export * from "./model-keys.ts";
57
+ export * from "./held-secrets.ts";
58
+ export * from "./secret-placement.ts";
59
+ export * from "./git-push.ts";
60
+ export * from "./git-clone.ts";
61
+ export * from "./custodian.ts";
62
+ export * from "./sandbox-kube.ts";
63
+ export {
64
+ effectorManifestKey,
65
+ LABEL_EFFECTOR_POD,
66
+ readSandboxEffectorManifest,
67
+ sandboxEffectorKeySecret,
68
+ sandboxEffectorObjectsOf,
69
+ type ProjectedSecret,
70
+ type SandboxEffectorManifest,
71
+ } from "./sandbox-effector.ts";
72
+ export * from "./repos.ts";
73
+ export * from "./repo-fetch.ts";
74
+ // Type-only: what a workflow event delivery looks like (the registration TABLE stays internal
75
+ // even here — ADR-0011).
76
+ export type { ChildErrorMarker, DeliveredEvent, PlacingMarker, RunMarker, TurnMarker } from "./registration.ts";
77
+ export * from "./machine-doc.ts";
78
+ export * from "./run-host.ts";
79
+ export * from "./http.ts";
80
+ export * from "./harness-client.ts";
81
+ export * from "./stub-harness.ts";
82
+ export * from "./instance.ts";
83
+ export * from "./server.ts";
84
+ export * from "./names.ts";
@@ -0,0 +1,333 @@
1
+ // The Orchestrator's Kubernetes client (ADR-0063): plain REST on Node's built-in `fetch`, with no
2
+ // dependency. The Orchestrator is always in-cluster (ADR-0019), so the whole of "configuration" is
3
+ // the ServiceAccount the kubelet mounts and the Service the API server publishes:
4
+ //
5
+ // base URL https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT
6
+ // bearer the SA token FILE, read on every request (below)
7
+ // trust the SA `ca.crt`, through NODE_EXTRA_CA_CERTS on the Deployment (deploy.ts) — so
8
+ // this module holds no TLS code at all
9
+ //
10
+ // It speaks for jr2's own kinds alone — Sandbox, Repo, Secret — and every call stays behind the
11
+ // Sandbox and Repo ports, so swapping it for a client library is a contained change if jr2 ever
12
+ // needs exec or many more kinds (ADR-0063's considered options).
13
+ //
14
+ // Its rules, each observed on a real cluster (ADR-0063):
15
+ //
16
+ // - The token is never cached. The kubelet replaced a 10-minute token only about 50 seconds
17
+ // before it expired, so a cached one fails in production and never in a test. A `401` re-reads
18
+ // the file and retries ONCE — a rotation between the read and the request.
19
+ // - Writes are capped (8–16 at once). 200 parallel writes each opened a TLS connection and cost
20
+ // about 130% CPU; a provision burst queues here instead.
21
+ // - A watch asks for bookmarks and `timeoutSeconds=240`: built-in `fetch` aborts a body silent
22
+ // for 300s (`UND_ERR_BODY_TIMEOUT`) and that cannot be changed without undici's dispatcher, so
23
+ // the server closing first — and bookmarks about every 60s — keep a healthy watch clear of it.
24
+ // The watch LOOP that reads this stream lives in sandbox-watch.ts.
25
+
26
+ import { readFile } from "node:fs/promises";
27
+
28
+ // The API server's CA (`SERVICE_ACCOUNT_CA`, names.ts) is not read here: the Deployment points
29
+ // NODE_EXTRA_CA_CERTS at it, and built-in `fetch` trusts it from there.
30
+
31
+ /** Where the kubelet mounts the Pod's ServiceAccount. */
32
+ export const SERVICE_ACCOUNT_DIR = "/var/run/secrets/kubernetes.io/serviceaccount";
33
+ /** The bearer — rotated by the kubelet, so read per request. */
34
+ export const SERVICE_ACCOUNT_TOKEN = `${SERVICE_ACCOUNT_DIR}/token`;
35
+
36
+ /** The field manager every server-side apply names — the Orchestrator's one identity as a writer. */
37
+ export const FIELD_MANAGER = "jr2";
38
+
39
+ /** The write cap when the caller names none: inside ADR-0063's 8–16. */
40
+ const DEFAULT_MAX_WRITES = 12;
41
+
42
+ /** One kind, as its REST path spells it. The core group is `""` (`/api/v1`). */
43
+ export type KubeKind = { group: string; version: string; plural: string };
44
+
45
+ export const SANDBOXES: KubeKind = { group: "core.jr2.dev", version: "v1alpha1", plural: "sandboxes" };
46
+ export const REPOS: KubeKind = { group: "core.jr2.dev", version: "v1alpha1", plural: "repos" };
47
+ export const SECRETS: KubeKind = { group: "", version: "v1", plural: "secrets" };
48
+ export const CONFIGMAPS: KubeKind = { group: "", version: "v1", plural: "configmaps" };
49
+ export const SERVICES: KubeKind = { group: "", version: "v1", plural: "services" };
50
+
51
+ /** The object fields every kind shares — what the client and the watch read off any of them. */
52
+ export type KubeObject = {
53
+ apiVersion?: string;
54
+ kind?: string;
55
+ metadata?: {
56
+ name?: string;
57
+ namespace?: string;
58
+ uid?: string;
59
+ resourceVersion?: string;
60
+ labels?: Record<string, string>;
61
+ annotations?: Record<string, string>;
62
+ };
63
+ };
64
+
65
+ /** One event off a watch stream. `ERROR` carries a `Status` (code 410 for a resourceVersion too
66
+ * old) INSIDE an HTTP 200 — the caller re-lists on any of them. */
67
+ export type WatchEvent<T extends KubeObject = KubeObject> =
68
+ | { type: "ADDED" | "MODIFIED" | "DELETED" | "BOOKMARK"; object: T }
69
+ | { type: "ERROR"; object: { kind?: "Status"; code?: number; reason?: string; message?: string } };
70
+
71
+ /** A refusal from the API server: the HTTP status and the `Status` body's own reason and words. */
72
+ export class KubeError extends Error {
73
+ readonly status: number;
74
+ readonly reason: string | undefined;
75
+ constructor(status: number, reason: string | undefined, message: string) {
76
+ super(message);
77
+ this.name = "KubeError";
78
+ this.status = status;
79
+ this.reason = reason;
80
+ }
81
+ }
82
+
83
+ export type KubeClientOptions = {
84
+ /** The API server. Default: the in-cluster Service from `KUBERNETES_SERVICE_HOST`/`_PORT`. */
85
+ baseUrl?: string;
86
+ /** The environment the default base URL is read from. Default `process.env`. */
87
+ env?: Record<string, string | undefined>;
88
+ /** The bearer, asked for on EVERY request — never cached (see the header). Default: the SA
89
+ * token file. */
90
+ token?: () => Promise<string>;
91
+ /** Injectable transport: the tests' fake API server. Default: global `fetch`. */
92
+ fetch?: typeof fetch;
93
+ /** How many writes may be in flight at once. Default 12. */
94
+ maxWrites?: number;
95
+ };
96
+
97
+ /** The client the Sandbox, Repo and fetch ports share: one process, one write cap. */
98
+ export interface KubeClient {
99
+ /** The object, or `undefined` when there is none (404). */
100
+ get<T extends KubeObject>(kind: KubeKind, namespace: string, name: string): Promise<T | undefined>;
101
+ /** Every object of the kind in the namespace, with the list's resourceVersion — where a watch
102
+ * starts. A kind the cluster does not serve (no CRD) is a {@link KubeError} with status 404. */
103
+ list<T extends KubeObject>(
104
+ kind: KubeKind,
105
+ namespace: string,
106
+ opts?: { labelSelector?: string },
107
+ ): Promise<{ items: T[]; resourceVersion: string }>;
108
+ /** Create; an existing object is a {@link KubeError} with status 409. */
109
+ create<T extends KubeObject>(kind: KubeKind, namespace: string, body: T): Promise<T & KubeObject>;
110
+ /** Delete; true when there was one to delete, false when it was already gone. */
111
+ delete(kind: KubeKind, namespace: string, name: string): Promise<boolean>;
112
+ /** A JSON merge patch — `null` removes a key. `status` patches the status subresource. Answers
113
+ * the object as it stands after the patch. */
114
+ patch<T extends KubeObject>(
115
+ kind: KubeKind,
116
+ namespace: string,
117
+ name: string,
118
+ body: object,
119
+ opts?: { status?: boolean },
120
+ ): Promise<T>;
121
+ /** Server-side apply as field manager `jr2`, forced: create-or-update of every field the body
122
+ * names. Answers the object as it stands afterwards (its `uid` included). */
123
+ apply<T extends KubeObject>(kind: KubeKind, namespace: string, body: T): Promise<T & KubeObject>;
124
+ /** One watch request's events, from `resourceVersion`, until the server closes it. Throws on a
125
+ * refused request or a broken stream — the caller decides what each means (sandbox-watch.ts). */
126
+ watch<T extends KubeObject>(
127
+ kind: KubeKind,
128
+ namespace: string,
129
+ opts: { resourceVersion: string; labelSelector?: string; timeoutSeconds: number; signal?: AbortSignal },
130
+ ): AsyncIterable<WatchEvent<T>>;
131
+ }
132
+
133
+ /** The in-cluster base URL, from the Service env the kubelet injects into every pod. */
134
+ export function inClusterBaseUrl(env: Record<string, string | undefined> = process.env): string {
135
+ const host = env.KUBERNETES_SERVICE_HOST;
136
+ const port = env.KUBERNETES_SERVICE_PORT ?? "443";
137
+ if (!host) {
138
+ throw new Error(
139
+ "no Kubernetes API server: KUBERNETES_SERVICE_HOST is unset. The Orchestrator drives the cluster " +
140
+ "only from inside it (ADR-0019, ADR-0063) — `jr2 up` deploys it there.",
141
+ );
142
+ }
143
+ // An IPv6 address needs brackets in a URL.
144
+ return `https://${host.includes(":") ? `[${host}]` : host}:${port}`;
145
+ }
146
+
147
+ /** Build the client. Nothing is read until the first request: a boot never fails on the cluster. */
148
+ export function kubeClient(opts: KubeClientOptions = {}): KubeClient {
149
+ const fetchImpl = opts.fetch ?? fetch;
150
+ const token = opts.token ?? (async () => (await readFile(SERVICE_ACCOUNT_TOKEN, "utf8")).trim());
151
+ const writes = semaphore(opts.maxWrites ?? DEFAULT_MAX_WRITES);
152
+ let base: string | undefined = opts.baseUrl;
153
+ const baseUrl = () => (base ??= inClusterBaseUrl(opts.env));
154
+
155
+ /** One request, with the bearer read fresh and a 401 re-read and retried once. */
156
+ const send = async (
157
+ method: string,
158
+ path: string,
159
+ init: { query?: Record<string, string>; body?: string; contentType?: string; signal?: AbortSignal } = {},
160
+ ): Promise<Response> => {
161
+ const url = new URL(path, baseUrl());
162
+ for (const [k, v] of Object.entries(init.query ?? {})) url.searchParams.set(k, v);
163
+ for (let attempt = 0; ; attempt++) {
164
+ const res = await fetchImpl(url, {
165
+ method,
166
+ headers: {
167
+ authorization: `Bearer ${await token()}`,
168
+ accept: "application/json",
169
+ ...(init.contentType ? { "content-type": init.contentType } : {}),
170
+ },
171
+ ...(init.body !== undefined ? { body: init.body } : {}),
172
+ ...(init.signal ? { signal: init.signal } : {}),
173
+ });
174
+ // A rotation landed between the read and the request: the file already holds the new one.
175
+ if (res.status === 401 && attempt === 0) {
176
+ await res.body?.cancel();
177
+ continue;
178
+ }
179
+ return res;
180
+ }
181
+ };
182
+
183
+ /** The answer as JSON, or a {@link KubeError} carrying the Status body's reason and words. */
184
+ const json = async <T>(res: Response, what: string): Promise<T> => {
185
+ const text = await res.text();
186
+ if (res.ok) return (text ? JSON.parse(text) : {}) as T;
187
+ throw errorOf(res.status, text, what);
188
+ };
189
+
190
+ /** A write: inside the cap until its answer is read, so the cap bounds connections, not calls. */
191
+ const write = <T>(method: string, path: string, what: string, init: Parameters<typeof send>[2]) =>
192
+ writes(async () => json<T>(await send(method, path, init), what));
193
+
194
+ return {
195
+ async get<T extends KubeObject>(kind: KubeKind, ns: string, name: string) {
196
+ const res = await send("GET", pathOf(kind, ns, name));
197
+ if (res.status === 404) {
198
+ await res.body?.cancel();
199
+ return undefined;
200
+ }
201
+ return json<T>(res, `get ${kind.plural}/${name}`);
202
+ },
203
+
204
+ async list<T extends KubeObject>(kind: KubeKind, ns: string, opts: { labelSelector?: string } = {}) {
205
+ const res = await send("GET", pathOf(kind, ns), {
206
+ query: opts.labelSelector ? { labelSelector: opts.labelSelector } : {},
207
+ });
208
+ const out = await json<{ items?: T[]; metadata?: { resourceVersion?: string } }>(res, `list ${kind.plural}`);
209
+ return { items: out.items ?? [], resourceVersion: out.metadata?.resourceVersion ?? "" };
210
+ },
211
+
212
+ create<T extends KubeObject>(kind: KubeKind, ns: string, body: T) {
213
+ return write<T & KubeObject>("POST", pathOf(kind, ns), `create ${kind.plural}/${body.metadata?.name ?? "?"}`, {
214
+ body: JSON.stringify(body),
215
+ contentType: "application/json",
216
+ });
217
+ },
218
+
219
+ delete(kind: KubeKind, ns: string, name: string) {
220
+ return writes(async () => {
221
+ const res = await send("DELETE", pathOf(kind, ns, name));
222
+ if (res.status === 404) {
223
+ await res.body?.cancel();
224
+ return false;
225
+ }
226
+ await json(res, `delete ${kind.plural}/${name}`);
227
+ return true;
228
+ });
229
+ },
230
+
231
+ patch<T extends KubeObject>(kind: KubeKind, ns: string, name: string, body: object, o: { status?: boolean } = {}) {
232
+ return write<T>(
233
+ "PATCH",
234
+ pathOf(kind, ns, name, o.status ? "status" : undefined),
235
+ `patch ${kind.plural}/${name}`,
236
+ {
237
+ body: JSON.stringify(body),
238
+ contentType: "application/merge-patch+json",
239
+ },
240
+ );
241
+ },
242
+
243
+ apply<T extends KubeObject>(kind: KubeKind, ns: string, body: T) {
244
+ const name = body.metadata?.name;
245
+ if (!name) throw new Error(`apply ${kind.plural}: the object names no metadata.name`);
246
+ // JSON is YAML, so the apply content type takes the object as it is.
247
+ return write<T & KubeObject>("PATCH", pathOf(kind, ns, name), `apply ${kind.plural}/${name}`, {
248
+ query: { fieldManager: FIELD_MANAGER, force: "true" },
249
+ body: JSON.stringify(body),
250
+ contentType: "application/apply-patch+yaml",
251
+ });
252
+ },
253
+
254
+ async *watch<T extends KubeObject>(
255
+ kind: KubeKind,
256
+ ns: string,
257
+ o: { resourceVersion: string; labelSelector?: string; timeoutSeconds: number; signal?: AbortSignal },
258
+ ): AsyncIterable<WatchEvent<T>> {
259
+ const res = await send("GET", pathOf(kind, ns), {
260
+ query: {
261
+ watch: "true",
262
+ allowWatchBookmarks: "true",
263
+ timeoutSeconds: String(o.timeoutSeconds),
264
+ resourceVersion: o.resourceVersion,
265
+ ...(o.labelSelector ? { labelSelector: o.labelSelector } : {}),
266
+ },
267
+ ...(o.signal ? { signal: o.signal } : {}),
268
+ });
269
+ if (!res.ok) throw errorOf(res.status, await res.text(), `watch ${kind.plural}`);
270
+ if (!res.body) return;
271
+ // One JSON object per line. A line can straddle two chunks, so the tail waits for the next.
272
+ let buf = "";
273
+ for await (const chunk of res.body.pipeThrough(new TextDecoderStream())) {
274
+ buf += chunk;
275
+ let nl: number;
276
+ while ((nl = buf.indexOf("\n")) >= 0) {
277
+ const line = buf.slice(0, nl).trim();
278
+ buf = buf.slice(nl + 1);
279
+ if (line) yield JSON.parse(line) as WatchEvent<T>;
280
+ }
281
+ }
282
+ if (buf.trim()) yield JSON.parse(buf) as WatchEvent<T>;
283
+ },
284
+ };
285
+ }
286
+
287
+ /** A `Status` body read into a {@link KubeError}; a body that is not one keeps its raw text. */
288
+ function errorOf(status: number, text: string, what: string): KubeError {
289
+ let reason: string | undefined;
290
+ let message = text;
291
+ try {
292
+ const parsed = JSON.parse(text) as { reason?: string; message?: string };
293
+ reason = parsed.reason;
294
+ message = parsed.message ?? text;
295
+ } catch {
296
+ // not a Status: the raw text is the most there is to say
297
+ }
298
+ return new KubeError(status, reason, `${what} failed (${status}${reason ? ` ${reason}` : ""}): ${message}`);
299
+ }
300
+
301
+ /** The REST path of a kind in a namespace, optionally one object and one subresource. */
302
+ function pathOf(kind: KubeKind, ns: string, name?: string, sub?: string): string {
303
+ const root = kind.group ? `/apis/${kind.group}/${kind.version}` : `/api/${kind.version}`;
304
+ return (
305
+ `${root}/namespaces/${encodeURIComponent(ns)}/${kind.plural}` +
306
+ (name !== undefined ? `/${encodeURIComponent(name)}` : "") +
307
+ (sub !== undefined ? `/${sub}` : "")
308
+ );
309
+ }
310
+
311
+ /** At most `n` of the wrapped calls at once; the rest queue in arrival order. */
312
+ function semaphore(n: number): <T>(fn: () => Promise<T>) => Promise<T> {
313
+ let active = 0;
314
+ const queue: Array<() => void> = [];
315
+ return async (fn) => {
316
+ // A released slot passes straight to the next waiter, so a caller arriving in between can
317
+ // never slip past the cap.
318
+ if (active >= n) await new Promise<void>((resolve) => queue.push(resolve));
319
+ else active++;
320
+ try {
321
+ return await fn();
322
+ } finally {
323
+ const next = queue.shift();
324
+ if (next) next();
325
+ else active--;
326
+ }
327
+ };
328
+ }
329
+
330
+ /** True for the API server's "no such object" — the one refusal most callers read as an answer. */
331
+ export const isNotFound = (err: unknown): boolean => err instanceof KubeError && err.status === 404;
332
+ /** True for "already exists" on a create. */
333
+ export const isAlreadyExists = (err: unknown): boolean => err instanceof KubeError && err.status === 409;
@@ -18,7 +18,10 @@
18
18
  import type { AnyStateMachine, StateNode, TransitionDefinition } from "xstate";
19
19
  import { z } from "zod";
20
20
  import type { EventAudience } from "@jr2/agent-protocol";
21
- import { vocabularyOf } from "./vocabulary.ts";
21
+ import { argvText, isEffect, targetText, type EffectParts } from "./effect.ts";
22
+ import { isFailSlot } from "./fail.ts";
23
+ import { loadWarningsOf, seatsOf, vocabularyOf, type MachineSeat } from "./vocabulary.ts";
24
+ import { open } from "./open.ts";
22
25
 
23
26
  /** One transition of the Machine, id-addressed at both ends. */
24
27
  export type MachineTransitionDoc = {
@@ -32,9 +35,34 @@ export type MachineTransitionDoc = {
32
35
  label: string;
33
36
  /** Normalized guard name, if guarded. */
34
37
  guard?: string;
38
+ /** xstate's own transition `description`, as authored: what this writing does, where one name
39
+ * has writings that differ (ADR-0015's "one def, one meaning"). */
40
+ description?: string;
35
41
  kind: "event" | "always" | "after" | "done" | "error";
36
42
  };
37
43
 
44
+ /** An Effect slot's parts, as the doc carries them (ADR-0067): a command's argv (an argv slot reads
45
+ * `<name>`), its timeout, and the Repo Slot whose Worktree is its cwd, when it names one. */
46
+ export type EffectDoc =
47
+ | { kind: "command"; argv: string[]; timeoutMs: number; slot?: string; secrets?: string[]; pin?: true }
48
+ /** An http Effect (ADR-0068): its method, its fixed target, the secrets it names — never a value
49
+ * — and `file` when its body is a Worktree file (ADR-0067). */
50
+ | {
51
+ kind: "http";
52
+ method: string;
53
+ target: string;
54
+ secrets: string[];
55
+ timeoutMs: number;
56
+ idempotent: boolean;
57
+ file?: true;
58
+ }
59
+ /** A push (ADR-0067): which options the author set. */
60
+ | { kind: "push"; sign: boolean; squash: boolean; force: boolean; pin: boolean; timeoutMs: number; slot?: string }
61
+ /** A dispatch (ADR-0071): the Workflow it starts a run of, whether it waits, and the other
62
+ * Orchestrator's origin (`<open>` while a package leaves it Open); no target is this Orchestrator.
63
+ * The token's secret is named on the parts, never here: the target is what a reader routes on. */
64
+ | { kind: "dispatch"; workflow: string; wait: boolean; target?: string; timeoutMs: number };
65
+
38
66
  /** One state of the Machine; `states` nests in document order. */
39
67
  export type MachineStateDoc = {
40
68
  /** Unique state id (custom `id:` respected). */
@@ -44,7 +72,10 @@ export type MachineStateDoc = {
44
72
  type: "atomic" | "compound" | "parallel" | "final" | "history";
45
73
  /** Initial child state id (compound only). */
46
74
  initial?: string;
47
- invoke: Array<{ id: string; src: string }>;
75
+ /** Each invoke's id and slot; an Effect's carries its plain-data parts (ADR-0067) — what it runs,
76
+ * where, and for how long, read without running anything. `fail`: the slot is a `fail()`, so this
77
+ * state can fail the run (ADR-0073). */
78
+ invoke: Array<{ id: string; src: string; effect?: EffectDoc; fail?: true }>;
48
79
  tags: string[];
49
80
  description?: string;
50
81
  states: MachineStateDoc[];
@@ -98,7 +129,8 @@ export type ChildMachineDoc = {
98
129
  export type MachineEventDoc = {
99
130
  name: string;
100
131
  description?: string;
101
- audience: EventAudience;
132
+ /** Absent: each writing's state decides who may send it (ADR-0015) — see the body's `seats`. */
133
+ audience?: EventAudience;
102
134
  /** The def's input schema as JSON Schema — what a form or a `jr2 send` prompt generates from. */
103
135
  input: unknown;
104
136
  };
@@ -112,6 +144,13 @@ export type MachineBodyDoc = {
112
144
  /** The events THIS Machine declares, and only this one (ADR-0049): a nested Machine's ride its
113
145
  * own `MachineBodyDoc`. Empty for a Machine not built by `jr2Setup`. */
114
146
  events: MachineEventDoc[];
147
+ /** Who may send what, per state (ADR-0015): one entry per party on each state that invokes one —
148
+ * the audience rule's answer for this Machine, as `jr2 up` prints it. Empty for a Machine not
149
+ * built by `jr2Setup`. */
150
+ seats: MachineSeat[];
151
+ /** What the build found legal and suspect (ADR-0015) — a mode nothing enters, a pick a Gate above
152
+ * takes from the Agent below, one name written for two parties on one path. */
153
+ warnings: string[];
115
154
  };
116
155
 
117
156
  /** The serialized structure of a workflow's Machine. */
@@ -122,6 +161,10 @@ export type MachineDoc = MachineBodyDoc & {
122
161
  * to discover the diagram is lying. Absent on the nested `MachineBodyDoc`s: it is a whole-doc
123
162
  * question, and the root's walk already covers them. */
124
163
  opaqueStates?: string[];
164
+ /** A run's doc only (`runMachine`, ADR-0070): each bound Chart the doc could not draw, so its Open
165
+ * body shows the slot's own subgraph — the body's path in the run, its slot, and why (a stored
166
+ * Chart the door no longer admits is drift, D-B). Absent when every bound Chart is drawn. */
167
+ chartsNotDrawn?: Array<{ path: string; src: string; reason: string }>;
125
168
  };
126
169
 
127
170
  /** Normalize a guard to a display name: setup() name, parameterized type, or `"inline"`. */
@@ -172,6 +215,7 @@ function serializeTransition(t: TransitionDefinition<any, any>): MachineTransiti
172
215
  kind,
173
216
  };
174
217
  if (guard !== undefined) doc.guard = guard;
218
+ if (t.description !== undefined) doc.description = t.description;
175
219
  return doc;
176
220
  }
177
221
 
@@ -287,7 +331,12 @@ function serializeState(node: StateNode<any, any>, into: MachineTransitionDoc[],
287
331
  id: node.id,
288
332
  key: node.key,
289
333
  type: node.type,
290
- invoke: node.invoke.map((inv) => ({ id: inv.id, src: srcName(inv.src) })),
334
+ invoke: node.invoke.map((inv) => {
335
+ const logic = typeof inv.src === "string" ? scope.actors[inv.src] : undefined;
336
+ if (isFailSlot(logic)) return { id: inv.id, src: srcName(inv.src), fail: true as const };
337
+ if (!isEffect(logic)) return { id: inv.id, src: srcName(inv.src) };
338
+ return { id: inv.id, src: srcName(inv.src), effect: effectDoc(logic.effect) };
339
+ }),
291
340
  tags: [...node.tags],
292
341
  states: children.map((child) => serializeState(child, into, scope)),
293
342
  children: childMachines(node, scope),
@@ -311,6 +360,18 @@ export function opaqueStates(doc: MachineDoc): string[] {
311
360
  return ids;
312
361
  }
313
362
 
363
+ /** Every Machine body in a doc — the root's, then each child Machine's, depth-first in state order.
364
+ * A recursive reference is not repeated: its body is already above it. */
365
+ export function machineBodies(doc: MachineBodyDoc): MachineBodyDoc[] {
366
+ const out: MachineBodyDoc[] = [doc];
367
+ const walk = (state: MachineStateDoc): void => {
368
+ for (const child of state.children) if (child.machine) out.push(...machineBodies(child.machine));
369
+ for (const nested of state.states) walk(nested);
370
+ };
371
+ walk(doc.root);
372
+ return out;
373
+ }
374
+
314
375
  /** Serialize one Machine — its own state tree and its own transitions. Each level carries its own
315
376
  * actor registry (a child machine resolves `src` names against ITS `setup`, not its parent's). */
316
377
  function serializeBody(machine: AnyStateMachine, path: Set<AnyStateMachine>): MachineBodyDoc {
@@ -321,10 +382,10 @@ function serializeBody(machine: AnyStateMachine, path: Set<AnyStateMachine>): Ma
321
382
  const events = [...(vocabularyOf(machine)?.values() ?? [])].map((def) => ({
322
383
  name: def.name,
323
384
  ...(def.description !== undefined ? { description: def.description } : {}),
324
- audience: def.audience,
385
+ ...(def.audience !== undefined ? { audience: def.audience } : {}),
325
386
  input: z.toJSONSchema(def.input),
326
387
  }));
327
- return { id: machine.id, root, transitions, events };
388
+ return { id: machine.id, root, transitions, events, seats: seatsOf(machine), warnings: loadWarningsOf(machine) };
328
389
  }
329
390
 
330
391
  /** Serialize a workflow's template Machine into the Console's DTO, child machines and all. */
@@ -332,3 +393,42 @@ export function serializeMachine(workflow: string, machine: AnyStateMachine): Ma
332
393
  const doc: MachineDoc = { workflow, ...serializeBody(machine, new Set()) };
333
394
  return { ...doc, opaqueStates: opaqueStates(doc) };
334
395
  }
396
+
397
+ /** An Effect's plain data as the doc carries it: what it runs, read without running anything. */
398
+ function effectDoc(parts: EffectParts): EffectDoc {
399
+ if (parts.kind === "http") {
400
+ const { method, secrets, timeoutMs, idempotent } = parts;
401
+ return {
402
+ kind: "http",
403
+ method,
404
+ target: targetText(parts),
405
+ secrets: [...secrets],
406
+ timeoutMs,
407
+ idempotent,
408
+ ...(parts.file ? { file: true as const } : {}),
409
+ };
410
+ }
411
+ if (parts.kind === "push") {
412
+ const { sign, squash, force, pin, timeoutMs, slot } = parts;
413
+ return { kind: "push", sign, squash, force, pin, timeoutMs, ...(slot ? { slot } : {}) };
414
+ }
415
+ if (parts.kind === "dispatch") {
416
+ const { workflow, wait, timeoutMs, target } = parts;
417
+ return {
418
+ kind: "dispatch",
419
+ workflow,
420
+ wait,
421
+ ...(target === undefined ? {} : { target: target === open ? "<open>" : target.url }),
422
+ timeoutMs,
423
+ };
424
+ }
425
+ const { argv, timeoutMs, slot, secrets, pin } = parts;
426
+ return {
427
+ kind: "command",
428
+ argv: argvText(argv),
429
+ timeoutMs,
430
+ ...(slot ? { slot } : {}),
431
+ ...(secrets?.length ? { secrets: [...secrets] } : {}),
432
+ ...(pin ? { pin } : {}),
433
+ };
434
+ }