@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
@@ -51,10 +51,15 @@ export default defineConfig({
51
51
  ```
52
52
 
53
53
  - A pod claims a Held secret when its [`workspace()`](../reference/authoring-surface/workspace.md) lists the name under
54
- `secrets`. A pod also claims the key of each provider that an Agent in that pod uses.
54
+ `secrets`. A pod also claims the key of each provider that an Agent in that pod uses, and the `secret` of each MCP
55
+ server that an Agent in that pod names.
55
56
  - The [Instance Harness](../reference/glossary.md#instance-harness) claims only the model keys of the Menu-only Agents
56
- that it hosts.
57
- - An Effect claims an Effect secret when it names it with [`secret()`](../reference/authoring-surface/secret.md).
57
+ that it hosts, and the secrets of the MCP servers that they name.
58
+ - An Effect claims an Effect secret when it names it with [`secret()`](../reference/authoring-surface/secret.md). An
59
+ `mcp` Effect claims the `secret` of its server for the
60
+ [Instance Effector](../reference/glossary.md#instance-effector).
61
+ - A [Hook](../reference/glossary.md#hook) that checks a signature with a shared secret (`mac`) claims that secret for
62
+ the Instance Effector, as an Effect does.
58
63
  - One entry can be a Held secret and an Effect secret at the same time. The placement is a fact of each place that names
59
64
  it, not of the entry.
60
65
  - An entry that nothing names goes nowhere, and [`jr2 up`](../reference/cli.md#up) warns about it.
@@ -81,13 +86,17 @@ these things:
81
86
  2. It replaces the Stand-in with the value, in the headers that the entry names.
82
87
  3. It opens its own TLS connection to the host, and verifies the certificate of the host.
83
88
 
84
- The Custodian sends the traffic to each other host with no change. It refuses a loopback, link-local or unspecified
85
- address. A request to a bound host that has no Stand-in in its headers does not leave the pod.
89
+ Two Held secrets can go to one host in one header, such as the tokens of two MCP servers of one service. The Custodian
90
+ chooses the value by the Stand-in that the header holds. The Custodian sends the traffic to each other host with no
91
+ change. It refuses a loopback, link-local or unspecified address. A request to a bound host that has no Stand-in in its
92
+ headers does not leave the pod.
86
93
 
87
94
  These fields of an entry bind a Held secret. They do not place it:
88
95
 
89
96
  - [`hosts`](../reference/config.md#secrets-hosts): the hosts that the value can go to. A known model key with one
90
- default host, such as `ANTHROPIC_API_KEY`, can leave it out. It then goes only to the host of its provider.
97
+ default host, such as `ANTHROPIC_API_KEY`, can leave it out. It then goes only to the host of its provider. The
98
+ `secret` of an MCP server can leave it out too. It then goes only to the host of the server's URL, in the header
99
+ `authorization`.
91
100
  - [`headers`](../reference/config.md#secrets-headers): the headers in which the Custodian replaces the Stand-in.
92
101
  - [`paths`](../reference/config.md#secrets-paths): the path prefixes that the value can go to.
93
102
 
@@ -128,9 +137,9 @@ The Instance Harness has a token of its own for the Menu. It runs no program of
128
137
  The Harness runs the [Turns](../reference/glossary.md#turn) of each Agent of the Sandbox. The Harness process itself
129
138
  runs only jr2's code. Each other process in the Harness container runs confined with Landlock: each command of the
130
139
  Agent's shell, and so the tests, the builds, git and its hooks; the search tools; the I/O of the read, write and edit
131
- tools; jr2's own git; and each `command` Effect that names no Effect secret. Code that one Agent writes in `/work` can
132
- run in a program of a different Agent, for example when a reviewer runs the tests of a coder. Thus jr2 confines each
133
- such program, and not only the programs of one Agent.
140
+ tools; jr2's own git; each stdio MCP server; and each `command` Effect that names no Effect secret. Code that one Agent
141
+ writes in `/work` can run in a program of a different Agent, for example when a reviewer runs the tests of a coder. Thus
142
+ jr2 confines each such program, and not only the programs of one Agent.
134
143
 
135
144
  All these programs are in one Landlock domain. They can see, signal and trace each other, as the programs of one user on
136
145
  a computer can. Thus a later shell command can find and stop a server that an earlier command started. When the process
@@ -212,46 +221,71 @@ make the Sandbox. [Give the cluster git credentials](../guides/give-the-cluster-
212
221
  ## The tokens
213
222
 
214
223
  jr2 makes its own tokens. `jr2 up` makes the Instance token and the signing key the first time, and keeps them in the
215
- `jr2-instance` Secret. Each other token comes from the signing key, so the Orchestrator stores no token for each pod.
224
+ `jr2-instance` Secret. Each token of a pod comes from the signing key, so the Orchestrator stores no token for each pod.
216
225
 
217
- | Token | Who holds it | What it lets the holder do |
218
- | -------------- | ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
219
- | Instance token | You, the CLI, the [Console](../reference/glossary.md#console) when you type it, and a webhook client | Everything: start a run, read its full state, answer a Gate, cancel a run. |
220
- | Control token | The Custodian of one Sandbox, as a file | Read the Menu and send the picks of the Agents in that Sandbox, for the Agent that the Harness names. |
221
- | Fetch token | The Custodian of one Sandbox, as a file | Ask for a fetch of a Repo that the Sandbox mounts. |
222
- | Harness bearer | The Orchestrator. The Harness holds only its SHA-256 digest. | Call the Harness in one pod: start a Turn, read it, stop it, set up a Worktree, and run a `command` Effect. |
223
- | Effector key | The Orchestrator, and the Effector of one placement | Sign a request to that Effector, and sign its answer. |
226
+ | Token | Who holds it | What it lets the holder do |
227
+ | ------------------ | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
228
+ | Instance token | You and the CLI, through the Kubernetes API | Everything: start a run, read its full state, answer a Gate, cancel a run, and the routes that no scope opens. |
229
+ | Named Caller token | A [Caller](../reference/glossary.md#caller) that has no access to the cluster, such as a CI job | The scopes that `callers.tokens` gives its name, for the Workflows that each scope names. |
230
+ | Control token | The Custodian of one Sandbox, as a file | Read the Menu and send the picks of the Agents in that Sandbox, for the Agent that the Harness names. |
231
+ | Fetch token | The Custodian of one Sandbox, as a file | Ask for a fetch of a Repo that the Sandbox mounts. |
232
+ | Harness bearer | The Orchestrator. The Harness holds only its SHA-256 digest. | Call the Harness in one pod: start a Turn, read it, stop it, set up a Worktree, and run a `command` Effect. |
233
+ | Effector key | The Orchestrator, and the Effector of one placement | Sign a request to that Effector, and sign its answer. |
224
234
 
225
235
  - The Instance token enters no Harness pod. The CLI reads it from the `jr2-instance` Secret over the Kubernetes API.
226
- Thus Kubernetes RBAC decides who holds it.
236
+ Thus Kubernetes RBAC decides who holds it. The CLI is the Caller `token/instance`. It also sends the name of your
237
+ Kubernetes user, and the run records it as the party that the Caller acts for.
238
+ - [`callers.tokens`](../reference/config.md#callers-tokens) declares each named Caller token. `jr2 up` makes its value
239
+ one time, in a Secret of its own, `jr2-caller-<name>`. The Orchestrator holds only the SHA-256 digest of each value,
240
+ in the `jr2-caller-digests` Secret, which it mounts. `jr2 caller token <name>` prints the value, which it reads over
241
+ the Kubernetes API.
242
+ - `jr2 caller rotate <name>` makes a new value. The Orchestrator reads the digests again when the mount changes. The
243
+ kubelet updates the mount in about one minute, and `jr2 caller rotate` waits for it. When it returns, the old value no
244
+ longer works. No pod restarts.
245
+ - When `callers.tokens` no longer declares a name, the next `jr2 up` deletes its Secret, and its value stops working.
227
246
  - A control token or a fetch token cannot answer a Gate, and cannot speak for a different pod. The Orchestrator accepts
228
247
  each one only on its own routes. Thus an Agent cannot approve its own work, and a Sandbox cannot send a pick for an
229
- Agent in a different Sandbox.
248
+ Agent in a different Sandbox. No token of a pod is a Caller.
230
249
  - A Harness can check its bearer, but it cannot make a bearer. Thus an Agent cannot drive a conversation in a different
231
250
  pod.
232
251
 
252
+ [Callers and Hooks](callers-and-hooks.md) tells how jr2 knows a Caller, and what each scope opens.
253
+ [Give a Caller a scope](../guides/give-a-caller-a-scope.md) declares a named Caller token and uses it.
254
+
233
255
  ## Open observation, guarded control
234
256
 
235
- Each route of the [HTTP API](../reference/http-api.md) is in one of three access bands:
257
+ Each route of the [HTTP API](../reference/http-api.md) is in one of these access bands:
236
258
 
237
259
  - [**Open**](../reference/http-api.md#band-open), with no token: the [Workflows](../reference/glossary.md#workflow), the
238
260
  structure of each Machine, and the live runs of a Workflow with their states. An open route shows no context of a run,
239
261
  and an [Emit](../reference/glossary.md#emit) shows only its type.
240
262
  - [**Authenticated**](../reference/http-api.md#band-authenticated), with the Instance token or a token of a Sandbox: the
241
263
  routes of the Agent's Menu and the fetch. A token of a Sandbox reaches only its own routes, in its own pod.
242
- - [**Instance token only**](../reference/http-api.md#band-instanceonly): the start of a run, the full state of a run,
243
- the Gates, and cancel.
244
-
245
- Thus the Console shows the Machines and the runs without a token. When you type the Instance token in the Console, it
246
- can also start runs and answer Gates. The Console keeps the token in the session storage of the browser tab, and forgets
247
- it when you close the tab.
264
+ - [**A Caller with a scope**](../reference/http-api.md#band-guarded): the start of a run, the full state of a run, the
265
+ Gates, and cancel. Each route needs one scope of a Caller (`start`, `read`, `answer`, `cancel` or `steer`) for the
266
+ Workflow of the run. The Instance token has each scope for each Workflow. A named Caller token has the scopes that its
267
+ entry gives it. A token of a Sandbox is not a Caller.
268
+ - [**Instance token only**](../reference/http-api.md#band-instanceonly): the routes that no scope opens, such as a
269
+ runtime Chart, the report of the Repos and the log of a Hook (`GET /hooks/:name`).
270
+ - [**A Hook's verifier**](../reference/http-api.md#band-hook), with no bearer token: `POST /hooks/:name`. The verifier
271
+ of the Hook checks each delivery.
272
+
273
+ Thus the Console shows the Machines and the runs without a token. When you type a Caller's token in the Console, or log
274
+ in at an issuer that the Instance declares, the Console also shows the controls that the Caller's scopes open: start,
275
+ the Gates, cancel and steer. The Console keeps the credential in the session storage of the browser tab, and forgets it
276
+ when you close the tab. [Open the Console](../guides/open-the-console.md) tells how.
248
277
 
249
278
  "Open" means open to each caller that can reach the Service of the Orchestrator: a port forward, a pod in the cluster,
250
279
  or an ingress. The names of the states and the types of the Emits are open.
251
280
 
252
281
  The Harness writes each Agent's conversation to its container log. The log has the prompts, the model's text and
253
- thinking, its tool calls and the answers to its picks. It does not write the results of the Working tools. Each person
254
- who can read the pod logs of the namespace can read these conversations.
282
+ thinking, its tool calls and the answers to its picks. It does not write the results of the Working tools or of the MCP
283
+ tools. Each person who can read the pod logs of the namespace can read these conversations.
284
+
285
+ The Orchestrator also keeps each conversation, with the same cut, for `runs.retain` (30 days by default). Thus the kept
286
+ copy has the same residual as the log: an answer to a pick can carry a held value that an upstream wrote into a
287
+ `command` output, or a secret in a form that the scrub does not catch. A Caller with `read` on the Workflow can read the
288
+ kept copy with `GET /runs/:runId/conversations`.
255
289
 
256
290
  ## Rules
257
291
 
@@ -264,7 +298,10 @@ who can read the pod logs of the namespace can read these conversations.
264
298
  - A file that the Harness process reads, such as the one that `AWS_CONFIG_FILE` or `GOOGLE_APPLICATION_CREDENTIALS`
265
299
  names in `harness.env`, cannot be in `/work`, `/tmp`, `/dev/shm` or `/home/jr2`: `jr2 up` refuses it. In another
266
300
  folder, `jr2 up` gives a warning: make sure that the user of the Sandbox cannot write the file.
267
- - A Held secret must name its `hosts`, unless it is a known model key with one default host.
301
+ - A Held secret must name its `hosts`, unless it is a known model key with one default host, or the `secret` of an MCP
302
+ server.
303
+ - An MCP server with a `secret` has an `https://` URL. When an entry sets `hosts`, `headers` or `paths`, they must let
304
+ the Stand-in through to the URL of each server that names the entry.
268
305
  - A name that a `workspace()` lists must be in the `secrets` list. A name that an Effect gives to `secret()` must be in
269
306
  it too.
270
307
  - An Effect secret has 8 characters or more.
@@ -285,22 +322,25 @@ who can read the pod logs of the namespace can read these conversations.
285
322
 
286
323
  ## Read next
287
324
 
325
+ - [Give a Caller a scope](../guides/give-a-caller-a-scope.md)
288
326
  - [Use a local model](../guides/use-a-local-model.md)
289
327
  - [Use a hosted provider or a gateway](../guides/use-a-hosted-provider.md)
290
328
  - [Give the cluster git credentials](../guides/give-the-cluster-git-credentials.md)
291
329
  - [Run a command or call a URL with an Effect](../guides/run-a-command-or-call-a-url.md)
330
+ - [Give an Agent MCP tools](../guides/give-an-agent-mcp-tools.md)
292
331
  - [Open the Console](../guides/open-the-console.md)
293
- - [Architecture](architecture.md) and [Effects](effects.md)
332
+ - [Callers and Hooks](callers-and-hooks.md), [Architecture](architecture.md) and [Effects](effects.md)
294
333
  - The [`secrets`](../reference/config.md#secrets), [`git`](../reference/config.md#git) and
295
334
  [`harness`](../reference/config.md#harness) fields of `jr2.config.ts`,
296
335
  [`secret()`](../reference/authoring-surface/secret.md), and the [HTTP API](../reference/http-api.md)
297
336
  - [ADR-0013](../reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md),
298
337
  [ADR-0014](../reference/decisions/0014-observation-is-open-run-state-is-not.md),
299
338
  [ADR-0023](../reference/decisions/0023-the-harness-prints-the-conversation.md),
300
- [ADR-0032](../reference/decisions/0032-the-console-unlocks-with-the-instance-token.md),
339
+ [ADR-0032](../reference/decisions/0032-the-console-unlocks-with-a-callers-credential.md),
301
340
  [ADR-0051](../reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md),
302
341
  [ADR-0058](../reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md),
303
342
  [ADR-0059](../reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md),
304
343
  [ADR-0068](../reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md),
305
- [ADR-0076](../reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md) and
306
- [ADR-0077](../reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md)
344
+ [ADR-0076](../reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md),
345
+ [ADR-0077](../reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md) and
346
+ [ADR-0079](../reference/decisions/0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md)
@@ -21,6 +21,11 @@ and the tools block is first in it. A change to the tools block makes the provid
21
21
  Thus jr2 keeps the Menu the same, and tells each [Turn](../reference/glossary.md#turn) which part of it the Turn can
22
22
  pick now.
23
23
 
24
+ The Menu is the last part of the tools block. The [Working tools](../reference/glossary.md#working-tools) come first,
25
+ then the [MCP tools](../reference/glossary.md#mcp-tools), then the Menu. The model reads a Menu tool as
26
+ `mcp__jr2__<event>`, and an MCP tool as `mcp__<server>__<tool>`, where `<server>` is never `jr2`. Only a call of a Menu
27
+ tool is a pick. A call of an MCP tool acts on a service outside the Machine, and the Turn goes on after it.
28
+
24
29
  ## Where the Menu comes from
25
30
 
26
31
  You do not write the Menu. `jr2Setup` makes the Menu from the Machine when you call `createMachine`:
@@ -336,6 +341,11 @@ Each event that a party sends carries its [Sender](../reference/glossary.md#send
336
341
  [`Sender`](../reference/authoring-surface/type-sender.md)). The [Orchestrator](../reference/glossary.md#orchestrator)
337
342
  writes it on each event that a party delivers. An event that the Machine sends to itself has no Sender.
338
343
 
344
+ A Gate's event also carries the [Caller](../reference/glossary.md#caller) that answered it: `via`, `name`, and
345
+ `onBehalfOf` when the Caller says that it acts for a person or a service. `checked` names the fields that jr2 verified.
346
+ jr2 always verifies `via` and `name`. It does not verify `onBehalfOf`, except for a Hook, whose signature covers it. So
347
+ do not use an unchecked `onBehalfOf` to decide who may do something.
348
+
339
349
  Use the Sender when two parties can send the same event to one state. The guard
340
350
  [`sentBy(id)`](../reference/authoring-surface/sentby.md) is true for an event from that party, and for an event with no
341
351
  Sender. It is false for an event from a different party, so xstate tries the next transition. `sentBy` can only make a
@@ -344,6 +354,7 @@ transition refuse other senders. It never gives an event to a party. The positio
344
354
  ## Rules
345
355
 
346
356
  - jr2 refuses a pick that is not on the Menu.
357
+ - A call of an MCP tool is not a pick. It does not move the Machine, and it does not end the Turn.
347
358
  - A pick that is on the Menu but not in the Allowed picks moves nothing. Its answer gives the Allowed picks.
348
359
  - While a pick is outstanding, or while the Agent's state is not at Rest, jr2 refuses each pick and delivers nothing.
349
360
  This includes a pick that would end the Turn.
@@ -583,7 +583,8 @@ In a Workspace with no Repo:
583
583
  - A `worktree()` needs a Repo Slot, a `push` needs a branch, and a `command` with an Effect secret runs on a commit.
584
584
  Thus `jr2 up` refuses all three.
585
585
 
586
- jr2 deletes `/work` with the Sandbox. Send what must stay before the body ends, as the example does with `upload`. A
586
+ jr2 deletes `/work` with the Sandbox. Send what must stay before the body ends, as the example does with `upload`. The
587
+ secret `files` that `upload` names is an entry of the `secrets` list of `jr2.config.ts`, as each Effect secret is. A
587
588
  Lost Sandbox has lost all of its work, because no commit was pushed.
588
589
 
589
590
  ## The pod
@@ -600,10 +601,10 @@ A Sandbox pod has two containers, and two more when the Machine needs them:
600
601
  ### The Sandbox Image
601
602
 
602
603
  The [Sandbox Image](../reference/glossary.md#sandbox-image) is the image of the Harness container. It holds the tools
603
- that an Agent's Working tools can use, and the shell that you get when you run `kubectl exec` into the pod. The image is
604
- yours. Its floor is glibc, git and a writable `HOME`. [Add tools to the Sandbox](../guides/add-tools-to-the-sandbox.md)
605
- gives the full list. jr2 adds nothing to the image. It mounts its own runtime into the pod at `/opt/jr2`, and starts the
606
- Harness from there. Thus the `ENTRYPOINT` and the `CMD` of the image do not run.
604
+ that an Agent's Working tools can use, and the shell that you get with `jr2 workspace shell`. The image is yours. Its
605
+ floor is glibc, git and a writable `HOME`. [Add tools to the Sandbox](../guides/add-tools-to-the-sandbox.md) gives the
606
+ full list. jr2 adds nothing to the image. It mounts its own runtime into the pod at `/opt/jr2`, and starts the Harness
607
+ from there. Thus the `ENTRYPOINT` and the `CMD` of the image do not run.
607
608
 
608
609
  `image` names it in one of two forms:
609
610
 
@@ -406,8 +406,7 @@ there:
406
406
  ```text
407
407
  …
408
408
  → context kind-jr2 / namespace my-instance
409
- run 8fecb896-2100-4ee6-88d6-788eaf0d7244 (triage) — attached
410
- → active "triaging"
409
+ run f40a189e-b554-4310-b6e5-3b92c74e414e (triage) — attached
411
410
  → active "triaging"
412
411
  → active "clarify"
413
412
  ```
@@ -427,10 +426,10 @@ can show the same state. `jr2 run` does not return while the run waits. Keep it
427
426
  > ```text
428
427
  > …
429
428
  > → context kind-jr2 / namespace my-instance
430
- > run 89cc6ea5-9e2d-4f8a-8d02-18a6e47f2a5c (triage) — attached
429
+ > run 4eafe1a5-b45d-4613-8ae3-4c85b26eeb0c (triage) — attached
431
430
  > → active "triaging"
432
431
  > → done "accepted"
433
- > {"runId":"89cc6ea5-9e2d-4f8a-8d02-18a6e47f2a5c","workflow":"triage","instanceId":"fac049b9-9dd2-4ba5-aca8-0b3ebe619701","status":"done","value":"accepted","context":{"request":"Add a --version flag to the CLI. It prints the installed version and exits.","exchange":[],"summary":"Add a --version flag to the CLI that prints the installed version and exits."},"children":[],"waiting":[],"held":[],"assembly":"3eb6cd4d61bf","output":{"outcome":"accepted","summary":"Add a --version flag to the CLI that prints the installed version and exits."}}
432
+ > {"runId":"4eafe1a5-b45d-4613-8ae3-4c85b26eeb0c","workflow":"triage","instanceId":"d4daa575-f8ac-4ba9-af2e-406084adbd89","startedAt":"2026-11-06T19:24:22.161Z","startedBy":{"kind":"caller","via":"token","name":"instance","onBehalfOf":"kubernetes-admin"},"status":"done","value":"accepted","context":{"request":"Add a --version flag to the CLI. It prints the installed version and exits.","exchange":[],"summary":"Add a --version flag to the CLI that prints the installed version and exits."},"children":[],"waiting":[],"held":[],"assembly":"9ff0a4d3c5da","output":{"outcome":"accepted","summary":"Add a --version flag to the CLI that prints the installed version and exits."}}
434
433
  > ```
435
434
  >
436
435
  > Your run id and your [Instance ID](../reference/glossary.md#instance-id) are different, and so is the `summary`, the
@@ -444,14 +443,15 @@ The Gate holds the question of the Agent in its `meta`. In the send terminal, ru
444
443
  jr2 status <run id>
445
444
  ```
446
445
 
447
- `<run id>` is the run id that `jr2 run` printed. You can give its first 4 characters or more, such as `8fecb896`.
446
+ `<run id>` is the run id that `jr2 run` printed. You can give its first 4 characters or more, such as `f40a189e`.
448
447
  [`jr2 status`](../reference/cli.md#status) prints the status of the run as one line of JSON. The Gate is in `gates`, at
449
448
  the end:
450
449
 
451
450
  ```text
452
451
  …
453
452
  → context kind-jr2 / namespace my-instance
454
- {"runId":"8fecb896-2100-4ee6-88d6-788eaf0d7244","workflow":"triage","instanceId":"a814d943-9b27-410c-a790-2c4225aa05ba","status":"active","value":"clarify","context":{"request":"People keep asking what version they are running.","exchange":[{"question":"Where specifically should the version number be displayed (e.g., CLI flag, About screen, or help output), and what exact user action should trigger it?"}]},"children":[],"waiting":[],"held":[],"assembly":"3eb6cd4d61bf","gates":[{"gate":"clarify","path":["clarify"],"accepts":[{"name":"answer","description":"Answer the triager's question. It triages the request again with the answer in hand.","input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"info":{"type":"string","description":"The answer, in prose."}},"required":["info"],"additionalProperties":false}}],"acceptsNow":["answer"],"meta":{"question":"Where specifically should the version number be displayed (e.g., CLI flag, About screen, or help output), and what exact user action should trigger it?"}}]}
453
+ {"runId":"f40a189e-b554-4310-b6e5-3b92c74e414e","workflow":"triage","instanceId":"6a6fb1c4-d6d8-4867-a51b-693c0772cc7b","startedAt":"2026-11-06T19:23:30.814Z","startedBy":{"kind":"caller","via":"token","name":"instance","onBehalfOf":"kubernetes-admin"},"status":"active","value":"clarify","context":{"request":"People keep asking what version they are running.","exchange":[{"question":"Where should the version number be displayed to users (e.g., a specific command like `--version`, an 'About' screen, or a startup log message)?"}]},"children":[],"waiting":[],"held":[],"assembly":"9ff0a4d3c5da","gates":[{"gate":"clarify","path":["clarify"],"accepts":[{"name":"answer","description":"Answer the triager's question. It triages the request again with the answer in hand.","input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"info":{"type":"string","description":"The answer, in prose."}},"required":["info"],"additionalProperties":false}}],"acceptsNow":["answer"],"meta":{"question":"Where should the version number be displayed to users (e.g., a specific command like `--version`, an 'About' screen, or a startup log message)?"}}]}
454
+ started by: token/instance on behalf of kubernetes-admin
455
455
  ```
456
456
 
457
457
  The `question` is in the words of the model, so your question is different. Your instance id is different too. The Gate
@@ -470,7 +470,7 @@ jr2 send <run id> --gate clarify --event answer --input '{"info":"Add a --versio
470
470
  ```text
471
471
  …
472
472
  → context kind-jr2 / namespace my-instance
473
- delivered answer to gate "clarify" on 8fecb896-2100-4ee6-88d6-788eaf0d7244
473
+ delivered answer to gate "clarify" on f40a189e-b554-4310-b6e5-3b92c74e414e
474
474
  ```
475
475
 
476
476
  The run goes back to `triaging`, and the Agent reads your answer. Look at the first terminal. When the Agent picks
@@ -481,7 +481,7 @@ The run goes back to `triaging`, and the Agent reads your answer. Look at the fi
481
481
  → active "triaging"
482
482
  → active "triaging"
483
483
  → done "accepted"
484
- {"runId":"8fecb896-2100-4ee6-88d6-788eaf0d7244","workflow":"triage","instanceId":"a814d943-9b27-410c-a790-2c4225aa05ba","status":"done","value":"accepted","context":{"request":"People keep asking what version they are running.","exchange":[{"question":"Where specifically should the version number be displayed (e.g., CLI flag, About screen, or help output), and what exact user action should trigger it?","info":"Add a --version flag to the CLI. It prints the installed version and exits."}],"summary":"Add a --version flag to the CLI that prints the installed version and exits."},"children":[],"waiting":[],"held":[],"assembly":"3eb6cd4d61bf","output":{"outcome":"accepted","summary":"Add a --version flag to the CLI that prints the installed version and exits."}}
484
+ {"runId":"f40a189e-b554-4310-b6e5-3b92c74e414e","workflow":"triage","instanceId":"6a6fb1c4-d6d8-4867-a51b-693c0772cc7b","startedAt":"2026-11-06T19:23:30.814Z","startedBy":{"kind":"caller","via":"token","name":"instance","onBehalfOf":"kubernetes-admin"},"status":"done","value":"accepted","context":{"request":"People keep asking what version they are running.","exchange":[{"question":"Where should the version number be displayed to users (e.g., a specific command like `--version`, an 'About' screen, or a startup log message)?","info":"Add a --version flag to the CLI. It prints the installed version and exits."}],"summary":"Add a --version flag to the CLI that prints the installed version and exits."},"children":[],"waiting":[],"held":[],"answers":[{"gate":"clarify","type":"answer","at":"2026-11-06T19:24:02.957Z","answeredBy":{"via":"token","name":"instance","onBehalfOf":"kubernetes-admin"}}],"assembly":"9ff0a4d3c5da","output":{"outcome":"accepted","summary":"Add a --version flag to the CLI that prints the installed version and exits."}}
485
485
  ```
486
486
 
487
487
  `summary` is the request as the model wrote it again, in one sentence. Your summary is different. The `output` of the
@@ -502,24 +502,25 @@ This request does not say what is wrong. The run goes to `clarify` and waits, as
502
502
  ```text
503
503
  …
504
504
  → context kind-jr2 / namespace my-instance
505
- run 84079cb2-3cdd-4b9a-ae39-51ab873c5f3c (triage) — attached
505
+ run 7e6c9deb-ca65-4103-b517-abf6b0028aff (triage) — attached
506
506
  → active "triaging"
507
507
  → active "clarify"
508
508
  ```
509
509
 
510
- `84079cb2-3cdd-4b9a-ae39-51ab873c5f3c` is the id of the second run. Your run id is different. If the Agent picks
510
+ `7e6c9deb-ca65-4103-b517-abf6b0028aff` is the id of the second run. Your run id is different. If the Agent picks
511
511
  `accept` or `reject`, start the run again.
512
512
 
513
513
  ## Answer from the Console
514
514
 
515
- Open `http://localhost:4000` in your browser. If the badge after the `instance token` box shows `live`, go to the next
516
- step. If the box is empty, get the token. In the send terminal, run:
515
+ Open `http://localhost:4000` in your browser. If the nav shows `as token/instance` and the badge `live`, go to the next
516
+ step. If the nav shows the `Caller token` box, get the token. In the send terminal, run:
517
517
 
518
518
  ```sh
519
519
  kubectl -n my-instance get secret jr2-instance -o jsonpath='{.data.JR2_INSTANCE_TOKEN}' | base64 -d
520
520
  ```
521
521
 
522
- Type the token in the `instance token` box. Push Enter. The badge after the box shows `live`.
522
+ Type the token in the `Caller token` box. Push Enter. The badge after the box shows `live`, and the nav shows
523
+ `as token/instance`.
523
524
 
524
525
  Click `triage` in the Fleet rail on the left. The center shows the Machine of `triage`, with its states and transitions.
525
526
  Click the run in the rail. The Console lights the state of the run, `clarify`.
@@ -137,7 +137,7 @@ seats: workflow "task" — who may send what, per state (ADR-0015)
137
137
  task.working agent:coder: [finish]
138
138
  task.review gate: [approve, request_changes]
139
139
  …
140
- sandbox image "default": building jr2-sandbox-my-instance-default:6389bdf72d50-amd64
140
+ sandbox image "default": building jr2-sandbox-my-instance-default:5e601a543032-amd64
141
141
  no USER declared — the pod's uid-1000 fallback applies
142
142
  kind load → cluster "jr2"
143
143
  …
@@ -161,7 +161,7 @@ jr2 run task --input '{"prompt":"In docs/site/tutorial/greet, one test fails. Fi
161
161
  ```text
162
162
  …
163
163
  → context kind-jr2 / namespace my-instance
164
- run 290b4be9-1e24-46a2-8cb5-27fabb3bbd3c (task) — attached
164
+ run 79b90360-cdbf-4538-8611-d52da1b8b914 (task) — attached
165
165
  → active "placing"
166
166
  → active "provisioning"
167
167
  → active "attaching"
@@ -187,8 +187,9 @@ the Gate. Its `gate` is `body.review`, and the line ends with its `meta`. After
187
187
  for each Worktree that the run cut:
188
188
 
189
189
  ```text
190
- …"gate":"body.review",…,"meta":{"summary":"Fixed a bug in docs/site/tutorial/greet/src/greet.js where the name argument wasn't being trimmed. The test \"trims the name\" failed because input like \" ada \" produced \"hello, ada \" instead of \"hello, ada\". Added `.trim()` to the name extraction so whitespace is stripped before constructing the greeting. All 6 tests now pass.","branch":"jr2/task-1f34eef3-5749-4d7c-a16b-488c56ec88cd","worktree":"/work/target/task"}}]}
191
- worktree: /work/target/task (target), branch jr2/task-1f34eef3-5749-4d7c-a16b-488c56ec88cd
190
+ …"gate":"body.review",…,"meta":{"summary":"Fixed the failing \"trims the name\" test in docs/site/tutorial/greet. The bug was in src/greet.js where the name argument wasn't being trimmed, causing `greet([\" ada \"])` to output `'hello, ada '` instead of `'hello, ada'`. Added `.trim()` to the name extraction line. All 6 tests now pass.","branch":"jr2/task-1e805d38-68ac-4ec9-acc8-fc5ce6a68f6b","worktree":"/work/target/task"}}]}
191
+ started by: token/instance on behalf of kubernetes-admin
192
+ worktree: /work/target/task (target), branch jr2/task-1e805d38-68ac-4ec9-acc8-fc5ce6a68f6b
192
193
  ```
193
194
 
194
195
  Run the command again until `gates` holds the Gate.
@@ -198,7 +199,7 @@ Run the command again until `gates` holds the Gate.
198
199
  - `worktree` is the folder in the Sandbox that holds the branch. It is
199
200
  [the Worktree](../reference/glossary.md#worktree).
200
201
 
201
- Copy `branch` and `worktree` from your output. The part after `jr2/task-`, here `1f34eef3-5749-4d7c-a16b-488c56ec88cd`,
202
+ Copy `branch` and `worktree` from your output. The part after `jr2/task-`, here `1e805d38-68ac-4ec9-acc8-fc5ce6a68f6b`,
202
203
  is the [Instance ID](../reference/glossary.md#instance-id) of the run. `jr2 status` prints it as `instanceId`. It is not
203
204
  the run id. Do not make the branch name from the run id. With the Instance token, the drawer of the
204
205
  [Console](../reference/glossary.md#console) shows the same `meta`.
@@ -208,27 +209,30 @@ If `meta` has a `reason` and no `summary`, the [Turn](../reference/glossary.md#t
208
209
 
209
210
  ## Find the Sandbox
210
211
 
211
- The label `jr2.dev/run` on the Sandbox holds the full run id. A prefix does not work here. Copy the full run id from the
212
- line `run <run id> (task) — attached` in the first terminal:
212
+ List the Workspaces of the run with [`jr2 workspace ls`](../reference/cli.md#workspace-ls):
213
213
 
214
214
  ```sh
215
- kubectl -n my-instance get sandboxes -l jr2.dev/run=<run id>
215
+ jr2 workspace ls <run id>
216
216
  ```
217
217
 
218
218
  ```text
219
- NAME PHASE ENDPOINT AGE
220
- ws-290b4be9-x-0-19gh8ze Ready http://ws-290b4be9-x-0-19gh8ze.my-instance.svc:8080 5m25s
219
+ …
220
+ → context kind-jr2 / namespace my-instance
221
+ WORKSPACE SANDBOX WORKFLOW PHASE AGE RUN
222
+ 79b90360/x:23 ws-79b90360-x-23-1gex8om task Ready 81s held
221
223
  ```
222
224
 
223
- `ws-290b4be9-x-0-19gh8ze` is the name of the Sandbox. Your Sandbox has a different name. It starts with `ws-` and the
224
- first 8 characters of the run id. The pod of the Sandbox has the same name. The commands below write it as `<sandbox>`.
225
+ `79b90360/x:23` is the handle of the Workspace: the first 8 characters of the run id, then the path of the Workspace in
226
+ the run. `ws-79b90360-x-23-1gex8om` is the name of the Sandbox. The name of the Sandbox is a handle too. Your values are
227
+ different. The commands below write the handle as `<workspace>`. `held` tells that the run holds the Sandbox.
225
228
 
226
229
  ## Look inside the Sandbox
227
230
 
228
- Go into the `harness` container of the pod. The Agent's tools run in this container:
231
+ Open a shell in the Sandbox with [`jr2 workspace shell`](../reference/cli.md#workspace-shell). The shell runs as the
232
+ Agent's tools run: in the same container, with the same files and the same limits:
229
233
 
230
234
  ```sh
231
- kubectl -n my-instance exec -it <sandbox> -c harness -- bash
235
+ jr2 workspace shell <workspace>
232
236
  ```
233
237
 
234
238
  In the Sandbox, go to the fixture in the Worktree. Then run its tests. `<worktree>` is the `worktree` that you copied:
@@ -239,12 +243,12 @@ node --test
239
243
  ```
240
244
 
241
245
  ```text
242
- ✔ greets the world by default (1.0597ms)
243
- ✔ greets a name (0.162102ms)
244
- ✔ shouts (0.08266ms)
245
- ✔ trims the name (0.057171ms)
246
- ✔ prints help (3.806196ms)
247
- ✔ refuses an unknown flag (0.222015ms)
246
+ ✔ greets the world by default (1.208079ms)
247
+ ✔ greets a name (0.113673ms)
248
+ ✔ shouts (0.070218ms)
249
+ ✔ trims the name (0.054069ms)
250
+ ✔ prints help (0.712678ms)
251
+ ✔ refuses an unknown flag (0.242712ms)
248
252
  ℹ tests 6
249
253
  ℹ suites 0
250
254
  ℹ pass 6
@@ -252,18 +256,18 @@ node --test
252
256
  ℹ cancelled 0
253
257
  ℹ skipped 0
254
258
  ℹ todo 0
255
- ℹ duration_ms 53.312932
259
+ ℹ duration_ms 49.489997
256
260
  ```
257
261
 
258
262
  Your times are different. Read the commits of the coder. The branch starts at the release tag, so the commits after the
259
263
  tag are the commits of the coder:
260
264
 
261
265
  ```sh
262
- git log --oneline v<version>..HEAD
266
+ git --no-pager log --oneline v<version>..HEAD
263
267
  ```
264
268
 
265
269
  ```text
266
- e467788 fix: trim name in greet.js to handle leading/trailing whitespace
270
+ 8afef7d (HEAD -> jr2/task-1e805d38-68ac-4ec9-acc8-fc5ce6a68f6b) fix: trim name in greet to handle leading/trailing whitespace
267
271
  ```
268
272
 
269
273
  Each line is one commit. The commit ids and the messages come from the model. The author of each commit is
@@ -285,7 +289,7 @@ jr2 send <run id> --gate body.review --event request_changes --input '{"notes":"
285
289
  ```text
286
290
  …
287
291
  → context kind-jr2 / namespace my-instance
288
- delivered request_changes to gate "body.review" on 290b4be9-1e24-46a2-8cb5-27fabb3bbd3c
292
+ delivered request_changes to gate "body.review" on 79b90360-cdbf-4538-8611-d52da1b8b914
289
293
  ```
290
294
 
291
295
  The coder continues the same conversation, with your notes as its next prompt. The Sandbox and the branch stay. Run
@@ -294,10 +298,10 @@ The coder continues the same conversation, with your notes as its next prompt. T
294
298
  ## Push the branch
295
299
 
296
300
  `task` does not push, and the Agent has no credential to push with. While the run waits at `review`, you push the branch
297
- from the Sandbox. Go into the Sandbox again:
301
+ from the Sandbox. Open a shell in the Sandbox again:
298
302
 
299
303
  ```sh
300
- kubectl -n my-instance exec -it <sandbox> -c harness -- bash
304
+ jr2 workspace shell <workspace>
301
305
  ```
302
306
 
303
307
  In the Sandbox, go to the Worktree. Then push the branch. `<branch>` is the `branch` that you copied:
@@ -314,7 +318,7 @@ Username for 'https://github.com': <you>
314
318
  Password for 'https://<you>@github.com':
315
319
  …
316
320
  To https://github.com/<you>/jr2.git
317
- * [new branch] jr2/task-1f34eef3-5749-4d7c-a16b-488c56ec88cd -> jr2/task-1f34eef3-5749-4d7c-a16b-488c56ec88cd
321
+ * [new branch] jr2/task-1e805d38-68ac-4ec9-acc8-fc5ce6a68f6b -> jr2/task-1e805d38-68ac-4ec9-acc8-fc5ce6a68f6b
318
322
  ```
319
323
 
320
324
  `origin` fetches through the Repo cache of the node, and pushes to your fork. Open your fork on GitHub to see the
@@ -339,7 +343,7 @@ jr2 send <run id> --gate body.review --event approve
339
343
  ```text
340
344
  …
341
345
  → context kind-jr2 / namespace my-instance
342
- delivered approve to gate "body.review" on 290b4be9-1e24-46a2-8cb5-27fabb3bbd3c
346
+ delivered approve to gate "body.review" on 79b90360-cdbf-4538-8611-d52da1b8b914
343
347
  ```
344
348
 
345
349
  In the first terminal, `jr2 run` prints the end of the run, then the final status as one line of JSON, and stops:
@@ -348,21 +352,25 @@ In the first terminal, `jr2 run` prints the end of the run, then the final statu
348
352
  …
349
353
  → active "teardown"
350
354
  → done "done"
351
- {"runId":"290b4be9-1e24-46a2-8cb5-27fabb3bbd3c","workflow":"task",…,"output":{"branch":"jr2/task-1f34eef3-5749-4d7c-a16b-488c56ec88cd"},"worktrees":[{"slot":"target","path":"/work/target/task","branch":"jr2/task-1f34eef3-5749-4d7c-a16b-488c56ec88cd"}]}
355
+ {"runId":"79b90360-cdbf-4538-8611-d52da1b8b914","workflow":"task",…,"output":{"branch":"jr2/task-1e805d38-68ac-4ec9-acc8-fc5ce6a68f6b"},"worktrees":[{"slot":"target","path":"/work/target/task","branch":"jr2/task-1e805d38-68ac-4ec9-acc8-fc5ce6a68f6b"}]}
352
356
  ```
353
357
 
354
358
  ## Make sure that the Sandbox is gone
355
359
 
356
- Use the full run id again:
360
+ List the Workspaces of the run again:
357
361
 
358
362
  ```sh
359
- kubectl -n my-instance get sandboxes -l jr2.dev/run=<run id>
363
+ jr2 workspace ls <run id>
360
364
  ```
361
365
 
362
366
  ```text
363
- No resources found in my-instance namespace.
367
+ …
368
+ → context kind-jr2 / namespace my-instance
369
+ WORKSPACE SANDBOX WORKFLOW PHASE AGE RUN
364
370
  ```
365
371
 
372
+ The list has no row, because the run removed its Sandbox.
373
+
366
374
  ## What happened
367
375
 
368
376
  - `task` is a Machine in a Workspace. The run made a Sandbox for the Workspace and cloned your fork into it. It cut the
@@ -373,8 +381,9 @@ No resources found in my-instance namespace.
373
381
  - The `review` Gate kept the Sandbox while the run waited. `request_changes` sent the coder back to the same
374
382
  conversation. `approve` ended the run, and the end of the Workspace removed the Sandbox. See
375
383
  [Gates](../concepts/gates.md) and [ADR-0012](../reference/decisions/0012-workspace-wrapper-machine.md).
376
- - You used `kubectl exec` to go into the Sandbox. The guide [Look inside a Sandbox](../guides/look-inside-a-sandbox.md)
377
- gives the same commands.
384
+ - You used `jr2 workspace shell` to go into the Sandbox. The shell runs as the Agent runs, so it cannot read the
385
+ credential of the Agent. The guide [Look inside a Sandbox](../guides/look-inside-a-sandbox.md) gives the same
386
+ commands.
378
387
  - You pushed by hand, because `task` does not push. On the next page, a Machine pushes the branch with an
379
388
  [Effect](../reference/glossary.md#effect).
380
389
 
@@ -183,7 +183,7 @@ jr2 up
183
183
  → .env: ANTHROPIC_API_KEY
184
184
  jr2 up — instance "my-instance" → context kind-jr2 / namespace my-instance
185
185
  …
186
- warning: secrets[0] (ANTHROPIC_API_KEY) is named by nothing; goes nowhere — no workspace() lists it, no Agent's provider reads it, and no Effect names it (ADR-0059, ADR-0068)
186
+ warning: secrets[0] (ANTHROPIC_API_KEY) is named by nothing; goes nowhere — no workspace() lists it, no Agent's provider reads it, and no Effect or Hook names it (ADR-0059, ADR-0068)
187
187
  …
188
188
  converged — `jr2 run <workflow>` when ready
189
189
  ```