@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
@@ -0,0 +1,205 @@
1
+ ---
2
+ title: Callers and Hooks
3
+ description: Who acts on an Instance from outside its Machines, what each one may do, and what a Hook does.
4
+ ---
5
+
6
+ # Callers and Hooks
7
+
8
+ A [Caller](../reference/glossary.md#caller) is the holder of a credential that acts on an
9
+ [Instance](../reference/glossary.md#instance) from outside its [Machines](../reference/glossary.md#machine). A person, a
10
+ CI job, a bridge to a chat service and an outside service are Callers. A Caller can start a run, read it, answer a
11
+ [Gate](../reference/glossary.md#gate), send a [Steering message](../reference/glossary.md#steering-message) or cancel a
12
+ run, for the [Workflows](../reference/glossary.md#workflow) that its scopes name. A
13
+ [Hook](../reference/glossary.md#hook) is the door of an Instance for one outside service, such as a forge. It is a
14
+ Caller that can only answer a Gate or bring items to a Source. Its [Hook kind](../reference/glossary.md#hook-kind) is
15
+ the protocol of the service.
16
+
17
+ ## Why it exists
18
+
19
+ One Instance token can do everything on each run. A CI job that starts runs, a bridge that answers Gates, and a teammate
20
+ with a browser need less than that. If each one holds the Instance token, each one can do each act on each run, and the
21
+ run cannot say who acted. Thus jr2 knows each Caller by its credential, gives it only its scopes, and records it on each
22
+ act.
23
+
24
+ An outside service sends a delivery, such as a review on a forge. It does not send a bearer token, and each service has
25
+ its own signature, its own body and its own handshake. A Hook checks that a delivery comes from its service, and decides
26
+ nothing. The Gate or the Source that hears it decides what the delivery means for its run.
27
+
28
+ ## How a Caller is known
29
+
30
+ The Orchestrator has an ordered list of authenticators. Each one looks at the credential of a request. It gives a
31
+ Caller, or it says that the credential is not its credential. The first one that claims the credential decides. When it
32
+ finds the credential bad, the answer is 401, and the next authenticator does not look at it. A credential that no
33
+ authenticator claims also gets 401.
34
+
35
+ | Authenticator | The credential | The Caller |
36
+ | ----------------- | --------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
37
+ | Instance token | The token in the `jr2-instance` Secret. The CLI reads it over the Kubernetes API. | `token/instance`, with each scope for each Workflow |
38
+ | Named token | A value that starts with `jr2c_`, for a name in [`callers.tokens`](../reference/config.md#callers-tokens) | `token/<name>`, with the scopes of its entry |
39
+ | JWT | A JWT of an issuer in [`callers.issuers`](../reference/config.md#callers-issuers) | `jwt/<sub>`, with the scopes that its groups give |
40
+ | A Hook's verifier | A delivery at `POST /hooks/<name>`, with no bearer token | `hook/<name>`, with `answer` only |
41
+
42
+ The tokens of a Sandbox and the publish token are not Callers, and no authenticator makes a Caller from them. Thus an
43
+ Agent cannot answer a Gate. [Secrets and trust](secrets-and-trust.md#the-tokens) tells where each token is kept.
44
+
45
+ For a JWT, jr2 checks the signature with the published keys of the issuer, and the issuer, the audience and the expiry.
46
+ The name of the Caller is the `sub` claim, and the name that jr2 shows is the `email` claim. The `groups` map of the
47
+ issuer entry gives scopes for the values of a claim, such as the groups of a person. A JWT that gets no scope from the
48
+ map can do nothing. An issuer with `login: true` also lets a person log in to the
49
+ [Console](../reference/glossary.md#console) ([Open the Console](../guides/open-the-console.md#log-in)).
50
+
51
+ [`jr2 whoami`](../reference/cli.md#whoami) and [`GET /whoami`](../reference/http-api.md#get-whoami) show the Caller of a
52
+ credential, with its scopes. Use them to see what a credential can do before a request fails.
53
+
54
+ ## Scopes
55
+
56
+ A scope is one act of a Caller. Its value is `"all"`, or a list of the names of the Workflows that it opens:
57
+
58
+ | Scope | The act |
59
+ | -------- | ----------------------------------------------------------------------------------------- |
60
+ | `start` | Start a run. |
61
+ | `read` | Read the full state of a run, follow its feed, list the runs, and read its conversations. |
62
+ | `answer` | Answer a Gate, and read the open Gates and their meta. |
63
+ | `cancel` | Cancel a run. |
64
+ | `steer` | Send a Steering message to an Agent of a run. |
65
+
66
+ For a route that addresses a run, jr2 checks the scope against the Workflow of that run. The name of a Chart wrapper
67
+ also opens each named Chart of that wrapper. A request without the scope gets 403, which names the scope and the
68
+ Workflow. [`GET /runs`](../reference/http-api.md#get-runs) shows only the runs of the Workflows that the Caller can
69
+ read. A few routes, such as a runtime Chart and the log of a Hook, need the Instance token: no scope opens them. The
70
+ [HTTP API](../reference/http-api.md#band-guarded) gives the scope of each route.
71
+
72
+ The `answer` scope can also name single Gates of a Workflow, with an item `{ workflow, gates }` beside the names of
73
+ Workflows. The name of a Gate is its invoke id. A Caller with such an item answers only those Gates of that Workflow,
74
+ and it sees only those Gates, in [`GET /gates`](../reference/http-api.md#get-gates) and in `GET /runs/:runId`. A gate
75
+ POST to another open Gate of that Workflow gets 403, which names the scope, the Workflow and the Gate. `jr2 up` refuses
76
+ a Gate name that the Machines of the Workflow do not have. The other scopes, and the `may.answer` of a Hook, name only
77
+ Workflows.
78
+
79
+ A scope does not limit what a run does. A run that a Caller starts can start a run of a different Workflow with a
80
+ [`dispatch`](../reference/authoring-surface/dispatch.md) Effect. The Machine is the limit of what its run does.
81
+
82
+ ## On behalf of
83
+
84
+ A Caller can say that it acts on behalf of a different party, in the header `jr2-on-behalf-of`. The CLI sends the name
85
+ of your Kubernetes user. A bridge can send the name of the person in the chat service. jr2 records this name and shows
86
+ it, but it does not check it. The one exception is a Hook: the name comes from the body of the delivery, and the
87
+ signature of the service covers the body.
88
+
89
+ ## The record of each act
90
+
91
+ The run records the Caller of each act on a guarded route: its `via`, its `name`, the name that jr2 shows, and
92
+ `onBehalfOf`. It does not record the scopes.
93
+
94
+ - `startedBy`: the Caller that started the run. A run that a local `dispatch` started records that run and the slot. A
95
+ run that jr2 started, such as the run of a standing Pool, records `jr2` and why: `boot`, `publish`, `hand-over` or
96
+ `resume`.
97
+ - `cancelledBy`: the Caller that cancelled the run.
98
+ - `answers`: the last 100 answers to the Gates of the run, each with the Caller that answered.
99
+
100
+ The [Sender](../reference/glossary.md#sender) of an event that a Gate delivers also holds the Caller, and `checked`
101
+ names the fields of it that jr2 checked. [`jr2 status`](../reference/cli.md#status) follows `startedBy` from run to run,
102
+ to the first Caller, and prints it as `started by:`. The open routes do not show these records.
103
+
104
+ ## How a Hook behaves
105
+
106
+ The `hooks` block of `jr2.config.ts` declares each Hook by its name, with its kind. The address of the Hook is
107
+ `POST /hooks/<name>`. The kind makes the Hook from two things: the credential, and `may.answer`, the Workflows whose
108
+ Gates the Hook can answer. The default is every Workflow. A Hook never starts a run. To bring items to a
109
+ [Pool](../reference/glossary.md#pool), it answers the `source` Gate of the Pool, so `may.answer` must name the Workflow
110
+ of the Pool.
111
+
112
+ **One delivery at a Hook**
113
+
114
+ - **Service**: a forge or a chat service
115
+ - **Door**: the Hook's address in the Orchestrator
116
+ - **Instance Effector**: holds a mac secret
117
+ - **Kind**: the protocol of the service
118
+ - **Handlers**: of the open Gates and Sources
119
+ - Service → Door: a delivery
120
+ - Door → Instance Effector: the signed bytes, for mac only
121
+ - Instance Effector → Door: match, or no match
122
+ - Door → Kind: the request, without a token header
123
+ - Kind → Door: a handshake, or the event key, the id and the payload
124
+ - Door → Handlers: the parsed payload
125
+ - Door → Service: the outcome, or a text for a person
126
+
127
+ The door does the same steps for each kind:
128
+
129
+ 1. **It verifies the delivery.** The kind tells what the service signed. The door runs one of three verifiers:
130
+ - `mac`: a shared secret signs the bytes, such as on GitHub and Slack. The
131
+ [Instance Effector](../reference/glossary.md#instance-effector) holds the secret, and it answers only "match" or
132
+ "no match". The Orchestrator never holds the secret, because a secret that verifies a delivery can also make one.
133
+ - `token`: the service sends a shared token in a header, such as on GitLab. The Orchestrator keeps only the SHA-256
134
+ digest of the token, and removes the header before the kind reads the request.
135
+ - `ed25519`: a key pair signs the delivery, such as on Discord. The config holds the public key, which cannot sign.
136
+
137
+ When the service signs a time, the door refuses a time that is more than 300 seconds from its clock. A delivery that
138
+ fails gets 401, and nothing more occurs.
139
+
140
+ Before the verifier, the door refuses a body that is larger than 2 MiB with 413. It reads no more of the body than
141
+ this limit.
142
+
143
+ 2. **The kind reads the delivery.** A handshake, such as a GitHub `ping`, gets the answer of the kind, and goes to no
144
+ Gate. Otherwise the kind gives the event key, the id of the delivery, `onBehalfOf` and the payload.
145
+ 3. **The schema of the event key parses the payload.** A payload that the schema refuses goes to no Gate.
146
+ 4. **The door looks for a repeat.** A delivery whose id or body the Hook got before goes to no Gate. The door does this
147
+ check after the parse, so a payload that the schema refused is parsed again when the service sends it again.
148
+ 5. **The delivery goes to each handler that hears it**: each open Gate and each Source, in each live run of a Workflow
149
+ that the Hook can answer, whose handlers name the kind and the event key. [Gates](gates.md#a-gate-that-hears-a-hook)
150
+ and [Pools and Sources](pools-and-sources.md#a-hook-reaches-a-source) tell what a handler gives.
151
+
152
+ The answer is the outcome of the delivery, as JSON: where it went, and what each Gate or Source did with it. For a chat
153
+ command or a click, the person who acted reads one text in the envelope of the kind
154
+ ([The answer that a person reads](gates.md#the-answer-that-a-person-reads)).
155
+
156
+ The log of the Hook keeps each delivery and its outcome for `runs.retain`, 30 days by default. The repeat check reads
157
+ it. Print it with [`jr2 hooks <name>`](../reference/cli.md#hooks).
158
+
159
+ ## Hook kinds
160
+
161
+ A Hook kind is code that `jr2.config.ts` imports. It tells what the service signed, how to read a delivery and its
162
+ payloads, the handshake of the service, and the envelope of a text that a person reads. A workflow module imports the
163
+ same kind for the types of its handlers. Thus a handler for an event key that the kind does not give does not compile.
164
+
165
+ - `@jr2/orchestrator/hooks` ships the kinds `github`, `gitlab`, `slack` and `discord`.
166
+ - [`webhook()`](../reference/hooks.md#webhook) makes a kind from data, for a service that sends JSON, signs only the
167
+ body, and has no handshake.
168
+ - [`defineHookKind()`](../reference/hooks.md#definehookkind) makes a kind for any other service, in a module of the
169
+ Instance or in a package of its own.
170
+
171
+ A kind never gets the credential: jr2 makes the constructor of the kind, so the credential goes from the config to the
172
+ door. A kind is pure and synchronous. It does no I/O, and it does not read a clock. The door does the verify, the time
173
+ window, the repeat check and the follow-up post. When a kind gives a follow-up post, such as the reply to a Slack click,
174
+ it declares the hosts of its posts, and `jr2 up` adds them to the targets of the Instance Effector.
175
+
176
+ ## Rules
177
+
178
+ - The first authenticator that claims a credential decides. A credential that it finds bad gets 401.
179
+ - A Sandbox token, a control token and the publish token are not Callers.
180
+ - A request without the scope for the Workflow gets 403. A JWT that gets no scope gets 403 on each route that needs a
181
+ scope.
182
+ - `onBehalfOf` is a claim. jr2 checks it only for a Hook, where the signature covers it.
183
+ - A named token's value is in a Secret of its own. The Orchestrator keeps only its SHA-256 digest.
184
+ - `cors` lists the origins of the browser apps on a different origin that can call the Orchestrator. The Console needs
185
+ no entry.
186
+ - A Hook can only answer a Gate or bring items to a Source, of the Workflows in its `may.answer`. It never starts a run.
187
+ - The door refuses a signed time that is more than 300 seconds from its clock. No config changes the window.
188
+ - Two kinds with one id fail the typecheck and `jr2 up`.
189
+
190
+ ## Read next
191
+
192
+ - [Give a Caller a scope](../guides/give-a-caller-a-scope.md)
193
+ - [Answer a Gate from a forge](../guides/answer-a-gate-from-a-forge.md)
194
+ - [Start a run for each alert](../guides/start-a-run-for-each-alert.md)
195
+ - [Write a Hook kind](../guides/write-a-hook-kind.md)
196
+ - [Open the Console](../guides/open-the-console.md)
197
+ - [Secrets and trust](secrets-and-trust.md), [Gates](gates.md) and [Pools and Sources](pools-and-sources.md)
198
+ - Reference: [`callers`](../reference/config.md#callers), [`hooks`](../reference/config.md#hooks),
199
+ [`cors`](../reference/config.md#cors), [`@jr2/orchestrator/hooks`](../reference/hooks.md),
200
+ [`jr2 whoami`](../reference/cli.md#whoami), [`jr2 hooks`](../reference/cli.md#hooks), and the
201
+ [HTTP API](../reference/http-api.md)
202
+ - [ADR-0078](../reference/decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md): the
203
+ Callers, the scopes, the Hooks and the Hook kinds
204
+ - [ADR-0014](../reference/decisions/0014-observation-is-open-run-state-is-not.md): which scope opens which route
205
+ - [ADR-0011](../reference/decisions/0011-workflow-defined-events.md): a Gate hears a Hook
@@ -30,9 +30,11 @@ it bad text.
30
30
  [Open](../reference/glossary.md#open) part that a run binds, not a composer. Its declaration has three parts:
31
31
 
32
32
  - **`lends`**: the parts of the Chart wrapper that a Chart can use, by their slot keys. These are Agents, Effects,
33
- Machines, guards, actions and delays. `gate: true` lends the [Gate](../reference/glossary.md#gate), so that a Chart
34
- can wait for a person. `fail: true` lends [`fail()`](../reference/authoring-surface/fail.md), so that a Chart can end
35
- its run as a [Failure](../reference/glossary.md#failure) with `invoke: { src: "fail", input: { reason } }`.
33
+ Machines, Gate slots (`gates`), guards, actions and delays. `gate: true` lends the
34
+ [Gate](../reference/glossary.md#gate), so that a Chart can wait for a person. A lent Gate slot hears a
35
+ [Hook](../reference/glossary.md#hook), and the door refuses a Chart whose `events` do not hold each event of the
36
+ slot's `delivers`. `fail: true` lends [`fail()`](../reference/authoring-surface/fail.md), so that a Chart can end its
37
+ run as a [Failure](../reference/glossary.md#failure) with `invoke: { src: "fail", input: { reason } }`.
36
38
  - **`allows`**: what the Agents that a Chart adds can have. `models` lists the models, and `workspace` lists the
37
39
  Workspace accesses. With no model, a Chart cannot add an Agent.
38
40
  - **`ceilings`**: the limits of one Chart: the Agent Turns, the wall clock, the transitions, the children at one time,
@@ -107,8 +109,8 @@ wrapper only. It comes from one of two places:
107
109
  - A **runtime Chart**: `PUT /workflows/<wrapper>/charts/<name>`, with the Instance token. The Orchestrator keeps it on
108
110
  its volume. `DELETE` on the same path removes it.
109
111
 
110
- [`jr2 runs`](../reference/cli.md#runs) lists each named Chart under its wrapper. When a publish removes a part that a
111
- runtime Chart uses, the Chart stays, and it is unrunnable until a publish gives the part back.
112
+ [`jr2 workflows`](../reference/cli.md#workflows) lists each named Chart under its wrapper. When a publish removes a part
113
+ that a runtime Chart uses, the Chart stays, and it is unrunnable until a publish gives the part back.
112
114
 
113
115
  ## A run keeps its Chart
114
116
 
@@ -250,7 +250,7 @@ show.
250
250
  | Key | What it changes |
251
251
  | ----------- | ------------------------------------------------------------------------------------- |
252
252
  | `agents` | The definition of an Agent, by slot, field by field. `{ model }` keeps the rest. |
253
- | `effects` | The Open url, argv or secret of an Effect, by slot. |
253
+ | `effects` | The Open url, argv, secret, MCP server or tool of an Effect, by slot. |
254
254
  | `actors` | A Machine that this Machine composes, by slot. The value has the same keys, one down. |
255
255
  | `repos` | The Repo Slots of the [`workspace()`](../reference/authoring-surface/workspace.md). |
256
256
  | `image` | The Sandbox Image of the `workspace()`. |
@@ -272,6 +272,7 @@ A Machine marks a part Open with [`open`](../reference/authoring-surface/open.md
272
272
  - a Repo Slot with no url, or the whole Repo Slot map (`repos: open`)
273
273
  - the model of an Agent (`agent({ model: open, … })`)
274
274
  - the url of an `http` Effect, the argv of a `command`, or the name of the secret of an Effect (`secret(open)`)
275
+ - the server or the tool of an `mcp` Effect (`mcp.call(open, open, { input })`)
275
276
 
276
277
  An Open part has no default. You bind it with `customize()`. Each Open part has two checks:
277
278
 
@@ -280,6 +281,14 @@ An Open part has no default. You bind it with `customize()`. Each Open part has
280
281
  - At run time, the part refuses too. An Agent does not start a [Turn](../reference/glossary.md#turn) with an Open model.
281
282
  An Effect does not run while it is Open.
282
283
 
284
+ One Open part only warns: the look of a Gate slot (`look: open`). `jr2 up` prints a warning for a look that nobody
285
+ bound, because a person can still answer the Gate with `jr2 send`.
286
+
287
+ The MCP servers of an Agent are not Open. A packaged Machine's Agent names the servers that it needs, such as
288
+ `slack-acme`. When the `mcp` block of your `jr2.config.ts` does not declare that name, `jr2 up` refuses the Workflow and
289
+ prints the `customize()` line that binds the Agent to a server that you declare:
290
+ `customize(machine, { agents: { <slot>: { mcp: [...] } } })`. The list replaces the Agent's whole `mcp` list.
291
+
283
292
  [Bind a shipped Machine's Open parts](../guides/bind-open-parts.md) shows each binding.
284
293
 
285
294
  ## @jr2/machines
@@ -40,7 +40,7 @@ The diagram shows what `jr2 up` makes, and what [`jr2 run`](../reference/cli.md#
40
40
  - **Assembly**: your Workflows and your config
41
41
  - **jr2-instance Secret**: holds the Instance token
42
42
  - **Instance Harness**: when an Agent is Menu-only
43
- - **Instance Effector**: when an Effect runs there
43
+ - **Instance Effector**: when an Effect runs there, or a Hook needs it
44
44
  - **Repo cache**: when a Machine has a Sandbox
45
45
  - ↓ the Orchestrator asks for each Sandbox · ↑ the operator makes its pod
46
46
  - **jr2-system**: one for each cluster
@@ -79,7 +79,9 @@ the cluster, see [Architecture](architecture.md).
79
79
  Until each check passes, `jr2 up` writes nothing that the Instance or the operator reads. The pod has up to 5
80
80
  minutes to start, and each model has up to 6 minutes. `jr2 up` writes each model's result as it comes. With
81
81
  `--skip-provider-preflight`, no pod checks the models, and `jr2 up` writes a warning that names each model that it
82
- did not check.
82
+ did not check. The same pod asks each remote MCP server that an Agent names for its tools, through its Custodian.
83
+ `jr2 up` refuses an allowlisted tool that the server does not list, and a server that refuses the token. The flag
84
+ skips this check too.
83
85
  12. It applies the operator. For a Repo with an ssh url, it asks where the deploy key comes from.
84
86
  13. It applies the Secrets and the Orchestrator, and waits for the rollout. When the Machines need them, it also waits
85
87
  for the Repo cache, the Instance Harness and the [Instance Effector](../reference/glossary.md#instance-effector).
@@ -7,7 +7,8 @@ description: How a run survives a restart of the Orchestrator, what ends it, and
7
7
 
8
8
  A run is durable. The [Orchestrator](../reference/glossary.md#orchestrator) saves each run after each transition of its
9
9
  [Machine](../reference/glossary.md#machine). When the Orchestrator starts again, it continues each run that did not end,
10
- from the state that it saved. A run that ended stays readable for one week.
10
+ from the state that it saved. A run that ended stays readable for [`runs.retain`](../reference/config.md#runs), 30 days
11
+ by default.
11
12
 
12
13
  ## Why runs are durable
13
14
 
@@ -78,7 +79,7 @@ A run that the Orchestrator does not continue gets one of these results:
78
79
 
79
80
  | Result | When | What stays |
80
81
  | --------- | ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
81
- | `lost` | No Workflow has the name of the run's file, or the row holds no snapshot. | The row and the reason, for one week. |
82
+ | `lost` | No Workflow has the name of the run's file, or the row holds no snapshot. | The row and the reason, for `runs.retain`. |
82
83
  | `drifted` | The Orchestrator cannot read the Assembly of the run, or the Machine of the Workflow has a different shape. | The row and the snapshot. |
83
84
  | `failed` | The restore of the run stopped with an error. | The run stays live. The next start tries it again. |
84
85
 
@@ -112,8 +113,9 @@ Orchestrator cannot read the Assembly that the run holds:
112
113
 
113
114
  The Orchestrator keeps a drifted run, and you can read it. [`jr2 status`](../reference/cli.md#status) shows its status,
114
115
  `drifted`, with the reason. The reason names the Workflow and the two digests, or the Assembly and why it cannot load.
115
- [`jr2 runs`](../reference/cli.md#runs) does not show it, because `jr2 runs` shows only the runs that run now. jr2 cannot
116
- move a drifted run to a new Machine. Thus, before you change the Kit version, let the runs end, or cancel them.
116
+ By default, [`jr2 runs`](../reference/cli.md#runs) shows only the live runs, so it does not show a drifted run.
117
+ `jr2 runs --status drifted`, `--status ended` or `--status all` shows it with its reason. jr2 cannot move a drifted run
118
+ to a new Machine. Thus, before you change the Kit version, let the runs end, or cancel them.
117
119
 
118
120
  ## CANCEL and stop
119
121
 
@@ -137,18 +139,40 @@ The operator removes them after the same idle timeout. A run that a Machine fail
137
139
 
138
140
  `jr2 status` shows a cancelled run with the status `cancelled` and the state that it was in.
139
141
 
140
- ## One week of history
142
+ ## History
141
143
 
142
- jr2 keeps the row of a run that ended for 7 days after its last write. This is true for each status that is not live:
143
- `done`, `failed`, `error`, `cancelled`, `lost` and `drifted`. The Orchestrator deletes the older rows when it starts,
144
- and one time each hour after that. A live run stays, whatever its age. A run that waits at a Gate for a month is still
145
- there.
144
+ jr2 keeps the row of a run that ended for 30 days after its last write. `runs.retain` in `jr2.config.ts` changes this
145
+ time, for example `runs: { retain: "90d" }`. It is a number and `m`, `h`, `d` or `w`, and it is at least `1h`. This is
146
+ true for each status that is not live: `done`, `failed`, `error`, `cancelled`, `lost` and `drifted`. The Orchestrator
147
+ deletes the older rows when it starts, and one time each hour after that. A live run stays, whatever its age. A run that
148
+ waits at a Gate for a month is still there.
146
149
 
147
- You cannot change the 7 days. jr2 is not the record of the results of a run. A Machine sends its results where they
148
- belong, such as a pull request. `jr2 status` on a run that ended is for debugging. After the 7 days, `jr2 status` says
149
- that the run does not exist, and a prefix of its id no longer finds it.
150
+ `jr2 runs --status ended` lists the runs that ended, newest first. `--workflow` and `--since` make the list shorter. jr2
151
+ is not the record of the results of a run. A Machine sends its results where they belong, such as a pull request.
152
+ `jr2 status` on a run that ended is for debugging. After `runs.retain`, `jr2 status` says that the run does not exist,
153
+ and a prefix of its id no longer finds it.
150
154
 
151
- The week of a drifted run starts at the restart or the publish that refused it, not at its last transition.
155
+ The time of a drifted run starts at the restart or the publish that refused it, not at its last transition.
156
+
157
+ The same time applies to the [Conversations](../reference/glossary.md#conversation) of a run and to the log of each
158
+ [Hook](../reference/glossary.md#hook). The Orchestrator keeps each conversation of an Agent while the run is kept, and
159
+ deletes it with the row of the run. Thus `jr2 logs` reads the conversations of a run that ended, also after its
160
+ [Sandbox](../reference/glossary.md#sandbox) is gone. [Read a conversation](../guides/read-a-conversation.md) gives the
161
+ commands.
162
+
163
+ ### The state volume
164
+
165
+ The Orchestrator keeps the rows of the runs, the conversations, the Assemblies and the
166
+ [Charts](../reference/glossary.md#chart) on its state volume. A new volume claim is `5Gi` by default, and `jr2 up` keeps
167
+ the size of a claim that exists. `runs.volume` in `jr2.config.ts` sets its size, such as `runs: { volume: "20Gi" }`.
168
+ `jr2 up` makes the claim larger when its StorageClass allows expansion, and refuses otherwise. It never makes a claim
169
+ smaller, because Kubernetes cannot.
170
+
171
+ A space guard keeps the volume from becoming full. When the volume is more than 80% full, the Orchestrator deletes the
172
+ conversations of the runs that ended, the oldest run first, until the volume is less than 80% full. The rows of the runs
173
+ stay, and each one shows that its conversations went for space. The guard never deletes a conversation of a live run.
174
+ When the volume is more than 95% full and no ended run has a conversation left, the Orchestrator keeps no more entries
175
+ of the open conversations. One conversation also has a limit of 8 MiB, after which the Orchestrator keeps no more of it.
152
176
 
153
177
  [`jr2 down`](../reference/cli.md#down) deletes the namespace of the Instance, and the store with it. The live runs go
154
178
  too.
@@ -162,7 +186,9 @@ too.
162
186
  - The Orchestrator never continues a drifted run, and never moves it to a new Machine.
163
187
  - The Orchestrator never continues a cancelled run.
164
188
  - `CANCEL` is the only run event that `jr2 send` sends without `--gate`. `--keep` goes only with `CANCEL`.
165
- - jr2 keeps a run that ended for 7 days. The hourly sweep never deletes a live run.
189
+ - jr2 keeps a run that ended for `runs.retain`, 30 days by default. The hourly sweep never deletes a live run.
190
+ - jr2 keeps the conversations of a run for as long as it keeps the run, unless the space guard deletes them first. The
191
+ space guard never deletes a conversation of a live run.
166
192
 
167
193
  [Cancel a run](../guides/cancel-a-run.md) and [Watch a run](../guides/watch-a-run.md) give the commands and their
168
194
  output.
@@ -170,6 +196,7 @@ output.
170
196
  ## Read next
171
197
 
172
198
  - [Watch a run](../guides/watch-a-run.md)
199
+ - [Read a conversation](../guides/read-a-conversation.md)
173
200
  - [Cancel a run](../guides/cancel-a-run.md)
174
201
  - [Iterate on a Machine on kind](../guides/iterate-on-a-machine-on-kind.md)
175
202
  - [Effects](effects.md), [Workspaces](workspaces.md) and [Converge](converge.md)
@@ -183,7 +210,7 @@ output.
183
210
  - [ADR-0025](../reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md): `CANCEL` ends a run, and a stop parks it
184
211
  - [ADR-0030](../reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md): a snapshot names the
185
212
  Machine that wrote it
186
- - [ADR-0065](../reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md): a save survives a crash,
187
- and jr2 keeps a week of history
213
+ - [ADR-0065](../reference/decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md): a save
214
+ survives a crash, and jr2 keeps history for `runs.retain`
188
215
  - [ADR-0069](../reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md): a
189
216
  run holds the Assembly that it started under
@@ -1,14 +1,14 @@
1
1
  ---
2
2
  title: Effects
3
- description: How a Machine runs a command, calls a URL, pushes a branch or starts a run, at most once.
3
+ description: How a Machine runs a command, calls a URL or an MCP tool, pushes a branch or starts a run.
4
4
  ---
5
5
 
6
6
  # Effects
7
7
 
8
8
  An [Effect](../reference/glossary.md#effect) is work that a [Machine](../reference/glossary.md#machine) does outside
9
- itself. jr2 has four kinds: `command` runs a program in the [Workspace](../reference/glossary.md#workspace), `http`
10
- calls a URL, `push` pushes the branch of a [Worktree](../reference/glossary.md#worktree), and `dispatch` starts a run of
11
- a [Workflow](../reference/glossary.md#workflow). The Kit also gives
9
+ itself. jr2 has five kinds: `command` runs a program in the [Workspace](../reference/glossary.md#workspace), `http`
10
+ calls a URL, `push` pushes the branch of a [Worktree](../reference/glossary.md#worktree), `dispatch` starts a run of a
11
+ [Workflow](../reference/glossary.md#workflow), and `mcp` calls a tool of an MCP server. The Kit also gives
12
12
  [`worktree()`](../reference/authoring-surface/worktree.md), an Effect on the `command` machinery that cuts a Worktree.
13
13
  You write an Effect as an actor slot, beside the [Agents](../reference/glossary.md#agent) of the Machine.
14
14
 
@@ -293,7 +293,7 @@ failures: workflow "test-and-push" — the states that fail the run (ADR-0073)
293
293
  test-and-push.lost: invokes fail
294
294
  ```
295
295
 
296
- ## The four kinds
296
+ ## The five kinds
297
297
 
298
298
  ### `command`
299
299
 
@@ -483,8 +483,9 @@ Worktree is, and [Cut a Worktree](../guides/cut-a-worktree.md) tells how to use
483
483
  [`dispatch`](../reference/authoring-surface/dispatch.md) starts a run of a Workflow. The new run is a run of its own: it
484
484
  has its own line in [`jr2 runs`](../reference/cli.md#runs), its own Workspaces and its own `CANCEL`. Its record names
485
485
  the run that started it: it is **dispatched by** that run. Use `dispatch` when a Machine waits for work, for example at
486
- a [Gate](../reference/glossary.md#gate) that a webhook answers, and each item of work needs a run of a Workflow that you
487
- have. To use a Machine as a part of one run, invoke it in the run instead (see [Composition](composition.md)).
486
+ a [Gate](../reference/glossary.md#gate) that a [Hook](../reference/glossary.md#hook) answers, and each item of work
487
+ needs a run of a Workflow that you have. To use a Machine as a part of one run, invoke it in the run instead (see
488
+ [Composition](composition.md)).
488
489
 
489
490
  - The name of the Workflow is a constant of the definition, as `jr2 run` writes it: `"triage"`, or `"<wrapper>/<name>"`
490
491
  for a named [Chart](../reference/glossary.md#chart). An Agent cannot choose it.
@@ -496,12 +497,27 @@ have. To use a Machine as a part of one run, invoke it in the run instead (see [
496
497
  minutes with `wait`.
497
498
  - With no `target`, the new run starts in the same Orchestrator. With
498
499
  `target: { url: "https://<host>", token: secret("<name>") }`, it starts on a different Orchestrator. `url` is the
499
- origin of that Orchestrator, and the secret holds its Instance token.
500
+ origin of that Orchestrator, and the secret holds the token of a [Caller](../reference/glossary.md#caller) of that
501
+ Orchestrator with the `start` scope for the Workflow, and also `read` when `wait` is `true`.
500
502
  - The new run starts under the current [Assembly](../reference/glossary.md#assembly), as a run of `jr2 run` does.
501
503
 
502
504
  The two runs are independent. A `CANCEL` of one run does not cancel the other. When the run that dispatched ends, the
503
505
  new run continues.
504
506
 
507
+ ### `mcp`
508
+
509
+ [`mcp`](../reference/authoring-surface/mcp.md) calls an MCP server: `mcp.call(server, tool, { input })` calls a tool,
510
+ `mcp.read(server, uri)` reads a resource, and `mcp.prompt(server, name, { input })` gets a prompt. `server` is a name of
511
+ the [`mcp`](../reference/config.md#mcp) block of `jr2.config.ts`, which holds the URL and the secret of the server. Thus
512
+ the definition holds no URL and no credential, and an Agent's word can reach only the arguments, through the input.
513
+
514
+ An Agent can also hold the tools of a server as [MCP tools](../reference/glossary.md#mcp-tools), and call them in its
515
+ Turn with no approval (see [Agents and Turns](agents-and-turns.md#the-mcp-tools)). Use an `mcp` Effect when the call
516
+ needs the approval of a person, or when the Machine makes the call with no model. The Agent picks, a Gate waits for the
517
+ approval, and the Effect calls the tool. The output of the Effect is the answer to the pick. Thus the approval is in the
518
+ record of the run, and the Machine routes on it. The default timeout is the `timeout` of the server: 60 seconds when it
519
+ sets none. [Call an MCP tool from a Machine](../guides/call-an-mcp-tool-from-a-machine.md) has an example.
520
+
505
521
  ## Where an Effect runs
506
522
 
507
523
  You do not choose where an Effect runs. The kind, and what the Effect names, decide it. Each
@@ -518,6 +534,7 @@ commands in it:
518
534
  | A `push` | The Sandbox Effector |
519
535
  | A `dispatch` with no `target` | The Orchestrator, in its own process |
520
536
  | A `dispatch` with a `target` | The Instance Effector |
537
+ | An `mcp` Effect | The Instance Effector |
521
538
 
522
539
  A `command` that names no Effect secret runs beside the Agent's [Working tools](../reference/glossary.md#working-tools).
523
540
  It runs as the Agent's user, with the tools of the [Sandbox Image](../reference/glossary.md#sandbox-image). The Harness
@@ -537,17 +554,19 @@ Effector is, and [Secrets and trust](secrets-and-trust.md) tells how an Effector
537
554
 
538
555
  An Effect gives each outcome to `onDone`, a failure too. Route on the `outcome` field:
539
556
 
540
- | Kind | The outcomes | The output type |
541
- | ---------- | --------------------------------------------------------- | ------------------------------------------------------------------------- |
542
- | `worktree` | `ready`, `refused`, `unknown` | [`WorktreeOutput`](../reference/authoring-surface/type-worktreeoutput.md) |
543
- | `command` | `exited`, `timeout`, `killed`, `unknown` | [`CommandOutput`](../reference/authoring-surface/type-commandoutput.md) |
544
- | `http` | `response`, `unreachable`, `refused`, `unknown` | [`HttpOutput`](../reference/authoring-surface/type-httpoutput.md) |
545
- | `push` | `pushed`, `rejected`, `diverged`, `unknown` | [`PushOutput`](../reference/authoring-surface/type-pushoutput.md) |
546
- | `dispatch` | `started`, `settled`, `refused`, `unreachable`, `unknown` | [`DispatchOutput`](../reference/authoring-surface/type-dispatchoutput.md) |
557
+ | Kind | The outcomes | The output type |
558
+ | ---------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------- |
559
+ | `worktree` | `ready`, `refused`, `unknown` | [`WorktreeOutput`](../reference/authoring-surface/type-worktreeoutput.md) |
560
+ | `command` | `exited`, `timeout`, `killed`, `unknown` | [`CommandOutput`](../reference/authoring-surface/type-commandoutput.md) |
561
+ | `http` | `response`, `unreachable`, `refused`, `unknown` | [`HttpOutput`](../reference/authoring-surface/type-httpoutput.md) |
562
+ | `push` | `pushed`, `rejected`, `diverged`, `unknown` | [`PushOutput`](../reference/authoring-surface/type-pushoutput.md) |
563
+ | `dispatch` | `started`, `settled`, `refused`, `unreachable`, `unknown` | [`DispatchOutput`](../reference/authoring-surface/type-dispatchoutput.md) |
564
+ | `mcp` | `returned`, `refused`, `unreachable`, `input-required`, `unknown` | [`McpCallOutput`](../reference/authoring-surface/type-mcpcalloutput.md) |
547
565
 
548
566
  A test that fails is `exited` with a code that is not 0. A program that the container does not have is `exited` with
549
567
  code 127. An HTTP 4xx or 5xx status is a `response`. A Workflow that refuses the run input of a `dispatch` is `refused`,
550
- with the status code and the report of the refusal. `unknown` means that jr2 cannot tell what the Effect did.
568
+ with the status code and the report of the refusal. A tool of an `mcp` Effect that ran and failed is `returned` with
569
+ `isError: true`. `unknown` means that jr2 cannot tell what the Effect did.
551
570
 
552
571
  Each union is open: it holds a stand-in member, `LaterOutcome`, so a later release can add an outcome and your Machine
553
572
  still works. Route on what you know, and give the rest a last arm with no guard. With no match, the run stays in that
@@ -576,6 +595,7 @@ jr2 records its output. After an Orchestrator restart, jr2 reads the record:
576
595
  | A `worktree()` | jr2 attaches to it again. When the Harness does not know it, jr2 sends it again: a cut is idempotent. |
577
596
  | A `push` | jr2 follows it. When the Sandbox Effector lost it, jr2 pushes the same commit again, and no other commit. With no commit recorded, the outcome is `unknown`. |
578
597
  | An `http` Effect | jr2 follows it. When no Effector holds it, jr2 sends it again only if it is `idempotent`. Else the outcome is `unknown`. |
598
+ | An `mcp` Effect | As an `http` Effect. jr2 does not read the `idempotentHint` of a tool: only your `idempotent` mark counts. |
579
599
  | A `dispatch` | jr2 sends the start again, with the same key. The Orchestrator answers with the run that the key started, and it starts no second run. |
580
600
 
581
601
  A `command` that names no Effect secret runs the code of the Worktree, and that code can spend a Stand-in. Thus jr2 does
@@ -583,7 +603,8 @@ not run it two times. A `push`, and a `command` that names an Effect secret, run
583
603
  jr2 can send them again with that commit.
584
604
 
585
605
  To try again, write it in the Machine: a route from the outcome back into the state. Mark an `http` Effect `idempotent`
586
- only when a second call is safe: the call has its own idempotency key, or it only reads.
606
+ only when a second call is safe: the call has its own idempotency key, or it only reads. The same rule is true for an
607
+ `mcp` Effect.
587
608
 
588
609
  ## When the state exits
589
610
 
@@ -592,8 +613,8 @@ When the run leaves the state, the Effect stops:
592
613
  - jr2 kills the process group of a `command` that names no Effect secret.
593
614
  - The Sandbox Effector stops a `command` that names an Effect secret. It sends `SIGTERM`, waits 5 seconds, then sends
594
615
  `SIGKILL`.
595
- - An `http` Effect and a `push` have no stop. jr2 stops waiting for the output, but the Effector can still finish the
596
- work.
616
+ - An `http` Effect, an `mcp` Effect and a `push` have no stop. jr2 stops waiting for the output, but the Effector can
617
+ still finish the work.
597
618
  - A `dispatch` with `wait` stops its wait. The run that it started continues.
598
619
 
599
620
  jr2 records the stop, and the Machine gets no output. There is no "finally" in a Machine: cleanup is a state. Put an
@@ -633,11 +654,14 @@ pick waits for its answer.
633
654
  a `committer` field there.
634
655
  - Each param of a pick that enters an Effect's state has a reader: the Effect's input, `fill`, a guard or an `assign`.
635
656
  `createMachine` refuses a param that nothing reads, and a `fill` key that is also a param.
636
- - A packaged Machine can leave an `http` URL, a `command` argv, one Effect secret name or the `target` of a `dispatch`
637
- Open. The composer binds it one time with `customize()`. `jr2 up` refuses an Open part that nobody bound.
657
+ - A packaged Machine can leave an `http` URL, a `command` argv, one Effect secret name, the `target` of a `dispatch`, or
658
+ the server and the tool of an `mcp` Effect Open. The composer binds it one time with `customize()`. `jr2 up` refuses
659
+ an Open part that nobody bound.
638
660
  - A `dispatch` with no `target` names a Workflow that the Assembly has. `jr2 up` refuses a name that it does not have,
639
661
  and an `input` schema that the input of that Workflow does not take.
640
662
  - The `url` of a `dispatch` target is an origin only: a scheme, a host and a port, with no path, no query and no user.
663
+ - An `mcp` Effect names a remote server that the `mcp` block declares. `jr2 up` refuses a server that the block does not
664
+ declare, and a stdio server.
641
665
 
642
666
  The guides give the refusal text of each rule.
643
667
 
@@ -646,13 +670,17 @@ The guides give the refusal text of each rule.
646
670
  - [Run a command or call a URL with an Effect](../guides/run-a-command-or-call-a-url.md)
647
671
  - [Push and sign a branch](../guides/push-and-sign-a-branch.md)
648
672
  - [Start a run from a run](../guides/start-a-run-from-a-run.md)
673
+ - [Call an MCP tool from a Machine](../guides/call-an-mcp-tool-from-a-machine.md)
649
674
  - [Workspaces](workspaces.md), [The Menu](the-menu.md), [Secrets and trust](secrets-and-trust.md)
650
675
  - Reference: [`command`](../reference/authoring-surface/command.md), [`http`](../reference/authoring-surface/http.md),
651
676
  [`push`](../reference/authoring-surface/push.md), [`dispatch`](../reference/authoring-surface/dispatch.md),
652
- [`fromPick`](../reference/authoring-surface/frompick.md), [`answer`](../reference/authoring-surface/answer.md)
677
+ [`mcp`](../reference/authoring-surface/mcp.md), [`fromPick`](../reference/authoring-surface/frompick.md),
678
+ [`answer`](../reference/authoring-surface/answer.md)
653
679
  - [ADR-0067](../reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md):
654
680
  an Effect is an actor slot, and runs at most once unless marked idempotent
655
681
  - [ADR-0068](../reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md): an Effector holds
656
682
  an Effect's secrets and signs its wire
657
683
  - [ADR-0071](../reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md): a run starts a run through a
658
684
  `dispatch` Effect
685
+ - [ADR-0079](../reference/decisions/0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md):
686
+ a Machine calls an MCP server as an Effect, and the Machine is the approval