@jr2/orchestrator 0.3.0 → 0.5.1

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 (354) hide show
  1. package/README.md +20 -2
  2. package/bin/server.ts +7 -4
  3. package/console/canvas.ts +27 -14
  4. package/console/components/app.ts +12 -1
  5. package/console/components/fleet.ts +52 -6
  6. package/console/components/machine-pane.ts +128 -2
  7. package/console/components/nav.ts +1 -1
  8. package/console/components/schema-form.ts +12 -4
  9. package/console/json-field.ts +23 -0
  10. package/console/main.ts +122 -39
  11. package/console/store.ts +321 -21
  12. package/console/style.css +111 -0
  13. package/docs/agents-block.md +5 -0
  14. package/docs/concepts/agents-and-turns.md +442 -0
  15. package/docs/concepts/architecture.md +230 -0
  16. package/docs/concepts/charts.md +143 -0
  17. package/docs/concepts/composition.md +357 -0
  18. package/docs/concepts/converge.md +254 -0
  19. package/docs/concepts/durable-runs.md +189 -0
  20. package/docs/concepts/effects.md +603 -0
  21. package/docs/concepts/gates.md +208 -0
  22. package/docs/concepts/machines-and-workflows.md +289 -0
  23. package/docs/concepts/pools-and-sources.md +230 -0
  24. package/docs/concepts/secrets-and-trust.md +222 -0
  25. package/docs/concepts/the-menu.md +378 -0
  26. package/docs/concepts/workspaces.md +678 -0
  27. package/docs/get-started/a-model-decides.md +563 -0
  28. package/docs/get-started/an-agent-in-a-sandbox.md +381 -0
  29. package/docs/get-started/connect-a-model.md +248 -0
  30. package/docs/get-started/create-an-instance.md +194 -0
  31. package/docs/get-started/have-your-assistant-write-a-machine.md +922 -0
  32. package/docs/get-started/install.md +126 -0
  33. package/docs/guides/add-tools-to-the-sandbox.md +295 -0
  34. package/docs/guides/answer-a-gate.md +304 -0
  35. package/docs/guides/bind-open-parts.md +223 -0
  36. package/docs/guides/cancel-a-run.md +146 -0
  37. package/docs/guides/clean-up.md +206 -0
  38. package/docs/guides/fix-a-failed-converge.md +361 -0
  39. package/docs/guides/give-the-cluster-git-credentials.md +222 -0
  40. package/docs/guides/iterate-on-a-machine-on-kind.md +190 -0
  41. package/docs/guides/let-an-agent-write-a-chart.md +226 -0
  42. package/docs/guides/look-inside-a-sandbox.md +174 -0
  43. package/docs/guides/mirror-the-kit-images.md +176 -0
  44. package/docs/guides/move-from-kind-to-a-shared-cluster.md +302 -0
  45. package/docs/guides/open-the-console.md +91 -0
  46. package/docs/guides/publish-a-machine-as-an-npm-package.md +350 -0
  47. package/docs/guides/push-and-sign-a-branch.md +486 -0
  48. package/docs/guides/run-a-chart-by-name.md +270 -0
  49. package/docs/guides/run-a-command-or-call-a-url.md +544 -0
  50. package/docs/guides/run-one-worker-per-item.md +321 -0
  51. package/docs/guides/set-an-agents-access-and-dials.md +375 -0
  52. package/docs/guides/size-a-workspace.md +204 -0
  53. package/docs/guides/start-a-run-from-a-run.md +237 -0
  54. package/docs/guides/start-a-run.md +207 -0
  55. package/docs/guides/upgrade-the-kit-version.md +240 -0
  56. package/docs/guides/use-a-hosted-provider.md +208 -0
  57. package/docs/guides/use-a-local-model.md +313 -0
  58. package/docs/guides/watch-a-run.md +326 -0
  59. package/docs/guides/write-a-workflow.md +318 -0
  60. package/docs/index.md +44 -0
  61. package/docs/llms.txt +216 -0
  62. package/docs/reference/authoring-surface/agent.md +128 -0
  63. package/docs/reference/authoring-surface/answer.md +140 -0
  64. package/docs/reference/authoring-surface/arg.md +60 -0
  65. package/docs/reference/authoring-surface/basic.md +62 -0
  66. package/docs/reference/authoring-surface/bearer.md +53 -0
  67. package/docs/reference/authoring-surface/chartschema.md +23 -0
  68. package/docs/reference/authoring-surface/chartschemafor.md +48 -0
  69. package/docs/reference/authoring-surface/command.md +191 -0
  70. package/docs/reference/authoring-surface/customize.md +64 -0
  71. package/docs/reference/authoring-surface/defineconfig.md +41 -0
  72. package/docs/reference/authoring-surface/defineevent.md +114 -0
  73. package/docs/reference/authoring-surface/dispatch.md +126 -0
  74. package/docs/reference/authoring-surface/fail.md +83 -0
  75. package/docs/reference/authoring-surface/file.md +122 -0
  76. package/docs/reference/authoring-surface/frompick.md +277 -0
  77. package/docs/reference/authoring-surface/http.md +159 -0
  78. package/docs/reference/authoring-surface/isfailure.md +58 -0
  79. package/docs/reference/authoring-surface/jr2setup.md +237 -0
  80. package/docs/reference/authoring-surface/open.md +74 -0
  81. package/docs/reference/authoring-surface/openbody.md +86 -0
  82. package/docs/reference/authoring-surface/paramsof.md +125 -0
  83. package/docs/reference/authoring-surface/pool.md +186 -0
  84. package/docs/reference/authoring-surface/push.md +112 -0
  85. package/docs/reference/authoring-surface/secret.md +118 -0
  86. package/docs/reference/authoring-surface/sentby.md +83 -0
  87. package/docs/reference/authoring-surface/source.md +51 -0
  88. package/docs/reference/authoring-surface/text.md +62 -0
  89. package/docs/reference/authoring-surface/type-agentdeclaration.md +27 -0
  90. package/docs/reference/authoring-surface/type-agentdefinition.md +80 -0
  91. package/docs/reference/authoring-surface/type-agentlogic.md +15 -0
  92. package/docs/reference/authoring-surface/type-agentslots.md +12 -0
  93. package/docs/reference/authoring-surface/type-answeraction.md +12 -0
  94. package/docs/reference/authoring-surface/type-arg.md +19 -0
  95. package/docs/reference/authoring-surface/type-argnames.md +12 -0
  96. package/docs/reference/authoring-surface/type-binding.md +62 -0
  97. package/docs/reference/authoring-surface/type-bindingof.md +12 -0
  98. package/docs/reference/authoring-surface/type-bodyacceptsdoor.md +15 -0
  99. package/docs/reference/authoring-surface/type-bodyacceptsslots.md +17 -0
  100. package/docs/reference/authoring-surface/type-ceilings.md +41 -0
  101. package/docs/reference/authoring-surface/type-chainlink.md +19 -0
  102. package/docs/reference/authoring-surface/type-chartaction.md +46 -0
  103. package/docs/reference/authoring-surface/type-chartagent.md +28 -0
  104. package/docs/reference/authoring-surface/type-chartevent.md +20 -0
  105. package/docs/reference/authoring-surface/type-chartguard.md +20 -0
  106. package/docs/reference/authoring-surface/type-chartinvoke.md +28 -0
  107. package/docs/reference/authoring-surface/type-chartjson.md +14 -0
  108. package/docs/reference/authoring-surface/type-chartstate.md +46 -0
  109. package/docs/reference/authoring-surface/type-charttext.md +40 -0
  110. package/docs/reference/authoring-surface/type-charttransition.md +22 -0
  111. package/docs/reference/authoring-surface/type-charttransitions.md +14 -0
  112. package/docs/reference/authoring-surface/type-chartvalue.md +15 -0
  113. package/docs/reference/authoring-surface/type-childat.md +12 -0
  114. package/docs/reference/authoring-surface/type-childslots.md +12 -0
  115. package/docs/reference/authoring-surface/type-commandenv.md +15 -0
  116. package/docs/reference/authoring-surface/type-commandinput.md +19 -0
  117. package/docs/reference/authoring-surface/type-commandoptions.md +78 -0
  118. package/docs/reference/authoring-surface/type-commandoutput.md +104 -0
  119. package/docs/reference/authoring-surface/type-customize.md +109 -0
  120. package/docs/reference/authoring-surface/type-dispatchdefinition.md +39 -0
  121. package/docs/reference/authoring-surface/type-dispatchinput.md +14 -0
  122. package/docs/reference/authoring-surface/type-dispatchopenparts.md +14 -0
  123. package/docs/reference/authoring-surface/type-dispatchoutput.md +65 -0
  124. package/docs/reference/authoring-surface/type-duration.md +15 -0
  125. package/docs/reference/authoring-surface/type-effectbinding.md +93 -0
  126. package/docs/reference/authoring-surface/type-effectlogic.md +12 -0
  127. package/docs/reference/authoring-surface/type-envopenparts.md +14 -0
  128. package/docs/reference/authoring-surface/type-eventaudience.md +27 -0
  129. package/docs/reference/authoring-surface/type-eventdef.md +45 -0
  130. package/docs/reference/authoring-surface/type-eventfrom.md +16 -0
  131. package/docs/reference/authoring-surface/type-eventsemantics.md +26 -0
  132. package/docs/reference/authoring-surface/type-execoutcome.md +34 -0
  133. package/docs/reference/authoring-surface/type-failslot.md +14 -0
  134. package/docs/reference/authoring-surface/type-failure.md +19 -0
  135. package/docs/reference/authoring-surface/type-failureinput.md +20 -0
  136. package/docs/reference/authoring-surface/type-filebody.md +41 -0
  137. package/docs/reference/authoring-surface/type-frominput.md +14 -0
  138. package/docs/reference/authoring-surface/type-hostinjectedinput.md +58 -0
  139. package/docs/reference/authoring-surface/type-httpdefinition.md +101 -0
  140. package/docs/reference/authoring-surface/type-httpinput.md +12 -0
  141. package/docs/reference/authoring-surface/type-httpmethod.md +14 -0
  142. package/docs/reference/authoring-surface/type-httpopenparts.md +12 -0
  143. package/docs/reference/authoring-surface/type-httpoutput.md +89 -0
  144. package/docs/reference/authoring-surface/type-httprequestparts.md +33 -0
  145. package/docs/reference/authoring-surface/type-jr2config.md +126 -0
  146. package/docs/reference/authoring-surface/type-lends.md +38 -0
  147. package/docs/reference/authoring-surface/type-logicat.md +12 -0
  148. package/docs/reference/authoring-surface/type-markedif.md +12 -0
  149. package/docs/reference/authoring-surface/type-mergedactors.md +12 -0
  150. package/docs/reference/authoring-surface/type-namedsecret.md +25 -0
  151. package/docs/reference/authoring-surface/type-noslotspec.md +26 -0
  152. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +32 -0
  153. package/docs/reference/authoring-surface/type-openbodydeclaration.md +40 -0
  154. package/docs/reference/authoring-surface/type-openbodylogic.md +14 -0
  155. package/docs/reference/authoring-surface/type-openbodyoutput.md +42 -0
  156. package/docs/reference/authoring-surface/type-openbodyslot.md +18 -0
  157. package/docs/reference/authoring-surface/type-opencommandinput.md +17 -0
  158. package/docs/reference/authoring-surface/type-openeffect.md +12 -0
  159. package/docs/reference/authoring-surface/type-openeffectslots.md +12 -0
  160. package/docs/reference/authoring-surface/type-openof.md +12 -0
  161. package/docs/reference/authoring-surface/type-opensecret.md +23 -0
  162. package/docs/reference/authoring-surface/type-opensecretmark.md +12 -0
  163. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +31 -0
  164. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +32 -0
  165. package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
  166. package/docs/reference/authoring-surface/type-poolspec.md +60 -0
  167. package/docs/reference/authoring-surface/type-pushinput.md +15 -0
  168. package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
  169. package/docs/reference/authoring-surface/type-pushoutput.md +124 -0
  170. package/docs/reference/authoring-surface/type-pushwireoutput.md +12 -0
  171. package/docs/reference/authoring-surface/type-quantity.md +14 -0
  172. package/docs/reference/authoring-surface/type-reached.md +12 -0
  173. package/docs/reference/authoring-surface/type-reposlot.md +70 -0
  174. package/docs/reference/authoring-surface/type-reposlotsof.md +16 -0
  175. package/docs/reference/authoring-surface/type-sandboxoptions.md +77 -0
  176. package/docs/reference/authoring-surface/type-secret.md +25 -0
  177. package/docs/reference/authoring-surface/type-sender.md +19 -0
  178. package/docs/reference/authoring-surface/type-sentbyguards.md +12 -0
  179. package/docs/reference/authoring-surface/type-size.md +85 -0
  180. package/docs/reference/authoring-surface/type-slothandles.md +32 -0
  181. package/docs/reference/authoring-surface/type-sourcenextresult.md +17 -0
  182. package/docs/reference/authoring-surface/type-sourcespec.md +44 -0
  183. package/docs/reference/authoring-surface/type-syncstate.md +12 -0
  184. package/docs/reference/authoring-surface/type-text.md +19 -0
  185. package/docs/reference/authoring-surface/type-textpart.md +24 -0
  186. package/docs/reference/authoring-surface/type-thinkinglevel.md +39 -0
  187. package/docs/reference/authoring-surface/type-toparameterizedobject.md +14 -0
  188. package/docs/reference/authoring-surface/type-toprovidedactor.md +14 -0
  189. package/docs/reference/authoring-surface/type-unknown.md +21 -0
  190. package/docs/reference/authoring-surface/type-usercontainer.md +77 -0
  191. package/docs/reference/authoring-surface/type-withoutemptyindex.md +20 -0
  192. package/docs/reference/authoring-surface/type-workflowevent.md +46 -0
  193. package/docs/reference/authoring-surface/type-workspaceaccess.md +16 -0
  194. package/docs/reference/authoring-surface/type-workspaced.md +94 -0
  195. package/docs/reference/authoring-surface/type-workspacehandles.md +23 -0
  196. package/docs/reference/authoring-surface/type-workspacemachine.md +29 -0
  197. package/docs/reference/authoring-surface/type-workspaceof.md +13 -0
  198. package/docs/reference/authoring-surface/type-workspaceoptions.md +39 -0
  199. package/docs/reference/authoring-surface/type-workspacespec.md +37 -0
  200. package/docs/reference/authoring-surface/workspace.md +259 -0
  201. package/docs/reference/authoring-surface.md +197 -0
  202. package/docs/reference/cli.md +433 -0
  203. package/docs/reference/config.md +756 -0
  204. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +113 -0
  205. package/docs/reference/decisions/0002-duplex-streaming-actor.md +110 -0
  206. package/docs/reference/decisions/0004-worktree-provisioning-model.md +124 -0
  207. package/docs/reference/decisions/0005-sandbox-pod-composition.md +230 -0
  208. package/docs/reference/decisions/0006-agent-control-surface.md +47 -0
  209. package/docs/reference/decisions/0007-durable-machine-state.md +109 -0
  210. package/docs/reference/decisions/0008-orchestrator-as-deployed-app.md +60 -0
  211. package/docs/reference/decisions/0009-cli-and-instance-interface.md +275 -0
  212. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +160 -0
  213. package/docs/reference/decisions/0011-workflow-defined-events.md +141 -0
  214. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +103 -0
  215. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +157 -0
  216. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +95 -0
  217. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +265 -0
  218. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +124 -0
  219. package/docs/reference/decisions/0017-pool-over-a-source-port.md +69 -0
  220. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +111 -0
  221. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +275 -0
  222. package/docs/reference/decisions/0020-private-ca-trust-is-a-named-concept.md +60 -0
  223. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +100 -0
  224. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +103 -0
  225. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +176 -0
  226. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +439 -0
  227. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +65 -0
  228. package/docs/reference/decisions/0026-a-turn-that-is-over-is-never-prompted.md +62 -0
  229. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +237 -0
  230. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +89 -0
  231. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +154 -0
  232. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +100 -0
  233. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +190 -0
  234. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +61 -0
  235. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +127 -0
  236. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +57 -0
  237. package/docs/reference/decisions/0035-a-runaway-turn-is-ended-rerolled-once-then-a-fault.md +92 -0
  238. package/docs/reference/decisions/0036-a-turn-compacts-its-own-context-mid-flight.md +138 -0
  239. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +196 -0
  240. package/docs/reference/decisions/0038-jr2-up-builds-every-image-it-deploys.md +170 -0
  241. package/docs/reference/decisions/0039-image-garbage-collects-by-reachability.md +115 -0
  242. package/docs/reference/decisions/0041-a-build-the-host-already-holds-is-not-spent-again.md +64 -0
  243. package/docs/reference/decisions/0042-ready-is-not-routable-an-admission-retries-its-connection.md +142 -0
  244. package/docs/reference/decisions/0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md +170 -0
  245. package/docs/reference/decisions/0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md +85 -0
  246. package/docs/reference/decisions/0045-the-platform-joins-the-image-address-and-the-cluster-chooses-it.md +82 -0
  247. package/docs/reference/decisions/0046-a-converge-failure-carries-its-diagnosis.md +34 -0
  248. package/docs/reference/decisions/0047-the-git-ssh-key-source-is-the-users-choice.md +47 -0
  249. package/docs/reference/decisions/0048-the-orchestrator-boots-without-its-repos.md +36 -0
  250. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +126 -0
  251. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +73 -0
  252. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +227 -0
  253. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +81 -0
  254. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +111 -0
  255. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +126 -0
  256. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +107 -0
  257. package/docs/reference/decisions/0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md +62 -0
  258. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +152 -0
  259. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +106 -0
  260. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +384 -0
  261. package/docs/reference/decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md +111 -0
  262. package/docs/reference/decisions/0061-a-memory-kill-ends-the-agents-processes-not-the-harness.md +83 -0
  263. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +212 -0
  264. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +83 -0
  265. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +79 -0
  266. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +65 -0
  267. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +550 -0
  268. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +430 -0
  269. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +464 -0
  270. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +180 -0
  271. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +323 -0
  272. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +183 -0
  273. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +144 -0
  274. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +122 -0
  275. package/docs/reference/decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md +76 -0
  276. package/docs/reference/decisions.md +83 -0
  277. package/docs/reference/glossary.md +648 -0
  278. package/docs/reference/http-api.md +1348 -0
  279. package/docs/reference/machines.md +190 -0
  280. package/docs/reference/mechanism-events.md +72 -0
  281. package/docs/why.md +206 -0
  282. package/package.json +6 -4
  283. package/src/actor.ts +976 -61
  284. package/src/agent.ts +114 -38
  285. package/src/ambient.ts +4 -1
  286. package/src/assembly-checks.ts +347 -0
  287. package/src/assembly-tar.ts +494 -0
  288. package/src/assembly.ts +721 -0
  289. package/src/cause.ts +234 -0
  290. package/src/chart.ts +2182 -0
  291. package/src/charts.ts +748 -0
  292. package/src/config.ts +520 -162
  293. package/src/content-hash.ts +105 -0
  294. package/src/custodian.lua +6 -5
  295. package/src/custodian.ts +143 -39
  296. package/src/customize.ts +388 -55
  297. package/src/dispatch-effect.ts +945 -0
  298. package/src/door-compat.ts +217 -0
  299. package/src/durability.ts +11 -4
  300. package/src/effect-checks.ts +557 -0
  301. package/src/effect.ts +1202 -0
  302. package/src/effector-wire.ts +674 -0
  303. package/src/fail.ts +209 -0
  304. package/src/fingerprint.ts +4 -3
  305. package/src/gate.ts +8 -0
  306. package/src/git-clone.ts +65 -0
  307. package/src/git-push.ts +336 -0
  308. package/src/harness-client.ts +764 -48
  309. package/src/held-secrets.ts +82 -26
  310. package/src/holds.ts +156 -0
  311. package/src/http-effect.ts +1423 -0
  312. package/src/http.ts +916 -113
  313. package/src/images.ts +47 -21
  314. package/src/index.ts +62 -43
  315. package/src/instance.ts +505 -59
  316. package/src/internal.ts +84 -0
  317. package/src/kube-client.ts +333 -0
  318. package/src/machine-doc.ts +106 -6
  319. package/src/mechanism-routes.ts +168 -0
  320. package/src/model-keys.generated.ts +53 -0
  321. package/src/model-keys.ts +18 -58
  322. package/src/mount-digest.ts +53 -0
  323. package/src/names.ts +66 -8
  324. package/src/notices.ts +141 -0
  325. package/src/open-body.ts +1580 -0
  326. package/src/open-effect.ts +217 -0
  327. package/src/open.ts +59 -6
  328. package/src/parts.ts +709 -41
  329. package/src/party.ts +534 -0
  330. package/src/pick.ts +761 -0
  331. package/src/pool.ts +248 -37
  332. package/src/registration.ts +382 -19
  333. package/src/repo-fetch.ts +60 -85
  334. package/src/repos.ts +65 -75
  335. package/src/rest.ts +133 -0
  336. package/src/run-host.ts +2383 -115
  337. package/src/run-output.ts +38 -0
  338. package/src/sandbox-effect.ts +1126 -0
  339. package/src/sandbox-effector.ts +111 -0
  340. package/src/sandbox-kube.ts +1721 -0
  341. package/src/sandbox-watch.ts +260 -0
  342. package/src/secret-placement.ts +529 -0
  343. package/src/server.ts +284 -84
  344. package/src/setup.ts +819 -117
  345. package/src/size.ts +454 -0
  346. package/src/snapshot-store.ts +115 -20
  347. package/src/stub-harness.ts +49 -16
  348. package/src/sync-client.ts +54 -0
  349. package/src/timers.ts +191 -0
  350. package/src/tokens.ts +35 -4
  351. package/src/vocabulary.ts +135 -15
  352. package/src/wire.ts +238 -10
  353. package/src/workspace.ts +1195 -338
  354. package/src/sandbox-kubectl.ts +0 -1108
package/console/store.ts CHANGED
@@ -24,6 +24,55 @@ export type ObservedRun = {
24
24
  status: string;
25
25
  value: unknown;
26
26
  children: Array<Record<string, unknown>>;
27
+ /** The run binds at least one Chart (ADR-0070): its Machine is the Chart wrapper's with each bound
28
+ * Open body's Chart compiled in, served per run. Absent when it binds none. */
29
+ bound?: true;
30
+ /** The hashes of the Charts the run binds now, distinct and sorted. A run can bind a second Chart
31
+ * in its life, so these, with the run id, key its doc. Absent from an older Orchestrator. */
32
+ chartHashes?: string[];
33
+ /** The run that dispatched this one, the caller's slot, and this run's depth in the chain
34
+ * (ADR-0071). Routing, on the open band (ADR-0014): the page reads it off the observation it
35
+ * already holds, with no fetch and no token. `instance` is set for a foreign caller, opaque text.
36
+ * Absent for a run nobody dispatched, and from an Orchestrator older than ADR-0071. */
37
+ dispatchedBy?: DispatchedByView;
38
+ /** The runs this run's `dispatch` Effects started, oldest first, by slot (ADR-0071): the chain's
39
+ * other direction, the same routing on the same band. Absent when it started none. */
40
+ dispatched?: Array<{ slot: string; run: string }>;
41
+ };
42
+
43
+ /** {@link ObservedRun.dispatchedBy} — mirrors `RunObservation.dispatchedBy` in run-host.ts. The
44
+ * idempotency key is not on the open band: it names the caller's ledger. */
45
+ export type DispatchedByView = { run: string; slot: string; depth: number; instance?: string };
46
+
47
+ /** Where a Workflow came from (ADR-0070): a Machine or a Chart file in the Assembly, or a runtime
48
+ * Chart put on the Instance. */
49
+ export type WorkflowOrigin = "machine" | "file" | "runtime";
50
+
51
+ /** One entry of `GET /workflows` (ADR-0070) — mirrors `WorkflowListing` in run-host.ts. A Chart
52
+ * wrapper's named Charts are inside its entry, not beside it. */
53
+ export type WorkflowListing = {
54
+ name: string;
55
+ origin: WorkflowOrigin;
56
+ wrapper?: {
57
+ body: string;
58
+ oneShot: boolean;
59
+ charts: Array<{ name: string; origin: "file" | "runtime"; unrunnable?: string }>;
60
+ };
61
+ unrunnable?: string;
62
+ };
63
+
64
+ /** One row of the fleet rail: a Workflow, or a named Chart indented under its Chart wrapper. */
65
+ export type FleetEntry = {
66
+ name: string;
67
+ origin: WorkflowOrigin;
68
+ /** 0 for a Workflow, 1 for a Chart under its wrapper. */
69
+ depth: 0 | 1;
70
+ /** A Chart wrapper's Open body slot, and whether its door takes a Chart one-shot. */
71
+ wrapper?: { body: string; oneShot: boolean };
72
+ unrunnable?: string;
73
+ /** A runtime Chart of the same name that this Chart file shadows (ADR-0070): one row per name,
74
+ * so the rail keys stay distinct; the shadowed Chart is unrunnable, and this says why. */
75
+ shadows?: { origin: WorkflowOrigin; unrunnable?: string };
27
76
  };
28
77
 
29
78
  /** One open gate as `GET /runs/:id` reports it — mirrors `GateView` in run-host.ts. */
@@ -34,6 +83,15 @@ export type GateCard = {
34
83
  meta?: Record<string, unknown>;
35
84
  };
36
85
 
86
+ /** One Workspace waiting for capacity as `GET /runs/:id` reports it (ADR-0064) — mirrors
87
+ * `RunWaiting` in run-host.ts. Guarded: `message` is the scheduler's or the quota's own words. */
88
+ export type WaitingLine = { child: string; on: "node" | "quota"; message: string; since: string };
89
+
90
+ /** One outstanding pick as `GET /runs/:id` reports it (ADR-0024) — mirrors `RunHeld` in
91
+ * run-host.ts. There is no default time bound on a held Turn: "held since" is how a human sees one.
92
+ * Guarded like the reason a Workspace waits: a pick names what an Agent did. */
93
+ export type HeldLine = { agent: string; pick: string; since: string };
94
+
37
95
  /** The credential's state — never its value, which stays in sessionStorage (ADR-0032). */
38
96
  export type TokenState = "none" | "checking" | "live" | "invalid";
39
97
 
@@ -45,7 +103,7 @@ export type Frame =
45
103
  | { kind: "gone"; runId: string }
46
104
  | { kind: "emit"; runId: string; type: string }
47
105
  | { kind: "connection"; state: Store["connection"] }
48
- | { kind: "workflows"; workflows: string[] }
106
+ | { kind: "workflows"; workflows: ReadonlyArray<WorkflowListing | string> }
49
107
  | { kind: "select"; workflow: string | null }
50
108
  | { kind: "selectRun"; runId: string | null }
51
109
  | { kind: "selectNode"; nodeId: string | null }
@@ -53,6 +111,8 @@ export type Frame =
53
111
  | { kind: "toggleWorkflow"; workflow: string }
54
112
  | { kind: "token"; state: TokenState }
55
113
  | { kind: "gates"; runId: string; workflow: string; gates: GateCard[] }
114
+ | { kind: "waiting"; runId: string; workflow: string; waiting: WaitingLine[]; held?: HeldLine[] }
115
+ | { kind: "failure"; runId: string; reason: string | null }
56
116
  | { kind: "inboxScope"; all: boolean }
57
117
  | { kind: "startForm"; workflow: string | null };
58
118
 
@@ -66,7 +126,10 @@ export type Frame =
66
126
  * `emits` newest-first, across the selected workflow's runs — each tagged with its run.
67
127
  * `connection` the socket's state, owned by the page, never by a frame off the wire.
68
128
  * `workflow` the selection the ADDRESS carries (`/workflows/:name`); null at `/`.
69
- * `workflows` the registered names (`GET /workflows`) — what the fleet rail lists.
129
+ * `workflows` every registered name (`GET /workflows`), Machines and named Charts alike — what the
130
+ * fleet polls and selects. A Chart's name is `<wrapper>/<name>` (ADR-0070).
131
+ * `listing` the same answer with its structure: each Workflow's origin, a Chart wrapper's Charts
132
+ * inside its entry — what the fleet rail draws ({@link fleetEntries}).
70
133
  * `fleet` REST snapshots per workflow (`GET /workflows/:name/runs`), for the workflows that do
71
134
  * NOT hold the feed. Never more than one EventSource — the rest of the fleet is polled.
72
135
  * `expanded` which workflows the rail shows runs for (the reader's folding, plus auto-expand on
@@ -76,6 +139,14 @@ export type Frame =
76
139
  * "invalid". Anything but "live" means observer mode.
77
140
  * `gates` the gate inbox: runId -> { workflow, gates } from `GET /runs/:id` re-fetches. Guarded
78
141
  * data (Instance band), so it cannot outlive the credential that read it.
142
+ * `waiting` why each run's Workspaces in `placing` wait (ADR-0064): runId -> { workflow, waiting },
143
+ * off the SAME `GET /runs/:id` re-fetch as `gates`, under the same rules — guarded, whole
144
+ * per run, gone with the credential. The open band sees `placing` lit and nothing more.
145
+ * `failures` the reason of each run this page knows that ended `failed` (ADR-0073): runId -> reason,
146
+ * off the SAME `GET /runs/:id` re-fetch, under the same rules — guarded (the open band
147
+ * sees the `failed` status and its color, never the output), gone with the credential.
148
+ * A failed run is a settled run, so `gone` keeps it; it goes with the run when the run
149
+ * leaves both `runs` and `settled`.
79
150
  * `inboxAll` the widen control: false = the inbox follows the selection, true = every workflow.
80
151
  * `startFormFor` which workflow's start-run form is open, if any.
81
152
  * `selectedNodeId` the diagram node the reader clicked — an elk node id (scope + state id), one at
@@ -93,10 +164,13 @@ export type Store = {
93
164
  connection: "connecting" | "live" | "retrying";
94
165
  workflow: string | null;
95
166
  workflows: string[];
167
+ listing: WorkflowListing[];
96
168
  fleet: Map<string, ObservedRun[]>;
97
169
  expanded: Set<string>;
98
170
  token: TokenState;
99
171
  gates: Map<string, { workflow: string; gates: GateCard[] }>;
172
+ waiting: Map<string, { workflow: string; waiting: WaitingLine[]; held: HeldLine[] }>;
173
+ failures: Map<string, string>;
100
174
  inboxAll: boolean;
101
175
  startFormFor: string | null;
102
176
  };
@@ -119,10 +193,13 @@ export function emptyStore(): Store {
119
193
  connection: "connecting",
120
194
  workflow: null,
121
195
  workflows: [],
196
+ listing: [],
122
197
  fleet: new Map(),
123
198
  expanded: new Set(),
124
199
  token: "none",
125
200
  gates: new Map(),
201
+ waiting: new Map(),
202
+ failures: new Map(),
126
203
  inboxAll: false,
127
204
  startFormFor: null,
128
205
  };
@@ -132,16 +209,24 @@ export function emptyStore(): Store {
132
209
  * that learns a new frame type does not break a page still running yesterday's script. */
133
210
  export function applyFrame(store: Store, frame: Frame): Store {
134
211
  switch (frame.kind) {
135
- case "runs":
212
+ case "runs": {
136
213
  // The server's whole truth about what is live. Runs that ended during a disconnect are simply
137
214
  // ABSENT — no `gone` was delivered for them and none is needed; they were never ours to settle.
138
215
  // The same wholeness settles the inbox: a selected-workflow run the frame does not mention can
139
- // never be re-fetched again, so its card leaves with it.
216
+ // never be re-fetched again, so its card leaves with it. A failure stays while its run is live
217
+ // or settled here.
218
+ const runs = new Map(frame.runs.map((r): [string, ObservedRun] => [r.runId, r]));
219
+ const orphans = [...store.failures.keys()].filter((id) => !runs.has(id) && !store.settled.has(id));
220
+ let failures = store.failures;
221
+ for (const id of orphans) failures = mapWithout(failures, id);
140
222
  return select({
141
223
  ...store,
142
- runs: new Map(frame.runs.map((r): [string, ObservedRun] => [r.runId, r])),
224
+ runs,
143
225
  gates: dropAbsent(store.gates, store.workflow, new Set(frame.runs.map((r) => r.runId))),
226
+ waiting: dropAbsent(store.waiting, store.workflow, new Set(frame.runs.map((r) => r.runId))),
227
+ failures,
144
228
  });
229
+ }
145
230
 
146
231
  case "status":
147
232
  return select({ ...store, runs: new Map(store.runs).set(frame.status.runId, frame.status) });
@@ -152,13 +237,19 @@ export function applyFrame(store: Store, frame: Frame): Store {
152
237
  // a reconnect window, which is ordinary. Its inbox card goes either way: a gate exists exactly
153
238
  // while its invoking state is entered (ADR-0011), and this run has no entered states left.
154
239
  const gates = mapWithout(store.gates, frame.runId);
240
+ const waiting = mapWithout(store.waiting, frame.runId);
155
241
  const departing = store.runs.get(frame.runId);
156
- if (!departing) return store.gates === gates ? store : { ...store, gates };
242
+ if (!departing) return store.gates === gates && store.waiting === waiting ? store : { ...store, gates, waiting };
157
243
  const runs = new Map(store.runs);
158
244
  runs.delete(frame.runId);
159
245
  const settled = new Map(store.settled).set(frame.runId, departing);
160
- while (settled.size > SETTLED_CAP) settled.delete(settled.keys().next().value!);
161
- return select({ ...store, runs, settled, gates });
246
+ let failures = store.failures;
247
+ while (settled.size > SETTLED_CAP) {
248
+ const evicted = settled.keys().next().value!;
249
+ settled.delete(evicted);
250
+ failures = mapWithout(failures, evicted);
251
+ }
252
+ return select({ ...store, runs, settled, gates, waiting, failures });
162
253
  }
163
254
 
164
255
  case "emit":
@@ -167,8 +258,14 @@ export function applyFrame(store: Store, frame: Frame): Store {
167
258
  case "connection":
168
259
  return { ...store, connection: frame.state };
169
260
 
170
- case "workflows":
171
- return { ...store, workflows: frame.workflows };
261
+ case "workflows": {
262
+ // Replaced wholesale. An Orchestrator older than ADR-0070 answers bare names: each reads as
263
+ // a Machine, so the rail has one shape to draw.
264
+ const listing = frame.workflows.map(
265
+ (w): WorkflowListing => (typeof w === "string" ? { name: w, origin: "machine" } : w),
266
+ );
267
+ return { ...store, listing, workflows: fleetEntries(listing).map((e) => e.name) };
268
+ }
172
269
 
173
270
  case "select": {
174
271
  // The address moved. The feed's state belongs to the OLD selection — the new feed's opening
@@ -188,6 +285,8 @@ export function applyFrame(store: Store, frame: Frame): Store {
188
285
  selectedRunId: null,
189
286
  selectedNodeId: null,
190
287
  connection: "connecting",
288
+ // Each failure belongs to a run of the old selection's `runs` or `settled`, which go.
289
+ failures: new Map(),
191
290
  };
192
291
  }
193
292
 
@@ -206,8 +305,9 @@ export function applyFrame(store: Store, frame: Frame): Store {
206
305
  // so it settles that workflow's inbox cards the same way (a run the snapshot does not carry
207
306
  // will never be re-fetched again).
208
307
  const fleet = new Map(store.fleet).set(frame.workflow, frame.runs);
209
- const gates = dropAbsent(store.gates, frame.workflow, new Set(frame.runs.map((r) => r.runId)));
210
- return { ...store, fleet, gates };
308
+ const present = new Set(frame.runs.map((r) => r.runId));
309
+ const gates = dropAbsent(store.gates, frame.workflow, present);
310
+ return { ...store, fleet, gates, waiting: dropAbsent(store.waiting, frame.workflow, present) };
211
311
  }
212
312
 
213
313
  case "toggleWorkflow": {
@@ -221,7 +321,7 @@ export function applyFrame(store: Store, frame: Frame): Store {
221
321
  // "invalid" (the any-later-401 drop of ADR-0032) empties the inbox. "checking" keeps it — a
222
322
  // re-validation of a token that turns out fine should not flash the inbox empty.
223
323
  if (frame.state === "none" || frame.state === "invalid") {
224
- return { ...store, token: frame.state, gates: new Map() };
324
+ return { ...store, token: frame.state, gates: new Map(), waiting: new Map(), failures: new Map() };
225
325
  }
226
326
  return { ...store, token: frame.state };
227
327
 
@@ -238,6 +338,38 @@ export function applyFrame(store: Store, frame: Frame): Store {
238
338
  return { ...store, gates };
239
339
  }
240
340
 
341
+ case "waiting": {
342
+ // The same guarded re-fetch's other half (ADR-0064), folded the same way: whole per run, and
343
+ // nothing folded without the credential that read it.
344
+ // The run's outstanding picks (ADR-0024) ride the same frame: one re-fetch, one fold.
345
+ if (store.token !== "live") return store;
346
+ const held = frame.held ?? [];
347
+ if (!frame.waiting.length && !held.length) {
348
+ const waiting = mapWithout(store.waiting, frame.runId);
349
+ return waiting === store.waiting ? store : { ...store, waiting };
350
+ }
351
+ const waiting = new Map(store.waiting).set(frame.runId, {
352
+ workflow: frame.workflow,
353
+ waiting: frame.waiting,
354
+ held,
355
+ });
356
+ return { ...store, waiting };
357
+ }
358
+
359
+ case "failure": {
360
+ // The same guarded re-fetch's third half (ADR-0073): the reason of a run that ended `failed`.
361
+ // Nothing folded without the credential, and nothing for a run that the page does not hold:
362
+ // the re-fetch can come back after a `select` moved on. `null` says the run did not fail.
363
+ if (store.token !== "live") return store;
364
+ if (frame.reason === null) {
365
+ const failures = mapWithout(store.failures, frame.runId);
366
+ return failures === store.failures ? store : { ...store, failures };
367
+ }
368
+ if (!store.runs.has(frame.runId) && !store.settled.has(frame.runId)) return store;
369
+ if (store.failures.get(frame.runId) === frame.reason) return store;
370
+ return { ...store, failures: new Map(store.failures).set(frame.runId, frame.reason) };
371
+ }
372
+
241
373
  case "inboxScope":
242
374
  return { ...store, inboxAll: frame.all };
243
375
 
@@ -258,15 +390,20 @@ function mapWithout<K, V>(map: Map<K, V>, key: K): Map<K, V> {
258
390
  return next;
259
391
  }
260
392
 
261
- /** Drop `workflow`'s inbox entries for runs a whole-set frame did not mention. Those runs will
262
- * never be re-fetched again (frames and snapshots are per-run triggers), so a card kept here would
263
- * be stale forever — this is the level-triggered idiom applied to the inbox, not bookkeeping. */
264
- function dropAbsent(gates: Store["gates"], workflow: string | null, present: Set<string>): Store["gates"] {
265
- if (!workflow) return gates;
266
- let next = gates;
267
- for (const [runId, entry] of gates) {
393
+ /** Drop `workflow`'s guarded entries (inbox cards, waits) for runs a whole-set frame did not
394
+ * mention. Those runs will never be re-fetched again (frames and snapshots are per-run triggers),
395
+ * so an entry kept here would be stale forever — this is the level-triggered idiom applied to the
396
+ * guarded maps, not bookkeeping. */
397
+ function dropAbsent<V extends { workflow: string }>(
398
+ entries: Map<string, V>,
399
+ workflow: string | null,
400
+ present: Set<string>,
401
+ ): Map<string, V> {
402
+ if (!workflow) return entries;
403
+ let next = entries;
404
+ for (const [runId, entry] of entries) {
268
405
  if (entry.workflow !== workflow || present.has(runId)) continue;
269
- if (next === gates) next = new Map(gates);
406
+ if (next === entries) next = new Map(entries);
270
407
  next.delete(runId);
271
408
  }
272
409
  return next;
@@ -310,6 +447,103 @@ export function fleetRuns(store: Store, workflow: string): Array<{ run: Observed
310
447
  return (store.fleet.get(workflow) ?? []).map((run) => ({ run, settled: false }));
311
448
  }
312
449
 
450
+ /**
451
+ * The fleet rail's rows (ADR-0070): each Workflow in the listing's order, a Chart wrapper's named
452
+ * Charts indented under it as `<wrapper>/<name>`. A Chart the listing also gives at the top level
453
+ * is drawn once, under its wrapper; one whose wrapper the listing does not give stays at the top.
454
+ */
455
+ export function fleetEntries(listing: readonly WorkflowListing[]): FleetEntry[] {
456
+ const full = (wrapper: string, name: string): string => (name.includes("/") ? name : `${wrapper}/${name}`);
457
+ const nested = new Set<string>();
458
+ for (const w of listing) for (const c of w.wrapper?.charts ?? []) nested.add(full(w.name, c.name));
459
+ const rows: FleetEntry[] = [];
460
+ for (const w of listing) {
461
+ if (w.origin !== "machine" && nested.has(w.name)) continue;
462
+ rows.push({
463
+ name: w.name,
464
+ origin: w.origin,
465
+ depth: 0,
466
+ ...(w.wrapper ? { wrapper: { body: w.wrapper.body, oneShot: w.wrapper.oneShot } } : {}),
467
+ ...(w.unrunnable ? { unrunnable: w.unrunnable } : {}),
468
+ });
469
+ for (const c of w.wrapper?.charts ?? []) {
470
+ const name = full(w.name, c.name);
471
+ // A Chart file and a runtime Chart of one name: the listing gives both, the file wins
472
+ // (ADR-0070). One row per name, the shadowed Chart a note on it, so no two rows share a key.
473
+ const twin = rows.find((r) => r.depth === 1 && r.name === name);
474
+ if (twin) {
475
+ const fileFirst = twin.origin === "file" || c.origin !== "file";
476
+ const shadowed = fileFirst ? c : { origin: twin.origin, unrunnable: twin.unrunnable };
477
+ if (!fileFirst) {
478
+ twin.origin = c.origin;
479
+ if (c.unrunnable) twin.unrunnable = c.unrunnable;
480
+ else delete twin.unrunnable;
481
+ }
482
+ twin.shadows = { origin: shadowed.origin, ...(shadowed.unrunnable ? { unrunnable: shadowed.unrunnable } : {}) };
483
+ continue;
484
+ }
485
+ rows.push({
486
+ name,
487
+ origin: c.origin,
488
+ depth: 1,
489
+ ...(c.unrunnable ? { unrunnable: c.unrunnable } : {}),
490
+ });
491
+ }
492
+ }
493
+ return rows;
494
+ }
495
+
496
+ /**
497
+ * Which Machine doc the center pane draws (ADR-0070), as a cache key and the route that serves it.
498
+ * A run that binds a Chart draws its own Machine, the Chart compiled into the Open body, so its doc
499
+ * is per run id; every other selection draws the Workflow's Machine, shared by all its runs. The
500
+ * Workflow name rides the route as one encoded segment (`<wrapper>%2F<name>` for a named Chart).
501
+ */
502
+ export function machineDocSource(
503
+ workflow: string,
504
+ run: Pick<ObservedRun, "runId" | "bound" | "chartHashes"> | null,
505
+ ): { key: string; path: string } {
506
+ const base = `/workflows/${encodeURIComponent(workflow)}`;
507
+ if (run?.bound === true) {
508
+ // The run id and the Charts it binds now: a second Chart in the run's life is a new doc.
509
+ const charts = run.chartHashes?.length ? `:${run.chartHashes.join(",")}` : "";
510
+ return { key: `run:${run.runId}${charts}`, path: `${base}/runs/${encodeURIComponent(run.runId)}/machine` };
511
+ }
512
+ return { key: `workflow:${workflow}`, path: `${base}/machine` };
513
+ }
514
+
515
+ /** The fleet row's badge for a dispatched run (ADR-0071): `↳ <run8>`, the caller's run id cut as
516
+ * the row cuts its own. Null for a run nobody dispatched. */
517
+ export function dispatchBadge(run: ObservedRun): string | null {
518
+ const by = run.dispatchedBy;
519
+ return by ? `↳ ${by.run.slice(0, 8)}` : null;
520
+ }
521
+
522
+ /** The Machine pane's strip for a dispatched run (ADR-0071): `dispatched by <run8> · <slot> ·
523
+ * depth n`, and `· from Orchestrator <name>` when the caller is on another Orchestrator (its
524
+ * name as it sent it, never an Instance ID). Null for a run
525
+ * nobody dispatched. */
526
+ export function dispatchStrip(run: ObservedRun | null): string | null {
527
+ const by = run?.dispatchedBy;
528
+ if (!by) return null;
529
+ const from = by.instance ? ` · from Orchestrator ${by.instance}` : "";
530
+ return `dispatched by ${by.run.slice(0, 8)} · ${by.slot} · depth ${by.depth}${from}`;
531
+ }
532
+
533
+ /** The most runs the pane's `dispatched` strip names: a watcher dispatches on every pass. */
534
+ const DISPATCHED_SHOWN = 5;
535
+
536
+ /** The Machine pane's strip for a run that dispatched (ADR-0071): `dispatched <run8> (<slot>) · …`,
537
+ * newest last, the older ones counted. Null for a run that started none. */
538
+ export function dispatchedStrip(run: ObservedRun | null): string | null {
539
+ const all = run?.dispatched ?? [];
540
+ if (all.length === 0) return null;
541
+ const shown = all.slice(-DISPATCHED_SHOWN);
542
+ const older = all.length - shown.length;
543
+ const named = shown.map((d) => `${d.run.slice(0, 8)} (${d.slot})`).join(" · ");
544
+ return `dispatched ${older > 0 ? `${older} more · ` : ""}${named}`;
545
+ }
546
+
313
547
  /** The inbox as the Attention tab draws it: every card, or the selection's slice. The inbox itself
314
548
  * is GLOBAL (every workflow the fleet knows) — the filter is presentation, which is why it lives
315
549
  * in a selector and not in `applyFrame`. */
@@ -327,6 +561,72 @@ export function selectedRunGates(store: Store): GateCard[] {
327
561
  return store.gates.get(store.selectedRunId)?.gates ?? [];
328
562
  }
329
563
 
564
+ /** Why the SELECTED run's Workspaces wait for capacity (ADR-0064) — empty in observer mode, where
565
+ * the diagram still lights `placing` but the reason is not the page's to know. */
566
+ export function selectedRunWaiting(store: Store): WaitingLine[] {
567
+ if (store.selectedRunId === null) return [];
568
+ return store.waiting.get(store.selectedRunId)?.waiting ?? [];
569
+ }
570
+
571
+ /** The reason of a run that ended `failed`, off a `GET /runs/:id` answer (ADR-0073): the Failure's
572
+ * `reason` in the run's output. Null for every other status, and for an output with no reason
573
+ * text — the same reading as `jr2 status`'s `failed:` line. */
574
+ export function failureReason(body: { status?: string; output?: unknown }): string | null {
575
+ if (body.status !== "failed") return null;
576
+ const reason = (body.output as { reason?: unknown } | null | undefined)?.reason;
577
+ return typeof reason === "string" ? reason : null;
578
+ }
579
+
580
+ /** Why the SELECTED run failed (ADR-0073): the reason of its Failure, or null when it did not fail
581
+ * or the page has no live token to read it with. */
582
+ export function selectedRunFailure(store: Store): string | null {
583
+ if (store.selectedRunId === null) return null;
584
+ return store.failures.get(store.selectedRunId) ?? null;
585
+ }
586
+
587
+ /** The SELECTED run's outstanding picks, each with since when it is held (ADR-0024) — empty in
588
+ * observer mode. */
589
+ export function selectedRunHeld(store: Store): HeldLine[] {
590
+ if (store.selectedRunId === null) return [];
591
+ return store.waiting.get(store.selectedRunId)?.held ?? [];
592
+ }
593
+
594
+ /** One party's seat on one state (ADR-0015): who may send which names there — mirrors
595
+ * `MachineSeat` in vocabulary.ts. */
596
+ export type Seat = { state: string; party: "agent" | "gate"; agent?: string; names: string[] };
597
+
598
+ /** The part of a Machine doc the seats are read from: each body's `seats` and `warnings`, and the
599
+ * child Machines below its states (canvas.ts's `MachineBodyDoc`, structurally). */
600
+ export type SeatedBody = {
601
+ seats?: Seat[];
602
+ warnings?: string[];
603
+ root: SeatedState;
604
+ };
605
+ type SeatedState = { states: SeatedState[]; children: Array<{ machine?: SeatedBody }> };
606
+
607
+ /**
608
+ * Who may send what, per state, and what the build found suspect (ADR-0015) — for the Machine and
609
+ * every Machine it composes, in the order `jr2 up` prints them: the root's body, then each child
610
+ * Machine's, depth-first in state order. A function of the doc, not of the store: the doc is
611
+ * structure the server serves (app.ts's `MachineView`), and this is the one piece of it worth a
612
+ * unit test outside the canvas.
613
+ */
614
+ export function docSeats(doc: SeatedBody | null): { seats: Seat[]; warnings: string[] } {
615
+ const seats: Seat[] = [];
616
+ const warnings: string[] = [];
617
+ const body = (b: SeatedBody): void => {
618
+ seats.push(...(b.seats ?? []));
619
+ warnings.push(...(b.warnings ?? []));
620
+ state(b.root);
621
+ };
622
+ const state = (s: SeatedState): void => {
623
+ for (const child of s.children) if (child.machine) body(child.machine);
624
+ s.states.forEach(state);
625
+ };
626
+ if (doc) body(doc);
627
+ return { seats, warnings };
628
+ }
629
+
330
630
  /** What the nav badge counts: every open gate everywhere — attention is global even when the
331
631
  * inbox view is filtered. */
332
632
  export function gateCount(store: Store): number {
package/console/style.css CHANGED
@@ -12,6 +12,8 @@
12
12
  --phosphor-soft: rgba(78, 240, 167, 0.12);
13
13
  --amber: #f0c04e;
14
14
  --red: #f06a5f;
15
+ /* A Failure (ADR-0073): an end the Machine chose, so not the red of a fault. */
16
+ --orange: #f0954e;
15
17
  --blue: #6aa7f0;
16
18
  --mono: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;
17
19
  }
@@ -162,6 +164,112 @@ main {
162
164
  display: none;
163
165
  }
164
166
 
167
+ /* Who may send what, per state (ADR-0015) — structure, folded by default so the diagram keeps the
168
+ pane; open, it scrolls inside its own cap rather than push the diagram out. */
169
+ #seats {
170
+ margin: 0 28px 16px;
171
+ max-width: 720px;
172
+ }
173
+ #root-transitions:empty + #seats {
174
+ margin-top: 28px;
175
+ }
176
+ #seats summary {
177
+ cursor: pointer;
178
+ color: var(--text-dim);
179
+ }
180
+ #seats .seats-warned {
181
+ color: var(--amber);
182
+ }
183
+ #seats ul {
184
+ list-style: none;
185
+ margin: 8px 0 0;
186
+ padding: 0;
187
+ max-height: 30vh;
188
+ overflow: auto;
189
+ }
190
+ #seats .seat-list li {
191
+ padding: 2px 0;
192
+ }
193
+ #seats .seat-names {
194
+ color: var(--blue);
195
+ }
196
+ #seats .seat-warnings li {
197
+ border: 1px solid var(--amber);
198
+ border-radius: 4px;
199
+ padding: 6px 10px;
200
+ margin-bottom: 6px;
201
+ color: var(--amber);
202
+ }
203
+
204
+ /* A wait for capacity (ADR-0064): the selected run's Workspaces in "placing", and why. Amber like
205
+ the notice — the run is not wrong, it is waiting on the cluster. */
206
+ #waiting {
207
+ list-style: none;
208
+ margin: 0 28px 16px;
209
+ padding: 0;
210
+ max-width: 720px;
211
+ }
212
+
213
+ #waiting:empty {
214
+ display: none;
215
+ }
216
+
217
+ #waiting li {
218
+ border: 1px solid var(--amber);
219
+ border-radius: 4px;
220
+ padding: 8px 12px;
221
+ margin-bottom: 8px;
222
+ }
223
+
224
+ #waiting .waiting-head {
225
+ color: var(--amber);
226
+ }
227
+
228
+ #waiting .waiting-message {
229
+ margin-top: 4px;
230
+ font-family: var(--mono);
231
+ color: var(--text-dim);
232
+ }
233
+
234
+ /* A failed run (ADR-0073): the reason of its Failure. Orange like the rail's `failed` status — the
235
+ Machine chose this end, so it is not the red of an `error`. */
236
+ #failure {
237
+ margin: 0 28px 16px;
238
+ max-width: 720px;
239
+ border: 1px solid var(--orange);
240
+ border-radius: 4px;
241
+ padding: 8px 12px;
242
+ color: var(--orange);
243
+ font-family: var(--mono);
244
+ }
245
+
246
+ #failure[hidden] {
247
+ display: none;
248
+ }
249
+
250
+ /* An outstanding pick (ADR-0024): an Agent's Turn held while the workflow runs its pick. */
251
+ #held {
252
+ list-style: none;
253
+ margin: 0 28px 16px;
254
+ padding: 0;
255
+ max-width: 720px;
256
+ }
257
+
258
+ #held:empty {
259
+ display: none;
260
+ }
261
+
262
+ #held li {
263
+ border: 1px solid var(--line);
264
+ border-radius: 4px;
265
+ padding: 8px 12px;
266
+ margin-bottom: 8px;
267
+ }
268
+
269
+ #held .held-head {
270
+ font-family: var(--mono);
271
+ }
272
+
165
273
  /* ---- Machine (SVG) ---- */
166
274
 
167
275
  /* Every box is click-to-select (outline only — the click is reserved beyond that). */
@@ -525,6 +633,9 @@ ul {
525
633
  .wf-runs .run-status.status--error {
526
634
  color: var(--red);
527
635
  }
636
+ .wf-runs .run-status.status--failed {
637
+ color: var(--orange);
638
+ }
528
639
  .wf-runs .run-status.status--done {
529
640
  color: var(--phosphor);
530
641
  }
@@ -0,0 +1,5 @@
1
+ jr2 is newer than your training data. Before you write a Machine or change jr2.config.ts, read the bundled docs in node_modules/@jr2/orchestrator/docs/. They are the docs of this Instance's Kit version, 0.5.1. Start with guides/write-a-workflow.md and with reference/authoring-surface.md, the contents page of the Authoring surface. Read one page with each call of your file tool, because a file tool cuts off a long output. Before you give a change to the user, run `npx --no jr2 check` in the Instance folder. It does the typecheck and the load of each Workflow that `jr2 up` does first, with no cluster, and it gives the same refusals.
2
+
3
+ A final state means that the work succeeded: the run ends `done`. When the work did not succeed, invoke `fail()` from a state (reference/authoring-surface/fail.md), and the run ends `failed`. Do not throw to fail: a throw is a defect, and the run ends `error`.
4
+
5
+ [jr2 docs index]|root: ./node_modules/@jr2/orchestrator/docs|reference:{authoring-surface.md,machines.md,config.md,cli.md,http-api.md,mechanism-events.md,glossary.md,decisions.md}|reference/authoring-surface:{one file per export and type; reference/authoring-surface.md lists them}|guides:{iterate-on-a-machine-on-kind.md,write-a-workflow.md,bind-open-parts.md,set-an-agents-access-and-dials.md,run-a-command-or-call-a-url.md,push-and-sign-a-branch.md,start-a-run-from-a-run.md,add-tools-to-the-sandbox.md,size-a-workspace.md,run-one-worker-per-item.md,publish-a-machine-as-an-npm-package.md,let-an-agent-write-a-chart.md,run-a-chart-by-name.md,start-a-run.md,answer-a-gate.md,watch-a-run.md,look-inside-a-sandbox.md,cancel-a-run.md,open-the-console.md,use-a-local-model.md,use-a-hosted-provider.md,give-the-cluster-git-credentials.md,move-from-kind-to-a-shared-cluster.md,mirror-the-kit-images.md,upgrade-the-kit-version.md,clean-up.md,fix-a-failed-converge.md}|concepts:{machines-and-workflows.md,agents-and-turns.md,the-menu.md,gates.md,effects.md,workspaces.md,composition.md,charts.md,pools-and-sources.md,architecture.md,secrets-and-trust.md,converge.md,durable-runs.md}|.:{index.md,why.md}|get-started:{install.md,create-an-instance.md,connect-a-model.md,a-model-decides.md,an-agent-in-a-sandbox.md,have-your-assistant-write-a-machine.md}|reference/decisions:{one file per ADR; reference/decisions.md lists them}