@jr2/orchestrator 0.5.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. package/console/components/app.ts +55 -15
  2. package/console/components/bottom-drawer.ts +259 -0
  3. package/console/components/drawer.ts +14 -6
  4. package/console/components/fleet.ts +36 -13
  5. package/console/components/machine-pane.ts +61 -5
  6. package/console/components/nav.ts +54 -8
  7. package/console/conversation.ts +161 -0
  8. package/console/login.ts +123 -0
  9. package/console/main.ts +600 -60
  10. package/console/store.ts +611 -64
  11. package/console/style.css +231 -0
  12. package/docs/agents-block.md +2 -2
  13. package/docs/concepts/agents-and-turns.md +142 -29
  14. package/docs/concepts/architecture.md +60 -34
  15. package/docs/concepts/callers-and-hooks.md +205 -0
  16. package/docs/concepts/charts.md +13 -8
  17. package/docs/concepts/composition.md +10 -1
  18. package/docs/concepts/converge.md +7 -2
  19. package/docs/concepts/durable-runs.md +43 -16
  20. package/docs/concepts/effects.md +133 -50
  21. package/docs/concepts/gates.md +95 -25
  22. package/docs/concepts/machines-and-workflows.md +14 -4
  23. package/docs/concepts/pools-and-sources.md +193 -17
  24. package/docs/concepts/secrets-and-trust.md +186 -62
  25. package/docs/concepts/the-menu.md +14 -0
  26. package/docs/concepts/workspaces.md +135 -56
  27. package/docs/get-started/a-model-decides.md +14 -13
  28. package/docs/get-started/an-agent-in-a-sandbox.md +46 -35
  29. package/docs/get-started/connect-a-model.md +1 -1
  30. package/docs/get-started/create-an-instance.md +17 -14
  31. package/docs/get-started/have-your-assistant-write-a-machine.md +111 -40
  32. package/docs/get-started/install.md +4 -0
  33. package/docs/guides/add-tools-to-the-sandbox.md +72 -15
  34. package/docs/guides/answer-a-gate-from-a-forge.md +304 -0
  35. package/docs/guides/answer-a-gate.md +74 -37
  36. package/docs/guides/bind-open-parts.md +29 -12
  37. package/docs/guides/call-an-mcp-tool-from-a-machine.md +572 -0
  38. package/docs/guides/cancel-a-run.md +26 -16
  39. package/docs/guides/cut-a-worktree.md +560 -0
  40. package/docs/guides/fix-a-failed-converge.md +19 -7
  41. package/docs/guides/give-a-caller-a-scope.md +203 -0
  42. package/docs/guides/give-an-agent-mcp-tools.md +422 -0
  43. package/docs/guides/iterate-on-a-machine-on-kind.md +10 -9
  44. package/docs/guides/let-an-agent-write-a-chart.md +14 -7
  45. package/docs/guides/look-inside-a-sandbox.md +115 -42
  46. package/docs/guides/move-from-kind-to-a-shared-cluster.md +16 -0
  47. package/docs/guides/open-the-console.md +63 -14
  48. package/docs/guides/push-and-sign-a-branch.md +67 -21
  49. package/docs/guides/read-a-conversation.md +225 -0
  50. package/docs/guides/run-a-chart-by-name.md +17 -16
  51. package/docs/guides/run-a-command-or-call-a-url.md +84 -41
  52. package/docs/guides/run-one-worker-per-item.md +34 -27
  53. package/docs/guides/set-an-agents-access-and-dials.md +69 -22
  54. package/docs/guides/size-a-workspace.md +19 -6
  55. package/docs/guides/start-a-run-for-each-alert.md +290 -0
  56. package/docs/guides/start-a-run-from-a-run.md +18 -16
  57. package/docs/guides/start-a-run.md +24 -18
  58. package/docs/guides/steer-an-agent.md +332 -0
  59. package/docs/guides/use-a-hosted-provider.md +7 -1
  60. package/docs/guides/watch-a-run.md +146 -117
  61. package/docs/guides/write-a-hook-kind.md +238 -0
  62. package/docs/guides/write-a-tool-as-an-mcp-server.md +351 -0
  63. package/docs/guides/write-a-workflow.md +18 -8
  64. package/docs/llms.txt +110 -23
  65. package/docs/reference/authoring-surface/agent.md +35 -12
  66. package/docs/reference/authoring-surface/answer.md +20 -4
  67. package/docs/reference/authoring-surface/arg.md +28 -6
  68. package/docs/reference/authoring-surface/basic.md +1 -1
  69. package/docs/reference/authoring-surface/bearer.md +1 -1
  70. package/docs/reference/authoring-surface/branchname.md +15 -0
  71. package/docs/reference/authoring-surface/carry.md +23 -0
  72. package/docs/reference/authoring-surface/chartschema.md +1 -1
  73. package/docs/reference/authoring-surface/chartschemafor.md +1 -1
  74. package/docs/reference/authoring-surface/command.md +37 -22
  75. package/docs/reference/authoring-surface/customize.md +4 -1
  76. package/docs/reference/authoring-surface/defineconfig.md +1 -1
  77. package/docs/reference/authoring-surface/defineevent.md +1 -1
  78. package/docs/reference/authoring-surface/dispatch.md +5 -4
  79. package/docs/reference/authoring-surface/fail.md +18 -5
  80. package/docs/reference/authoring-surface/file.md +37 -17
  81. package/docs/reference/authoring-surface/frompick.md +1 -1
  82. package/docs/reference/authoring-surface/gate.md +118 -0
  83. package/docs/reference/authoring-surface/group-agents.md +15 -0
  84. package/docs/reference/authoring-surface/group-charts.md +16 -0
  85. package/docs/reference/authoring-surface/group-composition.md +25 -0
  86. package/docs/reference/authoring-surface/group-config.md +17 -0
  87. package/docs/reference/authoring-surface/group-effects.md +48 -0
  88. package/docs/reference/authoring-surface/group-failure.md +13 -0
  89. package/docs/reference/authoring-surface/group-gates.md +16 -0
  90. package/docs/reference/authoring-surface/group-machine-and-events.md +15 -0
  91. package/docs/reference/authoring-surface/group-steering.md +12 -0
  92. package/docs/reference/authoring-surface/group-types-these-signatures-name.md +137 -0
  93. package/docs/reference/authoring-surface/group-workspaces-and-repos.md +15 -0
  94. package/docs/reference/authoring-surface/http.md +109 -9
  95. package/docs/reference/authoring-surface/isfailure.md +1 -1
  96. package/docs/reference/authoring-surface/isready.md +14 -0
  97. package/docs/reference/authoring-surface/jr2setup.md +14 -4
  98. package/docs/reference/authoring-surface/mcp.md +107 -0
  99. package/docs/reference/authoring-surface/open.md +41 -8
  100. package/docs/reference/authoring-surface/openbody.md +1 -1
  101. package/docs/reference/authoring-surface/paramsof.md +23 -5
  102. package/docs/reference/authoring-surface/pool.md +79 -11
  103. package/docs/reference/authoring-surface/push.md +31 -13
  104. package/docs/reference/authoring-surface/readyof.md +21 -0
  105. package/docs/reference/authoring-surface/reread.md +19 -0
  106. package/docs/reference/authoring-surface/secret.md +26 -5
  107. package/docs/reference/authoring-surface/sentby.md +1 -1
  108. package/docs/reference/authoring-surface/skip.md +22 -0
  109. package/docs/reference/authoring-surface/source.md +59 -4
  110. package/docs/reference/authoring-surface/steer.md +93 -0
  111. package/docs/reference/authoring-surface/text.md +1 -1
  112. package/docs/reference/authoring-surface/type-agentdefinition.md +16 -3
  113. package/docs/reference/authoring-surface/type-agentmcpserver.md +54 -0
  114. package/docs/reference/authoring-surface/type-agentslots.md +4 -1
  115. package/docs/reference/authoring-surface/type-anyframepath.md +14 -0
  116. package/docs/reference/authoring-surface/type-anyworktreepath.md +15 -0
  117. package/docs/reference/authoring-surface/type-bindactors.md +14 -0
  118. package/docs/reference/authoring-surface/type-binding.md +6 -7
  119. package/docs/reference/authoring-surface/type-caller.md +40 -0
  120. package/docs/reference/authoring-surface/type-callerrecord.md +32 -0
  121. package/docs/reference/authoring-surface/type-carry.md +19 -0
  122. package/docs/reference/authoring-surface/type-chainlink.md +3 -3
  123. package/docs/reference/authoring-surface/type-commandinput.md +4 -3
  124. package/docs/reference/authoring-surface/type-commandoptions.md +0 -22
  125. package/docs/reference/authoring-surface/type-commandoutput.md +23 -5
  126. package/docs/reference/authoring-surface/type-customize.md +20 -3
  127. package/docs/reference/authoring-surface/type-delivered.md +17 -0
  128. package/docs/reference/authoring-surface/type-deliveredevent.md +18 -0
  129. package/docs/reference/authoring-surface/type-dispatchdefinition.md +4 -3
  130. package/docs/reference/authoring-surface/type-dispatchoutput.md +9 -2
  131. package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
  132. package/docs/reference/authoring-surface/type-effectbinding.md +12 -2
  133. package/docs/reference/authoring-surface/type-filebody.md +4 -8
  134. package/docs/reference/authoring-surface/type-framepath.md +14 -0
  135. package/docs/reference/authoring-surface/type-gatebinding.md +15 -0
  136. package/docs/reference/authoring-surface/type-gatehooks.md +12 -0
  137. package/docs/reference/authoring-surface/type-gatelook.md +37 -0
  138. package/docs/reference/authoring-surface/type-gateoutcome.md +32 -0
  139. package/docs/reference/authoring-surface/type-gatereply.md +19 -0
  140. package/docs/reference/authoring-surface/type-gateslot.md +21 -0
  141. package/docs/reference/authoring-surface/type-gateslotinput.md +13 -0
  142. package/docs/reference/authoring-surface/type-gateslotparts.md +18 -0
  143. package/docs/reference/authoring-surface/type-gateslots.md +12 -0
  144. package/docs/reference/authoring-surface/type-gatetypesat.md +13 -0
  145. package/docs/reference/authoring-surface/type-hostinjectedinput.md +3 -5
  146. package/docs/reference/authoring-surface/type-httpdefinition.md +6 -3
  147. package/docs/reference/authoring-surface/type-httpfileinput.md +20 -0
  148. package/docs/reference/authoring-surface/type-httpoutput.md +8 -2
  149. package/docs/reference/authoring-surface/type-jr2config.md +37 -2
  150. package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
  151. package/docs/reference/authoring-surface/type-lends.md +5 -1
  152. package/docs/reference/authoring-surface/type-lookoutput.md +14 -0
  153. package/docs/reference/authoring-surface/type-mcpcalloutput.md +78 -0
  154. package/docs/reference/authoring-surface/type-mcpnotreturned.md +42 -0
  155. package/docs/reference/authoring-surface/type-mcpoptions.md +47 -0
  156. package/docs/reference/authoring-surface/type-mcpoutput.md +50 -0
  157. package/docs/reference/authoring-surface/type-mcpreadoutput.md +53 -0
  158. package/docs/reference/authoring-surface/type-mcpremoteserver.md +50 -0
  159. package/docs/reference/authoring-surface/type-mcpserver.md +41 -0
  160. package/docs/reference/authoring-surface/type-mcpstdioserver.md +42 -0
  161. package/docs/reference/authoring-surface/type-namedsecret.md +4 -4
  162. package/docs/reference/authoring-surface/type-namestart.md +77 -0
  163. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +1 -6
  164. package/docs/reference/authoring-surface/type-notworktreekeys.md +12 -0
  165. package/docs/reference/authoring-surface/type-openbodydeclaration.md +1 -1
  166. package/docs/reference/authoring-surface/type-openbodyoutput.md +1 -1
  167. package/docs/reference/authoring-surface/type-opencommandinput.md +7 -2
  168. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +1 -6
  169. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +5 -10
  170. package/docs/reference/authoring-surface/type-poolnextinput.md +22 -0
  171. package/docs/reference/authoring-surface/type-poolspec.md +11 -6
  172. package/docs/reference/authoring-surface/type-pushinput.md +22 -3
  173. package/docs/reference/authoring-surface/type-pushoutput.md +33 -8
  174. package/docs/reference/authoring-surface/type-pushsourcespec.md +38 -0
  175. package/docs/reference/authoring-surface/type-readsourcespec.md +47 -0
  176. package/docs/reference/authoring-surface/type-reposlot.md +6 -6
  177. package/docs/reference/authoring-surface/type-reread.md +18 -0
  178. package/docs/reference/authoring-surface/type-runsconfig.md +41 -0
  179. package/docs/reference/authoring-surface/type-sandboxoptions.md +11 -4
  180. package/docs/reference/authoring-surface/type-sender.md +26 -1
  181. package/docs/reference/authoring-surface/type-sendercallerfield.md +14 -0
  182. package/docs/reference/authoring-surface/type-size.md +1 -1
  183. package/docs/reference/authoring-surface/type-skip.md +19 -0
  184. package/docs/reference/authoring-surface/type-slothandles.md +5 -13
  185. package/docs/reference/authoring-surface/type-sourcehooks.md +23 -0
  186. package/docs/reference/authoring-surface/type-sourcein.md +25 -0
  187. package/docs/reference/authoring-surface/type-sourcereply.md +18 -0
  188. package/docs/reference/authoring-surface/type-sourcespec.md +26 -17
  189. package/docs/reference/authoring-surface/type-sourceverdict.md +49 -0
  190. package/docs/reference/authoring-surface/type-standingpoolmachine.md +12 -0
  191. package/docs/reference/authoring-surface/type-standingpoolspec.md +51 -0
  192. package/docs/reference/authoring-surface/type-steerargs.md +20 -0
  193. package/docs/reference/authoring-surface/type-thinkinglevel.md +1 -1
  194. package/docs/reference/authoring-surface/type-usercontainer.md +1 -1
  195. package/docs/reference/authoring-surface/type-workspaced.md +30 -9
  196. package/docs/reference/authoring-surface/type-workspacehandles.md +5 -6
  197. package/docs/reference/authoring-surface/type-workspaceoptions.md +1 -13
  198. package/docs/reference/authoring-surface/type-worktreefound.md +16 -0
  199. package/docs/reference/authoring-surface/type-worktreeinput.md +56 -0
  200. package/docs/reference/authoring-surface/type-worktreekey.md +14 -0
  201. package/docs/reference/authoring-surface/type-worktreeoptions.md +29 -0
  202. package/docs/reference/authoring-surface/type-worktreeoutput.md +45 -0
  203. package/docs/reference/authoring-surface/type-worktreepath.md +16 -0
  204. package/docs/reference/authoring-surface/type-worktreeready.md +37 -0
  205. package/docs/reference/authoring-surface/type-worktreerefused.md +21 -0
  206. package/docs/reference/authoring-surface/workspace.md +66 -38
  207. package/docs/reference/authoring-surface/worktree.md +71 -0
  208. package/docs/reference/authoring-surface.md +88 -147
  209. package/docs/reference/cli.md +413 -74
  210. package/docs/reference/config.md +248 -6
  211. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +10 -9
  212. package/docs/reference/decisions/0002-duplex-streaming-actor.md +81 -14
  213. package/docs/reference/decisions/0004-worktree-provisioning-model.md +27 -21
  214. package/docs/reference/decisions/0005-sandbox-pod-composition.md +39 -25
  215. package/docs/reference/decisions/0006-agent-control-surface.md +6 -3
  216. package/docs/reference/decisions/0007-durable-machine-state.md +1 -1
  217. package/docs/reference/decisions/0009-cli-and-instance-interface.md +150 -49
  218. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +5 -3
  219. package/docs/reference/decisions/0011-workflow-defined-events.md +256 -12
  220. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +39 -32
  221. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +54 -30
  222. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +53 -27
  223. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +26 -16
  224. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +9 -7
  225. package/docs/reference/decisions/0017-pool-over-a-source-port.md +226 -10
  226. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +15 -7
  227. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +53 -27
  228. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +16 -2
  229. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +4 -4
  230. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +124 -43
  231. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +36 -38
  232. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +10 -5
  233. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +16 -7
  234. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +36 -19
  235. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +18 -10
  236. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +3 -2
  237. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +23 -15
  238. package/docs/reference/decisions/0032-the-console-unlocks-with-a-callers-credential.md +101 -0
  239. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +22 -22
  240. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +6 -6
  241. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +13 -12
  242. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +23 -23
  243. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +6 -1
  244. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +57 -46
  245. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +5 -0
  246. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +20 -8
  247. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +55 -40
  248. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +14 -13
  249. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +31 -24
  250. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +18 -10
  251. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +137 -86
  252. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +165 -91
  253. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +7 -7
  254. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +2 -2
  255. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md +109 -0
  256. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +53 -23
  257. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +221 -153
  258. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +161 -96
  259. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +32 -9
  260. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +48 -33
  261. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +56 -30
  262. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +35 -25
  263. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +1 -1
  264. package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +296 -0
  265. package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +347 -0
  266. package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
  267. package/docs/reference/decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md +285 -0
  268. package/docs/reference/decisions/0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md +198 -0
  269. package/docs/reference/decisions.md +9 -4
  270. package/docs/reference/glossary.md +138 -45
  271. package/docs/reference/hooks.md +1077 -0
  272. package/docs/reference/http-api.md +988 -123
  273. package/docs/reference/machines.md +0 -1
  274. package/docs/why.md +7 -5
  275. package/package.json +10 -3
  276. package/src/actor.ts +194 -42
  277. package/src/agent.ts +56 -6
  278. package/src/ambient.ts +3 -5
  279. package/src/assembly-checks.ts +530 -4
  280. package/src/callers.ts +245 -0
  281. package/src/chart.ts +238 -26
  282. package/src/charts.ts +9 -2
  283. package/src/config.ts +566 -7
  284. package/src/conversations.ts +453 -0
  285. package/src/custodian.lua +17 -5
  286. package/src/custodian.ts +257 -77
  287. package/src/customize.ts +154 -7
  288. package/src/dispatch-effect.ts +142 -26
  289. package/src/durability.ts +2 -0
  290. package/src/duration.ts +20 -0
  291. package/src/effect-checks.ts +52 -4
  292. package/src/effect.ts +401 -132
  293. package/src/effector-wire.ts +278 -30
  294. package/src/fail.ts +17 -4
  295. package/src/gate-brand.ts +16 -0
  296. package/src/gate.ts +634 -48
  297. package/src/harness-client.ts +84 -35
  298. package/src/harness-env.ts +33 -0
  299. package/src/held-secrets.ts +197 -39
  300. package/src/holds.ts +5 -2
  301. package/src/hook-kinds/discord.ts +92 -0
  302. package/src/hook-kinds/door.ts +107 -0
  303. package/src/hook-kinds/github.ts +89 -0
  304. package/src/hook-kinds/gitlab.ts +72 -0
  305. package/src/hook-kinds/hook.ts +7 -0
  306. package/src/hook-kinds/index.ts +465 -0
  307. package/src/hook-kinds/slack.ts +151 -0
  308. package/src/hook-kinds/webhook.ts +113 -0
  309. package/src/hooks.ts +506 -0
  310. package/src/http-effect.ts +164 -70
  311. package/src/http.ts +933 -99
  312. package/src/index.ts +30 -5
  313. package/src/instance.ts +196 -26
  314. package/src/internal.ts +23 -3
  315. package/src/kube-client.ts +6 -3
  316. package/src/machine-doc.ts +29 -5
  317. package/src/mcp-effect.ts +424 -0
  318. package/src/model-keys.ts +24 -0
  319. package/src/mount-digest.ts +41 -7
  320. package/src/names.ts +82 -7
  321. package/src/notices.ts +576 -59
  322. package/src/open-body.ts +110 -30
  323. package/src/open-effect.ts +17 -4
  324. package/src/open.ts +40 -7
  325. package/src/parts.ts +125 -35
  326. package/src/party.ts +35 -1
  327. package/src/pick.ts +43 -9
  328. package/src/pool.ts +1172 -172
  329. package/src/registration.ts +249 -17
  330. package/src/repo-identity.ts +7 -3
  331. package/src/repos.ts +20 -2
  332. package/src/run-host.ts +1614 -190
  333. package/src/run-list.ts +191 -0
  334. package/src/sandbox-effect.ts +217 -66
  335. package/src/sandbox-kube.ts +116 -21
  336. package/src/secret-placement.ts +248 -35
  337. package/src/server.ts +53 -4
  338. package/src/setup.ts +158 -38
  339. package/src/snapshot-store.ts +565 -48
  340. package/src/steer.ts +111 -0
  341. package/src/stub-harness.ts +6 -2
  342. package/src/tokens.ts +362 -46
  343. package/src/vocabulary.ts +23 -4
  344. package/src/wire.ts +27 -10
  345. package/src/workspace.ts +314 -259
  346. package/src/worktree.ts +249 -0
  347. package/docs/reference/authoring-surface/type-noslotspec.md +0 -26
  348. package/docs/reference/authoring-surface/type-workspacespec.md +0 -37
  349. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +0 -61
  350. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +0 -65
package/src/callers.ts ADDED
@@ -0,0 +1,245 @@
1
+ // The JWT issuers of the Caller seam, and the record of a Caller on a run (ADR-0078).
2
+ //
3
+ // The seam itself (its order, the Instance token, the named `jr2c_` tokens) is in tokens.ts. This
4
+ // file adds its third authenticator: a JWT from an identity provider that the config declares, which
5
+ // the Orchestrator checks against the provider's published keys. That fetch is the Orchestrator's
6
+ // one call out of the Instance, and it spends no secret (ADR-0068).
7
+ //
8
+ // It also holds what a run records of a Caller: the Caller without its scopes, because a run
9
+ // records who acted, not what that credential could do then.
10
+
11
+ import { createRemoteJWKSet, customFetch, decodeJwt, jwtVerify, type JWTPayload } from "jose";
12
+ import { SCOPES, type AnswerGrant, type GateGrant, type JwtIssuer, type Scopes } from "./config.ts";
13
+ import type { Caller, CallerAnswer, CallerAuthenticator } from "./tokens.ts";
14
+
15
+ // ---- What a run records ------------------------------------------------------------------------
16
+
17
+ /** A Caller as a run records it (ADR-0078): who acted, and the party it says it acted for. Never its
18
+ * scopes. */
19
+ export type CallerRecord = {
20
+ via: Caller["via"];
21
+ name: string;
22
+ /** The name that jr2 shows, when it is not `name` (a JWT's `email`). */
23
+ shown?: string;
24
+ /** The Caller's claim about the party it acts for. jr2 records it and never checks it, except for
25
+ * a Hook, whose signature covers it. */
26
+ onBehalfOf?: string;
27
+ };
28
+
29
+ /**
30
+ * Who started a run (ADR-0078): a Caller on a guarded route; a run, through a local `dispatch`
31
+ * (ADR-0071); or jr2 itself, for a standing run (ADR-0017), at `boot`, at a `publish`, at a
32
+ * `hand-over`, or at a `resume`. A run that a foreign `dispatch` started records the Caller whose
33
+ * credential the other Instance sent.
34
+ */
35
+ export type RunStarter =
36
+ | ({ kind: "caller" } & CallerRecord)
37
+ | { kind: "run"; run: string; slot: string }
38
+ | { kind: "jr2"; why: "boot" | "publish" | "hand-over" | "resume" };
39
+
40
+ /** One answer to a Gate of a run, and the Caller that gave it (ADR-0011, ADR-0078). `delivery` is
41
+ * the Hook's delivery id when a Hook gave it: the run's `(delivery, gate)` pairs, so a restore never
42
+ * gives one delivery twice to one Gate. */
43
+ export type GateAnswer = { gate: string; type: string; at: string; answeredBy: CallerRecord; delivery?: string };
44
+
45
+ /** How many Gate answers a run keeps, the last ones: a standing run's Gate can take an answer for
46
+ * each delivery, and the run's blob is saved on each transition. */
47
+ export const GATE_ANSWERS_KEPT = 100;
48
+
49
+ /** The record of a Caller: the Caller without its scopes. */
50
+ export function callerRecord(caller: Caller): CallerRecord {
51
+ return {
52
+ via: caller.via,
53
+ name: caller.name,
54
+ ...(caller.shown !== undefined ? { shown: caller.shown } : {}),
55
+ ...(caller.onBehalfOf !== undefined ? { onBehalfOf: caller.onBehalfOf } : {}),
56
+ };
57
+ }
58
+
59
+ /** A Caller as a line names it: `token/ci`, then the name that jr2 shows and the party it acts for. */
60
+ export function callerText(caller: CallerRecord): string {
61
+ return (
62
+ `${caller.via}/${caller.name}` +
63
+ (caller.shown !== undefined ? ` (${caller.shown})` : "") +
64
+ (caller.onBehalfOf !== undefined ? ` on behalf of ${caller.onBehalfOf}` : "")
65
+ );
66
+ }
67
+
68
+ // ---- The JWT authenticator ---------------------------------------------------------------------
69
+
70
+ /** The algorithms a JWT may be signed with: the asymmetric ones alone, because a published key
71
+ * must not be able to sign. */
72
+ const ALGORITHMS = ["RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "ES256", "ES384", "ES512", "EdDSA"];
73
+
74
+ /** How far the issuer's clock and ours may differ, for `exp` and `nbf`. */
75
+ const CLOCK_TOLERANCE_S = 30;
76
+
77
+ /** How long the keys are kept before a JWT makes a fetch again. */
78
+ const KEYS_MAX_AGE_MS = 10 * 60_000;
79
+
80
+ /** The shortest time between two fetches of the keys, for a JWT whose key is not known; also how
81
+ * long a failed fetch of the discovery document is kept before a JWT makes a fetch again. */
82
+ const KEYS_COOLDOWN_MS = 30_000;
83
+
84
+ /** How long one fetch of the discovery document or the keys may take. */
85
+ const FETCH_TIMEOUT_MS = 5_000;
86
+
87
+ /** The claims a {@link JwtIssuer} reads when its `claims` names no other. */
88
+ const DEFAULT_CLAIMS = { name: "sub", shown: "email", groups: "groups" } as const;
89
+
90
+ /** The fetch that the JWT authenticator uses: the global one, or a test's. */
91
+ export type JwksFetch = (url: string, init: { signal?: AbortSignal }) => Promise<Response>;
92
+
93
+ /** The options of {@link jwtAuthenticator}, for tests. */
94
+ export type JwtAuthenticatorOptions = {
95
+ /** The fetch for the discovery document and the keys. Default: the global `fetch`. */
96
+ fetch?: JwksFetch;
97
+ /** The shortest time between two fetches of the keys, and of a discovery that failed. Default: 30 s. */
98
+ cooldownMs?: number;
99
+ /** The clock, in milliseconds, that measures the cooldown of a failed discovery. Default: `Date.now`. */
100
+ now?: () => number;
101
+ };
102
+
103
+ type KeySet = ReturnType<typeof createRemoteJWKSet>;
104
+
105
+ /**
106
+ * The JWT issuers' authenticator (ADR-0078). It claims a bearer that is a JWT whose `iss` claim is
107
+ * the `issuer` of an entry in `issuers()`, which is read on each request, so a publish applies with
108
+ * no restart. A claimed JWT that fails a check is "mine, and bad" (401). A JWT of an issuer that no
109
+ * entry names is not claimed.
110
+ */
111
+ export function jwtAuthenticator(
112
+ issuers: () => readonly JwtIssuer[],
113
+ opts: JwtAuthenticatorOptions = {},
114
+ ): CallerAuthenticator {
115
+ const doFetch: JwksFetch = opts.fetch ?? ((url, init) => fetch(url, init));
116
+ const cooldownMs = opts.cooldownMs ?? KEYS_COOLDOWN_MS;
117
+ const now = opts.now ?? Date.now;
118
+ // The key sets, by the issuer and its keys' URL: an entry that a publish changes gets a new one.
119
+ // `failedAt`: when its discovery failed. The failure is kept for the cooldown, so a flood of JWTs
120
+ // that name an issuer whose discovery fails makes at most one fetch in each cooldown.
121
+ const keySets = new Map<string, { keySet: Promise<KeySet>; failedAt?: number }>();
122
+ const idOf = (entry: JwtIssuer): string => JSON.stringify([entry.issuer, entry.jwks ?? null]);
123
+
124
+ const keySetOf = (entry: JwtIssuer): Promise<KeySet> => {
125
+ const id = idOf(entry);
126
+ const found = keySets.get(id);
127
+ if (found && (found.failedAt === undefined || now() - found.failedAt < cooldownMs)) return found.keySet;
128
+ const slot: { keySet: Promise<KeySet>; failedAt?: number } = {
129
+ keySet: (async () => {
130
+ const url = entry.jwks ?? (await discoverJwks(entry.issuer, doFetch));
131
+ return createRemoteJWKSet(new URL(url), {
132
+ cacheMaxAge: KEYS_MAX_AGE_MS,
133
+ cooldownDuration: cooldownMs,
134
+ timeoutDuration: FETCH_TIMEOUT_MS,
135
+ [customFetch]: (u, init) => doFetch(u, { signal: init.signal }),
136
+ });
137
+ })(),
138
+ };
139
+ slot.keySet.catch(() => {
140
+ slot.failedAt = now();
141
+ });
142
+ keySets.set(id, slot);
143
+ return slot.keySet;
144
+ };
145
+
146
+ /** Drop the key sets of each issuer and keys' URL that the config no longer names: the cache
147
+ * holds at most one entry for each entry of `issuers()`. */
148
+ const prune = (current: readonly JwtIssuer[]): void => {
149
+ if (keySets.size === 0) return;
150
+ const named = new Set(current.map(idOf));
151
+ for (const id of keySets.keys()) if (!named.has(id)) keySets.delete(id);
152
+ };
153
+
154
+ return async (bearer): Promise<CallerAnswer> => {
155
+ if (bearer.split(".").length !== 3) return undefined;
156
+ let unverified: JWTPayload;
157
+ try {
158
+ unverified = decodeJwt(bearer);
159
+ } catch {
160
+ return undefined;
161
+ }
162
+ const current = issuers();
163
+ prune(current);
164
+ const entry = current.find((i) => i.issuer === unverified.iss);
165
+ if (!entry) return undefined;
166
+
167
+ let payload: JWTPayload;
168
+ try {
169
+ ({ payload } = await jwtVerify(bearer, await keySetOf(entry), {
170
+ issuer: entry.issuer,
171
+ audience: entry.audience,
172
+ algorithms: ALGORITHMS,
173
+ clockTolerance: CLOCK_TOLERANCE_S,
174
+ requiredClaims: ["exp"],
175
+ }));
176
+ } catch (err) {
177
+ return { refused: `a JWT of ${entry.issuer} did not verify: ${err instanceof Error ? err.message : err}` };
178
+ }
179
+
180
+ const claims = { ...DEFAULT_CLAIMS, ...entry.claims };
181
+ const name = payload[claims.name];
182
+ if (typeof name !== "string" || name === "") {
183
+ return { refused: `a JWT of ${entry.issuer} has no "${claims.name}" claim, which names its Caller` };
184
+ }
185
+ const shown = payload[claims.shown];
186
+ return {
187
+ via: "jwt",
188
+ name,
189
+ ...(typeof shown === "string" && shown !== "" && shown !== name ? { shown } : {}),
190
+ may: joinScopes(
191
+ valuesOf(payload[claims.groups]).flatMap((v) => (Object.hasOwn(entry.groups, v) ? [entry.groups[v]!] : [])),
192
+ ),
193
+ };
194
+ };
195
+ }
196
+
197
+ /** The `jwks_uri` of an issuer's OIDC discovery document. */
198
+ async function discoverJwks(issuer: string, doFetch: JwksFetch): Promise<string> {
199
+ const url = `${issuer.replace(/\/+$/, "")}/.well-known/openid-configuration`;
200
+ const res = await doFetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
201
+ if (!res.ok) throw new Error(`${url} answered ${res.status}`);
202
+ const doc = (await res.json()) as { jwks_uri?: unknown };
203
+ if (typeof doc.jwks_uri !== "string") throw new Error(`${url} names no jwks_uri`);
204
+ return doc.jwks_uri;
205
+ }
206
+
207
+ /** The values of a groups claim: a list of strings, or one string. */
208
+ function valuesOf(claim: unknown): string[] {
209
+ if (typeof claim === "string") return [claim];
210
+ if (Array.isArray(claim)) return claim.filter((v): v is string => typeof v === "string");
211
+ return [];
212
+ }
213
+
214
+ /** The scopes of each grant, joined: a scope that one gives for every Workflow is for every
215
+ * Workflow, and lists join. A JWT whose groups give one team's Gate and another team's Gate of one
216
+ * Workflow may answer both. */
217
+ export function joinScopes(grants: readonly Scopes[]): Scopes {
218
+ const out: Record<string, AnswerGrant> = {};
219
+ for (const scope of SCOPES) {
220
+ let joined: AnswerGrant | undefined;
221
+ for (const may of grants) {
222
+ const grant: AnswerGrant | undefined = may[scope];
223
+ if (grant === undefined) continue;
224
+ if (grant === "all" || joined === "all") joined = "all";
225
+ else joined = joinItems(joined ?? [], grant);
226
+ }
227
+ if (joined !== undefined) out[scope] = joined;
228
+ }
229
+ return out as Scopes;
230
+ }
231
+
232
+ /** Two lists of one scope, joined: each Workflow name once, and each `answer` item that names Gates
233
+ * once (ADR-0078). An item that names Gates stays beside a bare name of its Workflow, which opens
234
+ * them anyway. */
235
+ function joinItems(a: readonly (string | GateGrant)[], b: readonly (string | GateGrant)[]): (string | GateGrant)[] {
236
+ const seen = new Set<string>();
237
+ const out: (string | GateGrant)[] = [];
238
+ for (const item of [...a, ...b]) {
239
+ const key = typeof item === "string" ? item : JSON.stringify([item.workflow, [...item.gates].sort()]);
240
+ if (seen.has(key)) continue;
241
+ seen.add(key);
242
+ out.push(item);
243
+ }
244
+ return out;
245
+ }