@jr2/orchestrator 0.5.1 → 0.7.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 (350) hide show
  1. package/console/components/app.ts +55 -15
  2. package/console/components/bottom-drawer.ts +259 -0
  3. package/console/components/drawer.ts +14 -6
  4. package/console/components/fleet.ts +36 -13
  5. package/console/components/machine-pane.ts +61 -5
  6. package/console/components/nav.ts +54 -8
  7. package/console/conversation.ts +161 -0
  8. package/console/login.ts +123 -0
  9. package/console/main.ts +600 -60
  10. package/console/store.ts +611 -64
  11. package/console/style.css +231 -0
  12. package/docs/agents-block.md +2 -2
  13. package/docs/concepts/agents-and-turns.md +142 -29
  14. package/docs/concepts/architecture.md +60 -34
  15. package/docs/concepts/callers-and-hooks.md +205 -0
  16. package/docs/concepts/charts.md +13 -8
  17. package/docs/concepts/composition.md +10 -1
  18. package/docs/concepts/converge.md +7 -2
  19. package/docs/concepts/durable-runs.md +43 -16
  20. package/docs/concepts/effects.md +133 -50
  21. package/docs/concepts/gates.md +95 -25
  22. package/docs/concepts/machines-and-workflows.md +14 -4
  23. package/docs/concepts/pools-and-sources.md +193 -17
  24. package/docs/concepts/secrets-and-trust.md +186 -62
  25. package/docs/concepts/the-menu.md +14 -0
  26. package/docs/concepts/workspaces.md +135 -56
  27. package/docs/get-started/a-model-decides.md +14 -13
  28. package/docs/get-started/an-agent-in-a-sandbox.md +46 -35
  29. package/docs/get-started/connect-a-model.md +1 -1
  30. package/docs/get-started/create-an-instance.md +17 -14
  31. package/docs/get-started/have-your-assistant-write-a-machine.md +111 -40
  32. package/docs/get-started/install.md +4 -0
  33. package/docs/guides/add-tools-to-the-sandbox.md +72 -15
  34. package/docs/guides/answer-a-gate-from-a-forge.md +304 -0
  35. package/docs/guides/answer-a-gate.md +74 -37
  36. package/docs/guides/bind-open-parts.md +29 -12
  37. package/docs/guides/call-an-mcp-tool-from-a-machine.md +572 -0
  38. package/docs/guides/cancel-a-run.md +26 -16
  39. package/docs/guides/cut-a-worktree.md +560 -0
  40. package/docs/guides/fix-a-failed-converge.md +19 -7
  41. package/docs/guides/give-a-caller-a-scope.md +203 -0
  42. package/docs/guides/give-an-agent-mcp-tools.md +422 -0
  43. package/docs/guides/iterate-on-a-machine-on-kind.md +10 -9
  44. package/docs/guides/let-an-agent-write-a-chart.md +14 -7
  45. package/docs/guides/look-inside-a-sandbox.md +115 -42
  46. package/docs/guides/move-from-kind-to-a-shared-cluster.md +16 -0
  47. package/docs/guides/open-the-console.md +63 -14
  48. package/docs/guides/push-and-sign-a-branch.md +67 -21
  49. package/docs/guides/read-a-conversation.md +225 -0
  50. package/docs/guides/run-a-chart-by-name.md +17 -16
  51. package/docs/guides/run-a-command-or-call-a-url.md +84 -41
  52. package/docs/guides/run-one-worker-per-item.md +34 -27
  53. package/docs/guides/set-an-agents-access-and-dials.md +69 -22
  54. package/docs/guides/size-a-workspace.md +19 -6
  55. package/docs/guides/start-a-run-for-each-alert.md +290 -0
  56. package/docs/guides/start-a-run-from-a-run.md +18 -16
  57. package/docs/guides/start-a-run.md +24 -18
  58. package/docs/guides/steer-an-agent.md +332 -0
  59. package/docs/guides/use-a-hosted-provider.md +7 -1
  60. package/docs/guides/watch-a-run.md +146 -117
  61. package/docs/guides/write-a-hook-kind.md +238 -0
  62. package/docs/guides/write-a-tool-as-an-mcp-server.md +351 -0
  63. package/docs/guides/write-a-workflow.md +18 -8
  64. package/docs/llms.txt +110 -23
  65. package/docs/reference/authoring-surface/agent.md +35 -12
  66. package/docs/reference/authoring-surface/answer.md +20 -4
  67. package/docs/reference/authoring-surface/arg.md +28 -6
  68. package/docs/reference/authoring-surface/basic.md +1 -1
  69. package/docs/reference/authoring-surface/bearer.md +1 -1
  70. package/docs/reference/authoring-surface/branchname.md +15 -0
  71. package/docs/reference/authoring-surface/carry.md +23 -0
  72. package/docs/reference/authoring-surface/chartschema.md +1 -1
  73. package/docs/reference/authoring-surface/chartschemafor.md +1 -1
  74. package/docs/reference/authoring-surface/command.md +37 -22
  75. package/docs/reference/authoring-surface/customize.md +4 -1
  76. package/docs/reference/authoring-surface/defineconfig.md +1 -1
  77. package/docs/reference/authoring-surface/defineevent.md +1 -1
  78. package/docs/reference/authoring-surface/dispatch.md +5 -4
  79. package/docs/reference/authoring-surface/fail.md +18 -5
  80. package/docs/reference/authoring-surface/file.md +37 -17
  81. package/docs/reference/authoring-surface/frompick.md +1 -1
  82. package/docs/reference/authoring-surface/gate.md +118 -0
  83. package/docs/reference/authoring-surface/group-agents.md +15 -0
  84. package/docs/reference/authoring-surface/group-charts.md +16 -0
  85. package/docs/reference/authoring-surface/group-composition.md +25 -0
  86. package/docs/reference/authoring-surface/group-config.md +17 -0
  87. package/docs/reference/authoring-surface/group-effects.md +48 -0
  88. package/docs/reference/authoring-surface/group-failure.md +13 -0
  89. package/docs/reference/authoring-surface/group-gates.md +16 -0
  90. package/docs/reference/authoring-surface/group-machine-and-events.md +15 -0
  91. package/docs/reference/authoring-surface/group-steering.md +12 -0
  92. package/docs/reference/authoring-surface/group-types-these-signatures-name.md +137 -0
  93. package/docs/reference/authoring-surface/group-workspaces-and-repos.md +15 -0
  94. package/docs/reference/authoring-surface/http.md +109 -9
  95. package/docs/reference/authoring-surface/isfailure.md +1 -1
  96. package/docs/reference/authoring-surface/isready.md +14 -0
  97. package/docs/reference/authoring-surface/jr2setup.md +14 -4
  98. package/docs/reference/authoring-surface/mcp.md +107 -0
  99. package/docs/reference/authoring-surface/open.md +41 -8
  100. package/docs/reference/authoring-surface/openbody.md +1 -1
  101. package/docs/reference/authoring-surface/paramsof.md +23 -5
  102. package/docs/reference/authoring-surface/pool.md +79 -11
  103. package/docs/reference/authoring-surface/push.md +31 -13
  104. package/docs/reference/authoring-surface/readyof.md +21 -0
  105. package/docs/reference/authoring-surface/reread.md +19 -0
  106. package/docs/reference/authoring-surface/secret.md +26 -5
  107. package/docs/reference/authoring-surface/sentby.md +1 -1
  108. package/docs/reference/authoring-surface/skip.md +22 -0
  109. package/docs/reference/authoring-surface/source.md +59 -4
  110. package/docs/reference/authoring-surface/steer.md +93 -0
  111. package/docs/reference/authoring-surface/text.md +1 -1
  112. package/docs/reference/authoring-surface/type-agentdefinition.md +16 -3
  113. package/docs/reference/authoring-surface/type-agentmcpserver.md +54 -0
  114. package/docs/reference/authoring-surface/type-agentslots.md +4 -1
  115. package/docs/reference/authoring-surface/type-anyframepath.md +14 -0
  116. package/docs/reference/authoring-surface/type-anyworktreepath.md +15 -0
  117. package/docs/reference/authoring-surface/type-bindactors.md +14 -0
  118. package/docs/reference/authoring-surface/type-binding.md +6 -7
  119. package/docs/reference/authoring-surface/type-caller.md +40 -0
  120. package/docs/reference/authoring-surface/type-callerrecord.md +32 -0
  121. package/docs/reference/authoring-surface/type-carry.md +19 -0
  122. package/docs/reference/authoring-surface/type-chainlink.md +3 -3
  123. package/docs/reference/authoring-surface/type-commandinput.md +4 -3
  124. package/docs/reference/authoring-surface/type-commandoptions.md +0 -22
  125. package/docs/reference/authoring-surface/type-commandoutput.md +23 -5
  126. package/docs/reference/authoring-surface/type-customize.md +20 -3
  127. package/docs/reference/authoring-surface/type-delivered.md +17 -0
  128. package/docs/reference/authoring-surface/type-deliveredevent.md +18 -0
  129. package/docs/reference/authoring-surface/type-dispatchdefinition.md +4 -3
  130. package/docs/reference/authoring-surface/type-dispatchoutput.md +9 -2
  131. package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
  132. package/docs/reference/authoring-surface/type-effectbinding.md +12 -2
  133. package/docs/reference/authoring-surface/type-filebody.md +4 -8
  134. package/docs/reference/authoring-surface/type-framepath.md +14 -0
  135. package/docs/reference/authoring-surface/type-gatebinding.md +15 -0
  136. package/docs/reference/authoring-surface/type-gatehooks.md +12 -0
  137. package/docs/reference/authoring-surface/type-gatelook.md +37 -0
  138. package/docs/reference/authoring-surface/type-gateoutcome.md +32 -0
  139. package/docs/reference/authoring-surface/type-gatereply.md +19 -0
  140. package/docs/reference/authoring-surface/type-gateslot.md +21 -0
  141. package/docs/reference/authoring-surface/type-gateslotinput.md +13 -0
  142. package/docs/reference/authoring-surface/type-gateslotparts.md +18 -0
  143. package/docs/reference/authoring-surface/type-gateslots.md +12 -0
  144. package/docs/reference/authoring-surface/type-gatetypesat.md +13 -0
  145. package/docs/reference/authoring-surface/type-hostinjectedinput.md +3 -5
  146. package/docs/reference/authoring-surface/type-httpdefinition.md +6 -3
  147. package/docs/reference/authoring-surface/type-httpfileinput.md +20 -0
  148. package/docs/reference/authoring-surface/type-httpoutput.md +8 -2
  149. package/docs/reference/authoring-surface/type-jr2config.md +37 -2
  150. package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
  151. package/docs/reference/authoring-surface/type-lends.md +5 -1
  152. package/docs/reference/authoring-surface/type-lookoutput.md +14 -0
  153. package/docs/reference/authoring-surface/type-mcpcalloutput.md +78 -0
  154. package/docs/reference/authoring-surface/type-mcpnotreturned.md +42 -0
  155. package/docs/reference/authoring-surface/type-mcpoptions.md +47 -0
  156. package/docs/reference/authoring-surface/type-mcpoutput.md +50 -0
  157. package/docs/reference/authoring-surface/type-mcpreadoutput.md +53 -0
  158. package/docs/reference/authoring-surface/type-mcpremoteserver.md +50 -0
  159. package/docs/reference/authoring-surface/type-mcpserver.md +41 -0
  160. package/docs/reference/authoring-surface/type-mcpstdioserver.md +42 -0
  161. package/docs/reference/authoring-surface/type-namedsecret.md +4 -4
  162. package/docs/reference/authoring-surface/type-namestart.md +77 -0
  163. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +1 -6
  164. package/docs/reference/authoring-surface/type-notworktreekeys.md +12 -0
  165. package/docs/reference/authoring-surface/type-openbodydeclaration.md +1 -1
  166. package/docs/reference/authoring-surface/type-openbodyoutput.md +1 -1
  167. package/docs/reference/authoring-surface/type-opencommandinput.md +7 -2
  168. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +1 -6
  169. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +5 -10
  170. package/docs/reference/authoring-surface/type-poolnextinput.md +22 -0
  171. package/docs/reference/authoring-surface/type-poolspec.md +11 -6
  172. package/docs/reference/authoring-surface/type-pushinput.md +22 -3
  173. package/docs/reference/authoring-surface/type-pushoutput.md +33 -8
  174. package/docs/reference/authoring-surface/type-pushsourcespec.md +38 -0
  175. package/docs/reference/authoring-surface/type-readsourcespec.md +47 -0
  176. package/docs/reference/authoring-surface/type-reposlot.md +6 -6
  177. package/docs/reference/authoring-surface/type-reread.md +18 -0
  178. package/docs/reference/authoring-surface/type-runsconfig.md +41 -0
  179. package/docs/reference/authoring-surface/type-sandboxoptions.md +11 -4
  180. package/docs/reference/authoring-surface/type-sender.md +26 -1
  181. package/docs/reference/authoring-surface/type-sendercallerfield.md +14 -0
  182. package/docs/reference/authoring-surface/type-size.md +1 -1
  183. package/docs/reference/authoring-surface/type-skip.md +19 -0
  184. package/docs/reference/authoring-surface/type-slothandles.md +5 -13
  185. package/docs/reference/authoring-surface/type-sourcehooks.md +23 -0
  186. package/docs/reference/authoring-surface/type-sourcein.md +25 -0
  187. package/docs/reference/authoring-surface/type-sourcereply.md +18 -0
  188. package/docs/reference/authoring-surface/type-sourcespec.md +26 -17
  189. package/docs/reference/authoring-surface/type-sourceverdict.md +49 -0
  190. package/docs/reference/authoring-surface/type-standingpoolmachine.md +12 -0
  191. package/docs/reference/authoring-surface/type-standingpoolspec.md +51 -0
  192. package/docs/reference/authoring-surface/type-steerargs.md +20 -0
  193. package/docs/reference/authoring-surface/type-thinkinglevel.md +1 -1
  194. package/docs/reference/authoring-surface/type-usercontainer.md +1 -1
  195. package/docs/reference/authoring-surface/type-workspaced.md +30 -9
  196. package/docs/reference/authoring-surface/type-workspacehandles.md +5 -6
  197. package/docs/reference/authoring-surface/type-workspaceoptions.md +1 -13
  198. package/docs/reference/authoring-surface/type-worktreefound.md +16 -0
  199. package/docs/reference/authoring-surface/type-worktreeinput.md +56 -0
  200. package/docs/reference/authoring-surface/type-worktreekey.md +14 -0
  201. package/docs/reference/authoring-surface/type-worktreeoptions.md +29 -0
  202. package/docs/reference/authoring-surface/type-worktreeoutput.md +45 -0
  203. package/docs/reference/authoring-surface/type-worktreepath.md +16 -0
  204. package/docs/reference/authoring-surface/type-worktreeready.md +37 -0
  205. package/docs/reference/authoring-surface/type-worktreerefused.md +21 -0
  206. package/docs/reference/authoring-surface/workspace.md +66 -38
  207. package/docs/reference/authoring-surface/worktree.md +71 -0
  208. package/docs/reference/authoring-surface.md +88 -147
  209. package/docs/reference/cli.md +413 -74
  210. package/docs/reference/config.md +248 -6
  211. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +10 -9
  212. package/docs/reference/decisions/0002-duplex-streaming-actor.md +81 -14
  213. package/docs/reference/decisions/0004-worktree-provisioning-model.md +27 -21
  214. package/docs/reference/decisions/0005-sandbox-pod-composition.md +39 -25
  215. package/docs/reference/decisions/0006-agent-control-surface.md +6 -3
  216. package/docs/reference/decisions/0007-durable-machine-state.md +1 -1
  217. package/docs/reference/decisions/0009-cli-and-instance-interface.md +150 -49
  218. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +5 -3
  219. package/docs/reference/decisions/0011-workflow-defined-events.md +256 -12
  220. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +39 -32
  221. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +54 -30
  222. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +53 -27
  223. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +26 -16
  224. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +9 -7
  225. package/docs/reference/decisions/0017-pool-over-a-source-port.md +226 -10
  226. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +15 -7
  227. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +53 -27
  228. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +16 -2
  229. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +4 -4
  230. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +124 -43
  231. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +36 -38
  232. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +10 -5
  233. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +16 -7
  234. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +36 -19
  235. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +18 -10
  236. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +3 -2
  237. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +23 -15
  238. package/docs/reference/decisions/0032-the-console-unlocks-with-a-callers-credential.md +101 -0
  239. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +22 -22
  240. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +6 -6
  241. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +13 -12
  242. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +23 -23
  243. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +6 -1
  244. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +57 -46
  245. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +5 -0
  246. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +20 -8
  247. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +55 -40
  248. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +14 -13
  249. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +31 -24
  250. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +18 -10
  251. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +137 -86
  252. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +165 -91
  253. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +7 -7
  254. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +2 -2
  255. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md +109 -0
  256. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +53 -23
  257. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +221 -153
  258. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +161 -96
  259. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +32 -9
  260. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +48 -33
  261. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +56 -30
  262. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +35 -25
  263. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +1 -1
  264. package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +296 -0
  265. package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +347 -0
  266. package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
  267. package/docs/reference/decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md +285 -0
  268. package/docs/reference/decisions/0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md +198 -0
  269. package/docs/reference/decisions.md +9 -4
  270. package/docs/reference/glossary.md +138 -45
  271. package/docs/reference/hooks.md +1077 -0
  272. package/docs/reference/http-api.md +988 -123
  273. package/docs/reference/machines.md +0 -1
  274. package/docs/why.md +7 -5
  275. package/package.json +10 -3
  276. package/src/actor.ts +194 -42
  277. package/src/agent.ts +56 -6
  278. package/src/ambient.ts +3 -5
  279. package/src/assembly-checks.ts +530 -4
  280. package/src/callers.ts +245 -0
  281. package/src/chart.ts +238 -26
  282. package/src/charts.ts +9 -2
  283. package/src/config.ts +566 -7
  284. package/src/conversations.ts +453 -0
  285. package/src/custodian.lua +17 -5
  286. package/src/custodian.ts +257 -77
  287. package/src/customize.ts +154 -7
  288. package/src/dispatch-effect.ts +142 -26
  289. package/src/durability.ts +2 -0
  290. package/src/duration.ts +20 -0
  291. package/src/effect-checks.ts +52 -4
  292. package/src/effect.ts +401 -132
  293. package/src/effector-wire.ts +278 -30
  294. package/src/fail.ts +17 -4
  295. package/src/gate-brand.ts +16 -0
  296. package/src/gate.ts +634 -48
  297. package/src/harness-client.ts +84 -35
  298. package/src/harness-env.ts +33 -0
  299. package/src/held-secrets.ts +197 -39
  300. package/src/holds.ts +5 -2
  301. package/src/hook-kinds/discord.ts +92 -0
  302. package/src/hook-kinds/door.ts +107 -0
  303. package/src/hook-kinds/github.ts +89 -0
  304. package/src/hook-kinds/gitlab.ts +72 -0
  305. package/src/hook-kinds/hook.ts +7 -0
  306. package/src/hook-kinds/index.ts +465 -0
  307. package/src/hook-kinds/slack.ts +151 -0
  308. package/src/hook-kinds/webhook.ts +113 -0
  309. package/src/hooks.ts +506 -0
  310. package/src/http-effect.ts +164 -70
  311. package/src/http.ts +933 -99
  312. package/src/index.ts +30 -5
  313. package/src/instance.ts +196 -26
  314. package/src/internal.ts +23 -3
  315. package/src/kube-client.ts +6 -3
  316. package/src/machine-doc.ts +29 -5
  317. package/src/mcp-effect.ts +424 -0
  318. package/src/model-keys.ts +24 -0
  319. package/src/mount-digest.ts +41 -7
  320. package/src/names.ts +82 -7
  321. package/src/notices.ts +576 -59
  322. package/src/open-body.ts +110 -30
  323. package/src/open-effect.ts +17 -4
  324. package/src/open.ts +40 -7
  325. package/src/parts.ts +125 -35
  326. package/src/party.ts +35 -1
  327. package/src/pick.ts +43 -9
  328. package/src/pool.ts +1172 -172
  329. package/src/registration.ts +249 -17
  330. package/src/repo-identity.ts +7 -3
  331. package/src/repos.ts +20 -2
  332. package/src/run-host.ts +1614 -190
  333. package/src/run-list.ts +191 -0
  334. package/src/sandbox-effect.ts +217 -66
  335. package/src/sandbox-kube.ts +116 -21
  336. package/src/secret-placement.ts +248 -35
  337. package/src/server.ts +53 -4
  338. package/src/setup.ts +158 -38
  339. package/src/snapshot-store.ts +565 -48
  340. package/src/steer.ts +111 -0
  341. package/src/stub-harness.ts +6 -2
  342. package/src/tokens.ts +362 -46
  343. package/src/vocabulary.ts +23 -4
  344. package/src/wire.ts +27 -10
  345. package/src/workspace.ts +314 -259
  346. package/src/worktree.ts +249 -0
  347. package/docs/reference/authoring-surface/type-noslotspec.md +0 -26
  348. package/docs/reference/authoring-surface/type-workspacespec.md +0 -37
  349. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +0 -61
  350. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +0 -65
package/src/open-body.ts CHANGED
@@ -24,6 +24,7 @@ import {
24
24
  type UnknownActorLogic,
25
25
  } from "xstate";
26
26
  import { isAgent, type AgentDefinition, type WorkspaceAccess } from "./agent.ts";
27
+ import { ambientHandlesFor } from "./ambient.ts";
27
28
  import {
28
29
  admitChart,
29
30
  chartDigest,
@@ -31,8 +32,10 @@ import {
31
32
  ChartRefusal,
32
33
  GENERIC_ACTIONS,
33
34
  pathSegments,
35
+ READY_GUARD,
34
36
  resolveSource,
35
37
  resolveReferences,
38
+ slotFolderFault,
36
39
  type AdmittedChart,
37
40
  type ChartAction,
38
41
  type ChartGuard,
@@ -43,11 +46,13 @@ import {
43
46
  type ChartTransition,
44
47
  type ChartTransitions,
45
48
  } from "./chart.ts";
46
- import { inputKeysOf, isEffect, type Duration } from "./effect.ts";
49
+ import { inputShapeOf, isEffect, type Duration, type InputShape } from "./effect.ts";
47
50
  import { fail, FAILURE_GUARD, failureGuard } from "./fail.ts";
48
51
  import { agent } from "./harness-client.ts";
49
52
  import { SENT_BY } from "./party.ts";
50
- import { wrapperBodyOf } from "./parts.ts";
53
+ import { composesSandbox, sandboxPartsOf, wrapperBodyOf } from "./parts.ts";
54
+ import { isGateSlot } from "./gate.ts";
55
+ import { open } from "./open.ts";
51
56
  import { actorPath, boundRun } from "./registration.ts";
52
57
  import { COMPILED_CHART, jr2Setup } from "./setup.ts";
53
58
  import { attachBoundMachine, boundMachineFor } from "./vocabulary.ts";
@@ -111,7 +116,9 @@ export function jsonOver(value: unknown, cap: number): boolean {
111
116
  }
112
117
 
113
118
  /** The parts of the Chart wrapper that a Chart can use, by their keys in `actors`, `guards`,
114
- * `actions` and `delays`. `gate: true` lends the Gate, so that a Chart can wait for an answer from
119
+ * `actions` and `delays`. `gates` lends Gate slots (`gate({ … })`) that hear a Hook, by slot name:
120
+ * the door refuses a Chart that invokes one whose `delivers` names an event that is not in the
121
+ * Chart's `events`. `gate: true` lends the Gate, so that a Chart can wait for an answer from
115
122
  * outside the run. `fail: true` lends the Part that fails the run, so that a Chart can end its run
116
123
  * as a Failure (ADR-0073): the Machine decides which ends a Chart can choose. A Chart can use no
117
124
  * other part. */
@@ -119,6 +126,7 @@ export type Lends = {
119
126
  agents?: string[];
120
127
  effects?: string[];
121
128
  machines?: string[];
129
+ gates?: string[];
122
130
  guards?: string[];
123
131
  actions?: string[];
124
132
  delays?: string[];
@@ -157,7 +165,7 @@ export type OpenBodySlot = { readonly openBody: OpenBodyDeclaration };
157
165
  /** The logic `openBody()` returns: actor logic that carries the brand. */
158
166
  export type OpenBodyLogic = UnknownActorLogic & OpenBodySlot;
159
167
 
160
- const LEND_KINDS = ["agents", "effects", "machines", "guards", "actions", "delays"] as const;
168
+ const LEND_KINDS = ["agents", "effects", "machines", "gates", "guards", "actions", "delays"] as const;
161
169
  const WORKSPACE: readonly WorkspaceAccess[] = ["write", "read", "none"];
162
170
  const CEILING_KEYS = ["turns", "wallClock", "transitions", "fanOut", "size"] as const;
163
171
  const DURATION = /^(\d+(\.\d+)?)(s|m|h)$/;
@@ -166,9 +174,9 @@ const MAX_WALL_CLOCK_MS = 2_147_483_647;
166
174
  /** A name a Chart's grammar reads as its own, so a lent part by that name would be shadowed. */
167
175
  const reservedName = (kind: (typeof LEND_KINDS)[number], v: string): string | undefined => {
168
176
  if (v.includes(".")) return `"${v}" holds a dot; a lent name is a slot key, and dotted names are jr2's and xstate's`;
169
- if ((kind === "agents" || kind === "effects" || kind === "machines") && v === "gate")
177
+ if ((kind === "agents" || kind === "effects" || kind === "machines" || kind === "gates") && v === "gate")
170
178
  return `"gate" is the kit's Gate; lend a slot of another name`;
171
- if ((kind === "agents" || kind === "effects" || kind === "machines") && v === "fail")
179
+ if ((kind === "agents" || kind === "effects" || kind === "machines" || kind === "gates") && v === "fail")
172
180
  return `"fail" is the kit's Part that fails the run, lent as \`fail: true\`; lend a slot of another name`;
173
181
  if (kind === "actions" && (GENERIC_ACTIONS as readonly string[]).includes(v))
174
182
  return `"${v}" is a generic action every Chart has; lend an action of another name`;
@@ -516,7 +524,12 @@ const EMPTY_LENT: LentParts = { actors: {}, guards: {}, actions: {}, delays: {}
516
524
  /** The lent names of a declaration, by the implementation map each resolves in. */
517
525
  function lentNames(decl: OpenBodyDeclaration) {
518
526
  return {
519
- actors: [...(decl.lends.agents ?? []), ...(decl.lends.effects ?? []), ...(decl.lends.machines ?? [])],
527
+ actors: [
528
+ ...(decl.lends.agents ?? []),
529
+ ...(decl.lends.effects ?? []),
530
+ ...(decl.lends.machines ?? []),
531
+ ...(decl.lends.gates ?? []),
532
+ ],
520
533
  guards: [...(decl.lends.guards ?? [])],
521
534
  actions: [...(decl.lends.actions ?? [])],
522
535
  delays: [...(decl.lends.delays ?? [])],
@@ -573,9 +586,12 @@ export function admitBodyChart(
573
586
  text: unknown,
574
587
  decl: OpenBodyDeclaration,
575
588
  lent: LentParts,
576
- options: { inputKeys?: readonly string[] } = {},
589
+ options: { inputKeys?: readonly string[]; repoSlots?: readonly string[] } = {},
577
590
  ): AdmittedChart {
578
- const admitted = admitChart(text, decl, lent, options.inputKeys ? { inputKeys: options.inputKeys } : {});
591
+ const admitted = admitChart(text, decl, lent, {
592
+ ...(options.inputKeys ? { inputKeys: options.inputKeys } : {}),
593
+ ...(options.repoSlots ? { repoSlots: options.repoSlots } : {}),
594
+ });
579
595
  try {
580
596
  compileChart(admitted, lent, decl);
581
597
  } catch (err) {
@@ -627,11 +643,23 @@ export function admitOpenBodyChart(
627
643
  const logic = (inner.implementations.actors as Record<string, unknown>)[slot];
628
644
  if (!isOpenBody(logic)) throw new Error(`machine "${machine.id}" has no Open body "${slot}"`);
629
645
  const decl = openBodyDeclarationOf(logic);
646
+ const repoSlots = repoSlotsAround(machine);
630
647
  return admitBodyChart(text, decl, lentIn(inner.implementations as Implementations, decl), {
631
648
  ...(inputKeys ? { inputKeys } : {}),
649
+ ...(repoSlots ? { repoSlots } : {}),
632
650
  });
633
651
  }
634
652
 
653
+ /** The Repo Slots of the `workspace()` around a Chart wrapper, where the door can see one: the
654
+ * wrapper IS the `workspace()`. Undefined: an Open map, or a Workspace that a parent state invokes,
655
+ * whose slots this door cannot read. Then the Open body's own door reads them off the running
656
+ * Workspace, and a Turn's or an Effect's start refuses what no door could see. */
657
+ export function repoSlotsAround(machine: AnyStateMachine): readonly string[] | undefined {
658
+ if (!composesSandbox(machine)) return undefined;
659
+ const { repos } = sandboxPartsOf(machine);
660
+ return repos === open ? undefined : Object.keys(repos);
661
+ }
662
+
635
663
  /**
636
664
  * Admit a Chart field of a Chart wrapper's door (ADR-0070), the door a run's start runs before the
637
665
  * run exists. The door cannot tell which Open body a field feeds, so the Chart is admitted against
@@ -648,7 +676,10 @@ export function admitWrapperChart(machine: AnyStateMachine, text: unknown): Admi
648
676
  for (const { slot, logic } of bodies) {
649
677
  const decl = openBodyDeclarationOf(logic);
650
678
  try {
651
- return admitBodyChart(text, decl, lentIn(inner.implementations as Implementations, decl));
679
+ const repoSlots = repoSlotsAround(machine);
680
+ return admitBodyChart(text, decl, lentIn(inner.implementations as Implementations, decl), {
681
+ ...(repoSlots ? { repoSlots } : {}),
682
+ });
652
683
  } catch (err) {
653
684
  if (!(err instanceof ChartRefusal)) throw err;
654
685
  report.push(...err.report.map((line) => (bodies.length > 1 ? `body "${slot}": ${line}` : line)));
@@ -675,7 +706,7 @@ const scopeOf = (args: Args) => ({
675
706
  input: chartInputOf((args.context?.[CHART_INPUT_KEY] as Record<string, unknown> | undefined) ?? {}),
676
707
  });
677
708
 
678
- /** An invoke's error whose message jr2 wrote for a Chart to read: a lent Effect's input fault. */
709
+ /** An invoke's error whose message jr2 wrote for a Chart to read: a fault in an input the door could not see. */
679
710
  class ChartInvokeFault extends Error {}
680
711
 
681
712
  /**
@@ -839,6 +870,10 @@ function transitionsOf(ts: ChartTransitions): unknown {
839
870
  return Array.isArray(ts) ? ts.map(transitionOf) : transitionOf(ts);
840
871
  }
841
872
 
873
+ /** `jr2.ready`: is this cut's output `ready`? On an `onDone`. */
874
+ const readyGuard = ({ event }: { event: unknown }): boolean =>
875
+ (event as { output?: { outcome?: unknown } } | undefined)?.output?.outcome === "ready";
876
+
842
877
  const mapValues = <T, U>(o: Record<string, T>, fn: (v: T) => U): Record<string, U> =>
843
878
  Object.fromEntries(Object.entries(o).map(([k, v]) => [k, fn(v)]));
844
879
 
@@ -847,15 +882,17 @@ const mapValues = <T, U>(o: Record<string, T>, fn: (v: T) => U): Record<string,
847
882
  * resolved object again at invoke time"), or undefined when it is. The door checked the literal
848
883
  * parts; a reference may still resolve to a value that is no string.
849
884
  */
850
- function effectInputFault(keys: readonly string[], value: unknown): string | undefined {
885
+ function effectInputFault(shape: InputShape, value: unknown): string | undefined {
851
886
  const given = typeof value === "object" && value !== null && !Array.isArray(value) ? Object.keys(value) : undefined;
887
+ const known = [...shape.required, ...shape.optional, ...(shape.oneOf ?? [])];
852
888
  const ok =
853
889
  given !== undefined &&
854
- given.length === keys.length &&
855
- keys.every((k) => typeof (value as Record<string, unknown>)[k] === "string");
890
+ shape.required.every((k) => given.includes(k)) &&
891
+ given.every((k) => known.includes(k) && typeof (value as Record<string, unknown>)[k] === "string") &&
892
+ (shape.oneOf === undefined || shape.oneOf.filter((k) => given.includes(k)).length === 1);
856
893
  return ok
857
894
  ? undefined
858
- : `its input resolved to ${JSON.stringify(value)}; it takes exactly ${keys.join(", ") || "nothing"}, each a string (ADR-0070)`;
895
+ : `its input resolved to ${JSON.stringify(value)}; it takes ${known.join(", ") || "nothing"}, each a string (ADR-0070)`;
859
896
  }
860
897
 
861
898
  const checkedEffects = new WeakMap<object, UnknownActorLogic>();
@@ -867,13 +904,15 @@ const checkedEffects = new WeakMap<object, UnknownActorLogic>();
867
904
  */
868
905
  function checkedEffect(slot: string, logic: UnknownActorLogic): UnknownActorLogic {
869
906
  if (!isEffect(logic)) return logic;
870
- const keys = inputKeysOf(logic.effect);
871
- if (keys === undefined) return logic;
907
+ const shape = inputShapeOf(logic.effect);
908
+ if (shape === undefined) return logic;
872
909
  const hit = checkedEffects.get(logic);
873
910
  if (hit) return hit;
874
911
  const checked = Object.create(logic) as UnknownActorLogic;
875
912
  checked.getInitialSnapshot = (scope, input) => {
876
- const fault = effectInputFault(keys, input);
913
+ // An Effect that takes nothing, invoked with no input, is a whole input: the door let it through.
914
+ const fault =
915
+ input === undefined && shape.required.length === 0 && !shape.oneOf ? undefined : effectInputFault(shape, input);
877
916
  if (fault === undefined) return logic.getInitialSnapshot(scope, input);
878
917
  return {
879
918
  status: "error",
@@ -885,9 +924,38 @@ function checkedEffect(slot: string, logic: UnknownActorLogic): UnknownActorLogi
885
924
  return checked;
886
925
  }
887
926
 
888
- /** The lent actors as a Chart invokes them: each Effect checked at its start (see {@link checkedEffect}). */
927
+ const checkedAgents = new WeakMap<object, UnknownActorLogic>();
928
+
929
+ /**
930
+ * An Agent as a Chart invokes it: the Agent itself, whose start refuses a resolved `cwd` that is a
931
+ * Repo Slot's folder. A reference hides that value from the door. The check is in `start`, because
932
+ * the Workspace's handles are registered at a start, and a Chart's first invoke resolves before
933
+ * that. The refusal is THAT invoke's error, as in {@link checkedEffect}.
934
+ */
935
+ function checkedAgent(slot: string, logic: UnknownActorLogic): UnknownActorLogic {
936
+ const hit = checkedAgents.get(logic);
937
+ if (hit) return hit;
938
+ const checked = Object.create(logic) as UnknownActorLogic;
939
+ checked.start = (snapshot, scope) => {
940
+ const cwd = ((snapshot as { input?: unknown }).input as { cwd?: unknown } | undefined)?.cwd;
941
+ const reads = (logic as { definition?: { workspace?: unknown } }).definition?.workspace !== "none";
942
+ const repos = reads && typeof cwd === "string" ? ambientHandlesFor(scope.self)?.repos : undefined;
943
+ const fault = repos ? slotFolderFault(cwd as string, Object.keys(repos)) : undefined;
944
+ if (fault !== undefined) throw new ChartInvokeFault(`the Agent "${slot}": ${fault}`);
945
+ logic.start?.(snapshot, scope);
946
+ };
947
+ checkedAgents.set(logic, checked);
948
+ return checked;
949
+ }
950
+
951
+ /** The lent actors as a Chart invokes them: each Effect and Agent checked at its start. */
889
952
  function chartActors(actors: Record<string, UnknownActorLogic>): Record<string, UnknownActorLogic> {
890
- return Object.fromEntries(Object.entries(actors).map(([slot, logic]) => [slot, checkedEffect(slot, logic)]));
953
+ return Object.fromEntries(
954
+ Object.entries(actors).map(([slot, logic]) => [
955
+ slot,
956
+ isAgent(logic) ? checkedAgent(slot, logic) : checkedEffect(slot, logic),
957
+ ]),
958
+ );
891
959
  }
892
960
 
893
961
  /** An Agent Turn's input with `prompt` and `cwd` as strings: a whole placeholder may resolve to
@@ -918,7 +986,7 @@ function gateInput(value: unknown, self: AnyActorRef): unknown {
918
986
  return { ...(value as Record<string, unknown>), gate: [...actorPath(self), id].join(".") };
919
987
  }
920
988
 
921
- function invokeOf(inv: ChartInvoke, agents: ReadonlySet<string>): unknown {
989
+ function invokeOf(inv: ChartInvoke, agents: ReadonlySet<string>, gates: ReadonlySet<string>): unknown {
922
990
  const out: Record<string, unknown> = { src: inv.src };
923
991
  if (inv.id !== undefined) out.id = inv.id;
924
992
  if (inv.input !== undefined) {
@@ -926,7 +994,7 @@ function invokeOf(inv: ChartInvoke, agents: ReadonlySet<string>): unknown {
926
994
  // Resolved at invoke time, with `{ context, event, input }` (ADR-0070).
927
995
  out.input = agents.has(inv.src)
928
996
  ? (args: Args) => turnInput(resolveAt(template, args), placementOf(args.context?.[CHART_INPUT_KEY]))
929
- : inv.src === "gate"
997
+ : inv.src === "gate" || gates.has(inv.src)
930
998
  ? (args: Args & { self: AnyActorRef }) => gateInput(resolveAt(template, args), args.self)
931
999
  : (args: Args) => resolveAt(template, args);
932
1000
  }
@@ -935,14 +1003,16 @@ function invokeOf(inv: ChartInvoke, agents: ReadonlySet<string>): unknown {
935
1003
  return out;
936
1004
  }
937
1005
 
938
- function stateOf(st: ChartState, agents: ReadonlySet<string>): Record<string, unknown> {
1006
+ function stateOf(st: ChartState, agents: ReadonlySet<string>, gates: ReadonlySet<string>): Record<string, unknown> {
939
1007
  const out: Record<string, unknown> = {};
940
1008
  if (st.type !== undefined) out.type = st.type;
941
1009
  if (st.initial !== undefined) out.initial = st.initial;
942
1010
  if (st.history !== undefined) out.history = st.history;
943
- if (st.states !== undefined) out.states = mapValues(st.states, (child) => stateOf(child, agents));
1011
+ if (st.states !== undefined) out.states = mapValues(st.states, (child) => stateOf(child, agents, gates));
944
1012
  if (st.invoke !== undefined)
945
- out.invoke = Array.isArray(st.invoke) ? st.invoke.map((inv) => invokeOf(inv, agents)) : invokeOf(st.invoke, agents);
1013
+ out.invoke = Array.isArray(st.invoke)
1014
+ ? st.invoke.map((inv) => invokeOf(inv, agents, gates))
1015
+ : invokeOf(st.invoke, agents, gates);
946
1016
  if (st.on !== undefined) out.on = mapValues(st.on, transitionsOf);
947
1017
  if (st.always !== undefined) out.always = transitionsOf(st.always);
948
1018
  if (st.after !== undefined) out.after = mapValues(st.after, transitionsOf);
@@ -964,7 +1034,7 @@ export type CompileOptions = OpenBodyOptions & {
964
1034
  /**
965
1035
  * Build the Machine a Chart describes (ADR-0070), with `jr2Setup` over: the lent actors by their
966
1036
  * slot names, `agent(def)` for each Agent the Chart mints, the kit's `gate`, and `fail()` where the
967
- * body lends it; the lent guards, `jr2.sentBy` and `jr2.failure`; the lent actions and the four
1037
+ * body lends it; the lent guards, `jr2.sentBy`, `jr2.failure` and `jr2.ready`; the lent actions and the four
968
1038
  * generic ones; the lent delays. Every reference resolves at invoke time, so the Machine holds the
969
1039
  * text's shape and no run's data. The Chart's
970
1040
  * input is the context key {@link CHART_INPUT_KEY}; a done Chart outputs `{ outcome: "done",
@@ -984,7 +1054,7 @@ export function compileChart(
984
1054
  const mint = options.mintAgent ?? ((_slot: string, definition: AgentDefinition) => agent(definition));
985
1055
  const minted = mapValues(admitted.agents, (def) => def);
986
1056
  const actors: Record<string, UnknownActorLogic> = chartActors(lent.actors);
987
- for (const [slot, def] of Object.entries(minted)) actors[slot] = mint(slot, def);
1057
+ for (const [slot, def] of Object.entries(minted)) actors[slot] = checkedAgent(slot, mint(slot, def));
988
1058
  // The kit's Part that fails the run, where the body lends it (ADR-0073): no slot of the wrapper's.
989
1059
  if (decl.lends.fail === true) actors.fail = fail();
990
1060
  // The slots an invoke runs an Agent Turn in: the lent Agents and the Chart's own.
@@ -992,6 +1062,9 @@ export function compileChart(
992
1062
  ...Object.keys(lent.actors).filter((slot) => isAgent(lent.actors[slot])),
993
1063
  ...Object.keys(minted),
994
1064
  ]);
1065
+ // The lent Gate slots (ADR-0011): an authored gate id sits below the body's actor path, as the kit
1066
+ // Gate's does.
1067
+ const gateSlots = new Set(Object.keys(lent.actors).filter((slot) => isGateSlot(lent.actors[slot])));
995
1068
  const initialContext = text.context ?? {};
996
1069
  const config = {
997
1070
  id: text.id ?? options.id ?? "chart",
@@ -1000,7 +1073,7 @@ export function compileChart(
1000
1073
  [CHART_INPUT_KEY]: input ?? {},
1001
1074
  }),
1002
1075
  initial: text.initial,
1003
- states: mapValues(text.states, (st) => stateOf(st, agentSlots)),
1076
+ states: mapValues(text.states, (st) => stateOf(st, agentSlots, gateSlots)),
1004
1077
  output: ({ context, event }: Args) =>
1005
1078
  ({
1006
1079
  outcome: "done",
@@ -1018,7 +1091,11 @@ export function compileChart(
1018
1091
  events: admitted.events,
1019
1092
  actors,
1020
1093
  // `jr2.failure` is in every Chart, as `jr2.sentBy` is: a predicate, with no capability (ADR-0073).
1021
- guards: { ...(lent.guards as Record<string, unknown>), [FAILURE_GUARD]: failureGuard } as never,
1094
+ guards: {
1095
+ ...(lent.guards as Record<string, unknown>),
1096
+ [FAILURE_GUARD]: failureGuard,
1097
+ [READY_GUARD]: readyGuard,
1098
+ } as never,
1022
1099
  actions: { ...(lent.actions as Record<string, unknown>), ...GENERIC } as never,
1023
1100
  delays: lent.delays as never,
1024
1101
  }).createMachine(config as never) as unknown as AnyStateMachine;
@@ -1382,7 +1459,10 @@ export function openBodyLogic(decl: OpenBodyDeclaration, options: OpenBodyOption
1382
1459
  /** Admit and compile a Chart text, and record the Machine the actor runs. Throws ChartRefusal. */
1383
1460
  const bind = (self: AnyActorRef, text: unknown, inputKeys: readonly string[]) => {
1384
1461
  const lent = liveLentOf(self, logic);
1385
- const admitted = admitChart(text, decl, lent, { inputKeys });
1462
+ // A Chart that comes after the Workspace started finds its slots here. A Chart in the step that
1463
+ // starts the Workspace comes before them, and the wrapper's door or the Turn's start saw it.
1464
+ const repos = ambientHandlesFor(self)?.repos;
1465
+ const admitted = admitChart(text, decl, lent, { inputKeys, ...(repos ? { repoSlots: Object.keys(repos) } : {}) });
1386
1466
  const src = (self as { src?: unknown }).src;
1387
1467
  const machine = compiledFor(admitted, lent, decl, {
1388
1468
  ...options,
@@ -28,8 +28,9 @@ import type { Arg, EffectLogic, EffectParts, EffectSnapshot } from "./effect.ts"
28
28
  import type { Secret, Text } from "./http-effect.ts";
29
29
 
30
30
  /** The parts of an Effect a package may leave Open: an http url, a command's argv, a secret's name,
31
- * a dispatch's target (its url and token together, ADR-0071). */
32
- export type OpenEffectPart = "url" | "argv" | "secret" | "target";
31
+ * a dispatch's target (its url and token together, ADR-0071), an `mcp` Effect's server and tool
32
+ * (ADR-0079). */
33
+ export type OpenEffectPart = "url" | "argv" | "secret" | "target" | "server" | "tool";
33
34
 
34
35
  // What a composer binds an Effect's Open parts to, each key one part (ADR-0054); a dispatch's
35
36
  // target is its url and token, bound as one (ADR-0071). One object for an
@@ -39,7 +40,8 @@ export type OpenEffectPart = "url" | "argv" | "secret" | "target";
39
40
  * The values that bind the Open parts of one Effect. A packaged Machine leaves a part of an Effect
40
41
  * Open when it cannot know it. That part is the url of an `http` Effect, the argv of a `command`,
41
42
  * the name of a secret, written `secret(open)`, or the target of a `dispatch`: the url of another
42
- * Orchestrator and the secret that holds its Instance token, bound as one. You bind it once, where you register the
43
+ * Orchestrator and the secret that holds its Instance token, bound as one, or the server or the tool
44
+ * of an `mcp` Effect. You bind it once, where you register the
43
45
  * Machine, with `customize(machine, { effects: { <slot>: binding } })`. Each key binds one Open
44
46
  * part. For an Effect with two Open parts, one object holds both keys.
45
47
  *
@@ -101,6 +103,11 @@ export type EffectBinding = {
101
103
  * `jr2.config.ts`. The Effector reads it from a file of that name. */
102
104
  readonly secret?: string;
103
105
  readonly target?: { readonly url: string; readonly token: Secret };
106
+ /** The server of an `mcp` Effect whose server is Open: a name of the `mcp` block of
107
+ * `jr2.config.ts`. The server's url and its secret are the config's. */
108
+ readonly server?: string;
109
+ /** The tool of an `mcp.call` Effect whose tool is Open: a tool name of the server. */
110
+ readonly tool?: string;
104
111
  };
105
112
 
106
113
  declare const openPart: unique symbol;
@@ -135,7 +142,11 @@ export function openPartsOf(parts: EffectParts): OpenEffectPart[] {
135
142
  if (parts.kind === "http" && parts.url === open) out.push("url");
136
143
  if (parts.kind === "command" && parts.argv === open) out.push("argv");
137
144
  if (parts.kind === "dispatch" && parts.target === open) out.push("target");
138
- if (parts.kind !== "push" && parts.openSecret) out.push("secret");
145
+ if (parts.kind === "mcp") {
146
+ if (parts.server === open) out.push("server");
147
+ if (parts.tool === open) out.push("tool");
148
+ }
149
+ if (parts.kind !== "push" && parts.kind !== "mcp" && parts.openSecret) out.push("secret");
139
150
  return out;
140
151
  }
141
152
 
@@ -151,6 +162,8 @@ const BINDING_SAMPLE: Record<OpenEffectPart, string> = {
151
162
  argv: 'argv: ["<program>", "<arg>"]',
152
163
  secret: 'secret: "<name>"',
153
164
  target: 'target: { url: "https://<host>", token: secret("<name>") }',
165
+ server: 'server: "<name>"',
166
+ tool: 'tool: "<tool>"',
154
167
  };
155
168
 
156
169
  /** One binding object for these Open parts — `{ url: "…", secret: "<name>" }` for two. */
package/src/open.ts CHANGED
@@ -14,17 +14,30 @@
14
14
  * - a Repo Slot: `repos: { app: open }`, or the whole map: `repos: open`;
15
15
  * - an Agent's model: `agent({ model: open, … })`;
16
16
  * - an Effect's url or argv: `http({ url: open, … })`, `command(open)`;
17
- * - the secret of an Effect's auth, of a header, or of a command's `env`: `secret(open)`.
17
+ * - the secret of an Effect's auth, of a header, or of a command's `env`: `secret(open)`;
18
+ * - the look of a Gate slot: `gate({ …, look: open })`.
18
19
  *
19
20
  * `jr2 up` refuses a Workflow with an Open part that nobody bound, and prints the `customize()`
20
21
  * line that binds it: ``refusing: workflow "<name>" leaves <part> open — bind it where the Machine
21
22
  * is registered: export const machine = customize(<import>, …) (<adr>)``. A run never sees an
22
- * Open part.
23
+ * Open part. An Open look is the one exception: `jr2 up` warns about it and does not refuse,
24
+ * because a person can still answer the Gate with `jr2 send`.
23
25
  *
24
26
  * @example
25
27
  * ```ts
26
28
  * import { z } from "zod";
27
- * import { agent, customize, defineEvent, fail, jr2Setup, open, workspace } from "@jr2/orchestrator";
29
+ * import {
30
+ * agent,
31
+ * customize,
32
+ * defineEvent,
33
+ * fail,
34
+ * isReady,
35
+ * jr2Setup,
36
+ * open,
37
+ * workspace,
38
+ * worktree,
39
+ * type Workspaced,
40
+ * } from "@jr2/orchestrator";
28
41
  *
29
42
  * const finish = defineEvent({
30
43
  * name: "finish",
@@ -32,19 +45,40 @@
32
45
  * input: z.object({}),
33
46
  * });
34
47
  *
48
+ * type Input = Workspaced<{}, "app">;
49
+ *
50
+ * const fixTree = (context: Input) => `${context.workspace.repos.app}/fix` as const;
51
+ *
35
52
  * const body = jr2Setup({
53
+ * types: {} as { context: Input; input: Input },
36
54
  * events: [finish],
37
- * actors: { coder: agent({ model: open, instructions: "You fix the failing test." }), fail: fail() },
55
+ * actors: {
56
+ * coder: agent({ model: open, instructions: "You fix the failing test." }),
57
+ * cut: worktree(),
58
+ * fail: fail(),
59
+ * },
38
60
  * }).createMachine({
39
61
  * id: "fix",
62
+ * context: ({ input }) => input,
40
63
  * on: { "workspace.lost": ".lost", "agent.fault": ".failed" },
41
- * initial: "coding",
64
+ * initial: "cutting",
42
65
  * states: {
66
+ * cutting: {
67
+ * invoke: {
68
+ * src: "cut",
69
+ * input: ({ context }) => ({ path: fixTree(context), branch: "jr2/fix" }),
70
+ * onDone: [{ guard: ({ event }) => isReady(event.output), target: "coding" }, { target: "notCut" }],
71
+ * },
72
+ * },
43
73
  * coding: {
44
- * invoke: { src: "coder", input: { prompt: "Make `node --test` pass." } },
74
+ * invoke: {
75
+ * src: "coder",
76
+ * input: ({ context }) => ({ prompt: "Make `node --test` pass.", cwd: fixTree(context) }),
77
+ * },
45
78
  * on: { finish: "done" },
46
79
  * },
47
80
  * done: { type: "final" },
81
+ * notCut: { invoke: { src: "fail", input: { reason: "the Worktree was not cut" } } },
48
82
  * failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
49
83
  * lost: { invoke: { src: "fail", input: { reason: "the Sandbox is gone" } } },
50
84
  * },
@@ -53,7 +87,6 @@
53
87
  * // What a package exports: the repository and the model are Open.
54
88
  * export const fix = workspace(body, {
55
89
  * repos: { app: open },
56
- * spec: () => ({ branch: "jr2/fix" }),
57
90
  * });
58
91
  *
59
92
  * // What an Instance's `workflows/fix.ts` exports: the same Machine, its Open parts bound.