@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
@@ -5,9 +5,9 @@ description: "Every route of the Orchestrator's HTTP API, by access band, and th
5
5
 
6
6
  # HTTP API
7
7
 
8
- The Orchestrator's HTTP API: what the `jr2` CLI, the Console, a webhook and the Harness call. Each route is in one access band, by the credential it asks for ([ADR-0014](decisions/0014-observation-is-open-run-state-is-not.md)). Send a token as `Authorization: Bearer <token>`. An error answer is `{ error }`, with the status the route gives. A path segment `:name` is a parameter; `:name{regex}` is a parameter that must match the regex.
8
+ The Orchestrator's HTTP API: what the `jr2` CLI, the Console, a webhook and the Harness call. Each route is in one access band, by the credential it asks for ([ADR-0014](decisions/0014-observation-is-open-run-state-is-not.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)). Send a token as `Authorization: Bearer <token>`. An error answer is `{ error }`, with the status the route gives. A path segment `:name` is a parameter; `:name{regex}` is a parameter that must match the regex.
9
9
 
10
- Bands: [Open](#band-open), [Authenticated](#band-authenticated), [Instance token only](#band-instanceonly), [Publish token only](#band-publishonly). The types the bodies and answers name are in [Types](#types).
10
+ Bands: [Open](#band-open), [Authenticated](#band-authenticated), [A Caller with a scope](#band-guarded), [Instance token only](#band-instanceonly), [Publish token only](#band-publishonly), [A Hook's verifier](#band-hook). The types the bodies and answers name are in [Types](#types).
11
11
 
12
12
  <a id="band-open"></a>
13
13
 
@@ -46,10 +46,10 @@ No token. Structure and observation: no run context, and no control.
46
46
 
47
47
  ### `GET /assembly`
48
48
 
49
- - Summary: The current Assembly and the Assemblies that live runs hold.
50
- - Response: [`AssemblyView`](#type-assemblyview)
49
+ - Summary: The current Assembly, the Assemblies that live runs hold, and each standing Workflow: live, paused or waiting, its run, and how many items it holds.
50
+ - Response: [`AssemblyView`](#type-assemblyview) & { standing?: [`StandingView`](#type-standingview)[] }
51
51
  - Stream: no
52
- - Decisions: [ADR-0069](decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md)
52
+ - Decisions: [ADR-0017](decisions/0017-pool-over-a-source-port.md), [ADR-0069](decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md)
53
53
 
54
54
  <a id="get-workflows-wrapper-chart-schema"></a>
55
55
 
@@ -76,7 +76,16 @@ No token. Structure and observation: no run context, and no control.
76
76
  - Summary: The Console page to a browser; a 404 to a JSON caller.
77
77
  - Response: text/html (the Console shell)
78
78
  - Stream: no
79
- - Decisions: [ADR-0032](decisions/0032-the-console-unlocks-with-the-instance-token.md)
79
+ - Decisions: [ADR-0032](decisions/0032-the-console-unlocks-with-a-callers-credential.md)
80
+
81
+ <a id="get-console-config"></a>
82
+
83
+ ### `GET /console/config`
84
+
85
+ - Summary: What the Console needs before a login: each JWT issuer that offers a login, and its client id. Both are public values.
86
+ - Response: { login: Array<{ issuer, clientId }> }
87
+ - Stream: no
88
+ - Decisions: [ADR-0032](decisions/0032-the-console-unlocks-with-a-callers-credential.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
80
89
 
81
90
  <a id="get-workflows-name"></a>
82
91
 
@@ -85,7 +94,7 @@ No token. Structure and observation: no run context, and no control.
85
94
  - Summary: The Console page to a browser; to a JSON caller, the workflow and its declared input schema.
86
95
  - Response: { name, machineId, input: JSON Schema | null }, or text/html
87
96
  - Stream: no
88
- - Decisions: [ADR-0032](decisions/0032-the-console-unlocks-with-the-instance-token.md), [ADR-0033](decisions/0033-a-machine-declares-the-input-that-starts-it.md)
97
+ - Decisions: [ADR-0032](decisions/0032-the-console-unlocks-with-a-callers-credential.md), [ADR-0033](decisions/0033-a-machine-declares-the-input-that-starts-it.md)
89
98
 
90
99
  <a id="get-workflows-name-machine"></a>
91
100
 
@@ -130,7 +139,7 @@ No token. Structure and observation: no run context, and no control.
130
139
  - Summary: The Console's stylesheet.
131
140
  - Response: text/css
132
141
  - Stream: no
133
- - Decisions: [ADR-0032](decisions/0032-the-console-unlocks-with-the-instance-token.md)
142
+ - Decisions: [ADR-0032](decisions/0032-the-console-unlocks-with-a-callers-credential.md)
134
143
 
135
144
  <a id="get-assets-elk-js"></a>
136
145
 
@@ -139,7 +148,7 @@ No token. Structure and observation: no run context, and no control.
139
148
  - Summary: The Console's graph layout library (the elkjs bundle).
140
149
  - Response: text/javascript
141
150
  - Stream: no
142
- - Decisions: [ADR-0032](decisions/0032-the-console-unlocks-with-the-instance-token.md)
151
+ - Decisions: [ADR-0032](decisions/0032-the-console-unlocks-with-a-callers-credential.md)
143
152
 
144
153
  <a id="get-assets-file"></a>
145
154
 
@@ -178,8 +187,8 @@ Any token jr2 minted: the Instance token, or a Sandbox token. A Sandbox token re
178
187
 
179
188
  ### `GET /agents/:instanceId/surface`
180
189
 
181
- - Summary: An Agent's Menu and this Turn's Allowed picks. A Sandbox token reads only its own Sandbox's.
182
- - Response: [`AgentSurfaceView`](#type-agentsurfaceview)
190
+ - Summary: An Agent's Menu, this Turn's Allowed picks, the conversation's pending notices and its slot's Steering messages. A control token reads only its own pod's, for the slot it states.
191
+ - Response: [`AgentSurfaceView`](#type-agentsurfaceview) & { notices: Array<{ id, notice }>, steering?: Array<{ id, message }> }
183
192
  - Stream: no
184
193
  - Decisions: [ADR-0013](decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md), [ADR-0029](decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md)
185
194
 
@@ -202,125 +211,239 @@ Any token jr2 minted: the Instance token, or a Sandbox token. A Sandbox token re
202
211
  - Stream: no
203
212
  - Decisions: [ADR-0013](decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md), [ADR-0024](decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md)
204
213
 
214
+ <a id="post-agents-instanceid-notices"></a>
215
+
216
+ ### `POST /agents/:instanceId/notices`
217
+
218
+ - Summary: Acknowledge the notices and Steering messages a step boundary appended to the Turn: they are heard in its conversation.
219
+ - Body: { heard: string[] }
220
+ - Response: { heard: number }
221
+ - Stream: no
222
+ - Decisions: [ADR-0062](decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md), [ADR-0076](decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md)
223
+
224
+ <a id="post-agents-instanceid-conversation"></a>
225
+
226
+ ### `POST /agents/:instanceId/conversation`
227
+
228
+ - Summary: Keep a batch of a conversation's entries. Only the control token of the pod that the run's admission ledger names, for the ledger's slot; never the Instance token. An entry kept already adds nothing.
229
+ - Body: { entries: Array<{ id, seq, at, kind, text }> }, at most 4 MiB
230
+ - Response: { kept, cut }; 409 while the run's ledger does not name the conversation yet (send again); 404 for a run that is not known
231
+ - Stream: no
232
+ - Decisions: [ADR-0023](decisions/0023-the-harness-prints-the-conversation.md), [ADR-0076](decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md)
233
+
205
234
  <a id="post-sandboxes-name-fetch"></a>
206
235
 
207
236
  ### `POST /sandboxes/:name/fetch`
208
237
 
209
- - Summary: Ask the node cache to fetch one Repo that this Sandbox mounts. A Sandbox token asks only for its own.
238
+ - Summary: Ask the node cache to fetch one Repo that this Sandbox mounts. A fetch token asks only for its own.
210
239
  - Body: { identity }
211
240
  - Response: [`FetchAnswer`](#type-fetchanswer): { fetched } | { stale, asOf }
212
241
  - Stream: no
213
242
  - Decisions: [ADR-0053](decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md)
214
243
 
215
- <a id="band-instanceonly"></a>
244
+ <a id="band-guarded"></a>
216
245
 
217
- ## Instance token only
246
+ ## A Caller with a scope
218
247
 
219
- The Instance token alone: run control, starting a run, and full run state. A Sandbox token is refused.
248
+ A Caller: the Instance token, a named Caller token, or a JWT. Each route needs one scope, for the Workflow of the run or the door that it addresses, and a 403 names the scope and the Workflow. The Instance token has every scope. A Sandbox token is not a Caller, and is refused. A Caller can state the party it acts for in the header `jr2-on-behalf-of`; jr2 records the claim and does not check it.
220
249
 
221
- <a id="put-workflows-wrapper-charts-name"></a>
250
+ <a id="get-whoami"></a>
222
251
 
223
- ### `PUT /workflows/:wrapper/charts/:name`
252
+ ### `GET /whoami`
224
253
 
225
- - Summary: Admit and keep a runtime Chart under a name. Its Workflow is `<wrapper>/<name>`.
226
- - Body: the Chart text alone
227
- - Response: 201 { name, origin, hash } when new, 200 when it replaced one; 422 { error, report } when refused
228
- - Stream: no
229
- - Decisions: [ADR-0070](decisions/0070-a-machine-lends-its-parts-to-a-chart.md)
230
-
231
- <a id="delete-workflows-wrapper-charts-name"></a>
232
-
233
- ### `DELETE /workflows/:wrapper/charts/:name`
234
-
235
- - Summary: Remove a runtime Chart. A live run of it keeps its own text.
236
- - Response: 204
254
+ - Summary: The request's Caller: how jr2 knows it, its name, what it may do, and the party it says it acts for.
255
+ - Scope: any Caller
256
+ - Response: [`Caller`](#type-caller)
237
257
  - Stream: no
238
- - Decisions: [ADR-0070](decisions/0070-a-machine-lends-its-parts-to-a-chart.md)
258
+ - Decisions: [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
239
259
 
240
260
  <a id="post-workflows-wrapper-charts-name-runs"></a>
241
261
 
242
262
  ### `POST /workflows/:wrapper/charts/:name/runs`
243
263
 
244
264
  - Summary: Start a run of a named Chart, as `POST /workflows/<wrapper>%2F<name>/runs` does.
265
+ - Scope: `start`
245
266
  - Body: the Chart wrapper's input, without the Chart
246
267
  - Response: 201 { runId, instanceId }; 409 when the named Chart cannot run now
247
268
  - Stream: no
248
- - Decisions: [ADR-0070](decisions/0070-a-machine-lends-its-parts-to-a-chart.md)
269
+ - Decisions: [ADR-0070](decisions/0070-a-machine-lends-its-parts-to-a-chart.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
249
270
 
250
271
  <a id="post-workflows-name-runs"></a>
251
272
 
252
273
  ### `POST /workflows/:name/runs`
253
274
 
254
- - Summary: Start a run of a registered workflow. A `dispatch` from another Orchestrator sends the header `jr2-dispatched-by`, and a repeated start with its key answers the run that it started.
275
+ - Summary: Start a run of a registered workflow. A `dispatch` from another Orchestrator sends the header `jr2-dispatched-by`, and a repeated start with its key answers the run that it started. With the header `jr2-key-only: true` too, the start is a key-only ask: it answers the run under the key, and starts nothing. For a standing Workflow, jr2 starts the run: the start only resumes a paused one, and `?drop-held=true` resumes it with no held item.
276
+ - Scope: `start`
255
277
  - Body: the workflow's input, checked against its declared input schema
256
- - Response: 201 { runId, instanceId }; 200 { runId, instanceId, existing: true } for a repeated dispatch; 422 { error, report } when the door refuses a Chart in the input
278
+ - Response: 201 { runId, instanceId }; 201 { runId, instanceId, resumed: true } for a resumed standing Workflow; 200 { runId, instanceId, existing: true } for a repeated dispatch; 200 { existing: false } for a key-only ask with no run under the key; 409 for a standing Workflow that is not paused, or a dispatch to one; 422 { error, report } when the door refuses a Chart in the input
257
279
  - Stream: no
258
- - Decisions: [ADR-0033](decisions/0033-a-machine-declares-the-input-that-starts-it.md), [ADR-0058](decisions/0058-a-harness-answers-the-orchestrator-alone.md), [ADR-0070](decisions/0070-a-machine-lends-its-parts-to-a-chart.md), [ADR-0071](decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md)
280
+ - Decisions: [ADR-0017](decisions/0017-pool-over-a-source-port.md), [ADR-0033](decisions/0033-a-machine-declares-the-input-that-starts-it.md), [ADR-0058](decisions/0058-a-harness-answers-the-orchestrator-alone.md), [ADR-0070](decisions/0070-a-machine-lends-its-parts-to-a-chart.md), [ADR-0071](decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
259
281
 
260
282
  <a id="get-runs"></a>
261
283
 
262
284
  ### `GET /runs`
263
285
 
264
- - Summary: The live runs, with their full status.
265
- - Response: [`RunStatus`](#type-runstatus)[]
286
+ - Summary: A page of the runs of the Workflows that the Caller may read, newest first: live runs by default; ?status=live|ended|all|<list> ?workflow ?since ?cursor ?limit.
287
+ - Scope: `read`
288
+ - Response: { runs: [`RunSummary`](#type-runsummary)[], next? }
266
289
  - Stream: no
267
- - Decisions: [ADR-0009](decisions/0009-cli-and-instance-interface.md), [ADR-0014](decisions/0014-observation-is-open-run-state-is-not.md)
290
+ - Decisions: [ADR-0009](decisions/0009-cli-and-instance-interface.md), [ADR-0014](decisions/0014-observation-is-open-run-state-is-not.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
268
291
 
269
- <a id="get-repos"></a>
292
+ <a id="get-runs-resolve"></a>
270
293
 
271
- ### `GET /repos`
294
+ ### `GET /runs/resolve`
272
295
 
273
- - Summary: The Repos as the cluster reports them.
274
- - Response: { dataPlane, repos: [`RepoStatus`](#type-repostatus)[] }
296
+ - Summary: The run ids that start with a prefix (`?prefix=`, at least 4 characters), of the Workflows in any scope of the Caller.
297
+ - Scope: any Caller
298
+ - Response: { prefix, runIds: string[], truncated }
275
299
  - Stream: no
276
- - Decisions: [ADR-0048](decisions/0048-the-orchestrator-boots-without-its-repos.md), [ADR-0051](decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md)
300
+ - Decisions: [ADR-0009](decisions/0009-cli-and-instance-interface.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
277
301
 
278
- <a id="get-runs-resolve"></a>
302
+ <a id="get-gates"></a>
279
303
 
280
- ### `GET /runs/resolve`
304
+ ### `GET /gates`
281
305
 
282
- - Summary: The run ids that start with a prefix (`?prefix=`, at least 4 characters).
283
- - Response: { prefix, runIds: string[], truncated }
306
+ - Summary: The Gate inbox: every open Gate of every live run that the Caller may answer, oldest first, never a run's context; ?workflow.
307
+ - Scope: `answer`
308
+ - Response: { gates: [`GateInboxEntry`](#type-gateinboxentry)[] }
284
309
  - Stream: no
285
- - Decisions: [ADR-0009](decisions/0009-cli-and-instance-interface.md)
310
+ - Decisions: [ADR-0011](decisions/0011-workflow-defined-events.md), [ADR-0014](decisions/0014-observation-is-open-run-state-is-not.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
286
311
 
287
312
  <a id="get-runs-runid"></a>
288
313
 
289
314
  ### `GET /runs/:runId`
290
315
 
291
- - Summary: One run's status, live or settled, with its open Gates.
292
- - Response: [`RunStatus`](#type-runstatus) & { gates: [`GateView`](#type-gateview)[] }
316
+ - Summary: One run's status, live or settled, with its open Gates. `read` opens the status, and `answer` opens the Gates that the Caller may answer: with `answer` alone the answer is the run's observation and those Gates, and never its context.
317
+ - Scope: `read` or `answer`; each opens its own part of the answer
318
+ - Response: [`RunStatus`](#type-runstatus) & { gates: [`GateView`](#type-gateview)[] }; [`RunStatus`](#type-runstatus) with `read` alone; [`RunObservation`](#type-runobservation) & { gates: [`GateView`](#type-gateview)[] } with `answer` alone
293
319
  - Stream: no
294
- - Decisions: [ADR-0009](decisions/0009-cli-and-instance-interface.md), [ADR-0011](decisions/0011-workflow-defined-events.md)
320
+ - Decisions: [ADR-0009](decisions/0009-cli-and-instance-interface.md), [ADR-0011](decisions/0011-workflow-defined-events.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
295
321
 
296
322
  <a id="post-runs-runid-gates-gate-events"></a>
297
323
 
298
324
  ### `POST /runs/:runId/gates/:gate/events`
299
325
 
300
- - Summary: Deliver an event to an open Gate of a run.
326
+ - Summary: Deliver an event to an open Gate of a run. A Caller whose `answer` names single Gates of the Workflow is refused 403 for another Gate.
327
+ - Scope: `answer`
301
328
  - Body: { type, ...payload }, checked against the Gate's schema for that event
302
329
  - Response: { ok: true }; 409 { error, acceptsNow } when the run does not take it now
303
330
  - Stream: no
304
- - Decisions: [ADR-0011](decisions/0011-workflow-defined-events.md), [ADR-0013](decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md)
331
+ - Decisions: [ADR-0011](decisions/0011-workflow-defined-events.md), [ADR-0013](decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
305
332
 
306
333
  <a id="get-runs-runid-events"></a>
307
334
 
308
335
  ### `GET /runs/:runId/events`
309
336
 
310
337
  - Summary: One run's full activity until it settles; a settled run's final status, once.
311
- - Response: SSE: status, emit, retry, admission, pick, placing, placed, child-error, effect, gone
338
+ - Scope: `read`
339
+ - Response: SSE: status, emit, retry, admission, pick, placing, placed, child-error, looked, hook-refused, pool, effect, gone
340
+ - Stream: yes: Server-Sent Events
341
+ - Decisions: [ADR-0011](decisions/0011-workflow-defined-events.md), [ADR-0022](decisions/0022-observation-is-a-level-triggered-feed.md), [ADR-0023](decisions/0023-the-harness-prints-the-conversation.md), [ADR-0064](decisions/0064-a-workspace-waits-for-capacity.md), [ADR-0067](decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
342
+
343
+ <a id="get-runs-runid-conversations"></a>
344
+
345
+ ### `GET /runs/:runId/conversations`
346
+
347
+ - Summary: A run's kept conversations, the first first: one row for each conversation of each Agent, Menu-only Agents too, with its Agent slot.
348
+ - Scope: `read`
349
+ - Response: Array<{ agent, slot?, instanceId, open, bytes, cut, swept?: "no space" }>
350
+ - Stream: no
351
+ - Decisions: [ADR-0023](decisions/0023-the-harness-prints-the-conversation.md), [ADR-0065](decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md)
352
+
353
+ <a id="get-runs-runid-conversations-instanceid"></a>
354
+
355
+ ### `GET /runs/:runId/conversations/:instanceId`
356
+
357
+ - Summary: The entries of one kept conversation after a sequence number (`?after=<seq>`, `?limit=`, 1000 by default). The Instance ID is URL-encoded.
358
+ - Scope: `read`
359
+ - Response: Array<{ id, seq, at, agent, kind, text }>
360
+ - Stream: no
361
+ - Decisions: [ADR-0023](decisions/0023-the-harness-prints-the-conversation.md)
362
+
363
+ <a id="get-runs-runid-conversations-instanceid-events"></a>
364
+
365
+ ### `GET /runs/:runId/conversations/:instanceId/events`
366
+
367
+ - Summary: Follow one kept conversation: its entries after `?after=<seq>`, then each new one, until the conversation is not open.
368
+ - Scope: `read`
369
+ - Response: SSE: entry, end
312
370
  - Stream: yes: Server-Sent Events
313
- - Decisions: [ADR-0022](decisions/0022-observation-is-a-level-triggered-feed.md), [ADR-0023](decisions/0023-the-harness-prints-the-conversation.md), [ADR-0064](decisions/0064-a-workspace-waits-for-capacity.md), [ADR-0067](decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md)
371
+ - Decisions: [ADR-0023](decisions/0023-the-harness-prints-the-conversation.md)
314
372
 
315
373
  <a id="post-runs-runid-events"></a>
316
374
 
317
375
  ### `POST /runs/:runId/events`
318
376
 
319
377
  - Summary: Cancel a run: it ends, and its Workspaces go unless `keep` is true.
378
+ - Scope: `cancel`
320
379
  - Body: { type: "CANCEL", keep?: boolean }
321
380
  - Response: { ok: true }
322
381
  - Stream: no
323
- - Decisions: [ADR-0002](decisions/0002-duplex-streaming-actor.md), [ADR-0025](decisions/0025-cancel-ends-a-run-stop-parks-it.md)
382
+ - Decisions: [ADR-0002](decisions/0002-duplex-streaming-actor.md), [ADR-0025](decisions/0025-cancel-ends-a-run-stop-parks-it.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
383
+
384
+ <a id="get-runs-runid-agents"></a>
385
+
386
+ ### `GET /runs/:runId/agents`
387
+
388
+ - Summary: The Agent slots of a run that a Steering message can go to: one row for each Agent of each live Machine instance.
389
+ - Scope: `steer`
390
+ - Response: Array<{ agent, open, steerable }>; [] for a run that is not live
391
+ - Stream: no
392
+ - Decisions: [ADR-0002](decisions/0002-duplex-streaming-actor.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
393
+
394
+ <a id="post-runs-runid-steering"></a>
395
+
396
+ ### `POST /runs/:runId/steering`
397
+
398
+ - Summary: Send a Steering message to one Agent slot of a live run: its open Turn hears it at a step boundary, or else its next Turn.
399
+ - Scope: `steer`
400
+ - Body: { agent, text }, `text` at most 16 KiB
401
+ - Response: 202 { id, agent, open }; 413 for a longer text; 409 { error, agents? } when no live Machine instance holds the slot; 403 when the Agent is not steerable
402
+ - Stream: no
403
+ - Decisions: [ADR-0002](decisions/0002-duplex-streaming-actor.md), [ADR-0062](decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
404
+
405
+ <a id="band-instanceonly"></a>
406
+
407
+ ## Instance token only
408
+
409
+ The Instance token alone: the routes that no scope opens. A named Caller token is refused, whatever its scopes, and so is a Sandbox token.
410
+
411
+ <a id="get-hooks-name"></a>
412
+
413
+ ### `GET /hooks/:name`
414
+
415
+ - Summary: A Hook's log, newest first: each delivery that jr2 keeps (`runs.retain`, 30 days by default), with what the door did with it. `?limit=` (default 50, at most 1000).
416
+ - Response: { hook, declared, lines: [`HookLogLine`](#type-hooklogline)[] }
417
+ - Stream: no
418
+ - Decisions: [ADR-0011](decisions/0011-workflow-defined-events.md), [ADR-0065](decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md)
419
+
420
+ <a id="put-workflows-wrapper-charts-name"></a>
421
+
422
+ ### `PUT /workflows/:wrapper/charts/:name`
423
+
424
+ - Summary: Admit and keep a runtime Chart under a name. Its Workflow is `<wrapper>/<name>`.
425
+ - Body: the Chart text alone
426
+ - Response: 201 { name, origin, hash } when new, 200 when it replaced one; 422 { error, report } when refused
427
+ - Stream: no
428
+ - Decisions: [ADR-0070](decisions/0070-a-machine-lends-its-parts-to-a-chart.md)
429
+
430
+ <a id="delete-workflows-wrapper-charts-name"></a>
431
+
432
+ ### `DELETE /workflows/:wrapper/charts/:name`
433
+
434
+ - Summary: Remove a runtime Chart. A live run of it keeps its own text.
435
+ - Response: 204
436
+ - Stream: no
437
+ - Decisions: [ADR-0070](decisions/0070-a-machine-lends-its-parts-to-a-chart.md)
438
+
439
+ <a id="get-repos"></a>
440
+
441
+ ### `GET /repos`
442
+
443
+ - Summary: The Repos as the cluster reports them.
444
+ - Response: { dataPlane, repos: [`RepoStatus`](#type-repostatus)[] }
445
+ - Stream: no
446
+ - Decisions: [ADR-0048](decisions/0048-the-orchestrator-boots-without-its-repos.md), [ADR-0051](decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md)
324
447
 
325
448
  <a id="band-publishonly"></a>
326
449
 
@@ -334,12 +457,154 @@ The publish token alone, which `jr2 up` reads from the instance Secret. The Inst
334
457
 
335
458
  - Summary: Publish the next Assembly. `jr2 up` calls it, and nothing else. A refused Assembly leaves the current one current.
336
459
  - Body: the Assembly as a tar stream, with the headers `jr2-kit-version` and `jr2-assembly-hash`
337
- - Response: 200 with the Assembly, its Workflows, the held Assemblies and the ones collected; 400, 409 or 422 with the reason
460
+ - Response: 200 with the Assembly, its Workflows, the held Assemblies, the ones collected and each standing Workflow's state; 400, 409 or 422 with the reason
338
461
  - Stream: no
339
462
  - Decisions: [ADR-0069](decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md)
340
463
 
464
+ <a id="band-hook"></a>
465
+
466
+ ## A Hook's verifier
467
+
468
+ No bearer token: the Hook's own verifier checks each delivery, as its kind tells (a `token` header, an `ed25519` signature or a `mac`). A delivery that fails the verifier, or whose signed time is more than 300 seconds from the Orchestrator's clock, gets 401.
469
+
470
+ <a id="post-hooks-name"></a>
471
+
472
+ ### `POST /hooks/:name`
473
+
474
+ - Summary: A delivery of an outside service to the Hook that the current Assembly's config names. The Hook's verifier authenticates it, and it goes to the open Gates whose handlers hear its kind and event key. 413 for a body above 2 MiB, before the verifier and with no more of the body read than that; 401 for a delivery that fails the verifier, 404 for a name that no Hook has, 503 with no current Assembly, and 503 when a standing Pool that hears it has no live run and is not paused, or when the delivery followed the Pool's hand-overs 3 times and the Pool handed its run over again. A 503 claims no delivery id.
475
+ - Body: the service's own request
476
+ - Response: the kind's answer to a handshake, or [`HookOutcome`](#type-hookoutcome): { delivery, seen, delivered, refused, ignored, sources, payloadRefused? }
477
+ - Stream: no
478
+ - Decisions: [ADR-0011](decisions/0011-workflow-defined-events.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
479
+
341
480
  ## Types
342
481
 
482
+ <a id="type-caller"></a>
483
+
484
+ ### `Caller`
485
+
486
+ ```ts
487
+ type Caller = {
488
+ via: "token" | "jwt" | "hook";
489
+ name: string;
490
+ shown?: string;
491
+ may: Scopes;
492
+ onBehalfOf?: string;
493
+ };
494
+ ```
495
+
496
+ A request's Caller ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)): who acts, through which authenticator, and what it may do.
497
+
498
+ **Fields**
499
+
500
+ - `via`: `"token" | "jwt" | "hook"`
501
+
502
+ The authenticator that made the Caller: `token` (the Instance token or a named token), `jwt`
503
+ or `hook`.
504
+ - `name`: `string`
505
+
506
+ The Caller's name for that authenticator: `instance`, a named token's name, a JWT's `sub`, a
507
+ Hook's name.
508
+ - `shown` (optional): `string`
509
+
510
+ The name that jr2 shows, when it is not `name` (a JWT's `email`).
511
+ - `may`: [`Scopes`](config.md#callers-tokens-name-may)
512
+
513
+ What the Caller may do.
514
+ - `onBehalfOf` (optional): `string`
515
+
516
+ The Caller's claim about the party it acts for. jr2 records it and never checks it.
517
+
518
+ <a id="type-hookoutcome"></a>
519
+
520
+ ### `HookOutcome`
521
+
522
+ ```ts
523
+ type HookOutcome = {
524
+ delivery: string;
525
+ seen: boolean;
526
+ delivered: HookFanIn["delivered"];
527
+ refused: HookFanIn["refused"];
528
+ ignored: number;
529
+ sources: HookFanIn["sources"];
530
+ payloadRefused?: { kind: string; key: string; issues: unknown[] };
531
+ };
532
+ ```
533
+
534
+ The answer to one delivery ([ADR-0011](decisions/0011-workflow-defined-events.md)): `delivery` is its id, `seen` says it was a repeat,
535
+ `delivered` the Gates that took it, `refused` the Gates that did not and why, `ignored` how many
536
+ open Gates of its kind and key gave nothing, and `sources` each Source's verdict ([ADR-0017](decisions/0017-pool-over-a-source-port.md)).
537
+ `payloadRefused` names the kind and the event key of a payload that the parse refused, with zod's
538
+ issues.
539
+
540
+ **Fields**
541
+
542
+ - `delivery`: `string`
543
+ - `seen`: `boolean`
544
+ - `delivered`: `HookFanIn["delivered"]`
545
+ - `refused`: `HookFanIn["refused"]`
546
+ - `ignored`: `number`
547
+ - `sources`: `HookFanIn["sources"]`
548
+ - `payloadRefused` (optional): `object`
549
+
550
+ - `kind`: `string`
551
+ - `key`: `string`
552
+ - `issues`: `unknown[]`
553
+
554
+ <a id="type-hooklogline"></a>
555
+
556
+ ### `HookLogLine`
557
+
558
+ ```ts
559
+ type HookLogLine = {
560
+ hook: string;
561
+ at: string;
562
+ kind:
563
+ | "pending"
564
+ | "delivered"
565
+ | "seen"
566
+ | "hold-full"
567
+ | "no-live-run"
568
+ | "handshake"
569
+ | "payload-refused"
570
+ | "unreadable"
571
+ | "too-large"
572
+ | "follow-up";
573
+ status: number;
574
+ delivery?: string;
575
+ digest?: string;
576
+ key?: string;
577
+ detail?: unknown;
578
+ };
579
+ ```
580
+
581
+ One line of the hook log ([ADR-0011](decisions/0011-workflow-defined-events.md)): what the door did with one delivery of one Hook, and when.
582
+ `kind` says which line it is: `delivered` (the fan-in ran; `outcome` holds what it did), `seen` (a
583
+ repeat), `hold-full` (the fan-in ran and every Source was full, [ADR-0017](decisions/0017-pool-over-a-source-port.md): the line does not claim
584
+ the id, so a send again is heard), `no-live-run` (the fan-in ran, and a standing Pool that hears the
585
+ kind and key has no live run and is not paused: 503, and the line does not claim the id either),
586
+ `handshake`, `payload-refused` (with zod's `issues`), `unreadable` (the kind could not
587
+ read the request: `error`) or `follow-up` (the follow-up post of an answer that a person reads
588
+ failed, or went to a url outside the kind's `posts`: `post` and `error`; its `status` is 0). A
589
+ `reply` that threw is in the `replyErrors` of its delivery's `detail`. `status` is the status that
590
+ the rules give, which is the status of the answer unless a person reads the answer: that answer is
591
+ 200 ([ADR-0011](decisions/0011-workflow-defined-events.md)).
592
+
593
+ **Fields**
594
+
595
+ - `hook`: `string`
596
+ - `at`: `string`
597
+ - `kind`: `"pending" | "delivered" | "seen" | "hold-full" | "no-live-run" | "handshake" | "payload-refused" | "unreadable" | "too-large" | "follow-up"`
598
+ - `status`: `number`
599
+ - `delivery` (optional): `string`
600
+ - `digest` (optional): `string`
601
+ - `key` (optional): `string`
602
+
603
+ The event key, when the kind read one.
604
+ - `detail` (optional): `unknown`
605
+
606
+ What the line says: the outcome, zod's issues, the error.
607
+
343
608
  <a id="type-workflowlisting"></a>
344
609
 
345
610
  ### `WorkflowListing`
@@ -391,6 +656,40 @@ type AssemblyView = {
391
656
  - `current`: `{ hash: string; kitVersion: string; workflows: string[]; } | null`
392
657
  - `held`: `HeldAssembly[]`
393
658
 
659
+ <a id="type-standingview"></a>
660
+
661
+ ### `StandingView`
662
+
663
+ ```ts
664
+ type StandingView = {
665
+ workflow: string;
666
+ pool: string;
667
+ callee: string;
668
+ state: "live" | "paused" | "waiting";
669
+ run?: string;
670
+ held: number;
671
+ why?: "cancel" | "errors" | "bound";
672
+ };
673
+ ```
674
+
675
+ A standing Workflow as `jr2 up` and `jr2 status` print it ([ADR-0017](decisions/0017-pool-over-a-source-port.md)): its Pool, its callee, its
676
+ state, its run, and how many items it holds. `live`: a run is live. `paused`: a cancel (`why`:
677
+ `cancel`), or a fourth run in a row in which no item that the run took started a run, after 3
678
+ hand-overs: the last one ended `error` or `failed` (`errors`), or took its bound (`bound`). The
679
+ held items are in the blob of `run`.
680
+ `waiting`: no run is live, and jr2 starts one after the back-off. Structure and counts only: no
681
+ key and no item, so it is on the open band with the Assembly.
682
+
683
+ **Fields**
684
+
685
+ - `workflow`: `string`
686
+ - `pool`: `string`
687
+ - `callee`: `string`
688
+ - `state`: `"live" | "paused" | "waiting"`
689
+ - `run` (optional): `string`
690
+ - `held`: `number`
691
+ - `why` (optional): `"cancel" | "errors" | "bound"`
692
+
394
693
  <a id="type-machinedoc"></a>
395
694
 
396
695
  ### `MachineDoc`
@@ -433,7 +732,7 @@ type RunObservation = {
433
732
  children: RunChild[];
434
733
  bound?: true;
435
734
  chartHashes?: string[];
436
- dispatchedBy?: Omit<DispatchedBy, "effect">;
735
+ dispatchedBy?: Omit<DispatchedBy, "effect" | "key" | "hook">;
437
736
  dispatched?: Array<{ slot: string; run: string }>;
438
737
  };
439
738
  ```
@@ -469,7 +768,7 @@ parallel work (`"F-1"`) — the same class of thing as a state key.
469
768
 
470
769
  The hashes of the Charts it binds now (see `RunStatus.chartHashes`). Structure: a hash
471
770
  of a Chart's text, which `runMachine` already serves on the open band.
472
- - `dispatchedBy` (optional): `Omit<DispatchedBy, "effect">`
771
+ - `dispatchedBy` (optional): `Omit<DispatchedBy, "effect" | "key" | "hook">`
473
772
 
474
773
  The run that dispatched this one, its slot, and the chain's depth ([ADR-0071](decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md)). Routing — two
475
774
  ids, a slot name and a number — the class of thing `runId` and `value` are, so [ADR-0014](decisions/0014-observation-is-open-run-state-is-not.md)'s line
@@ -479,13 +778,116 @@ parallel work (`"F-1"`) — the same class of thing as a state key.
479
778
  The runs it dispatched, by slot ([ADR-0071](decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md)): the same routing as `dispatchedBy`, the other
480
779
  direction.
481
780
 
781
+ <a id="type-runsummary"></a>
782
+
783
+ ### `RunSummary`
784
+
785
+ ```ts
786
+ type RunSummary = {
787
+ runId: string;
788
+ workflow: string;
789
+ status: ShownStatus;
790
+ reason?: string;
791
+ startedBy?: RunStarter;
792
+ startedAt: string;
793
+ endedAt?: string;
794
+ assembly?: string;
795
+ openGates: number;
796
+ placing?: string[];
797
+ dispatchedBy?: DispatchedBy;
798
+ swept?: "conversation swept: no space";
799
+ };
800
+ ```
801
+
802
+ One run as `GET /runs` lists it ([ADR-0009](decisions/0009-cli-and-instance-interface.md)): a summary, not the whole `RunStatus`. `status` is
803
+ `active` for a live run, else the store's word for how it ended. `reason` says why, where the
804
+ Machine did not: the host's reason (`drifted`, `lost`, [ADR-0030](decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md)), the Failure's reason
805
+ (`failed`, [ADR-0073](decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md)) or the fault (`error`). `endedAt` is the terminal write of an ended run.
806
+ `openGates` counts the Gates open now, so it is 0 for an ended run. `placing` names the child
807
+ paths of the Workspaces that wait for capacity ([ADR-0064](decisions/0064-a-workspace-waits-for-capacity.md)), and `dispatchedBy` the run that
808
+ started this one ([ADR-0071](decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md)).
809
+
810
+ **Fields**
811
+
812
+ - `runId`: `string`
813
+ - `workflow`: `string`
814
+ - `status`: [`ShownStatus`](#type-shownstatus)
815
+ - `reason` (optional): `string`
816
+ - `startedBy` (optional): [`RunStarter`](#type-runstarter)
817
+ - `startedAt`: `string`
818
+ - `endedAt` (optional): `string`
819
+ - `assembly` (optional): `string`
820
+ - `openGates`: `number`
821
+ - `placing` (optional): `string[]`
822
+ - `dispatchedBy` (optional): [`DispatchedBy`](#type-dispatchedby)
823
+ - `swept` (optional): `"conversation swept: no space"`
824
+
825
+ The space guard deleted the run's kept conversations ([ADR-0065](decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md)); the row stays.
826
+
827
+ <a id="type-repostatus"></a>
828
+
829
+ ### `RepoStatus`
830
+
831
+ ```ts
832
+ type RepoStatus = {
833
+ key: string;
834
+ url: string;
835
+ identity?: string;
836
+ bound: boolean;
837
+ lastAttached?: string;
838
+ nodes: RepoNodeState[];
839
+ };
840
+ ```
841
+
842
+ One `Repo` resource as the instance reports it (`GET /repos`).
843
+
844
+ **Fields**
845
+
846
+ - `key`: `string`
847
+
848
+ The cache key: the resource's name, the hostPath leaf, `/repos/<key>`.
849
+ - `url`: `string`
850
+
851
+ The Binding's own spelling — what the cache clones.
852
+ - `identity` (optional): `string`
853
+ - `bound`: `boolean`
854
+
855
+ A registered Machine binds it — never evicted by `jr2 gc`.
856
+ - `lastAttached` (optional): `string`
857
+
858
+ When a run last attached it — the eviction clock for a Repo nothing binds. Absent while only
859
+ the boot has stated it: a bound Repo no run has attached yet.
860
+ - `nodes`: `RepoNodeState[]`
861
+
862
+ <a id="type-gateinboxentry"></a>
863
+
864
+ ### `GateInboxEntry`
865
+
866
+ ```ts
867
+ type GateInboxEntry = { runId: string; workflow: string } & GateView & {
868
+ openedAt: string;
869
+ hook?: string[];
870
+ };
871
+ ```
872
+
873
+ One entry of the Gate inbox (`GET /gates` — [ADR-0011](decisions/0011-workflow-defined-events.md)): an open Gate as [`GateView`](#type-gateview) gives it,
874
+ with its run, the run's Workflow, and when it opened, ISO-8601. `hook` names the Hook kinds that
875
+ the Gate hears, when it hears one. Never the run's context ([ADR-0014](decisions/0014-observation-is-open-run-state-is-not.md)).
876
+
877
+ **Fields**: each field of [`GateView`](#type-gateview), and these:
878
+
879
+ - `runId`: `string`
880
+ - `workflow`: `string`
881
+ - `openedAt`: `string`
882
+ - `hook` (optional): `string[]`
883
+
482
884
  <a id="type-runstatus"></a>
483
885
 
484
886
  ### `RunStatus`
485
887
 
486
888
  ```ts
487
889
  type RunStatus = RunRecord & {
488
- status: string;
890
+ status: ShownStatus;
489
891
  value: unknown;
490
892
  context: unknown;
491
893
  children: RunChild[];
@@ -494,10 +896,16 @@ type RunStatus = RunRecord & {
494
896
  fault?: string;
495
897
  reason?: string;
496
898
  assembly?: string;
899
+ swept?: "conversation swept: no space";
497
900
  bound?: true;
498
901
  chartHashes?: string[];
499
902
  output?: unknown;
500
903
  dispatched?: Dispatched[];
904
+ cancelledBy?: CallerRecord;
905
+ handedOverTo?: string;
906
+ answers?: GateAnswer[];
907
+ steering?: SteeringRecord[];
908
+ worktrees?: RunWorktree[];
501
909
  };
502
910
  ```
503
911
 
@@ -507,7 +915,7 @@ A run's current observable state. `fault` carries the error message when status
507
915
 
508
916
  **Fields**: each field of [`RunRecord`](#type-runrecord), and these:
509
917
 
510
- - `status`: `string`
918
+ - `status`: [`ShownStatus`](#type-shownstatus)
511
919
  - `value`: `unknown`
512
920
  - `context`: `unknown`
513
921
  - `children`: `RunChild[]`
@@ -521,7 +929,7 @@ A run's current observable state. `fault` carries the error message when status
521
929
  - `held`: `RunHeld[]`
522
930
 
523
931
  Every pick of this run that is outstanding ([ADR-0024](decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md)): the Agent, its pick, and since when —
524
- the Console's "held since". There is no default time bound on a held Turn; this is how a
932
+ the Console's "outstanding since". There is no default time bound on a held Turn; this is how a
525
933
  human sees one. Behind the token, like `context`: a pick names what the Agent did.
526
934
  - `fault` (optional): `string`
527
935
  - `reason` (optional): `string`
@@ -534,6 +942,10 @@ A run's current observable state. `fault` carries the error message when status
534
942
 
535
943
  The hash of the Assembly the run started under ([ADR-0069](decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md)). Absent for a run no Assembly
536
944
  started.
945
+ - `swept` (optional): `"conversation swept: no space"`
946
+
947
+ The space guard deleted the run's kept conversations ([ADR-0065](decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md), [ADR-0023](decisions/0023-the-harness-prints-the-conversation.md)): `jr2 logs` and
948
+ the Console show this in place of them. The run's row stays.
537
949
  - `bound` (optional): `true`
538
950
 
539
951
  The run binds at least one Chart ([ADR-0070](decisions/0070-a-machine-lends-its-parts-to-a-chart.md)): its Machine is the bound one, `runMachine`.
@@ -554,41 +966,29 @@ A run's current observable state. `fault` carries the error message when status
554
966
  The runs this run's `dispatch` Effects started, oldest first ([ADR-0071](decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md)): the chain's other
555
967
  direction, read off the caller's own ledger rows. A foreign dispatch's run is a run of the
556
968
  Orchestrator its target names. Absent when it started none.
969
+ - `cancelledBy` (optional): [`CallerRecord`](authoring-surface/type-callerrecord.md)
557
970
 
558
- <a id="type-repostatus"></a>
559
-
560
- ### `RepoStatus`
561
-
562
- ```ts
563
- type RepoStatus = {
564
- key: string;
565
- url: string;
566
- identity?: string;
567
- bound: boolean;
568
- lastAttached?: string;
569
- nodes: RepoNodeState[];
570
- };
571
- ```
572
-
573
- One `Repo` resource as the instance reports it (`GET /repos`).
574
-
575
- **Fields**
576
-
577
- - `key`: `string`
578
-
579
- The cache key: the resource's name, the hostPath leaf, `/repos/<key>`.
580
- - `url`: `string`
971
+ The Caller that cancelled the run ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)). Behind the token: `observe` never
972
+ carries it.
973
+ - `handedOverTo` (optional): `string`
581
974
 
582
- The Binding's own spelling — what the cache clones.
583
- - `identity` (optional): `string`
584
- - `bound`: `boolean`
975
+ The run that took over from this standing run ([ADR-0017](decisions/0017-pool-over-a-source-port.md)): a hand-over ends the run `done`.
976
+ - `answers` (optional): `GateAnswer[]`
585
977
 
586
- A registered Machine binds it — never evicted by `jr2 gc`.
587
- - `lastAttached` (optional): `string`
978
+ The answers to the run's Gates, oldest first, each with the Caller that gave it ([ADR-0011](decisions/0011-workflow-defined-events.md),
979
+ [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)): the last `GATE_ANSWERS_KEPT`. An event that a Gate's `look` gives is not an
980
+ answer, and is not here. Behind the token. Absent when no Gate was answered.
981
+ - `steering` (optional): `SteeringRecord[]`
588
982
 
589
- When a run last attached it — the eviction clock for a Repo nothing binds. Absent while only
590
- the boot has stated it: a bound Repo no run has attached yet.
591
- - `nodes`: `RepoNodeState[]`
983
+ The run's Steering messages, oldest first ([ADR-0002](decisions/0002-duplex-streaming-actor.md)): each one's Agent slot, author, text,
984
+ time and outcome — the newest 100, and each one still held. Behind the token, like `context`.
985
+ Absent when the run got none.
986
+ - `worktrees` (optional): `RunWorktree[]`
987
+
988
+ The Worktrees this run's cuts made, oldest first ([ADR-0075](decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md)), read off its ledger's settled
989
+ `ready` rows, so an Agent's `git switch` does not change them. A re-cut of one path and branch
990
+ is one entry. Behind the token, like `context`: a branch name is the workflow's data. Absent
991
+ when the run cut none.
592
992
 
593
993
  <a id="type-gateview"></a>
594
994
 
@@ -632,6 +1032,7 @@ type AgentSurfaceView = {
632
1032
  instanceId: string;
633
1033
  runId: string;
634
1034
  sandbox?: string;
1035
+ slot?: string;
635
1036
  menu: SurfaceEvent[];
636
1037
  allowed: string[];
637
1038
  };
@@ -655,6 +1056,10 @@ sandbox), and serving it lets a caller fail loudly on a surface that is not its
655
1056
  - `instanceId`: `string`
656
1057
  - `runId`: `string`
657
1058
  - `sandbox` (optional): `string`
1059
+ - `slot` (optional): `string`
1060
+
1061
+ The slot this surface belongs to ([ADR-0076](decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md)): its Agent's actor path and agent name — the slot
1062
+ the Harness must state to speak here. Absent on a registration no pod hosts.
658
1063
  - `menu`: `SurfaceEvent[]`
659
1064
  - `allowed`: `string[]`
660
1065
 
@@ -763,6 +1168,81 @@ when nothing has ever fetched it, which the program prints as "an unknown time".
763
1168
  - `stale`: `string`
764
1169
  - `asOf`: `string | null`
765
1170
 
1171
+ <a id="type-scopes"></a>
1172
+
1173
+ ### `Scopes`
1174
+
1175
+ ```ts
1176
+ type Scopes = {
1177
+ start?: ScopeGrant;
1178
+ answer?: AnswerGrant;
1179
+ cancel?: ScopeGrant;
1180
+ read?: ScopeGrant;
1181
+ steer?: ScopeGrant;
1182
+ };
1183
+ ```
1184
+
1185
+ What a Caller may do: five scopes, each one for the Workflows that it names ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)). A scope
1186
+ that is absent opens nothing. Which scope opens which route is in the HTTP API Reference.
1187
+
1188
+ A Caller's scope does not limit what its run dispatches: a Caller with `start` on `triage` starts
1189
+ a run whose Machine can dispatch `deploy`. The Machine is the author's, and it is the boundary of
1190
+ what its run does.
1191
+
1192
+ **Fields**
1193
+
1194
+ - `start` (optional): [`ScopeGrant`](config.md#callers-tokens-name-may-start)
1195
+
1196
+ Start a run.
1197
+ - `answer` (optional): [`AnswerGrant`](config.md#callers-tokens-name-may-answer)
1198
+
1199
+ Answer a Gate, and read a run's open Gates without its context. A list item
1200
+ `{ workflow, gates }` opens only the Gates that it names, by invoke id (see [`GateGrant`](#type-gategrant)).
1201
+ - `cancel` (optional): [`ScopeGrant`](config.md#callers-tokens-name-may-start)
1202
+
1203
+ Cancel a run.
1204
+ - `read` (optional): [`ScopeGrant`](config.md#callers-tokens-name-may-start)
1205
+
1206
+ Read a run's state: its status, its context and its feed.
1207
+ - `steer` (optional): [`ScopeGrant`](config.md#callers-tokens-name-may-start)
1208
+
1209
+ Send a Steering message to an Agent of a run.
1210
+
1211
+ <a id="type-hookfanin"></a>
1212
+
1213
+ ### `HookFanIn`
1214
+
1215
+ ```ts
1216
+ type HookFanIn = {
1217
+ delivered: Array<{ run: string; workflow: string; gate: string; event: string }>;
1218
+ refused: Array<{ run: string; gate: string; event?: string; reason: string }>;
1219
+ ignored: number;
1220
+ sources: SourceLine[];
1221
+ waiting?: string[];
1222
+ replies?: HookReply[];
1223
+ };
1224
+ ```
1225
+
1226
+ What a Hook's fan-in did ([ADR-0011](decisions/0011-workflow-defined-events.md)): the Gates that took the delivery, the ones that did not and
1227
+ why, and how many open Gates of its kind and key gave nothing.
1228
+
1229
+ **Fields**
1230
+
1231
+ - `delivered`: `Array<{ run: string; workflow: string; gate: string; event: string; }>`
1232
+ - `refused`: `Array<{ run: string; gate: string; event?: string; reason: string; }>`
1233
+ - `ignored`: `number`
1234
+ - `sources`: `SourceLine[]`
1235
+
1236
+ Each Source's verdict ([ADR-0017](decisions/0017-pool-over-a-source-port.md)), one line for each Pool whose Source hears the kind and key.
1237
+ - `waiting` (optional): `string[]`
1238
+
1239
+ The standing Workflows whose Source hears the kind and key, and that have no live run and are
1240
+ not paused ([ADR-0017](decisions/0017-pool-over-a-source-port.md)): a hand-over waits out its back-off. The door answers 503.
1241
+ - `replies` (optional): `HookReply[]`
1242
+
1243
+ For a delivery whose answer a person reads: the `reply` of each target that has one, in the
1244
+ order of the fan-out ([ADR-0011](decisions/0011-workflow-defined-events.md)). The door joins them; it never puts them in the outcome.
1245
+
766
1246
  <a id="type-chartorigin"></a>
767
1247
 
768
1248
  ### `ChartOrigin`
@@ -858,7 +1338,15 @@ the same child machine are told apart.
858
1338
  ### `DispatchedBy`
859
1339
 
860
1340
  ```ts
861
- type DispatchedBy = { run: string; slot: string; effect: string; depth: number; instance?: string };
1341
+ type DispatchedBy = {
1342
+ run: string;
1343
+ slot: string;
1344
+ effect: string;
1345
+ depth: number;
1346
+ instance?: string;
1347
+ key?: string;
1348
+ hook?: { name: string; delivery: string; onBehalfOf?: string };
1349
+ };
862
1350
  ```
863
1351
 
864
1352
  Which run dispatched a run ([ADR-0071](decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md)): the caller's run id, the slot of its `dispatch` Effect, and
@@ -875,25 +1363,145 @@ verify: opaque text, never resolved.
875
1363
  - `effect`: `string`
876
1364
  - `depth`: `number`
877
1365
  - `instance` (optional): `string`
1366
+ - `key` (optional): `string`
1367
+
1368
+ The idempotency key when a Pool of runs started the run ([ADR-0017](decisions/0017-pool-over-a-source-port.md)): the item's id. The store
1369
+ scopes it to the callee Workflow, as it scopes `effect`. Absent: the key is `effect`.
1370
+ - `hook` (optional): `object`
1371
+
1372
+ The Hook delivery that carried the item to the Pool ([ADR-0017](decisions/0017-pool-over-a-source-port.md)): the Hook's name, the delivery
1373
+ id, and the party that the signed payload names. Absent for an item that a read or `next` gave.
1374
+
1375
+ - `name`: `string`
1376
+ - `delivery`: `string`
1377
+ - `onBehalfOf` (optional): `string`
1378
+
1379
+ <a id="type-shownstatus"></a>
1380
+
1381
+ ### `ShownStatus`
1382
+
1383
+ ```ts
1384
+ type ShownStatus = typeof LIVE_STATUS | EndedStatus;
1385
+ ```
1386
+
1387
+ A run status as jr2 shows it, on every route and feed: `LIVE_STATUS` while the run is
1388
+ live, else how it ended. The store's own `live` and xstate's `stopped` are never shown.
1389
+
1390
+ <a id="type-runstarter"></a>
1391
+
1392
+ ### `RunStarter`
1393
+
1394
+ ```ts
1395
+ type RunStarter =
1396
+ | ({ kind: "caller" } & CallerRecord)
1397
+ | { kind: "run"; run: string; slot: string }
1398
+ | { kind: "jr2"; why: "boot" | "publish" | "hand-over" | "resume" };
1399
+ ```
1400
+
1401
+ Who started a run ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)): a Caller on a guarded route; a run, through a local `dispatch`
1402
+ ([ADR-0071](decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md)); or jr2 itself, for a standing run ([ADR-0017](decisions/0017-pool-over-a-source-port.md)), at `boot`, at a `publish`, at a
1403
+ `hand-over`, or at a `resume`. A run that a foreign `dispatch` started records the Caller whose
1404
+ credential the other Instance sent.
1405
+
1406
+ **Forms**: it is one of these objects.
1407
+
1408
+ - Form 1: `kind`, `via`, `name`, `shown`, `onBehalfOf`
1409
+
1410
+ - `kind`: `"caller"`
1411
+ - `via`: `Caller["via"]`
1412
+ - `name`: `string`
1413
+ - `shown` (optional): `string`
1414
+
1415
+ The name that jr2 shows, when it is not `name` (a JWT's `email`).
1416
+ - `onBehalfOf` (optional): `string`
1417
+
1418
+ The Caller's claim about the party it acts for. jr2 records it and never checks it, except for
1419
+ a Hook, whose signature covers it.
1420
+ - Form 2: `kind`, `run`, `slot`
1421
+
1422
+ - `kind`: `"run"`
1423
+ - `run`: `string`
1424
+ - `slot`: `string`
1425
+ - Form 3: `kind`, `why`
1426
+
1427
+ - `kind`: `"jr2"`
1428
+ - `why`: `"boot" | "publish" | "hand-over" | "resume"`
1429
+
1430
+ <a id="type-reponodestate"></a>
1431
+
1432
+ ### `RepoNodeState`
1433
+
1434
+ ```ts
1435
+ type RepoNodeState = {
1436
+ node: string;
1437
+ present: boolean;
1438
+ synced: boolean;
1439
+ attempted?: "Probe" | "Clone" | "Fetch";
1440
+ lastAttempt?: string;
1441
+ lastFetched?: string;
1442
+ lastError?: string;
1443
+ };
1444
+ ```
1445
+
1446
+ One node's view of a Repo, as the cache agent reports it on the resource's status.
1447
+
1448
+ **Fields**
1449
+
1450
+ - `node`: `string`
1451
+ - `present`: `boolean`
1452
+
1453
+ A clone exists on this node.
1454
+ - `synced`: `boolean`
1455
+
1456
+ The last attempt — a probe, a clone, or a fetch — succeeded.
1457
+ - `attempted` (optional): `"Probe" | "Clone" | "Fetch"`
1458
+
1459
+ Which of the three the last attempt was. A failed Probe is `jr2 status`'s signal for a Repo
1460
+ no pod on the node mounts yet; only a failed Clone fails a Sandbox waiting on that node.
1461
+ - `lastAttempt` (optional): `string`
1462
+ - `lastFetched` (optional): `string`
1463
+
1464
+ The last successful clone or fetch.
1465
+ - `lastError` (optional): `string`
1466
+
1467
+ git's own words; absent when synced.
878
1468
 
879
1469
  <a id="type-runrecord"></a>
880
1470
 
881
1471
  ### `RunRecord`
882
1472
 
883
1473
  ```ts
884
- type RunRecord = { runId: string; workflow: string; instanceId: string; dispatchedBy?: DispatchedBy };
1474
+ type RunRecord = {
1475
+ runId: string;
1476
+ workflow: string;
1477
+ instanceId: string;
1478
+ startedAt: string;
1479
+ dispatchedBy?: DispatchedBy;
1480
+ startedBy?: RunStarter;
1481
+ resumedBy?: CallerRecord;
1482
+ };
885
1483
  ```
886
1484
 
887
1485
  The serializable identity of a run — what reconcile sees and what restore rebuilds from.
888
1486
  `dispatchedBy` is the run that started it through a `dispatch` Effect, and this run's depth in
889
1487
  that chain, as the caller sent it ([ADR-0071](decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md)); absent for a run a human or a client started.
1488
+ `startedBy` is who started it ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)): a Caller, a run, or jr2. Behind the token, like
1489
+ `context`: `observe` never carries it. Absent for a run that a host started with no
1490
+ starter, as a test does.
890
1491
 
891
1492
  **Fields**
892
1493
 
893
1494
  - `runId`: `string`
894
1495
  - `workflow`: `string`
895
1496
  - `instanceId`: `string`
1497
+ - `startedAt`: `string`
1498
+
1499
+ When the run started, ISO-8601 ([ADR-0009](decisions/0009-cli-and-instance-interface.md)): the order of `GET /runs`.
896
1500
  - `dispatchedBy` (optional): [`DispatchedBy`](#type-dispatchedby)
1501
+ - `startedBy` (optional): [`RunStarter`](#type-runstarter)
1502
+ - `resumedBy` (optional): [`CallerRecord`](authoring-surface/type-callerrecord.md)
1503
+
1504
+ The Caller whose `jr2 run` resumed this standing run ([ADR-0017](decisions/0017-pool-over-a-source-port.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)).
897
1505
 
898
1506
  <a id="type-runwaiting"></a>
899
1507
 
@@ -951,44 +1559,97 @@ ledger row was written.
951
1559
  - `run`: `string`
952
1560
  - `at`: `string`
953
1561
 
954
- <a id="type-reponodestate"></a>
1562
+ <a id="type-callerrecord"></a>
955
1563
 
956
- ### `RepoNodeState`
1564
+ ### `CallerRecord`
957
1565
 
958
1566
  ```ts
959
- type RepoNodeState = {
960
- node: string;
961
- present: boolean;
962
- synced: boolean;
963
- attempted?: "Probe" | "Clone" | "Fetch";
964
- lastAttempt?: string;
965
- lastFetched?: string;
966
- lastError?: string;
1567
+ type CallerRecord = {
1568
+ via: Caller["via"];
1569
+ name: string;
1570
+ shown?: string;
1571
+ onBehalfOf?: string;
967
1572
  };
968
1573
  ```
969
1574
 
970
- One node's view of a Repo, as the cache agent reports it on the resource's status.
1575
+ A Caller as a run records it ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)): who acted, and the party it says it acted for. Never its
1576
+ scopes.
971
1577
 
972
1578
  **Fields**
973
1579
 
974
- - `node`: `string`
975
- - `present`: `boolean`
1580
+ - `via`: `Caller["via"]`
1581
+ - `name`: `string`
1582
+ - `shown` (optional): `string`
976
1583
 
977
- A clone exists on this node.
978
- - `synced`: `boolean`
1584
+ The name that jr2 shows, when it is not `name` (a JWT's `email`).
1585
+ - `onBehalfOf` (optional): `string`
979
1586
 
980
- The last attempt — a probe, a clone, or a fetch — succeeded.
981
- - `attempted` (optional): `"Probe" | "Clone" | "Fetch"`
1587
+ The Caller's claim about the party it acts for. jr2 records it and never checks it, except for
1588
+ a Hook, whose signature covers it.
982
1589
 
983
- Which of the three the last attempt was. A failed Probe is `jr2 status`'s signal for a Repo
984
- no pod on the node mounts yet; only a failed Clone fails a Sandbox waiting on that node.
985
- - `lastAttempt` (optional): `string`
986
- - `lastFetched` (optional): `string`
1590
+ <a id="type-gateanswer"></a>
987
1591
 
988
- The last successful clone or fetch.
989
- - `lastError` (optional): `string`
1592
+ ### `GateAnswer`
990
1593
 
991
- git's own words; absent when synced.
1594
+ ```ts
1595
+ type GateAnswer = { gate: string; type: string; at: string; answeredBy: CallerRecord; delivery?: string };
1596
+ ```
1597
+
1598
+ One answer to a Gate of a run, and the Caller that gave it ([ADR-0011](decisions/0011-workflow-defined-events.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)). `delivery` is
1599
+ the Hook's delivery id when a Hook gave it: the run's `(delivery, gate)` pairs, so a restore never
1600
+ gives one delivery twice to one Gate.
1601
+
1602
+ **Fields**
1603
+
1604
+ - `gate`: `string`
1605
+ - `type`: `string`
1606
+ - `at`: `string`
1607
+ - `answeredBy`: [`CallerRecord`](authoring-surface/type-callerrecord.md)
1608
+ - `delivery` (optional): `string`
1609
+
1610
+ <a id="type-steeringrecord"></a>
1611
+
1612
+ ### `SteeringRecord`
1613
+
1614
+ ```ts
1615
+ type SteeringRecord = {
1616
+ id: string;
1617
+ agent: string;
1618
+ author: SteeringAuthor;
1619
+ text: string;
1620
+ at: string;
1621
+ outcome: SteeringOutcome;
1622
+ };
1623
+ ```
1624
+
1625
+ The run's record of one Steering message: the slot it went to, its author, its text, when it was
1626
+ sent, and what became of it.
1627
+
1628
+ **Fields**
1629
+
1630
+ - `id`: `string`
1631
+ - `agent`: `string`
1632
+ - `author`: [`SteeringAuthor`](#type-steeringauthor)
1633
+ - `text`: `string`
1634
+ - `at`: `string`
1635
+ - `outcome`: [`SteeringOutcome`](#type-steeringoutcome)
1636
+
1637
+ <a id="type-runworktree"></a>
1638
+
1639
+ ### `RunWorktree`
1640
+
1641
+ ```ts
1642
+ type RunWorktree = { slot: string; path: string; branch: string | null };
1643
+ ```
1644
+
1645
+ One Worktree a `worktree()` Effect of this run cut ([ADR-0075](decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md)): its Repo Slot, its path, and the
1646
+ branch of the cut. `branch` is `null` for a detached Worktree.
1647
+
1648
+ **Fields**
1649
+
1650
+ - `slot`: `string`
1651
+ - `path`: `string`
1652
+ - `branch`: `string | null`
992
1653
 
993
1654
  <a id="type-surfaceevent"></a>
994
1655
 
@@ -1055,6 +1716,80 @@ whether work ran. Plain data; the Harness writes the words. `answer`: the value
1055
1716
  - `kind`: `"answer"`
1056
1717
  - `value`: `unknown`
1057
1718
 
1719
+ <a id="type-scopegrant"></a>
1720
+
1721
+ ### `ScopeGrant`
1722
+
1723
+ ```ts
1724
+ type ScopeGrant = "all" | readonly string[];
1725
+ ```
1726
+
1727
+ The Workflows that one scope of a Caller opens ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)): `"all"` for every Workflow, or a list
1728
+ of Workflow names. A Chart wrapper's name in the list also opens each named Chart of that wrapper
1729
+ (`plan` opens `plan/weekly`), because a named Chart runs its wrapper's Machine. A named Chart's
1730
+ own name (`plan/weekly`) opens that Chart alone.
1731
+
1732
+ <a id="type-answergrant"></a>
1733
+
1734
+ ### `AnswerGrant`
1735
+
1736
+ ```ts
1737
+ type AnswerGrant = "all" | readonly (string | GateGrant)[];
1738
+ ```
1739
+
1740
+ The Gates that the `answer` scope opens ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)): `"all"`, or a list in which each item is a
1741
+ Workflow name, which opens every Gate of that Workflow, or a [`GateGrant`](#type-gategrant), which opens only
1742
+ the Gates that it names: `["triage", { workflow: "deploy", gates: ["approve"] }]`.
1743
+
1744
+ <a id="type-sourceline"></a>
1745
+
1746
+ ### `SourceLine`
1747
+
1748
+ ```ts
1749
+ type SourceLine = { pool: string; workflow: string; source: string } & SourceVerdictLine;
1750
+ ```
1751
+
1752
+ One Source's verdict on one delivery ([ADR-0017](decisions/0017-pool-over-a-source-port.md)): the Pool's run, its Workflow, the `source`
1753
+ Gate's id, and the verdict.
1754
+
1755
+ **Fields**: each field of [`SourceVerdictLine`](#type-sourceverdictline), and these:
1756
+
1757
+ - `pool`: `string`
1758
+ - `workflow`: `string`
1759
+ - `source`: `string`
1760
+
1761
+ <a id="type-hookreply"></a>
1762
+
1763
+ ### `HookReply`
1764
+
1765
+ ```ts
1766
+ type HookReply = {
1767
+ target: "source" | "gate";
1768
+ workflow: string;
1769
+ run: string;
1770
+ gate: string;
1771
+ text?: string;
1772
+ skipped?: true;
1773
+ error?: string;
1774
+ };
1775
+ ```
1776
+
1777
+ What one target's `reply` gave ([ADR-0011](decisions/0011-workflow-defined-events.md)): its place in the join (a Source or a Gate, its
1778
+ Workflow and its run), and its text, or the error of a `reply` that threw or gave no text. A
1779
+ `skipped` Source's text shows only when no other target gave one.
1780
+
1781
+ **Fields**
1782
+
1783
+ - `target`: `"source" | "gate"`
1784
+ - `workflow`: `string`
1785
+ - `run`: `string`
1786
+ - `gate`: `string`
1787
+
1788
+ The Gate's id, or the Pool's `source` Gate's id.
1789
+ - `text` (optional): `string`
1790
+ - `skipped` (optional): `true`
1791
+ - `error` (optional): `string`
1792
+
1058
1793
  <a id="type-machinestatedoc"></a>
1059
1794
 
1060
1795
  ### `MachineStateDoc`
@@ -1201,6 +1936,57 @@ input names `accepts:` itself.
1201
1936
  - `agent` (optional): `string`
1202
1937
  - `names`: `string[]`
1203
1938
 
1939
+ <a id="type-endedstatus"></a>
1940
+
1941
+ ### `EndedStatus`
1942
+
1943
+ ```ts
1944
+ type EndedStatus = "done" | "failed" | "error" | "cancelled" | "lost" | "drifted";
1945
+ ```
1946
+
1947
+ How a run ended, as the store writes it ([ADR-0065](decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md), [ADR-0025](decisions/0025-cancel-ends-a-run-stop-parks-it.md), [ADR-0030](decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md), [ADR-0073](decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md), [ADR-0074](decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md)).
1948
+
1949
+ <a id="type-steeringauthor"></a>
1950
+
1951
+ ### `SteeringAuthor`
1952
+
1953
+ ```ts
1954
+ type SteeringAuthor =
1955
+ | { kind: "caller"; via: "token" | "jwt" | "hook"; name: string; onBehalfOf?: string }
1956
+ | { kind: "machine"; from?: { party: "agent" | "gate"; id: string } };
1957
+ ```
1958
+
1959
+ Who wrote a Steering message. `caller`: a Caller sent it from outside the run ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)), with
1960
+ the name jr2 shows and the party it says it acts for. `machine`: the Machine sent it with its
1961
+ `steer` action; `from` is the party whose words it relays, when it relays some.
1962
+
1963
+ **Forms**: it is one of these objects.
1964
+
1965
+ - Form 1: `kind`, `via`, `name`, `onBehalfOf`
1966
+
1967
+ - `kind`: `"caller"`
1968
+ - `via`: `"token" | "jwt" | "hook"`
1969
+ - `name`: `string`
1970
+ - `onBehalfOf` (optional): `string`
1971
+ - Form 2: `kind`, `from`
1972
+
1973
+ - `kind`: `"machine"`
1974
+ - `from` (optional): `object`
1975
+
1976
+ - `party`: `"agent" | "gate"`
1977
+ - `id`: `string`
1978
+
1979
+ <a id="type-steeringoutcome"></a>
1980
+
1981
+ ### `SteeringOutcome`
1982
+
1983
+ ```ts
1984
+ type SteeringOutcome = "held" | "heard" | "not-delivered";
1985
+ ```
1986
+
1987
+ What became of one Steering message ([ADR-0002](decisions/0002-duplex-streaming-actor.md)): `held` until a Turn of its slot hears it,
1988
+ `heard` once one did, `not-delivered` when its Machine instance ended first.
1989
+
1204
1990
  <a id="type-eventsemantics"></a>
1205
1991
 
1206
1992
  ### `EventSemantics`
@@ -1223,13 +2009,65 @@ How the party that sends an event gets its answer.
1223
2009
  - [The Menu](../concepts/the-menu.md)
1224
2010
  - [ADR-0006: The agent picks from a Machine-defined, schema-backed menu; it does not drive the workflow](decisions/0006-agent-control-surface.md)
1225
2011
 
2012
+ <a id="type-gategrant"></a>
2013
+
2014
+ ### `GateGrant`
2015
+
2016
+ ```ts
2017
+ type GateGrant = {
2018
+ workflow: string;
2019
+ gates: readonly string[];
2020
+ };
2021
+ ```
2022
+
2023
+ Some Gates of one Workflow, in the `answer` scope ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)). A Gate's name is its invoke id: the
2024
+ static name that it has in the Machine that invokes it, and the id that its events carry as their
2025
+ sender ([ADR-0011](decisions/0011-workflow-defined-events.md)). That is the invoke `id` that the author wrote, or else the key path of its
2026
+ gated state below the root of that Machine (`body.humanReview`). One name opens every Gate of the
2027
+ Workflow with that id, in each child of a Pool too.
2028
+
2029
+ `jr2 up` refuses an item for a Workflow that the Assembly does not have, a name that no Machine
2030
+ of the Workflow has, and a name that Gates in more than one Machine of the Workflow have. A Chart
2031
+ wrapper's item (`{ workflow: "plan", gates }`) opens those Gates in each Chart of `plan` too, and
2032
+ `jr2 up` checks its names against the wrapper's Machines and its Chart files. A runtime Chart is
2033
+ not in the Assembly, so `jr2 up` cannot check its Gates: a name that no Machine and no Chart file
2034
+ of the wrapper has, and an item for a runtime Chart (`plan/weekly` with no Chart file), are
2035
+ warnings, not refusals.
2036
+
2037
+ **Fields**
2038
+
2039
+ - `workflow`: `string`
2040
+
2041
+ The Workflow, as a bare name in the list names it.
2042
+ - `gates`: `readonly string[]`
2043
+
2044
+ The Gates' names.
2045
+
2046
+ <a id="type-sourceverdictline"></a>
2047
+
2048
+ ### `SourceVerdictLine`
2049
+
2050
+ ```ts
2051
+ type SourceVerdictLine = { verdict: SourceVerdictName; key?: string; run?: string; reason?: string };
2052
+ ```
2053
+
2054
+ A Source's verdict on one delivery ([ADR-0017](decisions/0017-pool-over-a-source-port.md)): `key` for a carried item, `run` when a run holds
2055
+ that key, `reason` for a skip.
2056
+
2057
+ **Fields**
2058
+
2059
+ - `verdict`: [`SourceVerdictName`](#type-sourceverdictname)
2060
+ - `key` (optional): `string`
2061
+ - `run` (optional): `string`
2062
+ - `reason` (optional): `string`
2063
+
1226
2064
  <a id="type-effectdoc"></a>
1227
2065
 
1228
2066
  ### `EffectDoc`
1229
2067
 
1230
2068
  ```ts
1231
2069
  type EffectDoc =
1232
- | { kind: "command"; argv: string[]; timeoutMs: number; slot?: string; secrets?: string[]; pin?: true }
2070
+ | { kind: "command"; argv: string[]; timeoutMs: number; secrets?: string[]; pin?: true }
1233
2071
  | {
1234
2072
  kind: "http";
1235
2073
  method: string;
@@ -1240,20 +2078,29 @@ type EffectDoc =
1240
2078
  file?: true;
1241
2079
  }
1242
2080
  | { kind: "push"; sign: boolean; squash: boolean; force: boolean; pin: boolean; timeoutMs: number; slot?: string }
1243
- | { kind: "dispatch"; workflow: string; wait: boolean; target?: string; timeoutMs: number };
2081
+ | { kind: "dispatch"; workflow: string; wait: boolean; target?: string; timeoutMs: number }
2082
+ | {
2083
+ kind: "mcp";
2084
+ op: "call" | "read" | "prompt";
2085
+ server: string;
2086
+ tool?: string;
2087
+ uri?: string;
2088
+ prompt?: string;
2089
+ timeoutMs?: number;
2090
+ idempotent: boolean;
2091
+ };
1244
2092
  ```
1245
2093
 
1246
2094
  An Effect slot's parts, as the doc carries them ([ADR-0067](decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md)): a command's argv (an argv slot reads
1247
- `<name>`), its timeout, and the Repo Slot whose Worktree is its cwd, when it names one.
2095
+ `<name>`) and its timeout. Its input names its Worktree, so the doc does not ([ADR-0075](decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md)).
1248
2096
 
1249
2097
  **Forms**: it is one of these objects.
1250
2098
 
1251
- - Form 1: `kind`, `argv`, `timeoutMs`, `slot`, `secrets`, `pin`
2099
+ - Form 1: `kind`, `argv`, `timeoutMs`, `secrets`, `pin`
1252
2100
 
1253
2101
  - `kind`: `"command"`
1254
2102
  - `argv`: `string[]`
1255
2103
  - `timeoutMs`: `number`
1256
- - `slot` (optional): `string`
1257
2104
  - `secrets` (optional): `string[]`
1258
2105
  - `pin` (optional): `true`
1259
2106
  - Form 2: `kind`, `method`, `target`, `secrets`, `timeoutMs`, `idempotent`, `file`
@@ -1281,6 +2128,16 @@ An Effect slot's parts, as the doc carries them ([ADR-0067](decisions/0067-an-ef
1281
2128
  - `wait`: `boolean`
1282
2129
  - `target` (optional): `string`
1283
2130
  - `timeoutMs`: `number`
2131
+ - Form 5: `kind`, `op`, `server`, `tool`, `uri`, `prompt`, `timeoutMs`, `idempotent`
2132
+
2133
+ - `kind`: `"mcp"`
2134
+ - `op`: `"call" | "read" | "prompt"`
2135
+ - `server`: `string`
2136
+ - `tool` (optional): `string`
2137
+ - `uri` (optional): `string`
2138
+ - `prompt` (optional): `string`
2139
+ - `timeoutMs` (optional): `number`
2140
+ - `idempotent`: `boolean`
1284
2141
 
1285
2142
  <a id="type-childmachinedoc"></a>
1286
2143
 
@@ -1346,3 +2203,11 @@ event to the other party too (`"any"`). Or set it to keep an event on the root f
1346
2203
  - [The Menu](../concepts/the-menu.md)
1347
2204
  - [Gates](../concepts/gates.md)
1348
2205
  - [ADR-0015: Workflows are authored through `jr2Setup`; vocabulary and agent menus derive from the machine](decisions/0015-authoring-surface-absorbs-the-mechanism.md)
2206
+
2207
+ <a id="type-sourceverdictname"></a>
2208
+
2209
+ ### `SourceVerdictName`
2210
+
2211
+ ```ts
2212
+ type SourceVerdictName = (typeof SOURCE_VERDICTS)[number];
2213
+ ```