@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/index.ts CHANGED
@@ -17,32 +17,56 @@ export { jr2Setup } from "./setup.ts";
17
17
  export { sentBy } from "./party.ts";
18
18
  export type { HostInjectedInput } from "./vocabulary.ts";
19
19
 
20
+ // Gates: a Gate slot hears a Hook (ADR-0011).
21
+ export { gate } from "./gate.ts";
22
+ export type { Delivered, GateLook, GateOutcome, GateReply } from "./gate.ts";
23
+
20
24
  // Failure: the Part a Machine invokes to fail its run, and the guard that tells a Failure from a
21
25
  // defect in an `onError` (ADR-0073).
22
26
  export { fail, isFailure } from "./fail.ts";
23
27
 
24
28
  // Agents: an Agent slot and the definition it carries (ADR-0028, ADR-0049).
25
29
  export { agent } from "./harness-client.ts";
26
- export type { AgentDefinition, ThinkingLevel } from "./agent.ts";
30
+ export type { AgentDefinition, AgentMcpServer, ThinkingLevel } from "./agent.ts";
27
31
 
28
32
  // Workspaces and Repos: a Sandbox and the Repo Slots it binds (ADR-0012, ADR-0051).
29
33
  export { workspace } from "./workspace.ts";
30
34
  export type { Workspaced } from "./workspace.ts";
31
35
  export type { Binding, RepoSlot } from "./parts.ts";
32
36
 
33
- // Effects: work a Machine does outside itself, as an actor slot (ADR-0067, ADR-0068, ADR-0071),
37
+ // Effects: work a Machine does outside itself, as an actor slot (ADR-0067, ADR-0068, ADR-0071,
38
+ // ADR-0079),
34
39
  // and what an author writes between a pick and that work.
35
40
  export { arg } from "./effect.ts";
36
- export type { CommandOutput } from "./effect.ts";
41
+ export type { CommandOutput, LaterOutcome } from "./effect.ts";
37
42
  export { command } from "./harness-client.ts";
38
43
  export { basic, bearer, file, http, secret, text } from "./http-effect.ts";
39
44
  export type { HttpOutput } from "./effector-wire.ts";
40
45
  export { push } from "./sandbox-effect.ts";
41
46
  export type { PushOutput } from "./sandbox-effect.ts";
47
+ export { branchName, isReady, readyOf, worktree } from "./worktree.ts";
48
+ export type {
49
+ AnyFramePath,
50
+ AnyWorktreePath,
51
+ FramePath,
52
+ WorktreeFound,
53
+ WorktreeInput,
54
+ WorktreeOptions,
55
+ WorktreeOutput,
56
+ WorktreePath,
57
+ WorktreeReady,
58
+ WorktreeRefused,
59
+ } from "./worktree.ts";
42
60
  export { dispatch } from "./dispatch-effect.ts";
43
61
  export type { DispatchOutput } from "./effect.ts";
62
+ export { mcp } from "./mcp-effect.ts";
63
+ export type { McpOptions } from "./mcp-effect.ts";
64
+ export type { McpCallOutput, McpOutput, McpReadOutput } from "./effector-wire.ts";
44
65
  export { answer, fromPick, paramsOf } from "./pick.ts";
45
66
 
67
+ // Steering: the Machine's free text to one of its Agents, which the Agent hears once (ADR-0002).
68
+ export { steer } from "./steer.ts";
69
+
46
70
  // Composition: a part left Open, a Machine's parts retuned, a Pool over a Source (ADR-0017,
47
71
  // ADR-0049, ADR-0054, ADR-0060).
48
72
  export { open } from "./parts.ts";
@@ -50,7 +74,8 @@ export { customize } from "./customize.ts";
50
74
  export type { Customize } from "./customize.ts";
51
75
  export type { EffectBinding } from "./open-effect.ts";
52
76
  export type { Size, UserContainer } from "./size.ts";
53
- export { pool, source } from "./pool.ts";
77
+ export { carry, pool, reread, skip, source } from "./pool.ts";
78
+ export type { PoolNextInput, SourceReply, SourceVerdict } from "./pool.ts";
54
79
 
55
80
  // Charts: the Open body a Chart wrapper places in `actors`, and the grammar a planner's pick
56
81
  // declares, whole or narrowed to one wrapper (ADR-0070).
@@ -60,4 +85,4 @@ export { chartSchema, chartSchemaFor } from "./chart.ts";
60
85
 
61
86
  // Config: `jr2.config.ts`, the deployment facts a Machine cannot carry (ADR-0050).
62
87
  export { defineConfig } from "./config.ts";
63
- export type { JR2Config } from "./config.ts";
88
+ export type { JR2Config, McpRemoteServer, McpServer, McpStdioServer, RunsConfig } from "./config.ts";
package/src/instance.ts CHANGED
@@ -14,7 +14,7 @@
14
14
  // 3. `restore()` in-flight runs from the store (reconcile against the live world — ADR-0007),
15
15
  // each inside the Assembly its snapshot names (ADR-0069). With no current Assembly the live
16
16
  // rows wait, untouched, and the first publish restores them. Then sweep finished runs older
17
- // than a week and collect every Assembly no live row holds, and again every hour (ADR-0065,
17
+ // than `runs.retain` and collect every Assembly no live row holds, and again every hour (ADR-0065,
18
18
  // ADR-0069);
19
19
  // 4. serve the hono HTTP surface (`createApp`) so the CLI / humans can push + control + observe.
20
20
  //
@@ -26,7 +26,16 @@
26
26
 
27
27
  import { mkdir, readdir, stat } from "node:fs/promises";
28
28
  import type { JR2Config } from "./config.ts";
29
- import { loadConfig } from "./config.ts";
29
+ import { loadConfig, retainMs, STATE_VOLUME_DEFAULT } from "./config.ts";
30
+ import { statSync } from "node:fs";
31
+ import {
32
+ bytesUnder,
33
+ ConversationStore,
34
+ deleteOrphanConversations,
35
+ fileSystemBytes,
36
+ guardSpace,
37
+ quantityBytes,
38
+ } from "./conversations.ts";
30
39
  import {
31
40
  assemblyDoor,
32
41
  heldAssemblies,
@@ -62,16 +71,28 @@ import { RunHost } from "./run-host.ts";
62
71
  import type { RunRecord, WorkflowDef } from "./run-host.ts";
63
72
  import { SqliteSnapshotStore } from "./snapshot-store.ts";
64
73
  import type { SnapshotStore } from "./snapshot-store.ts";
65
- import { createAuthenticator, effectorKey, harnessToken, loadSigningKey, mintInstanceToken } from "./tokens.ts";
74
+ import {
75
+ createAuthenticator,
76
+ effectorKey,
77
+ harnessToken,
78
+ loadSigningKey,
79
+ mintInstanceToken,
80
+ namedTokens,
81
+ } from "./tokens.ts";
66
82
  import type { SandboxPort } from "./workspace.ts";
83
+ import { jwtAuthenticator } from "./callers.ts";
67
84
  import type { EffectorPlacement, EffectorTarget } from "./http-effect.ts";
68
- import { WORKFLOW_NAME } from "./effector-wire.ts";
85
+ import { WORKFLOW_NAME, createMacClient, nodeEffectorTransport, type HookMac } from "./effector-wire.ts";
86
+ import { createHookPost, hookDoor, type HookPost } from "./hooks.ts";
69
87
 
70
88
  export type InstanceOptions = {
71
89
  /** The instance folder: the state volume `.jr2/` (its `state.db` unless `store` is supplied, and
72
90
  * its published Assemblies). Nothing else in it is read: the Workflows are the current
73
91
  * Assembly's (ADR-0069). */
74
92
  dir: string;
93
+ /** The size of the state volume claim, as `jr2 up` applied it (`JR2_STATE_VOLUME`, ADR-0065): the
94
+ * space guard measures against it. Absent (host-booted), the config's `runs.volume` or `5Gi`. */
95
+ stateVolume?: string;
75
96
  /** Which Assembly to load (ADR-0069). Default: `resolveAssembly(dir)`. The entrypoint resolves
76
97
  * it first, because it reads the config from the same root before it builds the ports. */
77
98
  assembly?: ResolvedAssembly;
@@ -81,8 +102,8 @@ export type InstanceOptions = {
81
102
  /** The image map, read when the door checks a publish (ADR-0069). Present only when deployed;
82
103
  * absent, the door does not check `file:` image contexts. */
83
104
  imageMap?: () => Promise<ImageRefs>;
84
- /** The digests of the mounted ConfigMaps, which `GET /assembly` reports (ADR-0069). Present only
85
- * when deployed. */
105
+ /** The digests of the mounted ConfigMaps and of the loaded digests Secrets, which
106
+ * `GET /assembly` reports (ADR-0069, ADR-0078). Present only when deployed. */
86
107
  mounts?: () => Promise<Record<string, string>>;
87
108
  /** Called after a publish has landed and the Workflows swapped: the new current Assembly, its
88
109
  * config and its Machines, which the entrypoint swaps into the ports (ADR-0069). Synchronous, so
@@ -110,6 +131,9 @@ export type InstanceOptions = {
110
131
  hostname?: string;
111
132
  /** Override the durable store. Default: sqlite at `<dir>/.jr2/state.db`. */
112
133
  store?: SnapshotStore;
134
+ /** Override the kept conversations (ADR-0023). Default: sqlite at `<dir>/.jr2/conversations.db`,
135
+ * or in memory when `store` is supplied. The space guard watches only the default file. */
136
+ conversations?: ConversationStore;
113
137
  /** Probe the live world before re-attaching on restore (ADR-0007). Default: always present. */
114
138
  reconcile?: (run: RunRecord) => boolean | Promise<boolean>;
115
139
  /** The Sandbox backend for `workspace()` workflows (ADR-0012). Composed by the caller — wired
@@ -148,6 +172,23 @@ export type InstanceOptions = {
148
172
  * the instance's Secret and the entrypoint passes it here, so a pod restart keeps the credential
149
173
  * the CLI reads from that Secret. Default: minted per boot. */
150
174
  instanceToken?: string;
175
+ /** The digests of the named Caller tokens, by name (ADR-0078), read on each request. Deployed, the
176
+ * entrypoint reads them from the `jr2-caller-digests` mount. The current Assembly's config gives
177
+ * each name its scopes. Default: none, so only the Instance token is a Caller. */
178
+ callerDigests?: () => Readonly<Record<string, string>>;
179
+ /** The digests of the `token` Hooks' secrets, by secret name (ADR-0078), read on each delivery.
180
+ * Deployed, the entrypoint reads them from the `jr2-hook-digests` mount. Default: none, so a
181
+ * `token` Hook refuses each delivery. */
182
+ hookDigests?: () => Readonly<Record<string, string>>;
183
+ /** The `mac` check of a Hook's delivery (ADR-0068, ADR-0078). Default: over the Instance
184
+ * Effector's signed wire when `instanceEffector` is set, else none, so a `mac` Hook answers 503.
185
+ * A test injects its own. */
186
+ hookMac?: HookMac;
187
+ /** The follow-up post of a Hook kind's answer, such as the reply to a Slack click (ADR-0011,
188
+ * ADR-0068). Default: one `http` call over the Instance Effector's signed wire when
189
+ * `instanceEffector` is set, else none, so a follow-up fails and the hook log says so. A test
190
+ * injects its own. */
191
+ hookPost?: HookPost;
151
192
  };
152
193
 
153
194
  /** What one restore did with the runs it found persisted (ADR-0007, ADR-0030). */
@@ -210,13 +251,11 @@ export type RunningInstance = {
210
251
  close: () => Promise<void>;
211
252
  };
212
253
 
213
- /**
214
- * How long a finished run's row lives, and how often the sweep looks (ADR-0065). Fixed, not config,
215
- * like the Sandbox idle timeout: jr2 is not the record of a run's results, and `jr2 status` on an
216
- * old run is a debugging act. A week is the time a human has to read it.
217
- */
218
- const RETAIN_MS = 7 * 24 * 60 * 60 * 1000;
254
+ /** How often the sweep looks (ADR-0065). How long an ended run's row lives is `runs.retain` of the
255
+ * current config (`retainMs`), read at each sweep, so a publish changes it. */
219
256
  const SWEEP_EVERY_MS = 60 * 60 * 1000;
257
+ /** How often the space guard looks at the state volume between two sweeps (ADR-0065). */
258
+ const GUARD_EVERY_MS = 60 * 1000;
220
259
 
221
260
  /** Boot an instance folder into a running orchestrator (discover → restore → serve). */
222
261
  export async function startInstance(opts: InstanceOptions): Promise<RunningInstance> {
@@ -230,6 +269,14 @@ export async function startInstance(opts: InstanceOptions): Promise<RunningInsta
230
269
  }
231
270
  await store.init();
232
271
 
272
+ // The kept conversations (ADR-0023): a second SQLite file beside `state.db` on the state volume.
273
+ // A test that supplies its store keeps them in memory, and no guard measures a volume for it.
274
+ const stateDir = join(opts.dir, ".jr2");
275
+ const onVolume = !opts.conversations && !opts.store;
276
+ const conversations =
277
+ opts.conversations ?? new ConversationStore(onVolume ? join(stateDir, "conversations.db") : ":memory:");
278
+ conversations.init();
279
+
233
280
  // Resolved BEFORE the host, and the signing key with it: the host derives every Harness bearer
234
281
  // from the key (ADR-0058) — admission, stream, abort, echo. The key is loaded from (or minted
235
282
  // into) the instance folder, NOT generated per process: live Sandboxes outlive a restart, their
@@ -336,6 +383,9 @@ export async function startInstance(opts: InstanceOptions): Promise<RunningInsta
336
383
  // files, as its record lists them. `GET /assembly` answers these; the runtime Charts the host
337
384
  // also registers are not part of an Assembly.
338
385
  let currentWorkflows: string[] = [];
386
+ // The config of the current Assembly (ADR-0069), for what the HTTP surface reads per request: the
387
+ // named Callers' scopes and the `cors` origins (ADR-0078). A publish replaces it.
388
+ let currentConfig: JR2Config | undefined;
339
389
 
340
390
  /** Swap the registry to `loaded` and make `next` current, in one synchronous step: nothing in it
341
391
  * awaits, so no request sees half of one Assembly and half of another (ADR-0069). The named
@@ -370,6 +420,7 @@ export async function startInstance(opts: InstanceOptions): Promise<RunningInsta
370
420
  // by name. No current Assembly → no Workflows, and the process still serves (ADR-0048).
371
421
  if (current) {
372
422
  const booted = current;
423
+ currentConfig = await loadConfig(booted.root);
373
424
  const loaded = await importAssembly(booted.hash, booted.root);
374
425
  swap(loaded, await readChartFiles(booted.root, undefined, contextDirsOf(loaded)), booted);
375
426
  } else {
@@ -431,6 +482,7 @@ export async function startInstance(opts: InstanceOptions): Promise<RunningInsta
431
482
  // Every named Chart is re-admitted against the new Assembly (ADR-0070): a runtime Chart
432
483
  // that fails is marked unrunnable, kept on the volume, and named in the publish's answer.
433
484
  unrunnable = swap(workflows, files, next);
485
+ currentConfig = config;
434
486
  // The Machines the walk imported are this hash's, so a run restored inside it later
435
487
  // (the next boot is a new process; this is for the same one) needs no second import.
436
488
  imported.set(next.hash, Promise.resolve(workflows));
@@ -441,15 +493,20 @@ export async function startInstance(opts: InstanceOptions): Promise<RunningInsta
441
493
  // Assembly was published for the old Kit version drifts now, and that is final. The
442
494
  // door's collection follows in the same publish, so it removes the Assemblies only those
443
495
  // drifted rows named.
444
- if (!host.restorePending) return;
445
- try {
446
- const outcome = await host.restore();
447
- opts.onRestore?.({ assembly: next.hash, ...outcome });
448
- } catch (err) {
449
- // Left pending: the next publish, or the next boot, tries again. The publish itself
450
- // landed, so it is not refused for this.
451
- console.error(`restore after publish failed: ${err instanceof Error ? err.message : err}`);
496
+ if (host.restorePending) {
497
+ try {
498
+ const outcome = await host.restore();
499
+ opts.onRestore?.({ assembly: next.hash, ...outcome });
500
+ } catch (err) {
501
+ // Left pending: the next publish, or the next boot, tries again. The publish itself
502
+ // landed, so it is not refused for this.
503
+ console.error(`restore after publish failed: ${err instanceof Error ? err.message : err}`);
504
+ }
452
505
  }
506
+ // The standing Workflows (ADR-0017), after the restore: a live run of the Assembly before
507
+ // is handed over, and a Workflow that this publish adds gets its first run. Not while a
508
+ // deferred restore is still pending: its rows may hold the live standing runs.
509
+ if (!host.restorePending) await host.keepStanding("publish");
453
510
  },
454
511
  };
455
512
  },
@@ -475,10 +532,13 @@ export async function startInstance(opts: InstanceOptions): Promise<RunningInsta
475
532
  const restored: RestoreOutcome = current
476
533
  ? await host.restore()
477
534
  : { reattached: [], lost: [], drifted: [], failed: [] };
535
+ // The standing Workflows (ADR-0017), after the restore, so a restored standing run is kept and not
536
+ // started a second time. With no current Assembly there is none: the first publish starts them.
537
+ if (current) await host.keepStanding("boot");
478
538
  const waiting = current ? [] : (await store.live()).map((row) => row.runId);
479
539
 
480
540
  // Right after restore, not before: restore reads live rows only, and a row it marks `lost` or
481
- // `drifted` here is written now, so its week starts now (ADR-0065). After the rows, the
541
+ // `drifted` here is written now, so its `runs.retain` starts now (ADR-0065). After the rows, the
482
542
  // Assemblies: restore decided which rows stay live, and so which Assemblies stay held (ADR-0069).
483
543
  // A sweep deletes finished rows only, so it never takes a waiting run; and with the restore
484
544
  // deferred, every live row is a root, and so is the pointer's target, so the boot collection
@@ -486,11 +546,72 @@ export async function startInstance(opts: InstanceOptions): Promise<RunningInsta
486
546
  // A sweep is housekeeping, never a reason to stay up — nor to fail a boot that has already
487
547
  // resumed runs: a failed sweep or collection, at boot or on the hour, is logged, and the next
488
548
  // hour tries again. The timer holds no process alive.
549
+ // With no current Assembly, no `runs.retain` is bound, so the rows are not swept: in the Kit bump
550
+ // window every boot has no current Assembly, and a sweep at the default would delete the history
551
+ // that a longer `runs.retain` keeps. The first sweep after the publish uses the published value.
552
+ // The collection still runs. The cutoff is never before the epoch, so a very long `runs.retain`
553
+ // keeps every row and does not fail the sweep.
554
+ // The space guard (ADR-0065). It measures the files on the volume against the smaller of the
555
+ // claim and what `statfs` says, because a hostPath class such as kind's reports the node's disk.
556
+ // The claim is `runs.volume`, or the default when the config sets none. The files other than the
557
+ // two databases (the Assemblies, the Charts) are measured at each sweep, and the databases at each
558
+ // look, so the guard can look every minute and walk the volume only every hour.
559
+ const dbFiles = ["state.db", "state.db-wal", "conversations.db", "conversations.db-wal"].map((f) =>
560
+ join(stateDir, f),
561
+ );
562
+ const dbBytes = (): number =>
563
+ dbFiles.reduce((sum, path) => {
564
+ try {
565
+ return sum + statSync(path).size;
566
+ } catch {
567
+ return sum;
568
+ }
569
+ }, 0);
570
+ let otherBytes = 0;
571
+ const measureOthers = () => {
572
+ otherBytes = Math.max(0, bytesUnder(stateDir) - dbBytes());
573
+ };
574
+ let guarding = false;
575
+ const guard = async (): Promise<void> => {
576
+ if (guarding) return;
577
+ guarding = true;
578
+ try {
579
+ await guardSpace({
580
+ conversations,
581
+ used: () => otherBytes + dbBytes(),
582
+ capacity: () =>
583
+ Math.min(
584
+ fileSystemBytes(stateDir),
585
+ quantityBytes(opts.stateVolume ?? currentConfig?.runs?.volume ?? STATE_VOLUME_DEFAULT) ??
586
+ Number.POSITIVE_INFINITY,
587
+ ),
588
+ isLive: (runId) => host.runIsLive(runId),
589
+ onSwept: (runId) =>
590
+ console.log(
591
+ `space guard: the state volume is more than 80% full — deleted the kept conversations of run ${runId} (ADR-0065)`,
592
+ ),
593
+ });
594
+ } catch (err) {
595
+ console.error(`space guard failed: ${err instanceof Error ? err.message : err}`);
596
+ } finally {
597
+ guarding = false;
598
+ }
599
+ };
600
+ // A run's kept conversations go with its row (ADR-0065), and then the space guard looks.
489
601
  const sweep = async (): Promise<string[]> => {
490
- await store
491
- .sweep(new Date(Date.now() - RETAIN_MS))
492
- .catch((err) => console.error(`sweep failed: ${err instanceof Error ? err.message : err}`));
493
- return door.collect();
602
+ if (current) {
603
+ await store
604
+ .sweep(new Date(Math.max(0, Date.now() - retainMs(currentConfig?.runs))))
605
+ .then((swept) => conversations.deleteRuns(swept))
606
+ .then(() => deleteOrphanConversations(conversations, store))
607
+ .catch((err) => console.error(`sweep failed: ${err instanceof Error ? err.message : err}`));
608
+ }
609
+ const collected = await door.collect();
610
+ if (onVolume) {
611
+ measureOthers();
612
+ await guard();
613
+ }
614
+ return collected;
494
615
  };
495
616
  const collected = await sweep();
496
617
  const onCollect =
@@ -506,18 +627,65 @@ export async function startInstance(opts: InstanceOptions): Promise<RunningInsta
506
627
  .catch((err) => console.error(`collection announce failed: ${err instanceof Error ? err.message : err}`));
507
628
  }, SWEEP_EVERY_MS);
508
629
  sweeper.unref();
630
+ // The guard looks every minute too: a conversation can grow fast between two sweeps.
631
+ const guarder = onVolume ? setInterval(() => void guard(), GUARD_EVERY_MS) : undefined;
632
+ guarder?.unref();
509
633
 
510
634
  // 4. Serve, authenticated (ADR-0013) — the tokens and key resolved above.
511
- const auth = createAuthenticator({ instanceToken, signingKey, publishToken });
635
+ // The named Callers (ADR-0078): the names and scopes of the current config, joined per request with
636
+ // the digests `jr2 up` gave, so a publish or a rotation applies with no restart.
637
+ const auth = createAuthenticator({
638
+ instanceToken,
639
+ signingKey,
640
+ publishToken,
641
+ namedTokens: () => namedTokens(currentConfig?.callers?.tokens, opts.callerDigests?.() ?? {}),
642
+ // The JWT issuers of the current config, read per request, so a publish changes them.
643
+ callers: [jwtAuthenticator(() => currentConfig?.callers?.issuers ?? [])],
644
+ });
645
+ // The Hook's door (ADR-0011, ADR-0078): the Hooks of the current config, read per delivery, so a
646
+ // publish changes them; none while no Assembly is current. A `mac` Hook is checked by the
647
+ // Instance Effector, which holds its secret (ADR-0068).
648
+ const hookMac =
649
+ opts.hookMac ??
650
+ (opts.instanceEffector
651
+ ? createMacClient({
652
+ key: effectorKey(signingKey, "instance"),
653
+ transport: nodeEffectorTransport(opts.instanceEffector),
654
+ })
655
+ : undefined);
656
+ // The follow-up of an answer that a person reads goes out through the same Effector (ADR-0068).
657
+ const hookPost =
658
+ opts.hookPost ??
659
+ (opts.instanceEffector
660
+ ? createHookPost({
661
+ key: effectorKey(signingKey, "instance"),
662
+ transport: nodeEffectorTransport(opts.instanceEffector),
663
+ })
664
+ : undefined);
665
+ const hooks = hookDoor({
666
+ host,
667
+ store,
668
+ hooks: () => (current ? (currentConfig?.hooks ?? {}) : undefined),
669
+ digests: () => opts.hookDigests?.() ?? {},
670
+ ...(hookMac ? { mac: hookMac } : {}),
671
+ ...(hookPost ? { post: hookPost } : {}),
672
+ });
512
673
  // The Repos are read PER REQUEST, never snapshotted here: a cache the agent cloned minutes
513
674
  // after boot must show as present the next time anyone asks (ADR-0048/0051).
514
675
  const app = createApp(host, auth, {
515
676
  charts,
677
+ conversations,
516
678
  dataPlane: opts.dataPlane,
517
679
  repos: opts.repos,
518
680
  fetchRepo: opts.fetchRepo,
519
681
  assembly: door,
520
682
  ...(opts.mounts ? { mounts: opts.mounts } : {}),
683
+ cors: () => currentConfig?.cors ?? [],
684
+ hooks,
685
+ consoleLogin: () =>
686
+ (currentConfig?.callers?.issuers ?? [])
687
+ .filter((i) => i.login === true)
688
+ .map((i) => ({ issuer: i.issuer, clientId: i.audience })),
521
689
  });
522
690
  const server = serve({ fetch: app.fetch, port: opts.port ?? 0, hostname });
523
691
  const port = await new Promise<number>((resolve) => {
@@ -562,9 +730,11 @@ export async function startInstance(opts: InstanceOptions): Promise<RunningInsta
562
730
  // From that call on, `/agents/*` answers 503 until the listener closes: the Turns are parked
563
731
  // for the next boot's restore, not over, so no Harness may read a 404 here (ADR-0026).
564
732
  clearInterval(sweeper);
733
+ if (guarder) clearInterval(guarder);
565
734
  await host.close();
566
735
  await new Promise<void>((resolve) => server.close(() => resolve()));
567
736
  await store.close();
737
+ conversations.close();
568
738
  },
569
739
  };
570
740
  }
package/src/internal.ts CHANGED
@@ -18,10 +18,13 @@ export * from "./effect.ts";
18
18
  export * from "./http-effect.ts";
19
19
  export * from "./sandbox-effect.ts";
20
20
  export * from "./dispatch-effect.ts";
21
+ export * from "./mcp-effect.ts";
21
22
  export type { EffectBinding, OpenEffect, OpenEffectPart } from "./open-effect.ts";
22
23
  export { openPartsText } from "./open-effect.ts";
23
24
  export * from "./effector-wire.ts";
24
25
  export * from "./pick.ts";
26
+ export * from "./steer.ts";
27
+ export * from "./notices.ts";
25
28
  export * from "./chart.ts";
26
29
  export * from "./open-body.ts";
27
30
  export * from "./charts.ts";
@@ -36,11 +39,15 @@ export * from "./setup.ts";
36
39
  export { sentBy } from "./party.ts";
37
40
  export * from "./vocabulary.ts";
38
41
  export * from "./tokens.ts";
42
+ // What a run records of its Callers (ADR-0078): the CLI's `jr2 status` walks `startedBy`.
43
+ export type { CallerRecord, GateAnswer, RunStarter } from "./callers.ts";
39
44
  export * from "./durability.ts";
40
45
  export * from "./snapshot-store.ts";
46
+ // The run listing of `GET /runs` (ADR-0009): the CLI reads its summary and its query words.
47
+ export * from "./run-list.ts";
41
48
  export * from "./actor.ts";
42
- // The `gate` actor: jr2Setup pre-registers it and a state invokes it as `src: "gate"`, so no
43
- // author writes the value (ADR-0015).
49
+ // The `gate` actor: jr2Setup pre-registers it and a state invokes it as `src: "gate"` (ADR-0015).
50
+ // The Authoring surface has `gate({ … })`, a Gate slot that hears a Hook (ADR-0011).
44
51
  export * from "./gate.ts";
45
52
  // The Failure's brand stays in fail.ts, and so does the logic that throws it: the kit makes a Failure
46
53
  // only through `fail()` and `workspace()`, which imports that logic from fail.ts (ADR-0073).
@@ -59,6 +66,7 @@ export * from "./secret-placement.ts";
59
66
  export * from "./git-push.ts";
60
67
  export * from "./git-clone.ts";
61
68
  export * from "./custodian.ts";
69
+ export * from "./harness-env.ts";
62
70
  export * from "./sandbox-kube.ts";
63
71
  export {
64
72
  effectorManifestKey,
@@ -73,7 +81,19 @@ export * from "./repos.ts";
73
81
  export * from "./repo-fetch.ts";
74
82
  // Type-only: what a workflow event delivery looks like (the registration TABLE stays internal
75
83
  // even here — ADR-0011).
76
- export type { ChildErrorMarker, DeliveredEvent, PlacingMarker, RunMarker, TurnMarker } from "./registration.ts";
84
+ export type {
85
+ ChildErrorMarker,
86
+ DeliveredEvent,
87
+ GateMarker,
88
+ HookRefusedMarker,
89
+ PlacingMarker,
90
+ PoolMarker,
91
+ RunMarker,
92
+ SourceVerdictLine,
93
+ TurnMarker,
94
+ } from "./registration.ts";
95
+ export type { HookOutcome } from "./hooks.ts";
96
+ export type { HookLogLine } from "./snapshot-store.ts";
77
97
  export * from "./machine-doc.ts";
78
98
  export * from "./run-host.ts";
79
99
  export * from "./http.ts";
@@ -108,7 +108,7 @@ export interface KubeClient {
108
108
  /** Create; an existing object is a {@link KubeError} with status 409. */
109
109
  create<T extends KubeObject>(kind: KubeKind, namespace: string, body: T): Promise<T & KubeObject>;
110
110
  /** Delete; true when there was one to delete, false when it was already gone. */
111
- delete(kind: KubeKind, namespace: string, name: string): Promise<boolean>;
111
+ delete(kind: KubeKind, namespace: string, name: string, opts?: { foreground?: boolean }): Promise<boolean>;
112
112
  /** A JSON merge patch — `null` removes a key. `status` patches the status subresource. Answers
113
113
  * the object as it stands after the patch. */
114
114
  patch<T extends KubeObject>(
@@ -216,9 +216,12 @@ export function kubeClient(opts: KubeClientOptions = {}): KubeClient {
216
216
  });
217
217
  },
218
218
 
219
- delete(kind: KubeKind, ns: string, name: string) {
219
+ delete(kind: KubeKind, ns: string, name: string, opts: { foreground?: boolean } = {}) {
220
220
  return writes(async () => {
221
- const res = await send("DELETE", pathOf(kind, ns, name));
221
+ // Foreground: the object stays until its dependents, a Sandbox's pod, are gone.
222
+ const res = await send("DELETE", pathOf(kind, ns, name), {
223
+ ...(opts.foreground ? { query: { propagationPolicy: "Foreground" } } : {}),
224
+ });
222
225
  if (res.status === 404) {
223
226
  await res.body?.cancel();
224
227
  return false;
@@ -42,9 +42,9 @@ export type MachineTransitionDoc = {
42
42
  };
43
43
 
44
44
  /** An Effect slot's parts, as the doc carries them (ADR-0067): a command's argv (an argv slot reads
45
- * `<name>`), its timeout, and the Repo Slot whose Worktree is its cwd, when it names one. */
45
+ * `<name>`) and its timeout. Its input names its Worktree, so the doc does not (ADR-0075). */
46
46
  export type EffectDoc =
47
- | { kind: "command"; argv: string[]; timeoutMs: number; slot?: string; secrets?: string[]; pin?: true }
47
+ | { kind: "command"; argv: string[]; timeoutMs: number; secrets?: string[]; pin?: true }
48
48
  /** An http Effect (ADR-0068): its method, its fixed target, the secrets it names — never a value
49
49
  * — and `file` when its body is a Worktree file (ADR-0067). */
50
50
  | {
@@ -61,7 +61,19 @@ export type EffectDoc =
61
61
  /** A dispatch (ADR-0071): the Workflow it starts a run of, whether it waits, and the other
62
62
  * Orchestrator's origin (`<open>` while a package leaves it Open); no target is this Orchestrator.
63
63
  * The token's secret is named on the parts, never here: the target is what a reader routes on. */
64
- | { kind: "dispatch"; workflow: string; wait: boolean; target?: string; timeoutMs: number };
64
+ | { kind: "dispatch"; workflow: string; wait: boolean; target?: string; timeoutMs: number }
65
+ /** An mcp Effect (ADR-0079): what it asks of which server, by the server's name in the `mcp`
66
+ * block (`<open>` while a package leaves it Open), and the author's timeout, when it set one. */
67
+ | {
68
+ kind: "mcp";
69
+ op: "call" | "read" | "prompt";
70
+ server: string;
71
+ tool?: string;
72
+ uri?: string;
73
+ prompt?: string;
74
+ timeoutMs?: number;
75
+ idempotent: boolean;
76
+ };
65
77
 
66
78
  /** One state of the Machine; `states` nests in document order. */
67
79
  export type MachineStateDoc = {
@@ -422,12 +434,24 @@ function effectDoc(parts: EffectParts): EffectDoc {
422
434
  timeoutMs,
423
435
  };
424
436
  }
425
- const { argv, timeoutMs, slot, secrets, pin } = parts;
437
+ if (parts.kind === "mcp") {
438
+ const { op, server, tool, uri, prompt, timeoutMs, idempotent } = parts;
439
+ return {
440
+ kind: "mcp",
441
+ op,
442
+ server: server === open ? "<open>" : server,
443
+ ...(tool !== undefined ? { tool: tool === open ? "<open>" : tool } : {}),
444
+ ...(uri !== undefined ? { uri } : {}),
445
+ ...(prompt !== undefined ? { prompt } : {}),
446
+ ...(timeoutMs !== undefined ? { timeoutMs } : {}),
447
+ idempotent,
448
+ };
449
+ }
450
+ const { argv, timeoutMs, secrets, pin } = parts;
426
451
  return {
427
452
  kind: "command",
428
453
  argv: argvText(argv),
429
454
  timeoutMs,
430
- ...(slot ? { slot } : {}),
431
455
  ...(secrets?.length ? { secrets: [...secrets] } : {}),
432
456
  ...(pin ? { pin } : {}),
433
457
  };