@jr2/orchestrator 0.6.0 → 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 (301) 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 +86 -9
  14. package/docs/concepts/architecture.md +41 -28
  15. package/docs/concepts/callers-and-hooks.md +205 -0
  16. package/docs/concepts/charts.md +7 -5
  17. package/docs/concepts/composition.md +10 -1
  18. package/docs/concepts/converge.md +4 -2
  19. package/docs/concepts/durable-runs.md +43 -16
  20. package/docs/concepts/effects.md +50 -22
  21. package/docs/concepts/gates.md +95 -25
  22. package/docs/concepts/machines-and-workflows.md +5 -4
  23. package/docs/concepts/pools-and-sources.md +187 -11
  24. package/docs/concepts/secrets-and-trust.md +73 -33
  25. package/docs/concepts/the-menu.md +11 -0
  26. package/docs/concepts/workspaces.md +6 -5
  27. package/docs/get-started/a-model-decides.md +14 -13
  28. package/docs/get-started/an-agent-in-a-sandbox.md +43 -34
  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/guides/add-tools-to-the-sandbox.md +55 -11
  32. package/docs/guides/answer-a-gate-from-a-forge.md +304 -0
  33. package/docs/guides/answer-a-gate.md +74 -37
  34. package/docs/guides/bind-open-parts.md +24 -8
  35. package/docs/guides/call-an-mcp-tool-from-a-machine.md +572 -0
  36. package/docs/guides/cancel-a-run.md +26 -16
  37. package/docs/guides/fix-a-failed-converge.md +18 -6
  38. package/docs/guides/give-a-caller-a-scope.md +203 -0
  39. package/docs/guides/give-an-agent-mcp-tools.md +422 -0
  40. package/docs/guides/iterate-on-a-machine-on-kind.md +10 -9
  41. package/docs/guides/let-an-agent-write-a-chart.md +9 -6
  42. package/docs/guides/look-inside-a-sandbox.md +101 -31
  43. package/docs/guides/open-the-console.md +63 -14
  44. package/docs/guides/read-a-conversation.md +225 -0
  45. package/docs/guides/run-a-chart-by-name.md +17 -16
  46. package/docs/guides/run-one-worker-per-item.md +15 -9
  47. package/docs/guides/set-an-agents-access-and-dials.md +2 -0
  48. package/docs/guides/start-a-run-for-each-alert.md +290 -0
  49. package/docs/guides/start-a-run-from-a-run.md +18 -16
  50. package/docs/guides/start-a-run.md +24 -18
  51. package/docs/guides/steer-an-agent.md +332 -0
  52. package/docs/guides/use-a-hosted-provider.md +1 -1
  53. package/docs/guides/watch-a-run.md +127 -121
  54. package/docs/guides/write-a-hook-kind.md +238 -0
  55. package/docs/guides/write-a-tool-as-an-mcp-server.md +351 -0
  56. package/docs/guides/write-a-workflow.md +17 -7
  57. package/docs/llms.txt +81 -15
  58. package/docs/reference/authoring-surface/agent.md +1 -1
  59. package/docs/reference/authoring-surface/answer.md +1 -1
  60. package/docs/reference/authoring-surface/arg.md +1 -1
  61. package/docs/reference/authoring-surface/basic.md +1 -1
  62. package/docs/reference/authoring-surface/bearer.md +1 -1
  63. package/docs/reference/authoring-surface/branchname.md +1 -1
  64. package/docs/reference/authoring-surface/carry.md +23 -0
  65. package/docs/reference/authoring-surface/chartschema.md +1 -1
  66. package/docs/reference/authoring-surface/chartschemafor.md +1 -1
  67. package/docs/reference/authoring-surface/command.md +1 -1
  68. package/docs/reference/authoring-surface/customize.md +4 -1
  69. package/docs/reference/authoring-surface/defineconfig.md +1 -1
  70. package/docs/reference/authoring-surface/defineevent.md +1 -1
  71. package/docs/reference/authoring-surface/dispatch.md +5 -4
  72. package/docs/reference/authoring-surface/fail.md +1 -1
  73. package/docs/reference/authoring-surface/file.md +1 -1
  74. package/docs/reference/authoring-surface/frompick.md +1 -1
  75. package/docs/reference/authoring-surface/gate.md +118 -0
  76. package/docs/reference/authoring-surface/group-agents.md +15 -0
  77. package/docs/reference/authoring-surface/group-charts.md +16 -0
  78. package/docs/reference/authoring-surface/group-composition.md +25 -0
  79. package/docs/reference/authoring-surface/group-config.md +17 -0
  80. package/docs/reference/authoring-surface/group-effects.md +48 -0
  81. package/docs/reference/authoring-surface/group-failure.md +13 -0
  82. package/docs/reference/authoring-surface/group-gates.md +16 -0
  83. package/docs/reference/authoring-surface/group-machine-and-events.md +15 -0
  84. package/docs/reference/authoring-surface/group-steering.md +12 -0
  85. package/docs/reference/authoring-surface/group-types-these-signatures-name.md +137 -0
  86. package/docs/reference/authoring-surface/group-workspaces-and-repos.md +15 -0
  87. package/docs/reference/authoring-surface/http.md +1 -1
  88. package/docs/reference/authoring-surface/isfailure.md +1 -1
  89. package/docs/reference/authoring-surface/isready.md +1 -1
  90. package/docs/reference/authoring-surface/jr2setup.md +13 -3
  91. package/docs/reference/authoring-surface/mcp.md +107 -0
  92. package/docs/reference/authoring-surface/open.md +5 -3
  93. package/docs/reference/authoring-surface/openbody.md +1 -1
  94. package/docs/reference/authoring-surface/paramsof.md +1 -1
  95. package/docs/reference/authoring-surface/pool.md +77 -9
  96. package/docs/reference/authoring-surface/push.md +1 -1
  97. package/docs/reference/authoring-surface/readyof.md +1 -1
  98. package/docs/reference/authoring-surface/reread.md +19 -0
  99. package/docs/reference/authoring-surface/secret.md +1 -1
  100. package/docs/reference/authoring-surface/sentby.md +1 -1
  101. package/docs/reference/authoring-surface/skip.md +22 -0
  102. package/docs/reference/authoring-surface/source.md +56 -1
  103. package/docs/reference/authoring-surface/steer.md +93 -0
  104. package/docs/reference/authoring-surface/text.md +1 -1
  105. package/docs/reference/authoring-surface/type-agentdefinition.md +14 -1
  106. package/docs/reference/authoring-surface/type-agentmcpserver.md +54 -0
  107. package/docs/reference/authoring-surface/type-anyframepath.md +1 -1
  108. package/docs/reference/authoring-surface/type-anyworktreepath.md +1 -1
  109. package/docs/reference/authoring-surface/type-binding.md +1 -1
  110. package/docs/reference/authoring-surface/type-caller.md +40 -0
  111. package/docs/reference/authoring-surface/type-callerrecord.md +32 -0
  112. package/docs/reference/authoring-surface/type-carry.md +19 -0
  113. package/docs/reference/authoring-surface/type-chainlink.md +3 -3
  114. package/docs/reference/authoring-surface/type-commandoutput.md +1 -1
  115. package/docs/reference/authoring-surface/type-customize.md +16 -1
  116. package/docs/reference/authoring-surface/type-delivered.md +17 -0
  117. package/docs/reference/authoring-surface/type-deliveredevent.md +18 -0
  118. package/docs/reference/authoring-surface/type-dispatchdefinition.md +4 -3
  119. package/docs/reference/authoring-surface/type-dispatchoutput.md +1 -1
  120. package/docs/reference/authoring-surface/type-effectbinding.md +12 -2
  121. package/docs/reference/authoring-surface/type-framepath.md +1 -1
  122. package/docs/reference/authoring-surface/type-gatebinding.md +15 -0
  123. package/docs/reference/authoring-surface/type-gatehooks.md +12 -0
  124. package/docs/reference/authoring-surface/type-gatelook.md +37 -0
  125. package/docs/reference/authoring-surface/type-gateoutcome.md +32 -0
  126. package/docs/reference/authoring-surface/type-gatereply.md +19 -0
  127. package/docs/reference/authoring-surface/type-gateslot.md +21 -0
  128. package/docs/reference/authoring-surface/type-gateslotinput.md +13 -0
  129. package/docs/reference/authoring-surface/type-gateslotparts.md +18 -0
  130. package/docs/reference/authoring-surface/type-gateslots.md +12 -0
  131. package/docs/reference/authoring-surface/type-gatetypesat.md +13 -0
  132. package/docs/reference/authoring-surface/type-hostinjectedinput.md +1 -1
  133. package/docs/reference/authoring-surface/type-httpoutput.md +1 -1
  134. package/docs/reference/authoring-surface/type-jr2config.md +37 -2
  135. package/docs/reference/authoring-surface/type-lateroutcome.md +1 -1
  136. package/docs/reference/authoring-surface/type-lends.md +5 -1
  137. package/docs/reference/authoring-surface/type-lookoutput.md +14 -0
  138. package/docs/reference/authoring-surface/type-mcpcalloutput.md +78 -0
  139. package/docs/reference/authoring-surface/type-mcpnotreturned.md +42 -0
  140. package/docs/reference/authoring-surface/type-mcpoptions.md +47 -0
  141. package/docs/reference/authoring-surface/type-mcpoutput.md +50 -0
  142. package/docs/reference/authoring-surface/type-mcpreadoutput.md +53 -0
  143. package/docs/reference/authoring-surface/type-mcpremoteserver.md +50 -0
  144. package/docs/reference/authoring-surface/type-mcpserver.md +41 -0
  145. package/docs/reference/authoring-surface/type-mcpstdioserver.md +42 -0
  146. package/docs/reference/authoring-surface/type-namedsecret.md +4 -4
  147. package/docs/reference/authoring-surface/type-openbodydeclaration.md +1 -1
  148. package/docs/reference/authoring-surface/type-openbodyoutput.md +1 -1
  149. package/docs/reference/authoring-surface/type-poolnextinput.md +1 -1
  150. package/docs/reference/authoring-surface/type-poolspec.md +11 -6
  151. package/docs/reference/authoring-surface/type-pushoutput.md +1 -1
  152. package/docs/reference/authoring-surface/type-pushsourcespec.md +38 -0
  153. package/docs/reference/authoring-surface/type-readsourcespec.md +47 -0
  154. package/docs/reference/authoring-surface/type-reposlot.md +1 -1
  155. package/docs/reference/authoring-surface/type-reread.md +18 -0
  156. package/docs/reference/authoring-surface/type-runsconfig.md +41 -0
  157. package/docs/reference/authoring-surface/type-sender.md +26 -1
  158. package/docs/reference/authoring-surface/type-sendercallerfield.md +14 -0
  159. package/docs/reference/authoring-surface/type-size.md +1 -1
  160. package/docs/reference/authoring-surface/type-skip.md +19 -0
  161. package/docs/reference/authoring-surface/type-sourcehooks.md +23 -0
  162. package/docs/reference/authoring-surface/type-sourcein.md +25 -0
  163. package/docs/reference/authoring-surface/type-sourcereply.md +18 -0
  164. package/docs/reference/authoring-surface/type-sourcespec.md +16 -7
  165. package/docs/reference/authoring-surface/type-sourceverdict.md +49 -0
  166. package/docs/reference/authoring-surface/type-standingpoolmachine.md +12 -0
  167. package/docs/reference/authoring-surface/type-standingpoolspec.md +51 -0
  168. package/docs/reference/authoring-surface/type-steerargs.md +20 -0
  169. package/docs/reference/authoring-surface/type-thinkinglevel.md +1 -1
  170. package/docs/reference/authoring-surface/type-usercontainer.md +1 -1
  171. package/docs/reference/authoring-surface/type-workspaced.md +1 -1
  172. package/docs/reference/authoring-surface/type-worktreefound.md +3 -3
  173. package/docs/reference/authoring-surface/type-worktreeinput.md +1 -1
  174. package/docs/reference/authoring-surface/type-worktreeoptions.md +1 -1
  175. package/docs/reference/authoring-surface/type-worktreeoutput.md +1 -1
  176. package/docs/reference/authoring-surface/type-worktreepath.md +1 -1
  177. package/docs/reference/authoring-surface/type-worktreeready.md +1 -1
  178. package/docs/reference/authoring-surface/type-worktreerefused.md +4 -4
  179. package/docs/reference/authoring-surface/workspace.md +1 -1
  180. package/docs/reference/authoring-surface/worktree.md +1 -1
  181. package/docs/reference/authoring-surface.md +88 -167
  182. package/docs/reference/cli.md +413 -74
  183. package/docs/reference/config.md +235 -2
  184. package/docs/reference/decisions/0002-duplex-streaming-actor.md +71 -11
  185. package/docs/reference/decisions/0005-sandbox-pod-composition.md +14 -10
  186. package/docs/reference/decisions/0006-agent-control-surface.md +4 -4
  187. package/docs/reference/decisions/0007-durable-machine-state.md +1 -1
  188. package/docs/reference/decisions/0009-cli-and-instance-interface.md +150 -49
  189. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +5 -3
  190. package/docs/reference/decisions/0011-workflow-defined-events.md +256 -12
  191. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +9 -2
  192. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +20 -14
  193. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +53 -27
  194. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +18 -14
  195. package/docs/reference/decisions/0017-pool-over-a-source-port.md +226 -12
  196. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +15 -7
  197. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +32 -18
  198. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +14 -1
  199. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +4 -4
  200. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +116 -34
  201. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +38 -37
  202. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +10 -5
  203. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +7 -6
  204. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +23 -7
  205. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +18 -10
  206. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +3 -2
  207. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +23 -18
  208. package/docs/reference/decisions/0032-the-console-unlocks-with-a-callers-credential.md +101 -0
  209. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +4 -3
  210. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +6 -6
  211. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +5 -4
  212. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +19 -19
  213. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +6 -1
  214. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +55 -40
  215. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +14 -13
  216. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +7 -0
  217. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +13 -7
  218. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +38 -30
  219. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +64 -17
  220. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +2 -1
  221. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +2 -2
  222. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md +109 -0
  223. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +51 -23
  224. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +94 -54
  225. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +101 -47
  226. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +32 -9
  227. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +17 -13
  228. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +56 -30
  229. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +35 -25
  230. package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +4 -3
  231. package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +37 -10
  232. package/docs/reference/decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md +285 -0
  233. 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
  234. package/docs/reference/decisions.md +6 -4
  235. package/docs/reference/glossary.md +96 -42
  236. package/docs/reference/hooks.md +1077 -0
  237. package/docs/reference/http-api.md +976 -149
  238. package/docs/why.md +2 -1
  239. package/package.json +10 -3
  240. package/src/actor.ts +36 -10
  241. package/src/agent.ts +50 -0
  242. package/src/assembly-checks.ts +530 -4
  243. package/src/callers.ts +245 -0
  244. package/src/chart.ts +52 -7
  245. package/src/config.ts +550 -1
  246. package/src/conversations.ts +453 -0
  247. package/src/custodian.ts +8 -2
  248. package/src/customize.ts +143 -4
  249. package/src/dispatch-effect.ts +142 -26
  250. package/src/durability.ts +2 -0
  251. package/src/duration.ts +20 -0
  252. package/src/effect-checks.ts +2 -1
  253. package/src/effect.ts +60 -7
  254. package/src/effector-wire.ts +272 -27
  255. package/src/gate-brand.ts +16 -0
  256. package/src/gate.ts +634 -48
  257. package/src/harness-client.ts +10 -4
  258. package/src/held-secrets.ts +72 -23
  259. package/src/holds.ts +2 -2
  260. package/src/hook-kinds/discord.ts +92 -0
  261. package/src/hook-kinds/door.ts +107 -0
  262. package/src/hook-kinds/github.ts +89 -0
  263. package/src/hook-kinds/gitlab.ts +72 -0
  264. package/src/hook-kinds/hook.ts +7 -0
  265. package/src/hook-kinds/index.ts +465 -0
  266. package/src/hook-kinds/slack.ts +151 -0
  267. package/src/hook-kinds/webhook.ts +113 -0
  268. package/src/hooks.ts +506 -0
  269. package/src/http-effect.ts +76 -38
  270. package/src/http.ts +889 -94
  271. package/src/index.ts +16 -5
  272. package/src/instance.ts +196 -26
  273. package/src/internal.ts +22 -3
  274. package/src/machine-doc.ts +26 -1
  275. package/src/mcp-effect.ts +424 -0
  276. package/src/mount-digest.ts +41 -7
  277. package/src/names.ts +64 -2
  278. package/src/notices.ts +138 -10
  279. package/src/open-body.ts +25 -11
  280. package/src/open-effect.ts +17 -4
  281. package/src/open.ts +4 -2
  282. package/src/parts.ts +87 -9
  283. package/src/party.ts +2 -1
  284. package/src/pick.ts +2 -2
  285. package/src/pool.ts +1142 -163
  286. package/src/registration.ts +203 -13
  287. package/src/run-host.ts +1509 -171
  288. package/src/run-list.ts +191 -0
  289. package/src/sandbox-kube.ts +53 -5
  290. package/src/secret-placement.ts +239 -33
  291. package/src/server.ts +53 -4
  292. package/src/setup.ts +72 -32
  293. package/src/snapshot-store.ts +565 -48
  294. package/src/steer.ts +111 -0
  295. package/src/stub-harness.ts +6 -2
  296. package/src/tokens.ts +263 -22
  297. package/src/wire.ts +17 -0
  298. package/src/workspace.ts +50 -6
  299. package/src/worktree.ts +3 -3
  300. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +0 -61
  301. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +0 -65
@@ -84,7 +84,7 @@ jr2 run release --input '{"version":"1.4.0"}'
84
84
 
85
85
  ```text
86
86
  → context <context> / namespace <namespace>
87
- run 2319932e-02c1-4f9d-98db-3c5a410395da (release) — attached
87
+ run 0c948d65-fd6f-453a-87fa-71ec00c7fe68 (release) — attached
88
88
  → active "review"
89
89
  ```
90
90
 
@@ -96,12 +96,13 @@ next steps in a second terminal. [Start a run](start-a-run.md) gives the other w
96
96
  Run [`jr2 status`](../reference/cli.md#status) with the run id:
97
97
 
98
98
  ```sh
99
- jr2 status 2319
99
+ jr2 status 0c94
100
100
  ```
101
101
 
102
102
  ```text
103
103
  → context <context> / namespace <namespace>
104
- {"runId":"2319932e-02c1-4f9d-98db-3c5a410395da","workflow":"release","instanceId":"c898d8c7-e48b-4a56-8101-7148905b6aed","status":"active","value":"review","context":{"version":"1.4.0"},"children":[],"waiting":[],"held":[],"assembly":"83858e28b054","gates":[{"gate":"review","path":["review"],"accepts":[{"name":"approve","description":"Release this version.","input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false}},{"name":"reject","description":"Do not release this version. Say why.","input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"reason":{"type":"string","minLength":1}},"required":["reason"],"additionalProperties":false}}],"acceptsNow":["approve","reject"],"meta":{"version":"1.4.0"}}]}
104
+ {"runId":"0c948d65-fd6f-453a-87fa-71ec00c7fe68","workflow":"release","instanceId":"6db54032-0b87-4005-9583-049f7335b75f","startedAt":"2026-11-06T19:15:39.018Z","startedBy":{"kind":"caller","via":"token","name":"instance","onBehalfOf":"kubernetes-admin"},"status":"active","value":"review","context":{"version":"1.4.0"},"children":[],"waiting":[],"held":[],"assembly":"19d0bf0cbe10","gates":[{"gate":"review","path":["review"],"accepts":[{"name":"approve","description":"Release this version.","input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false}},{"name":"reject","description":"Do not release this version. Say why.","input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"reason":{"type":"string","minLength":1}},"required":["reason"],"additionalProperties":false}}],"acceptsNow":["approve","reject"],"meta":{"version":"1.4.0"}}]}
105
+ started by: token/instance on behalf of kubernetes-admin
105
106
  ```
106
107
 
107
108
  `gates` holds each open Gate of the run, as a [`GateView`](../reference/http-api.md#type-gateview):
@@ -114,18 +115,37 @@ jr2 status 2319
114
115
 
115
116
  A run that has no open Gate has `"gates":[]`.
116
117
 
118
+ To find the open Gates of all the live runs, run [`jr2 gates`](../reference/cli.md#gates). It prints the Gates that you
119
+ can answer, the oldest first. Give `--workflow <name>` to see only the Gates of the runs of one Workflow:
120
+
121
+ ```sh
122
+ jr2 gates --workflow release
123
+ ```
124
+
125
+ ```text
126
+ → context <context> / namespace <namespace>
127
+ [{"runId":"0c948d65-fd6f-453a-87fa-71ec00c7fe68","workflow":"release","gate":"review","path":["review"],"accepts":[{"name":"approve","description":"Release this version.","input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false}},{"name":"reject","description":"Do not release this version. Say why.","input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"reason":{"type":"string","minLength":1}},"required":["reason"],"additionalProperties":false}}],"acceptsNow":["approve","reject"],"meta":{"version":"1.4.0"},"openedAt":"2026-11-06T19:15:39.019Z"}]
128
+ ```
129
+
130
+ Each entry has the fields of a Gate in `gates`, and also `runId`, `workflow` and `openedAt`, the time when the Gate
131
+ opened. When the Gate hears a [Hook](../reference/glossary.md#hook), `hook` gives the
132
+ [Hook kinds](../reference/glossary.md#hook-kind). An entry never holds the `context` of the run. A Gate that takes no
133
+ event from a [Caller](../reference/glossary.md#caller), such as the `source` Gate of a
134
+ [Pool](../reference/glossary.md#pool) that hears only Hooks, is not in the list. `jr2 gates` reads
135
+ [`GET /gates`](../reference/http-api.md#get-gates), and it needs the `answer` scope.
136
+
117
137
  ## Answer from the CLI
118
138
 
119
139
  Run [`jr2 send`](../reference/cli.md#send). Give the Gate in `--gate`, the event in `--event`, and the input of the
120
140
  event as a JSON object in `--input`:
121
141
 
122
142
  ```sh
123
- jr2 send 2319 --gate review --event reject --input '{"reason":"The changelog is not ready."}'
143
+ jr2 send 0c94 --gate review --event reject --input '{"reason":"The changelog is not ready."}'
124
144
  ```
125
145
 
126
146
  ```text
127
147
  → context <context> / namespace <namespace>
128
- delivered reject to gate "review" on 2319932e-02c1-4f9d-98db-3c5a410395da
148
+ delivered reject to gate "review" on 0c948d65-fd6f-453a-87fa-71ec00c7fe68
129
149
  ```
130
150
 
131
151
  `jr2 send` prints `delivered` only when the run took the event. In the first terminal, the run ends. A `reject` ends the
@@ -136,7 +156,7 @@ ends `failed`. Its output is the [Failure](../reference/glossary.md#failure), an
136
156
  → active "rejected"
137
157
  → failed "rejected"
138
158
  failed: The changelog is not ready.
139
- {"runId":"2319932e-02c1-4f9d-98db-3c5a410395da","workflow":"release","instanceId":"c898d8c7-e48b-4a56-8101-7148905b6aed","status":"failed","value":"rejected","context":{"version":"1.4.0","reason":"The changelog is not ready."},"children":[],"waiting":[],"held":[],"assembly":"83858e28b054","output":{"reason":"The changelog is not ready."}}
159
+ {"runId":"0c948d65-fd6f-453a-87fa-71ec00c7fe68","workflow":"release","instanceId":"6db54032-0b87-4005-9583-049f7335b75f","startedAt":"2026-11-06T19:15:39.018Z","startedBy":{"kind":"caller","via":"token","name":"instance","onBehalfOf":"kubernetes-admin"},"status":"failed","value":"rejected","context":{"version":"1.4.0","reason":"The changelog is not ready."},"children":[],"waiting":[],"held":[],"answers":[{"gate":"review","type":"reject","at":"2026-11-06T19:15:52.901Z","answeredBy":{"via":"token","name":"instance","onBehalfOf":"kubernetes-admin"}}],"assembly":"19d0bf0cbe10","output":{"reason":"The changelog is not ready."}}
140
160
  ```
141
161
 
142
162
  The run ended, so its Gate closed. Each example below answers a run that waits. Before each example, start a new run in
@@ -154,24 +174,25 @@ jr2 send <run id> --gate review --event approve
154
174
 
155
175
  ```text
156
176
  → context <context> / namespace <namespace>
157
- delivered approve to gate "review" on c099e2dc-6dd2-45da-9358-0723c8b83c68
177
+ delivered approve to gate "review" on 342fef91-53a1-4640-87bf-6fb4395beebf
158
178
  ```
159
179
 
160
180
  `jr2 send` prints the full run id, also when you give a prefix.
161
181
 
162
182
  ## Answer from the Console
163
183
 
164
- You need the Instance token to answer a Gate from the Console. [Open the Console](open-the-console.md) tells how to open
165
- it and how to get the token.
184
+ You need a credential with the `answer` scope for the Workflow, such as the Instance token, to answer a Gate from the
185
+ Console. [Open the Console](open-the-console.md) tells how to open it, how to get the token and how to log in.
166
186
 
167
187
  1. Open the Console.
168
- 2. Type the Instance token in the `instance token` box in the nav.
188
+ 2. Type the token in the `Caller token` box in the nav, or click `log in`.
169
189
  3. Push Enter. The badge beside the box shows `…`, then `live`.
170
190
  4. Click the `Attention` tab in the drawer on the right. It shows a card for each open Gate of the Workflow that you
171
- selected. To see the Gates of all the Workflows, select `all workflows`. When you select no Workflow, it shows them
172
- all.
173
- 5. Find the card. Its top line shows `⚑` and the id of the Gate at the left. At the right, it shows the Workflow and the
174
- first 8 characters of the run id. Below it, the card shows the `meta` of the Gate.
191
+ selected and that your credential can answer, the oldest first. To see the Gates of all the Workflows, select
192
+ `all workflows`. When you select no Workflow, it shows them all. The Console reads the cards from
193
+ [`GET /gates`](../reference/http-api.md#get-gates).
194
+ 5. Find the card. Its top line shows `⚑` and the id of the Gate at the left. At the right, it shows the Workflow, the
195
+ first 8 characters of the run id and `open since <time>`. Below it, the card shows the `meta` of the Gate.
175
196
  6. The card has a form for each event that the Gate accepts. Fill in the form of the event that you send. A field that
176
197
  has `*` after its name is necessary.
177
198
  7. Click `send` in that form.
@@ -184,9 +205,13 @@ The `⚑` badge in the nav gives the number of open Gates in all the Workflows.
184
205
  ## Answer from a webhook or CI
185
206
 
186
207
  A webhook handler or a CI job answers a Gate with
187
- [`POST /runs/:runId/gates/:gate/events`](../reference/http-api.md#post-runs-runid-gates-gate-events) and the Instance
188
- token. jr2 has no webhook receiver of its own. Your handler or job reads its own event, finds the run and the Gate, and
189
- sends the call. The body is the event: `type` is the name of the event, and the other properties are its input.
208
+ [`POST /runs/:runId/gates/:gate/events`](../reference/http-api.md#post-runs-runid-gates-gate-events) and the token of a
209
+ named Caller with the `answer` scope for the Workflow. A CI job has no access to the cluster, so it cannot read the
210
+ Instance token, and it must not have it: the Instance token can do everything.
211
+ [Give a Caller a scope](give-a-caller-a-scope.md) makes the Caller token. A service that a Hook kind covers, such as a
212
+ forge, sends its webhook to a Hook: see [Answer a Gate from a forge](answer-a-gate-from-a-forge.md). Any other handler
213
+ or CI job uses the Gate route. It reads its own event, finds the run and the Gate, and sends the call. The body is the
214
+ event: `type` is the name of the event, and the other properties are its input.
190
215
 
191
216
  `jr2 up` makes the Service `jr2-orchestrator` on port 4000, with no address outside the cluster. Thus a caller outside
192
217
  the cluster uses a port forward, or an address that you make for the Service.
@@ -197,7 +222,8 @@ In a new terminal, forward a port to the Orchestrator. Keep this command open:
197
222
  kubectl -n <namespace> port-forward svc/jr2-orchestrator 4000:4000
198
223
  ```
199
224
 
200
- In a different terminal, get the Instance token into a variable:
225
+ In a different terminal, put a token into a variable. A CI job uses its Caller token. To try the route from your own
226
+ terminal, you can use the Instance token:
201
227
 
202
228
  ```sh
203
229
  TOKEN=$(kubectl -n <namespace> get secret jr2-instance -o jsonpath='{.data.JR2_INSTANCE_TOKEN}' | base64 -d)
@@ -217,9 +243,9 @@ curl -s -X POST http://localhost:4000/runs/<run id>/gates/review/events \
217
243
  ```
218
244
 
219
245
  A webhook handler often knows a fact, such as a version, but not the run id. It can find the run from the `meta` of the
220
- Gate. [`GET /runs`](../reference/http-api.md#get-runs) gives the live runs, and
221
- [`GET /runs/:runId`](../reference/http-api.md#get-runs-runid) gives the open Gates of one run. This script approves the
222
- `release` run that waits for one version. It needs `curl` and `jq`. Save it as `approve-release.sh`:
246
+ Gate. [`GET /gates`](../reference/http-api.md#get-gates) gives the open Gates of all the live runs that the token can
247
+ answer, each with its run id and its `meta`. It needs only the `answer` scope. This script approves the `release` run
248
+ that waits for one version. It needs `curl` and `jq`. Save it as `approve-release.sh`:
223
249
 
224
250
  ```sh
225
251
  #!/bin/sh
@@ -229,15 +255,13 @@ set -eu
229
255
 
230
256
  auth="Authorization: Bearer $JR2_TOKEN"
231
257
 
232
- # The live runs of the release Workflow.
233
- for run in $(curl -fsS -H "$auth" "$JR2_URL/runs" | jq -r '.[] | select(.workflow == "release") | .runId'); do
234
- # The open Gates of the run, with their meta.
235
- version=$(curl -fsS -H "$auth" "$JR2_URL/runs/$run" | jq -r '.gates[] | select(.gate == "review") | .meta.version')
236
- if [ "$version" = "$VERSION" ]; then
237
- curl -fsS -X POST -H "$auth" -H "Content-Type: application/json" \
238
- -d '{"type":"approve"}' "$JR2_URL/runs/$run/gates/review/events"
239
- echo
240
- fi
258
+ # The open Gates of the live release runs that this token can answer, with their meta.
259
+ runs=$(curl -fsS -H "$auth" "$JR2_URL/gates?workflow=release" |
260
+ jq -r --arg v "$VERSION" '.gates[] | select(.gate == "review" and .meta.version == $v) | .runId')
261
+ for run in $runs; do
262
+ curl -fsS -X POST -H "$auth" -H "Content-Type: application/json" \
263
+ -d '{"type":"approve"}' "$JR2_URL/runs/$run/gates/review/events"
264
+ echo
241
265
  done
242
266
  ```
243
267
 
@@ -258,15 +282,15 @@ JR2_URL=http://localhost:4000 JR2_TOKEN=$TOKEN jr2 send <run id> --gate review -
258
282
 
259
283
  ```text
260
284
  → http://localhost:4000
261
- delivered approve to gate "review" on c44c6e49-452d-4150-8ce4-4d036363d2a0
285
+ delivered approve to gate "review" on cad78069-0d8d-4c04-9cf4-3f771693274f
262
286
  ```
263
287
 
264
288
  ## Rules
265
289
 
266
290
  - The Orchestrator refuses a Gate that is not open on the run, with the status code `404`:
267
291
  `no open gate "<gate>" on run "<run id>" (open: <gates>)`. The part `(open: <gates>)` shows only when the run has an
268
- open Gate. When an event takes the run out of the state of the Gate, the Gate closes. Thus a second answer gets this
269
- refusal.
292
+ open Gate that the Caller can answer. When an event takes the run out of the state of the Gate, the Gate closes. Thus
293
+ a second answer gets this refusal.
270
294
  - The Orchestrator refuses an event that the Gate does not accept, with the status code `400`:
271
295
  `"<gate>" does not accept "<event>" (accepts: <events>)`
272
296
  - The Orchestrator refuses an input that does not agree with the schema of the event, with the status code `400`:
@@ -278,10 +302,23 @@ delivered approve to gate "review" on c44c6e49-452d-4150-8ce4-4d036363d2a0
278
302
  `event body must carry a string "type" (one of the gate's accepted names)`
279
303
  - The Orchestrator refuses a body that holds `sender`, with the status code `400`:
280
304
  `` `sender` is written by the Orchestrator on every delivered event; a caller cannot send it ``
281
- - The Orchestrator refuses an event without the Instance token, with the status code `401`: `unauthorized`.
305
+ - The Orchestrator refuses an event with no valid credential, with the status code `401`: `unauthorized`. A credential
306
+ is the Instance token, or that of a [Caller](../reference/glossary.md#caller) with the `answer` scope.
307
+ [Give a Caller a scope](give-a-caller-a-scope.md) makes one.
282
308
  - The Orchestrator refuses an event with the token of a [Sandbox](../reference/glossary.md#sandbox), with the status
283
- code `403`: `a Sandbox token cannot deliver to a gate — gates are not on the Agent's surface`. Thus an Agent cannot
284
- answer a Gate.
309
+ code `403`:
310
+ `this route needs a Caller, and a Sandbox token is not one: no Agent reaches run state, run control or a Gate`. Thus
311
+ an Agent cannot answer a Gate.
312
+ - The Orchestrator refuses an event from a Caller that has the `answer` scope for no Workflow, with the status code
313
+ `403`, before it reads the run: `the Caller <via>/<name> has no "answer" scope for any Workflow (ADR-0078)`. The body
314
+ also holds `scope`.
315
+ - The Orchestrator refuses an event from a Caller that does not have the `answer` scope for the Workflow of the run,
316
+ with the status code `403`: `the Caller <via>/<name> needs the "answer" scope for Workflow "<workflow>" (ADR-0078)`
317
+ - The Orchestrator refuses an event to an open Gate from a Caller whose `answer` scope names other Gates of the
318
+ Workflow, with the status code `403`:
319
+ `the Caller <via>/<name> needs the "answer" scope for Gate "<name>" of Workflow "<workflow>" (ADR-0078)`. `<name>` is
320
+ the name of the Gate, its invoke id. The body also holds `scope`, `workflow` and `gate`.
321
+ [Give a Caller a scope](give-a-caller-a-scope.md) tells how a scope names single Gates.
285
322
  - `jr2 send` prints each refusal of the Orchestrator as `error: <refusal>`, and exits with `1`. After a `404` refusal,
286
323
  it also prints the version of the Instance in one more line:
287
324
  ``instance: <version> (<hash>) — if it predates this CLI, `jr2 up` converges the cluster to this kit``. `<hash>` is
@@ -298,7 +335,7 @@ delivered approve to gate "review" on c44c6e49-452d-4150-8ce4-4d036363d2a0
298
335
  - [ADR-0011](../reference/decisions/0011-workflow-defined-events.md): a Gate, its events, and its `meta`.
299
336
  - [ADR-0013](../reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md): why an
300
337
  Agent cannot answer a Gate, and why a refused event is not delivered.
301
- - [ADR-0032](../reference/decisions/0032-the-console-unlocks-with-the-instance-token.md): the Instance token in the
338
+ - [ADR-0032](../reference/decisions/0032-the-console-unlocks-with-a-callers-credential.md): a Caller's credential in the
302
339
  Console.
303
340
  - [Start a run](start-a-run.md)
304
341
  - [Watch a run](watch-a-run.md)
@@ -137,7 +137,9 @@ others. A Machine that names its slots, such as `repos: { app: open }`, takes on
137
137
 
138
138
  `agents` takes the fields of the definition of an Agent, by slot. Each field goes over the field that the author wrote.
139
139
  Thus `{ model }` changes only the model, and the coder keeps the instructions of `task`. You can also change other
140
- fields, such as `thinkingLevel` or `instructions`.
140
+ fields, such as `thinkingLevel` or `instructions`. `{ mcp }` replaces the whole list of the MCP servers of the Agent.
141
+ `jr2 up` refuses a server name that the `mcp` block of `jr2.config.ts` does not declare, and prints the line with `mcp`
142
+ (see [Give an Agent MCP tools](give-an-agent-mcp-tools.md#bind-the-servers-of-a-packaged-machine)).
141
143
  [`AgentDefinition`](../reference/authoring-surface/type-agentdefinition.md) gives the fields.
142
144
 
143
145
  ## Bind the parts of a Machine that you compose
@@ -164,19 +166,29 @@ When `task` is a child of your Machine, give it `branch` in its input. Only the
164
166
 
165
167
  ## Bind the Open parts of an Effect
166
168
 
167
- A packaged Machine can leave the url of an `http` [Effect](../reference/glossary.md#effect), the argv of a `command`, or
168
- the name of a secret Open. Bind them with `effects`, by the slot of the Effect:
169
- `customize(machine, { effects: { notify: { url } } })`. One object binds all the Open parts of one Effect. You bind each
170
- part one time, to a value. No run chooses it. [`EffectBinding`](../reference/authoring-surface/type-effectbinding.md)
171
- gives the values, and [Run a command or call a URL with an Effect](run-a-command-or-call-a-url.md) shows an Effect.
169
+ A packaged Machine can leave the url of an `http` [Effect](../reference/glossary.md#effect), the argv of a `command`,
170
+ the name of a secret, or the server and the tool of an `mcp` Effect Open. Bind them with `effects`, by the slot of the
171
+ Effect: `customize(machine, { effects: { notify: { url } } })`, or
172
+ `customize(machine, { effects: { post: { server: "slack-acme", tool: "post_message" } } })`. One object binds all the
173
+ Open parts of one Effect. You bind each part one time, to a value. No run chooses it.
174
+ [`EffectBinding`](../reference/authoring-surface/type-effectbinding.md) gives the values, and
175
+ [Run a command or call a URL with an Effect](run-a-command-or-call-a-url.md) shows an Effect. A bound server is a name
176
+ of the `mcp` block. Its secret comes from that block, so an `mcp` Effect has no Open secret.
177
+
178
+ ## Bind the look of a Gate
179
+
180
+ A packaged Machine can leave the look of a Gate slot Open: `look: open`. Bind it with `gates`, by the slot of the Gate:
181
+ `customize(machine, { gates: { review: { look } } })`. The same key adds, replaces or removes a handler of the Gate, by
182
+ Hook kind and event key, where `null` removes one. `jr2 up` does not refuse an Open look. It warns, because a person can
183
+ still answer the Gate with `jr2 send`. [Answer a Gate from a forge](answer-a-gate-from-a-forge.md) shows a Gate slot.
172
184
 
173
185
  ## Rules
174
186
 
175
187
  - `jr2 up` refuses a registered Machine that leaves a part Open. It changes nothing in the cluster:
176
188
  `refusing: workflow "<name>" leaves <part> open — bind it where the Machine is registered: export const machine = customize(<import>, <binding>) (<adr>)`.
177
189
  `<part>` is one of `its Repo Slots`, `Repo Slot "<slot>"`, `Agent "<slot>"`, or `the <parts> of Effect "<slot>"`,
178
- where `<parts>` is such as `url` or `url and secret`. For a child Machine, `(on the Machine composed as "<slot>")`
179
- comes after `open`.
190
+ where `<parts>` is such as `url`, `url and secret`, `server` or `server and tool`. For a child Machine,
191
+ `(on the Machine composed as "<slot>")` comes after `open`.
180
192
  - `jr2 up` refuses an Open part on a Machine that a state writes in its `invoke.src`, because no `customize()` gets to
181
193
  it. Put that Machine under `actors`:
182
194
  `refusing: workflow "<name>" leaves <part> open on a Machine invoked inline — no customize() reaches an actor without a slot key; declare that Machine under setup({ actors }) and bind it there (ADR-0049, <adr>)`
@@ -189,6 +201,7 @@ gives the values, and [Run a command or call a URL with an Effect](run-a-command
189
201
  - `customize(): machine "<id>" composes no Machine under "<slot>" — the Machines it composes are: <slots> (ADR-0049).`
190
202
  - `customize(): machine "<id>" declares no Repo Slot "<slot>" — its slots are: <slots> (ADR-0051)`
191
203
  - `customize(): machine "<id>" carries no Effect slot "<slot>" — its Effects are: <slots> (ADR-0067). Only declared parts can be bound.`
204
+ - `customize(): machine "<id>" carries no Gate slot "<slot>" — its Gate slots are: <slots> (ADR-0011). Only declared parts can be bound.`
192
205
  - `customize()` refuses an Open map that you bind with no slot:
193
206
  ``customize(): machine "<id>" declares its Repo Slots open as a map — name at least one: `repos: { app: "https://…" }` (ADR-0051).``
194
207
  - `customize()` refuses a Repo Slot key that is not a directory name. The key becomes `/work/<slot>`:
@@ -201,6 +214,9 @@ gives the values, and [Run a command or call a URL with an Effect](run-a-command
201
214
  - `customize()` refuses `open` or a function as the value of an Effect's part:
202
215
  ``customize(): the <kind> Effect "<slot>"'s <part> is bound once, to a value — not `open` again: no run chooses an Effect's <part> (ADR-0054)``.
203
216
  For a function, the text says `a function of the run` in place of `` `open` again ``.
217
+ - `jr2 up` refuses an Agent that names an MCP server that the `mcp` block does not declare:
218
+ `refusing: workflow "<name>": Agent "<slot>" names the MCP server "<server>", which the mcp block declares "<names>" — bind the Agent to a declared server where the Machine is registered: export const machine = customize(<import>, { agents: { <slot>: { mcp: [<entries>] } } }); or declare it in jr2.config.ts: mcp: { "<server>": { url: "https://<host>/mcp", secret: "<SECRET>" } } (ADR-0079)`.
219
+ For a child Machine, the line names the child, and `customize()` goes through `actors`.
204
220
  - An Agent does not start a [Turn](../reference/glossary.md#turn) while its model is Open:
205
221
  ``agent "<slot>" has an Open model — nobody bound it; bind it where the Machine is registered (`customize(<machine>, { agents: { <slot>: { model: "<provider>/<model>" } } })`), because a packaged Machine cannot pick a model on your behalf (ADR-0054)``.
206
222
  `jr2 up` refuses this case first.