@jr2/orchestrator 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (361) hide show
  1. package/README.md +20 -2
  2. package/console/components/app.ts +2 -0
  3. package/console/components/fleet.ts +14 -1
  4. package/console/components/machine-pane.ts +41 -2
  5. package/console/main.ts +13 -1
  6. package/console/store.ts +102 -6
  7. package/console/style.css +21 -0
  8. package/docs/agents-block.md +5 -0
  9. package/docs/concepts/agents-and-turns.md +478 -0
  10. package/docs/concepts/architecture.md +243 -0
  11. package/docs/concepts/charts.md +146 -0
  12. package/docs/concepts/composition.md +357 -0
  13. package/docs/concepts/converge.md +257 -0
  14. package/docs/concepts/durable-runs.md +189 -0
  15. package/docs/concepts/effects.md +658 -0
  16. package/docs/concepts/gates.md +208 -0
  17. package/docs/concepts/machines-and-workflows.md +298 -0
  18. package/docs/concepts/pools-and-sources.md +230 -0
  19. package/docs/concepts/secrets-and-trust.md +306 -0
  20. package/docs/concepts/the-menu.md +381 -0
  21. package/docs/concepts/workspaces.md +756 -0
  22. package/docs/get-started/a-model-decides.md +563 -0
  23. package/docs/get-started/an-agent-in-a-sandbox.md +383 -0
  24. package/docs/get-started/connect-a-model.md +248 -0
  25. package/docs/get-started/create-an-instance.md +194 -0
  26. package/docs/get-started/have-your-assistant-write-a-machine.md +993 -0
  27. package/docs/get-started/install.md +130 -0
  28. package/docs/guides/add-tools-to-the-sandbox.md +308 -0
  29. package/docs/guides/answer-a-gate.md +304 -0
  30. package/docs/guides/bind-open-parts.md +224 -0
  31. package/docs/guides/cancel-a-run.md +146 -0
  32. package/docs/guides/clean-up.md +206 -0
  33. package/docs/guides/cut-a-worktree.md +560 -0
  34. package/docs/guides/fix-a-failed-converge.md +361 -0
  35. package/docs/guides/give-the-cluster-git-credentials.md +222 -0
  36. package/docs/guides/iterate-on-a-machine-on-kind.md +190 -0
  37. package/docs/guides/let-an-agent-write-a-chart.md +230 -0
  38. package/docs/guides/look-inside-a-sandbox.md +177 -0
  39. package/docs/guides/mirror-the-kit-images.md +176 -0
  40. package/docs/guides/move-from-kind-to-a-shared-cluster.md +318 -0
  41. package/docs/guides/open-the-console.md +91 -0
  42. package/docs/guides/publish-a-machine-as-an-npm-package.md +350 -0
  43. package/docs/guides/push-and-sign-a-branch.md +532 -0
  44. package/docs/guides/run-a-chart-by-name.md +270 -0
  45. package/docs/guides/run-a-command-or-call-a-url.md +587 -0
  46. package/docs/guides/run-one-worker-per-item.md +322 -0
  47. package/docs/guides/set-an-agents-access-and-dials.md +420 -0
  48. package/docs/guides/size-a-workspace.md +217 -0
  49. package/docs/guides/start-a-run-from-a-run.md +237 -0
  50. package/docs/guides/start-a-run.md +207 -0
  51. package/docs/guides/upgrade-the-kit-version.md +240 -0
  52. package/docs/guides/use-a-hosted-provider.md +214 -0
  53. package/docs/guides/use-a-local-model.md +313 -0
  54. package/docs/guides/watch-a-run.md +349 -0
  55. package/docs/guides/write-a-workflow.md +318 -0
  56. package/docs/index.md +44 -0
  57. package/docs/llms.txt +237 -0
  58. package/docs/reference/authoring-surface/agent.md +151 -0
  59. package/docs/reference/authoring-surface/answer.md +156 -0
  60. package/docs/reference/authoring-surface/arg.md +82 -0
  61. package/docs/reference/authoring-surface/basic.md +62 -0
  62. package/docs/reference/authoring-surface/bearer.md +53 -0
  63. package/docs/reference/authoring-surface/branchname.md +15 -0
  64. package/docs/reference/authoring-surface/chartschema.md +23 -0
  65. package/docs/reference/authoring-surface/chartschemafor.md +48 -0
  66. package/docs/reference/authoring-surface/command.md +206 -0
  67. package/docs/reference/authoring-surface/customize.md +64 -0
  68. package/docs/reference/authoring-surface/defineconfig.md +41 -0
  69. package/docs/reference/authoring-surface/defineevent.md +114 -0
  70. package/docs/reference/authoring-surface/dispatch.md +126 -0
  71. package/docs/reference/authoring-surface/fail.md +96 -0
  72. package/docs/reference/authoring-surface/file.md +142 -0
  73. package/docs/reference/authoring-surface/frompick.md +277 -0
  74. package/docs/reference/authoring-surface/http.md +259 -0
  75. package/docs/reference/authoring-surface/isfailure.md +58 -0
  76. package/docs/reference/authoring-surface/isready.md +14 -0
  77. package/docs/reference/authoring-surface/jr2setup.md +237 -0
  78. package/docs/reference/authoring-surface/open.md +105 -0
  79. package/docs/reference/authoring-surface/openbody.md +86 -0
  80. package/docs/reference/authoring-surface/paramsof.md +143 -0
  81. package/docs/reference/authoring-surface/pool.md +186 -0
  82. package/docs/reference/authoring-surface/push.md +130 -0
  83. package/docs/reference/authoring-surface/readyof.md +21 -0
  84. package/docs/reference/authoring-surface/secret.md +139 -0
  85. package/docs/reference/authoring-surface/sentby.md +83 -0
  86. package/docs/reference/authoring-surface/source.md +51 -0
  87. package/docs/reference/authoring-surface/text.md +62 -0
  88. package/docs/reference/authoring-surface/type-agentdeclaration.md +27 -0
  89. package/docs/reference/authoring-surface/type-agentdefinition.md +80 -0
  90. package/docs/reference/authoring-surface/type-agentlogic.md +15 -0
  91. package/docs/reference/authoring-surface/type-agentslots.md +15 -0
  92. package/docs/reference/authoring-surface/type-answeraction.md +12 -0
  93. package/docs/reference/authoring-surface/type-anyframepath.md +14 -0
  94. package/docs/reference/authoring-surface/type-anyworktreepath.md +15 -0
  95. package/docs/reference/authoring-surface/type-arg.md +19 -0
  96. package/docs/reference/authoring-surface/type-argnames.md +12 -0
  97. package/docs/reference/authoring-surface/type-bindactors.md +14 -0
  98. package/docs/reference/authoring-surface/type-binding.md +61 -0
  99. package/docs/reference/authoring-surface/type-bindingof.md +12 -0
  100. package/docs/reference/authoring-surface/type-bodyacceptsdoor.md +15 -0
  101. package/docs/reference/authoring-surface/type-bodyacceptsslots.md +17 -0
  102. package/docs/reference/authoring-surface/type-ceilings.md +41 -0
  103. package/docs/reference/authoring-surface/type-chainlink.md +19 -0
  104. package/docs/reference/authoring-surface/type-chartaction.md +46 -0
  105. package/docs/reference/authoring-surface/type-chartagent.md +28 -0
  106. package/docs/reference/authoring-surface/type-chartevent.md +20 -0
  107. package/docs/reference/authoring-surface/type-chartguard.md +20 -0
  108. package/docs/reference/authoring-surface/type-chartinvoke.md +28 -0
  109. package/docs/reference/authoring-surface/type-chartjson.md +14 -0
  110. package/docs/reference/authoring-surface/type-chartstate.md +46 -0
  111. package/docs/reference/authoring-surface/type-charttext.md +40 -0
  112. package/docs/reference/authoring-surface/type-charttransition.md +22 -0
  113. package/docs/reference/authoring-surface/type-charttransitions.md +14 -0
  114. package/docs/reference/authoring-surface/type-chartvalue.md +15 -0
  115. package/docs/reference/authoring-surface/type-childat.md +12 -0
  116. package/docs/reference/authoring-surface/type-childslots.md +12 -0
  117. package/docs/reference/authoring-surface/type-commandenv.md +15 -0
  118. package/docs/reference/authoring-surface/type-commandinput.md +20 -0
  119. package/docs/reference/authoring-surface/type-commandoptions.md +56 -0
  120. package/docs/reference/authoring-surface/type-commandoutput.md +122 -0
  121. package/docs/reference/authoring-surface/type-customize.md +111 -0
  122. package/docs/reference/authoring-surface/type-dispatchdefinition.md +39 -0
  123. package/docs/reference/authoring-surface/type-dispatchinput.md +14 -0
  124. package/docs/reference/authoring-surface/type-dispatchopenparts.md +14 -0
  125. package/docs/reference/authoring-surface/type-dispatchoutput.md +72 -0
  126. package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
  127. package/docs/reference/authoring-surface/type-duration.md +15 -0
  128. package/docs/reference/authoring-surface/type-effectbinding.md +93 -0
  129. package/docs/reference/authoring-surface/type-effectlogic.md +12 -0
  130. package/docs/reference/authoring-surface/type-envopenparts.md +14 -0
  131. package/docs/reference/authoring-surface/type-eventaudience.md +27 -0
  132. package/docs/reference/authoring-surface/type-eventdef.md +45 -0
  133. package/docs/reference/authoring-surface/type-eventfrom.md +16 -0
  134. package/docs/reference/authoring-surface/type-eventsemantics.md +26 -0
  135. package/docs/reference/authoring-surface/type-execoutcome.md +34 -0
  136. package/docs/reference/authoring-surface/type-failslot.md +14 -0
  137. package/docs/reference/authoring-surface/type-failure.md +19 -0
  138. package/docs/reference/authoring-surface/type-failureinput.md +20 -0
  139. package/docs/reference/authoring-surface/type-filebody.md +37 -0
  140. package/docs/reference/authoring-surface/type-framepath.md +14 -0
  141. package/docs/reference/authoring-surface/type-frominput.md +14 -0
  142. package/docs/reference/authoring-surface/type-hostinjectedinput.md +56 -0
  143. package/docs/reference/authoring-surface/type-httpdefinition.md +104 -0
  144. package/docs/reference/authoring-surface/type-httpfileinput.md +20 -0
  145. package/docs/reference/authoring-surface/type-httpinput.md +12 -0
  146. package/docs/reference/authoring-surface/type-httpmethod.md +14 -0
  147. package/docs/reference/authoring-surface/type-httpopenparts.md +12 -0
  148. package/docs/reference/authoring-surface/type-httpoutput.md +95 -0
  149. package/docs/reference/authoring-surface/type-httprequestparts.md +33 -0
  150. package/docs/reference/authoring-surface/type-jr2config.md +126 -0
  151. package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
  152. package/docs/reference/authoring-surface/type-lends.md +38 -0
  153. package/docs/reference/authoring-surface/type-logicat.md +12 -0
  154. package/docs/reference/authoring-surface/type-markedif.md +12 -0
  155. package/docs/reference/authoring-surface/type-mergedactors.md +12 -0
  156. package/docs/reference/authoring-surface/type-namedsecret.md +25 -0
  157. package/docs/reference/authoring-surface/type-namestart.md +77 -0
  158. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +27 -0
  159. package/docs/reference/authoring-surface/type-notworktreekeys.md +12 -0
  160. package/docs/reference/authoring-surface/type-openbodydeclaration.md +40 -0
  161. package/docs/reference/authoring-surface/type-openbodylogic.md +14 -0
  162. package/docs/reference/authoring-surface/type-openbodyoutput.md +42 -0
  163. package/docs/reference/authoring-surface/type-openbodyslot.md +18 -0
  164. package/docs/reference/authoring-surface/type-opencommandinput.md +22 -0
  165. package/docs/reference/authoring-surface/type-openeffect.md +12 -0
  166. package/docs/reference/authoring-surface/type-openeffectslots.md +12 -0
  167. package/docs/reference/authoring-surface/type-openof.md +12 -0
  168. package/docs/reference/authoring-surface/type-opensecret.md +23 -0
  169. package/docs/reference/authoring-surface/type-opensecretmark.md +12 -0
  170. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +26 -0
  171. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +27 -0
  172. package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
  173. package/docs/reference/authoring-surface/type-poolnextinput.md +22 -0
  174. package/docs/reference/authoring-surface/type-poolspec.md +60 -0
  175. package/docs/reference/authoring-surface/type-pushinput.md +34 -0
  176. package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
  177. package/docs/reference/authoring-surface/type-pushoutput.md +149 -0
  178. package/docs/reference/authoring-surface/type-pushwireoutput.md +12 -0
  179. package/docs/reference/authoring-surface/type-quantity.md +14 -0
  180. package/docs/reference/authoring-surface/type-reached.md +12 -0
  181. package/docs/reference/authoring-surface/type-reposlot.md +70 -0
  182. package/docs/reference/authoring-surface/type-reposlotsof.md +16 -0
  183. package/docs/reference/authoring-surface/type-sandboxoptions.md +84 -0
  184. package/docs/reference/authoring-surface/type-secret.md +25 -0
  185. package/docs/reference/authoring-surface/type-sender.md +19 -0
  186. package/docs/reference/authoring-surface/type-sentbyguards.md +12 -0
  187. package/docs/reference/authoring-surface/type-size.md +85 -0
  188. package/docs/reference/authoring-surface/type-slothandles.md +24 -0
  189. package/docs/reference/authoring-surface/type-sourcenextresult.md +17 -0
  190. package/docs/reference/authoring-surface/type-sourcespec.md +44 -0
  191. package/docs/reference/authoring-surface/type-syncstate.md +12 -0
  192. package/docs/reference/authoring-surface/type-text.md +19 -0
  193. package/docs/reference/authoring-surface/type-textpart.md +24 -0
  194. package/docs/reference/authoring-surface/type-thinkinglevel.md +39 -0
  195. package/docs/reference/authoring-surface/type-toparameterizedobject.md +14 -0
  196. package/docs/reference/authoring-surface/type-toprovidedactor.md +14 -0
  197. package/docs/reference/authoring-surface/type-unknown.md +21 -0
  198. package/docs/reference/authoring-surface/type-usercontainer.md +77 -0
  199. package/docs/reference/authoring-surface/type-withoutemptyindex.md +20 -0
  200. package/docs/reference/authoring-surface/type-workflowevent.md +46 -0
  201. package/docs/reference/authoring-surface/type-workspaceaccess.md +16 -0
  202. package/docs/reference/authoring-surface/type-workspaced.md +115 -0
  203. package/docs/reference/authoring-surface/type-workspacehandles.md +22 -0
  204. package/docs/reference/authoring-surface/type-workspacemachine.md +29 -0
  205. package/docs/reference/authoring-surface/type-workspaceof.md +13 -0
  206. package/docs/reference/authoring-surface/type-workspaceoptions.md +27 -0
  207. package/docs/reference/authoring-surface/type-worktreefound.md +16 -0
  208. package/docs/reference/authoring-surface/type-worktreeinput.md +56 -0
  209. package/docs/reference/authoring-surface/type-worktreekey.md +14 -0
  210. package/docs/reference/authoring-surface/type-worktreeoptions.md +29 -0
  211. package/docs/reference/authoring-surface/type-worktreeoutput.md +45 -0
  212. package/docs/reference/authoring-surface/type-worktreepath.md +16 -0
  213. package/docs/reference/authoring-surface/type-worktreeready.md +37 -0
  214. package/docs/reference/authoring-surface/type-worktreerefused.md +21 -0
  215. package/docs/reference/authoring-surface/workspace.md +287 -0
  216. package/docs/reference/authoring-surface/worktree.md +71 -0
  217. package/docs/reference/authoring-surface.md +217 -0
  218. package/docs/reference/cli.md +433 -0
  219. package/docs/reference/config.md +765 -0
  220. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +114 -0
  221. package/docs/reference/decisions/0002-duplex-streaming-actor.md +117 -0
  222. package/docs/reference/decisions/0004-worktree-provisioning-model.md +130 -0
  223. package/docs/reference/decisions/0005-sandbox-pod-composition.md +240 -0
  224. package/docs/reference/decisions/0006-agent-control-surface.md +50 -0
  225. package/docs/reference/decisions/0007-durable-machine-state.md +109 -0
  226. package/docs/reference/decisions/0008-orchestrator-as-deployed-app.md +60 -0
  227. package/docs/reference/decisions/0009-cli-and-instance-interface.md +275 -0
  228. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +160 -0
  229. package/docs/reference/decisions/0011-workflow-defined-events.md +141 -0
  230. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +103 -0
  231. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +175 -0
  232. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +95 -0
  233. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +271 -0
  234. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +126 -0
  235. package/docs/reference/decisions/0017-pool-over-a-source-port.md +71 -0
  236. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +111 -0
  237. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +287 -0
  238. package/docs/reference/decisions/0020-private-ca-trust-is-a-named-concept.md +60 -0
  239. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +101 -0
  240. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +103 -0
  241. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +175 -0
  242. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +436 -0
  243. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +65 -0
  244. package/docs/reference/decisions/0026-a-turn-that-is-over-is-never-prompted.md +62 -0
  245. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +245 -0
  246. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +90 -0
  247. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +154 -0
  248. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +100 -0
  249. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +193 -0
  250. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +61 -0
  251. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +126 -0
  252. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +57 -0
  253. package/docs/reference/decisions/0035-a-runaway-turn-is-ended-rerolled-once-then-a-fault.md +92 -0
  254. package/docs/reference/decisions/0036-a-turn-compacts-its-own-context-mid-flight.md +138 -0
  255. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +196 -0
  256. package/docs/reference/decisions/0038-jr2-up-builds-every-image-it-deploys.md +170 -0
  257. package/docs/reference/decisions/0039-image-garbage-collects-by-reachability.md +115 -0
  258. package/docs/reference/decisions/0041-a-build-the-host-already-holds-is-not-spent-again.md +64 -0
  259. package/docs/reference/decisions/0042-ready-is-not-routable-an-admission-retries-its-connection.md +142 -0
  260. package/docs/reference/decisions/0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md +170 -0
  261. package/docs/reference/decisions/0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md +85 -0
  262. package/docs/reference/decisions/0045-the-platform-joins-the-image-address-and-the-cluster-chooses-it.md +82 -0
  263. package/docs/reference/decisions/0046-a-converge-failure-carries-its-diagnosis.md +34 -0
  264. package/docs/reference/decisions/0047-the-git-ssh-key-source-is-the-users-choice.md +47 -0
  265. package/docs/reference/decisions/0048-the-orchestrator-boots-without-its-repos.md +36 -0
  266. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +126 -0
  267. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +73 -0
  268. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +238 -0
  269. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +86 -0
  270. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +123 -0
  271. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +126 -0
  272. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +107 -0
  273. package/docs/reference/decisions/0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md +62 -0
  274. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +152 -0
  275. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +108 -0
  276. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +427 -0
  277. package/docs/reference/decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md +111 -0
  278. package/docs/reference/decisions/0061-a-memory-kill-ends-the-agents-processes-not-the-harness.md +83 -0
  279. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +239 -0
  280. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +82 -0
  281. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +79 -0
  282. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +65 -0
  283. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +552 -0
  284. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +458 -0
  285. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +475 -0
  286. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +180 -0
  287. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +334 -0
  288. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +183 -0
  289. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +144 -0
  290. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +122 -0
  291. package/docs/reference/decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md +76 -0
  292. package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +295 -0
  293. package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +320 -0
  294. package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
  295. package/docs/reference/decisions.md +86 -0
  296. package/docs/reference/glossary.md +687 -0
  297. package/docs/reference/http-api.md +1386 -0
  298. package/docs/reference/machines.md +189 -0
  299. package/docs/reference/mechanism-events.md +72 -0
  300. package/docs/why.md +207 -0
  301. package/package.json +5 -3
  302. package/src/actor.ts +694 -106
  303. package/src/agent.ts +114 -38
  304. package/src/ambient.ts +3 -4
  305. package/src/assembly-checks.ts +189 -7
  306. package/src/cause.ts +234 -0
  307. package/src/chart.ts +345 -46
  308. package/src/charts.ts +43 -8
  309. package/src/config.ts +440 -178
  310. package/src/custodian.lua +17 -5
  311. package/src/custodian.ts +325 -99
  312. package/src/customize.ts +199 -49
  313. package/src/dispatch-effect.ts +945 -0
  314. package/src/door-compat.ts +217 -0
  315. package/src/durability.ts +11 -4
  316. package/src/effect-checks.ts +50 -3
  317. package/src/effect.ts +641 -128
  318. package/src/effector-wire.ts +165 -13
  319. package/src/fail.ts +222 -0
  320. package/src/harness-client.ts +587 -50
  321. package/src/harness-env.ts +33 -0
  322. package/src/held-secrets.ts +125 -16
  323. package/src/holds.ts +3 -0
  324. package/src/http-effect.ts +556 -58
  325. package/src/http.ts +480 -30
  326. package/src/index.ts +74 -82
  327. package/src/instance.ts +68 -8
  328. package/src/internal.ts +85 -0
  329. package/src/kube-client.ts +6 -3
  330. package/src/machine-doc.ts +24 -7
  331. package/src/mechanism-routes.ts +168 -0
  332. package/src/model-keys.generated.ts +53 -0
  333. package/src/model-keys.ts +42 -58
  334. package/src/names.ts +19 -6
  335. package/src/notices.ts +483 -41
  336. package/src/open-body.ts +242 -40
  337. package/src/open-effect.ts +81 -8
  338. package/src/open.ts +90 -6
  339. package/src/parts.ts +213 -30
  340. package/src/party.ts +101 -2
  341. package/src/pick.ts +497 -11
  342. package/src/pool.ts +282 -50
  343. package/src/registration.ts +133 -12
  344. package/src/repo-identity.ts +7 -3
  345. package/src/repos.ts +22 -3
  346. package/src/run-host.ts +665 -45
  347. package/src/run-output.ts +38 -0
  348. package/src/sandbox-effect.ts +457 -71
  349. package/src/sandbox-kube.ts +66 -17
  350. package/src/secret-placement.ts +59 -17
  351. package/src/server.ts +3 -0
  352. package/src/setup.ts +325 -62
  353. package/src/size.ts +121 -9
  354. package/src/snapshot-store.ts +54 -10
  355. package/src/stub-harness.ts +44 -16
  356. package/src/timers.ts +14 -1
  357. package/src/tokens.ts +107 -32
  358. package/src/vocabulary.ts +88 -14
  359. package/src/wire.ts +100 -26
  360. package/src/workspace.ts +771 -238
  361. package/src/worktree.ts +249 -0
@@ -77,26 +77,40 @@ import {
77
77
  type WireBody,
78
78
  } from "./effector-wire.ts";
79
79
  import { boundRun, type AnyActorSystem } from "./registration.ts";
80
+ import type { AnyWorktreePath } from "./worktree.ts";
80
81
  import { open } from "./open.ts";
81
82
  import { effectBindingShape, openEffectLogic, openPartsOf, type OpenOf } from "./open-effect.ts";
82
83
  import { httpFileWith, sandboxEffectorPort } from "./sandbox-effect.ts";
83
84
 
84
85
  // --- Naming a secret -----------------------------------------------------------------------------
85
86
 
86
- /** A secret an Effect names, by its name in the config's `secrets` list (ADR-0068). Naming it in an
87
- * Effect is what places it in that Effect's Effector: an Effect secret. Its value never leaves the
88
- * Effector; on the wire it is a name. */
87
+ // Naming it in an Effect is what places it in that Effect's Effector (ADR-0068). On the wire it is
88
+ // a name.
89
+ /** What `secret("name")` gives: an Effect secret, by its name in the `secrets` list of
90
+ * `jr2.config.ts`. Put it in an Effect's `auth`, a header, a `text` or a command's `env`. Its
91
+ * `.path` is the path of a file that holds the value. The value stays in the Effector, so the
92
+ * Machine and the Agent never see it. */
89
93
  export type Secret = { readonly secret: string; readonly path: { readonly secret: string; readonly path: true } };
90
94
 
91
- /** A string the Effector builds: literal text and secrets, in order — the `text` template's value. */
95
+ /** What the `text` template gives: a string of literal text and Effect secrets, in order. The
96
+ * Effector builds the string, so the Machine never sees the value of a secret. */
92
97
  export type Text = { readonly text: readonly TextPart[] };
93
98
 
94
99
  declare const openSecretMark: unique symbol;
95
100
  /** The TYPE half of an Open secret (ADR-0054): whatever carries `secret(open)` — the secret, its
96
101
  * `.path`, a `text` or a `basic` pair holding it, a `bearer` of it — carries this mark, so the
97
- * Effect's constructor states the Open part "secret" and `customize()` offers `{ secret }`. Phantom. */
102
+ * Effect's constructor states the Open part "secret" and `customize()` offers `{ secret }`.
103
+ * Phantom.
104
+ *
105
+ * @internal
106
+ */
98
107
  export type OpenSecretMark = { readonly [openSecretMark]: true };
99
108
 
109
+ /** A named secret, never `secret(open)`: where a definition's Open part is a whole that holds a
110
+ * secret — a dispatch's target (ADR-0071) — the secret is bound with it, to a name, so the mark
111
+ * `secret(open)` carries may not be present. One type for the definition and the binding. */
112
+ export type NamedSecret = Secret & { readonly [K in keyof OpenSecretMark]?: never };
113
+
100
114
  /** `secret(open)`: a secret a packaged Machine names in its auth, a header or a command's env, and
101
115
  * leaves for its composer to name (ADR-0054). */
102
116
  export type OpenSecret = {
@@ -116,9 +130,118 @@ const SECRET_NAME = /^[A-Za-z0-9._-]{1,253}$/;
116
130
  * (a value and its `.path`) is told from one that names two. */
117
131
  const openSecretOf = new WeakMap<object, object>();
118
132
 
119
- /** Name a secret: `secret("jira")`. `secret(open)` leaves the name to the composer (ADR-0054). */
120
- export function secret(name: typeof open): OpenSecret;
133
+ /**
134
+ * Name an Effect secret: `secret("jira")`. The name is the `name` of an entry in the `secrets`
135
+ * list of `jr2.config.ts`. The Effector reads the value and puts it into the call, or into the env
136
+ * of a `command`. The Agent never reads it, and the Orchestrator never holds it.
137
+ *
138
+ * Use it in `bearer`, `basic` or `text`, or in the `env` of a `command`. In an `env`,
139
+ * `secret("x")` gives the value, and `secret("x").path` gives the path of a file that holds the
140
+ * value.
141
+ *
142
+ * `secret(open)` leaves the name to the composer of a packaged Machine, who binds it one time:
143
+ * `customize(machine, { effects: { <slot>: { secret: "<name>" } } })`. An Effect can have one Open
144
+ * secret, in its `auth`, a header or its `env`. A url cannot have one: leave the whole url Open
145
+ * (`url: open`) instead.
146
+ *
147
+ * It refuses, when you call it:
148
+ * - a name that is not letters, digits, `.`, `_` and `-`, that starts with `.`, or that is longer
149
+ * than 253 characters: `secret("<name>"): a secret's name is letters, digits, ".", "_" and "-",
150
+ * and does not start with "." — it is the file the Effector reads it from (ADR-0068)`
151
+ * - an Effect that names `secret(open)` in two places that are two secrets: `an Effect takes one
152
+ * Open secret — it names secret(open) in two places that are two secrets; name one secret(open)
153
+ * and use it in each place, or name the others with secret("<name>") (ADR-0054)`
154
+ * - `secret(open)` in a url: ``secret(open) in a url: a url's secret is not an Open part — leave
155
+ * the whole url Open (`url: open`) and let the composer bind it with its secret in its path
156
+ * (ADR-0054)``
157
+ *
158
+ * And at `jr2 up`:
159
+ * - a name that the `secrets` list does not have: ``workflow "<workflow>": the <kind> Effect
160
+ * "<slot>" names secret("<name>"), which the `secrets` list does not hold — add it: secrets: [{
161
+ * name: "<name>", value: process.env.<NAME> }] (ADR-0068)``
162
+ * - a value of fewer than 8 characters: `secrets[<i>] (<name>) is an Effect secret shorter than 8
163
+ * characters — the Effector replaces every match of a secret in what it returns, and a value
164
+ * this short would match ordinary text. Use the whole credential; the value itself is not shown
165
+ * (ADR-0068)`
166
+ *
167
+ * @param name - The name of an entry in the `secrets` list.
168
+ * @returns The Effect secret, for an Effect. Its `.path` names the file of the secret, for the
169
+ * `env` of a `command`.
170
+ *
171
+ * @example
172
+ * ```ts
173
+ * import {
174
+ * command,
175
+ * fail,
176
+ * isReady,
177
+ * jr2Setup,
178
+ * secret,
179
+ * workspace,
180
+ * worktree,
181
+ * type Workspaced,
182
+ * } from "@jr2/orchestrator";
183
+ *
184
+ * type Input = Workspaced<{}, "app">;
185
+ *
186
+ * const deployTree = (context: Input) => `${context.workspace.repos.app}/deploy` as const;
187
+ *
188
+ * const body = jr2Setup({
189
+ * types: {} as { context: Input; input: Input },
190
+ * actors: {
191
+ * cut: worktree(),
192
+ * // The command reads the file at $KUBECONFIG. The Agent never reads the secret.
193
+ * deploy: command(["kubectl", "apply", "-f", "k8s/"], {
194
+ * env: { KUBECONFIG: secret("kube").path },
195
+ * }),
196
+ * fail: fail(),
197
+ * },
198
+ * }).createMachine({
199
+ * id: "deploy",
200
+ * context: ({ input }) => input,
201
+ * on: { "workspace.lost": ".lost" },
202
+ * initial: "cutting",
203
+ * states: {
204
+ * cutting: {
205
+ * invoke: {
206
+ * src: "cut",
207
+ * input: ({ context }) => ({ path: deployTree(context), branch: "deploy" }),
208
+ * onDone: [{ guard: ({ event }) => isReady(event.output), target: "deploying" }, { target: "notCut" }],
209
+ * },
210
+ * },
211
+ * deploying: {
212
+ * invoke: { src: "deploy", input: ({ context }) => ({ worktree: deployTree(context) }), onDone: "done" },
213
+ * },
214
+ * done: { type: "final" },
215
+ * notCut: { invoke: { src: "fail", input: { reason: "the Worktree was not cut" } } },
216
+ * lost: { invoke: { src: "fail", input: { reason: "the Sandbox is gone" } } },
217
+ * },
218
+ * });
219
+ *
220
+ * export const machine = workspace(body, {
221
+ * repos: { app: { url: "https://github.com/you/app.git" } },
222
+ * });
223
+ * ```
224
+ *
225
+ * @see docs/concepts/secrets-and-trust.md
226
+ * @see docs/guides/run-a-command-or-call-a-url.md
227
+ * @see docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md
228
+ * @see docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md
229
+ */
121
230
  export function secret(name: string): Secret;
231
+ /**
232
+ * Leave an Effect secret Open: `secret(open)`, in a Machine that you publish as a package. The
233
+ * composer binds its name one time: `customize(machine, { effects: { <slot>: { secret: "<name>"
234
+ * } } })`. {@link secret} tells the rest, and what it refuses.
235
+ *
236
+ * @param name - `open`.
237
+ * @returns The Open secret, for an Effect. Its `.path` names the file of the secret, for the `env`
238
+ * of a `command`.
239
+ *
240
+ * @see docs/concepts/secrets-and-trust.md
241
+ * @see docs/guides/bind-open-parts.md
242
+ * @see docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md
243
+ */
244
+ export function secret(name: typeof open): OpenSecret;
122
245
  export function secret(name: string | typeof open): Secret | OpenSecret {
123
246
  if ((name as unknown) === open) {
124
247
  // An Open secret (ADR-0054): the composer binds its name once, with `customize()`'s
@@ -144,7 +267,11 @@ export function secret(name: string | typeof open): Secret | OpenSecret {
144
267
  return s;
145
268
  }
146
269
 
147
- /** What a value that may hold `secret(open)` gives: marked when it does. */
270
+ /**
271
+ * What a value that may hold `secret(open)` gives: marked when it does.
272
+ *
273
+ * @internal
274
+ */
148
275
  type MarkedIf<V, T> = [Extract<V, OpenSecretMark>] extends [never] ? T : T & OpenSecretMark;
149
276
 
150
277
  /** The values of a record type, or never. */
@@ -154,7 +281,10 @@ type ValuesOf<R> = R extends Readonly<Record<string, infer V>> ? V : never;
154
281
  export type OpenSecretIn<V> = [Extract<V, OpenSecretMark>] extends [never] ? never : "secret";
155
282
 
156
283
  /** The Open parts an http definition's types state (ADR-0054): its url, when it is `open`, and its
157
- * secret, when its auth or a header holds `secret(open)`. */
284
+ * secret, when its auth or a header holds `secret(open)`.
285
+ *
286
+ * @internal
287
+ */
158
288
  export type HttpOpenParts<U, A, H> = (U extends typeof open ? "url" : never) | OpenSecretIn<A | ValuesOf<H>>;
159
289
 
160
290
  /** A secret's wire part: `{ secret: name }` — for an Open secret, a part that stands for it. */
@@ -165,7 +295,46 @@ function partOf(s: Secret): TextPart {
165
295
  return part;
166
296
  }
167
297
 
168
- /** A string with secrets in it: text`Token ${secret("x")}`. Each `${…}` is a secret or a string. */
298
+ /**
299
+ * Make a string from literal text and Effect secrets, as a tagged template:
300
+ * `` text`Token ${secret("x")}` ``. The Effector puts in the value of each secret when it makes
301
+ * the call. Use it for a url with a secret in its path, or for a header value. Use it also for
302
+ * `auth`, where it is the whole `Authorization` header, or for a part of `basic`.
303
+ *
304
+ * It refuses a `${…}` that is not a `secret(...)` or a string: `` text`…`: a value is a
305
+ * secret("name") or a string, got <value> ``.
306
+ *
307
+ * @param strings - The literal text of the template.
308
+ * @param values - The `${…}` values, each a `secret(...)` or a string.
309
+ * @returns The string, which the Effector makes.
310
+ *
311
+ * @example
312
+ * ```ts
313
+ * import { http, jr2Setup, secret, text } from "@jr2/orchestrator";
314
+ *
315
+ * export const machine = jr2Setup({
316
+ * actors: {
317
+ * notify: http({
318
+ * method: "POST",
319
+ * url: text`https://api.telegram.org/bot${secret("telegram")}/sendMessage`,
320
+ * headers: { "x-run-source": text`jr2` },
321
+ * request: () => ({ json: { chat_id: "12345", text: "The run is done." } }),
322
+ * }),
323
+ * },
324
+ * }).createMachine({
325
+ * id: "notify",
326
+ * initial: "notifying",
327
+ * states: {
328
+ * notifying: { invoke: { src: "notify", onDone: "done" } },
329
+ * done: { type: "final" },
330
+ * },
331
+ * });
332
+ * ```
333
+ *
334
+ * @see docs/guides/run-a-command-or-call-a-url.md
335
+ * @see docs/concepts/secrets-and-trust.md
336
+ * @see docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md
337
+ */
169
338
  export function text<const V extends Array<Secret | OpenSecret | string>>(
170
339
  strings: TemplateStringsArray,
171
340
  ...values: V
@@ -189,15 +358,87 @@ export function text(strings: TemplateStringsArray, ...values: Array<Secret | st
189
358
  return { text: parts };
190
359
  }
191
360
 
192
- /** `Authorization: Bearer <value>`. The secret may be `secret(open)` (ADR-0054). */
361
+ /**
362
+ * Make the `auth` of an `http` Effect that sends `Authorization: Bearer <value>`, with the value
363
+ * of an Effect secret. In a packaged Machine, the secret can be `secret(open)`.
364
+ *
365
+ * It refuses a value that is not a secret: `bearer(…) takes a secret("name")`.
366
+ *
367
+ * @param s - The secret: `secret("name")`, or `secret(open)`.
368
+ * @returns The `auth` of an `http` Effect.
369
+ *
370
+ * @example
371
+ * ```ts
372
+ * import { bearer, http, jr2Setup, secret } from "@jr2/orchestrator";
373
+ *
374
+ * export const machine = jr2Setup({
375
+ * actors: {
376
+ * status: http({
377
+ * method: "GET",
378
+ * url: "https://api.github.com/repos/you/app",
379
+ * auth: bearer(secret("github")),
380
+ * }),
381
+ * },
382
+ * }).createMachine({
383
+ * id: "status",
384
+ * initial: "reading",
385
+ * states: {
386
+ * reading: { invoke: { src: "status", onDone: "done" } },
387
+ * done: { type: "final" },
388
+ * },
389
+ * });
390
+ * ```
391
+ *
392
+ * @see docs/guides/run-a-command-or-call-a-url.md
393
+ * @see docs/concepts/secrets-and-trust.md
394
+ * @see docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md
395
+ * @see docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md
396
+ */
193
397
  export function bearer<const S extends Secret>(s: S): MarkedIf<S, { readonly bearer: Secret }>;
194
398
  export function bearer(s: Secret): { readonly bearer: Secret } {
195
399
  if (!isSecret(s)) throw new Error('bearer(…) takes a secret("name")');
196
400
  return { bearer: s };
197
401
  }
198
402
 
199
- /** `Authorization: Basic base64(user:password)`. Each is a string, a secret, or a `text`; either may
200
- * hold `secret(open)` (ADR-0054). */
403
+ /**
404
+ * Make the `auth` of an `http` Effect that sends `Authorization: Basic <value>`, where the value
405
+ * is the base64 of `user:password`. The Effector puts in the value of each secret.
406
+ *
407
+ * Each of `user` and `password` is a string, a `secret(...)` or a `text`. In a packaged Machine,
408
+ * one of them can hold `secret(open)`. It refuses another value: `` expected a string, a
409
+ * secret("name") or a text`…`, got <value> ``.
410
+ *
411
+ * @param user - The user name: a string, a `secret(...)` or a `text`.
412
+ * @param password - The password: a string, a `secret(...)` or a `text`.
413
+ * @returns The `auth` of an `http` Effect.
414
+ *
415
+ * @example
416
+ * ```ts
417
+ * import { basic, http, jr2Setup, secret } from "@jr2/orchestrator";
418
+ *
419
+ * export const machine = jr2Setup({
420
+ * actors: {
421
+ * issue: http({
422
+ * method: "GET",
423
+ * url: "https://jira.example.com/rest/api/2/issue/APP-1",
424
+ * auth: basic("jr2-bot@example.com", secret("jira-token")),
425
+ * }),
426
+ * },
427
+ * }).createMachine({
428
+ * id: "issue",
429
+ * initial: "reading",
430
+ * states: {
431
+ * reading: { invoke: { src: "issue", onDone: "done" } },
432
+ * done: { type: "final" },
433
+ * },
434
+ * });
435
+ * ```
436
+ *
437
+ * @see docs/guides/run-a-command-or-call-a-url.md
438
+ * @see docs/concepts/secrets-and-trust.md
439
+ * @see docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md
440
+ * @see docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md
441
+ */
201
442
  export function basic<const U extends string | Secret | Text, const P extends string | Secret | Text>(
202
443
  user: U,
203
444
  password: P,
@@ -282,39 +523,157 @@ function asText(v: string | Secret | Text): Text {
282
523
  /** A value of a file body's part: as written, or from the Effect's input. */
283
524
  export type FromInput<S, T> = T | ((input: S extends z.ZodType ? z.infer<S> : void) => T);
284
525
 
285
- /** `file(path, { slot?, segments?, contentType? })`: a Worktree file as an `http` body. */
526
+ /** `file(path, { segments?, contentType? })`: a Worktree file as an `http` body. */
286
527
  export type FileBody<S extends z.ZodType | undefined = undefined> = {
287
528
  readonly file: {
288
- /** The file, relative to the Repo Slot's Worktree. */
529
+ /** The file, relative to the Worktree that `worktree` of the Effect's input names, or to `/work`
530
+ * in a Workspace with no Repo Slot. */
289
531
  readonly path: FromInput<S, string>;
290
- /** The Repo Slot whose Worktree holds it — needed only where the Workspace carries several. */
291
- readonly slot?: string;
292
- /** Path segments appended to the url, as `request`'s `path` is for a json or text body. */
532
+ /** Path segments that jr2 adds to the end of the url, as `path` of `request` does for a json
533
+ * or text body. */
293
534
  readonly segments?: FromInput<S, readonly string[]>;
294
- /** The body's content type: this or a `content-type` header of the definition's, not both (a
295
- * definition with both is refused). With neither it is `application/octet-stream`. */
535
+ /** The content type of the body. Write it or a `content-type` header, not both. With neither,
536
+ * it is `application/octet-stream`. */
296
537
  readonly contentType?: string;
297
538
  };
298
539
  };
299
540
 
300
541
  /**
301
- * A Worktree file as an `http` body (ADR-0067): `body: file("dist/report.pdf")`. The Effect then runs in
302
- * the Sandbox Effector of the Workspace whose body invokes it, which opens the file beneath the
303
- * Worktree — a symlink at any component, `..`, an absolute path, a FIFO, a directory and a file
304
- * with more than one link are output `refused`, with nothing sent. `path` and `segments` may be
305
- * functions of the Effect's input.
542
+ * Make a Worktree file the body of an `http` Effect: `body: file("dist/report.pdf")`. The Effect
543
+ * then runs in the Sandbox Effector of the Workspace whose body invokes it. Under a Workspace with
544
+ * Repo Slots, the invoke's input names the Worktree: `input: { worktree: "/work/app/fix" }`. It is a
545
+ * Worktree that a cut of this run made. That Effector opens the file in that Worktree and sends it.
546
+ * In a Workspace with no Repo Slot, the input names no `worktree`, and the path is relative to
547
+ * `/work`, the folder that its steps share. Use it with `PUT`, `POST` or `PATCH`. `path` and
548
+ * `segments` can be functions of the input of the Effect.
549
+ *
550
+ * Some inputs, paths and files give the output `refused`, and then nothing is sent. The `worktree`
551
+ * has no cut in this Sandbox: `no cut made <path> in this Sandbox: cut it with worktree(); nothing
552
+ * was sent`. Or the input names a `worktree` in a Workspace with no Repo Slot. Or the path has a
553
+ * symlink in it, has `..`, or is absolute. Or the file is a FIFO, a directory, or a file with more
554
+ * than one link.
555
+ *
556
+ * It refuses, when you call it:
557
+ * - a path that is not a string or a function: `file(path): the path is a string, or a function of
558
+ * the Effect's input that returns one`
559
+ *
560
+ * And when `http` takes the definition:
561
+ * - a method other than `PUT`, `POST` and `PATCH`: `http({ method: "<method>", body: file(...) }):
562
+ * a file body is sent with PUT, POST, PATCH (ADR-0068)`
563
+ * - a `request` beside it: ``http({ body: file(...), request }): with a file body the run's part
564
+ * is file()'s — its path, from the input if it must be, and its `segments` — so write no
565
+ * `request` (ADR-0068)``
566
+ * - a `content-type` header and `contentType`: `http({ headers, body: file(path, { contentType })
567
+ * }): "content-type" is set by file()'s contentType — write one of them (ADR-0067)`
568
+ *
569
+ * And at `jr2 up`, an Effect that no `workspace()` invokes: `workflow "<workflow>": the http Effect
570
+ * "<slot>" sends a Worktree file as its body, and no workspace() invokes it — outside a Workspace
571
+ * there is no Worktree to read; invoke it from a workspace()'s body, or send a json or text body
572
+ * (ADR-0068)`.
573
+ *
574
+ * And when it starts, under a Workspace with Repo Slots, an input with no `worktree`: ``http
575
+ * "<slot>" reads a Worktree file, under a Workspace with Repo Slots, and its input names no
576
+ * `worktree` ``.
577
+ *
578
+ * @param path - The file, relative to the Worktree that `worktree` of the input names, or to
579
+ * `/work` in a Workspace with no Repo Slot: a string, or a function of the input of the Effect
580
+ * that gives one.
581
+ * @param options - Optional. Each field says what it does.
582
+ * @returns The `body` of an `http` Effect.
583
+ *
584
+ * @example
585
+ * ```ts
586
+ * import {
587
+ * bearer,
588
+ * command,
589
+ * fail,
590
+ * file,
591
+ * http,
592
+ * isReady,
593
+ * jr2Setup,
594
+ * secret,
595
+ * workspace,
596
+ * worktree,
597
+ * type Workspaced,
598
+ * } from "@jr2/orchestrator";
599
+ *
600
+ * type Input = Workspaced<{}, "app">;
601
+ *
602
+ * const reportTree = (context: Input) => `${context.workspace.repos.app}/report` as const;
603
+ *
604
+ * const body = jr2Setup({
605
+ * types: {} as { context: Input; input: Input },
606
+ * actors: {
607
+ * cut: worktree(),
608
+ * build: command(["npm", "run", "report"]),
609
+ * upload: http({
610
+ * method: "PUT",
611
+ * url: "https://files.example.com/reports",
612
+ * auth: bearer(secret("files")),
613
+ * body: file("dist/report.pdf", {
614
+ * segments: ["report.pdf"],
615
+ * contentType: "application/pdf",
616
+ * }),
617
+ * }),
618
+ * fail: fail(),
619
+ * },
620
+ * }).createMachine({
621
+ * id: "report",
622
+ * context: ({ input }) => input,
623
+ * on: { "workspace.lost": ".lost" },
624
+ * initial: "cutting",
625
+ * states: {
626
+ * cutting: {
627
+ * invoke: {
628
+ * src: "cut",
629
+ * input: ({ context }) => ({ path: reportTree(context), branch: "report" }),
630
+ * onDone: [{ guard: ({ event }) => isReady(event.output), target: "building" }, { target: "notCut" }],
631
+ * },
632
+ * },
633
+ * building: {
634
+ * invoke: { src: "build", input: ({ context }) => ({ worktree: reportTree(context) }), onDone: "uploading" },
635
+ * },
636
+ * uploading: {
637
+ * invoke: { src: "upload", input: ({ context }) => ({ worktree: reportTree(context) }), onDone: "done" },
638
+ * },
639
+ * done: { type: "final" },
640
+ * notCut: { invoke: { src: "fail", input: { reason: "the Worktree was not cut" } } },
641
+ * lost: { invoke: { src: "fail", input: { reason: "the Sandbox is gone" } } },
642
+ * },
643
+ * });
644
+ *
645
+ * export const machine = workspace(body, {
646
+ * repos: { app: { url: "https://github.com/you/app.git" } },
647
+ * });
648
+ * ```
649
+ *
650
+ * @see docs/guides/run-a-command-or-call-a-url.md
651
+ * @see docs/concepts/effects.md
652
+ * @see docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md
306
653
  */
307
654
  export function file<S extends z.ZodType | undefined = undefined>(
308
655
  path: FromInput<S, string>,
309
- options: { slot?: string; segments?: FromInput<S, readonly string[]>; contentType?: string } = {},
656
+ options: {
657
+ /**
658
+ * Path segments that jr2 adds to the end of the url, as `path` does in `request` for a json or
659
+ * text body. A list of strings, or a function of the input of the Effect that gives one.
660
+ */
661
+ segments?: FromInput<S, readonly string[]>;
662
+ /**
663
+ * The content type of the body. Default `application/octet-stream`. Do not write it beside a
664
+ * `content-type` header.
665
+ */
666
+ contentType?: string;
667
+ } = {},
310
668
  ): FileBody<S> {
669
+ // The Sandbox Effector opens the file beneath the Worktree with no symlink at any component
670
+ // (ADR-0067, ADR-0068); the refusals it makes are output, never a fault.
311
671
  if (typeof path !== "string" && typeof path !== "function") {
312
672
  throw new Error("file(path): the path is a string, or a function of the Effect's input that returns one");
313
673
  }
314
674
  return {
315
675
  file: {
316
676
  path,
317
- ...(options.slot !== undefined ? { slot: options.slot } : {}),
318
677
  ...(options.segments !== undefined ? { segments: options.segments } : {}),
319
678
  ...(options.contentType !== undefined ? { contentType: options.contentType } : {}),
320
679
  },
@@ -326,12 +685,16 @@ const FILE_METHODS: readonly HttpMethod[] = ["PUT", "POST", "PATCH"];
326
685
 
327
686
  // --- The definition ------------------------------------------------------------------------------
328
687
 
329
- /** What `request` returns: the run's part of the request. Everything else is the definition's. */
688
+ /** What the `request` of an `http` definition returns: the part of the request that changes for
689
+ * each run. The definition fixes the rest. */
330
690
  export type HttpRequestParts = {
331
- /** Path segments appended to the url: each is encoded; `.`, `..` and an empty one are refused
332
- * in the Effector as output, with nothing sent. */
691
+ /**
692
+ * Path segments that jr2 adds to the end of the url. jr2 encodes each segment. A segment that
693
+ * is `.`, `..` or empty gives the output `refused`, and nothing is sent.
694
+ */
333
695
  path?: readonly string[];
334
- /** A JSON body. */
696
+ /** A JSON body. Do not give `text` too. When it starts, the Effect refuses both: ``http:
697
+ * `request` returned both json and text``. */
335
698
  json?: unknown;
336
699
  /** A text body. */
337
700
  text?: string;
@@ -339,35 +702,107 @@ export type HttpRequestParts = {
339
702
 
340
703
  /** An `http` Effect's definition. */
341
704
  export type HttpDefinition<S extends z.ZodType | undefined = undefined> = {
705
+ /**
706
+ * The HTTP method: `GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE` or `OPTIONS`. It refuses
707
+ * another: `http({ method: "<method>" }): the method is one of GET, HEAD, POST, PUT, PATCH,
708
+ * DELETE, OPTIONS`.
709
+ */
342
710
  method: HttpMethod;
343
- /** The url. Its scheme and host are fixed here — never a param — and a secret may sit only in
344
- * its path: text`https://api.telegram.org/bot${secret("tg")}/sendMessage`. Or `open`: a packaged
345
- * Machine that cannot know the host leaves the url Open, and the composer binds it once with
346
- * `customize(machine, { effects: { <slot>: { url } } })` — never per run (ADR-0054). */
711
+ // The scheme and host are literal so that no param reaches the host (ADR-0068); an Open url is
712
+ // bound once by the composer, never per run (ADR-0054).
713
+ /**
714
+ * The url: `http://` or `https://`, then a literal host. No input, param or Effect secret can
715
+ * change where the call goes. An Effect secret can be only in the path:
716
+ * `` text`https://api.telegram.org/bot${secret("tg")}/sendMessage` ``.
717
+ *
718
+ * Or `open`: a packaged Machine that cannot know the host leaves the url Open, and the composer
719
+ * binds it one time with `customize(machine, { effects: { <slot>: { url } } })`.
720
+ *
721
+ * It refuses:
722
+ * - a scheme or host that is not literal: `` http({ url }): the url is an http:// or https:// url
723
+ * whose scheme and host are literal — a secret may sit only in its path, after a "/":
724
+ * text`https://api.example.com/bot${secret("tg")}/send` (ADR-0068) ``
725
+ * - an Effect secret after `?` or `#`: `` http({ url }): secret("<name>") sits in the url's query
726
+ * or fragment — a secret may sit only in its path; send it in `auth` or a header instead:
727
+ * headers: { "x-api-key": text`${secret("...")}` } (ADR-0068) ``
728
+ * - a host that is not a host: `http({ url }): "<scheme>://<host>" is not a host`
729
+ * - a user name or password in the url: ``http({ url }): the url carries userinfo — use `auth` ``
730
+ */
347
731
  url: string | Text | typeof open;
348
- /** Static headers, lowercase; a value may be a `text` with a secret. */
732
+ /**
733
+ * Headers that are the same for each run. Each name is lowercase. A value is a string, or a
734
+ * `text` with an Effect secret.
735
+ *
736
+ * It refuses:
737
+ * - a name that is not lowercase, or that frames or routes the request (`host`,
738
+ * `content-length`, `transfer-encoding`, `connection`, `upgrade`, `te`, `trailer`,
739
+ * `keep-alive`, `proxy-*`): `http({ headers }): "<name>" is not a header an Effect sets — a
740
+ * lowercase name that does not frame or route the request (ADR-0068)`
741
+ * - an `authorization` header beside `auth`: ``http({ headers }): "authorization" is set by
742
+ * `auth` — write one of them``
743
+ */
349
744
  headers?: Readonly<Record<string, string | Text>>;
745
+ /**
746
+ * The `Authorization` header: `bearer(secret("x"))`, `basic(user, password)`, or a `text` that
747
+ * is the whole value.
748
+ */
350
749
  auth?: { readonly bearer: Secret } | { readonly basic: { user: Text; password: Text } } | Text;
351
- /** The Effect's input — what an invoke's `input` (or `fromPick`) hands it. */
750
+ /**
751
+ * The zod schema of the input of the Effect: what the `input` of the `invoke`, or `fromPick`,
752
+ * gives it. jr2 parses the input with it before the call. When it starts, input that does not
753
+ * agree with it makes the Effect fail (its `onError` route): `http "<slot>": its input does not
754
+ * match the definition's input schema — <path>: <message>`, one `<path>: <message>` for each
755
+ * problem, joined by `; `.
756
+ */
352
757
  input?: S;
353
- /** The run's part of the request, from the input. */
758
+ /**
759
+ * A function that makes the part of the request that changes for each run, from the input:
760
+ * `path` segments, and a `json` or `text` body. It cannot change the host, a header or an
761
+ * Effect secret. Do not write it beside `body`.
762
+ */
354
763
  request?: (input: S extends z.ZodType ? z.infer<S> : void) => HttpRequestParts;
355
- /** A Worktree file as the body: `file(path, { slot?, segments?, contentType? })` (ADR-0067). Then the
356
- * Effect runs in its Workspace's Sandbox Effector, and `request` is not written: `file()`'s
357
- * `segments` are the path. */
764
+ // A file body is an ADR-0067 Effect: the Sandbox Effector of the Workspace sends it.
765
+ /**
766
+ * A Worktree file as the body: `file(path, { segments?, contentType? })`. The Effect then
767
+ * runs in the Sandbox Effector of its Workspace. Under a Workspace with Repo Slots, the invoke's
768
+ * input names a Worktree that a cut of this run made, in `worktree`; with none, it refuses when
769
+ * it starts: ``http "<slot>" reads a Worktree file, under a Workspace with Repo Slots, and its
770
+ * input names no `worktree` ``. Do not write `request` beside it: the `segments`
771
+ * of `file` are the path. It refuses another body: ``http({ body }): a body is file(path, {
772
+ * segments?, contentType? }) — json and text come from `request` ``.
773
+ */
358
774
  body?: FileBody<S>;
359
- /** Default 30s. Past it with no answer: output `unknown`. */
775
+ /**
776
+ * How long jr2 waits for the answer: a number and a unit, such as `"30s"` or `"2m"`. Default
777
+ * `"30s"`. With no answer in that time, the output is `unknown`. It refuses a value that is not
778
+ * a positive duration: `http: timeout "<timeout>" is not a positive duration (like "30s",
779
+ * "2m")`.
780
+ */
360
781
  timeout?: Duration;
361
- /** Bytes of a text body returned. Default 65536. */
782
+ /**
783
+ * The largest number of bytes of a text response body that the output keeps. Default 65536.
784
+ * When jr2 cuts the body, `truncated` is `true`. It refuses a value that is not a positive whole
785
+ * number: `http({ maxBody }): a positive number of bytes`.
786
+ */
362
787
  maxBody?: number;
363
- /** Marked by the author: the call may be sent twice (it carries its own idempotency key, or it
364
- * is a read). A restore that finds no replica holding it then sends it again; unmarked, it
365
- * settles `unknown` (ADR-0067). Default false. */
788
+ // The restore rule (ADR-0067): a restore that finds no replica holding the call sends it again
789
+ // only when it is marked; unmarked, it settles `unknown`.
790
+ /**
791
+ * `true` when it is safe to send the call two times: it has its own idempotency key, or it only
792
+ * reads. Default `false`. After an Orchestrator restart, jr2 cannot always tell if the call went
793
+ * out. Then it sends an idempotent call again, and gives any other call the output `unknown`.
794
+ */
366
795
  idempotent?: boolean;
367
796
  };
368
797
 
798
+ /** @internal */
369
799
  export type HttpInput<S> = S extends z.ZodType ? z.infer<S> : void;
370
800
 
801
+ /** The input of an `http` Effect with a Worktree file as its body. Under a Workspace with Repo
802
+ * Slots, `worktree` names the Worktree. In a Workspace with no Repo Slot, the input has no
803
+ * `worktree`, and the path is relative to `/work`. */
804
+ export type HttpFileInput<S> = (S extends z.ZodType ? z.infer<S> : unknown) & { worktree?: AnyWorktreePath };
805
+
371
806
  const DEFAULT_TIMEOUT_MS = 30_000;
372
807
  const DEFAULT_MAX_BODY = 65_536;
373
808
  /** How long a re-asked request waits, at most, after a transport failure. */
@@ -443,7 +878,7 @@ export function httpParts<S extends z.ZodType | undefined>(def: HttpDefinition<S
443
878
  const body = def.body?.file;
444
879
  if (def.body !== undefined && (typeof body !== "object" || body === null)) {
445
880
  throw new Error(
446
- "http({ body }): a body is file(path, { slot?, segments?, contentType? }) — json and text come from `request`",
881
+ "http({ body }): a body is file(path, { segments?, contentType? }) — json and text come from `request`",
447
882
  );
448
883
  }
449
884
  if (body && !FILE_METHODS.includes(def.method)) {
@@ -479,7 +914,6 @@ export function httpParts<S extends z.ZodType | undefined>(def: HttpDefinition<S
479
914
  ...(body
480
915
  ? {
481
916
  file: {
482
- ...(body.slot !== undefined ? { slot: body.slot } : {}),
483
917
  ...(body.contentType !== undefined ? { contentType: body.contentType } : {}),
484
918
  },
485
919
  }
@@ -504,8 +938,8 @@ export function openHttpParts<S extends z.ZodType | undefined>(def: HttpDefiniti
504
938
  /**
505
939
  * The unbound logic of an http definition with Open parts (ADR-0054) — its url, an Open secret in
506
940
  * its auth or a header, or both — or undefined when every part is fixed. Binding builds the Effect
507
- * again, whole, with `rebuild` (its own constructor) over the definition with the bound parts, so the
508
- * bound one is checked like any definition; a binding that names one of two Open parts gives an
941
+ * again, whole, with `rebuild` (its own constructor) over the definition with the bound parts, so
942
+ * the bound one is checked like any definition; a binding that names one of two Open parts gives an
509
943
  * Effect still Open in the other.
510
944
  */
511
945
  export function openHttpOf<S extends z.ZodType | undefined>(
@@ -879,8 +1313,8 @@ async function drive<I, O>(
879
1313
  /**
880
1314
  * The state that invoked this Effect exited (D1) — or the host is parking the run. Parking keeps
881
1315
  * the call for the restore to find. An exit stops the polling and records the abort, "outcome
882
- * unknown": an http Effect has no stop route, so the Effector finishes the call and drops an outcome nobody
883
- * reads. The Machine never hears it.
1316
+ * unknown": an http Effect has no stop route, so the Effector finishes the call and drops an
1317
+ * outcome nobody reads. The Machine never hears it.
884
1318
  */
885
1319
  function stopped(self: AnyActorRef, system: AnyActorSystem, effectId: string): void {
886
1320
  const run = running.get(self);
@@ -955,13 +1389,75 @@ function parseDuration(d: Duration): number {
955
1389
  }
956
1390
 
957
1391
  /**
958
- * THE authoring surface for an `http` Effect (ADR-0068): `actors: { comment: http({ method, url,
959
- * auth: bearer(secret("jira")), input, request }) }`, invoked as `src: "comment"`. It runs in the
960
- * Instance Effector, which `jr2 up` converges because this definition is placed there — or, with
961
- * `body: file(...)`, in the Sandbox Effector of the Workspace whose body invokes it (ADR-0067). The
962
- * Orchestrator signs the call and holds no secret. Every outcome is output:
963
- * `response | unreachable | refused | unknown`.
1392
+ * Make an `http` Effect: a call to a URL, with Effect secrets that the Agent never reads. Put it
1393
+ * in `actors`, and invoke it by its key: `actors: { comment: http({ ... }) }`, then `invoke: {
1394
+ * src: "comment" }`.
1395
+ *
1396
+ * It runs in the Instance Effector, which `jr2 up` deploys when a Machine has an `http` Effect.
1397
+ * With `body: file(...)`, it runs in the Sandbox Effector of the Workspace whose body invokes it,
1398
+ * and under Repo Slots its input names the Worktree in `worktree`. The Effector makes the call. The
1399
+ * Orchestrator holds no secret.
1400
+ *
1401
+ * Every outcome is output (`HttpOutput`): `response`, `unreachable`, `refused` or `unknown`. A 4xx
1402
+ * or 5xx status is output too. jr2 sends the call at most one time, unless you mark it
1403
+ * `idempotent`. When its state exits while the call runs, jr2 stops waiting for it. The Effector
1404
+ * can still send it.
1405
+ *
1406
+ * Each field of {@link HttpDefinition} tells what it refuses. `createMachine` also refuses an
1407
+ * Effect written directly in an `invoke`: `machine "<id>": state "<state>" invokes an Effect
1408
+ * inline — a Part is a key of actors, so it has a name that the ledger, the report and
1409
+ * customize() share: actors: { <name>: http(...) }, then invoke: { src: "<name>" }`.
1410
+ *
1411
+ * @param def - The definition. Each field tells what it does.
1412
+ * @returns An Effect, for `actors`.
1413
+ *
1414
+ * @example
1415
+ * ```ts
1416
+ * import { z } from "zod";
1417
+ * import { bearer, http, jr2Setup, secret } from "@jr2/orchestrator";
1418
+ *
1419
+ * const comment = http({
1420
+ * method: "POST",
1421
+ * url: "https://jira.example.com/rest/api/2/issue",
1422
+ * auth: bearer(secret("jira")),
1423
+ * input: z.object({ key: z.string(), body: z.string() }),
1424
+ * request: ({ key, body }) => ({ path: [key, "comment"], json: { body } }),
1425
+ * });
1426
+ *
1427
+ * export const machine = jr2Setup({
1428
+ * types: {} as { context: { key: string }; input: { key: string } },
1429
+ * actors: { comment },
1430
+ * }).createMachine({
1431
+ * id: "comment",
1432
+ * context: ({ input }) => input,
1433
+ * initial: "commenting",
1434
+ * states: {
1435
+ * commenting: {
1436
+ * invoke: {
1437
+ * src: "comment",
1438
+ * input: ({ context }) => ({ key: context.key, body: "The build passed." }),
1439
+ * onDone: "done",
1440
+ * },
1441
+ * },
1442
+ * done: { type: "final" },
1443
+ * },
1444
+ * });
1445
+ * ```
1446
+ *
1447
+ * @see docs/concepts/effects.md
1448
+ * @see docs/guides/run-a-command-or-call-a-url.md
1449
+ * @see docs/concepts/secrets-and-trust.md
1450
+ * @see docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md
1451
+ * @see docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md
964
1452
  */
1453
+ export function http<
1454
+ S extends z.ZodType | undefined = undefined,
1455
+ const U extends HttpDefinition<S>["url"] = string,
1456
+ const A extends HttpDefinition<S>["auth"] = undefined,
1457
+ const H extends HttpDefinition<S>["headers"] = undefined,
1458
+ >(
1459
+ def: HttpDefinition<S> & { url: U; auth?: A; headers?: H; body: FileBody<S> },
1460
+ ): EffectLogic<HttpFileInput<S>, HttpOutput> & OpenOf<HttpOpenParts<U, A, H>>;
965
1461
  export function http<
966
1462
  S extends z.ZodType | undefined = undefined,
967
1463
  const U extends HttpDefinition<S>["url"] = string,
@@ -973,6 +1469,8 @@ export function http<
973
1469
  export function http<S extends z.ZodType | undefined = undefined>(
974
1470
  def: HttpDefinition<S>,
975
1471
  ): EffectLogic<HttpInput<S>, HttpOutput> {
1472
+ // The Orchestrator signs the start, and the Effector builds the request: the definition's parts,
1473
+ // and `request`'s from the input (ADR-0068).
976
1474
  if (def.body !== undefined) return httpFileWith(sandboxEffectorPort, def);
977
1475
  return httpWith(
978
1476
  (target) => createEffectorClient({ key: target.key, transport: nodeEffectorTransport(target.service) }),