@jr2/orchestrator 0.4.1 → 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 (327) hide show
  1. package/README.md +20 -2
  2. package/console/components/app.ts +2 -0
  3. package/console/components/fleet.ts +14 -1
  4. package/console/components/machine-pane.ts +41 -2
  5. package/console/main.ts +13 -1
  6. package/console/store.ts +102 -6
  7. package/console/style.css +21 -0
  8. package/docs/agents-block.md +5 -0
  9. package/docs/concepts/agents-and-turns.md +442 -0
  10. package/docs/concepts/architecture.md +230 -0
  11. package/docs/concepts/charts.md +143 -0
  12. package/docs/concepts/composition.md +357 -0
  13. package/docs/concepts/converge.md +254 -0
  14. package/docs/concepts/durable-runs.md +189 -0
  15. package/docs/concepts/effects.md +603 -0
  16. package/docs/concepts/gates.md +208 -0
  17. package/docs/concepts/machines-and-workflows.md +289 -0
  18. package/docs/concepts/pools-and-sources.md +230 -0
  19. package/docs/concepts/secrets-and-trust.md +222 -0
  20. package/docs/concepts/the-menu.md +378 -0
  21. package/docs/concepts/workspaces.md +678 -0
  22. package/docs/get-started/a-model-decides.md +563 -0
  23. package/docs/get-started/an-agent-in-a-sandbox.md +381 -0
  24. package/docs/get-started/connect-a-model.md +248 -0
  25. package/docs/get-started/create-an-instance.md +194 -0
  26. package/docs/get-started/have-your-assistant-write-a-machine.md +922 -0
  27. package/docs/get-started/install.md +126 -0
  28. package/docs/guides/add-tools-to-the-sandbox.md +295 -0
  29. package/docs/guides/answer-a-gate.md +304 -0
  30. package/docs/guides/bind-open-parts.md +223 -0
  31. package/docs/guides/cancel-a-run.md +146 -0
  32. package/docs/guides/clean-up.md +206 -0
  33. package/docs/guides/fix-a-failed-converge.md +361 -0
  34. package/docs/guides/give-the-cluster-git-credentials.md +222 -0
  35. package/docs/guides/iterate-on-a-machine-on-kind.md +190 -0
  36. package/docs/guides/let-an-agent-write-a-chart.md +226 -0
  37. package/docs/guides/look-inside-a-sandbox.md +174 -0
  38. package/docs/guides/mirror-the-kit-images.md +176 -0
  39. package/docs/guides/move-from-kind-to-a-shared-cluster.md +302 -0
  40. package/docs/guides/open-the-console.md +91 -0
  41. package/docs/guides/publish-a-machine-as-an-npm-package.md +350 -0
  42. package/docs/guides/push-and-sign-a-branch.md +486 -0
  43. package/docs/guides/run-a-chart-by-name.md +270 -0
  44. package/docs/guides/run-a-command-or-call-a-url.md +544 -0
  45. package/docs/guides/run-one-worker-per-item.md +321 -0
  46. package/docs/guides/set-an-agents-access-and-dials.md +375 -0
  47. package/docs/guides/size-a-workspace.md +204 -0
  48. package/docs/guides/start-a-run-from-a-run.md +237 -0
  49. package/docs/guides/start-a-run.md +207 -0
  50. package/docs/guides/upgrade-the-kit-version.md +240 -0
  51. package/docs/guides/use-a-hosted-provider.md +208 -0
  52. package/docs/guides/use-a-local-model.md +313 -0
  53. package/docs/guides/watch-a-run.md +326 -0
  54. package/docs/guides/write-a-workflow.md +318 -0
  55. package/docs/index.md +44 -0
  56. package/docs/llms.txt +216 -0
  57. package/docs/reference/authoring-surface/agent.md +128 -0
  58. package/docs/reference/authoring-surface/answer.md +140 -0
  59. package/docs/reference/authoring-surface/arg.md +60 -0
  60. package/docs/reference/authoring-surface/basic.md +62 -0
  61. package/docs/reference/authoring-surface/bearer.md +53 -0
  62. package/docs/reference/authoring-surface/chartschema.md +23 -0
  63. package/docs/reference/authoring-surface/chartschemafor.md +48 -0
  64. package/docs/reference/authoring-surface/command.md +191 -0
  65. package/docs/reference/authoring-surface/customize.md +64 -0
  66. package/docs/reference/authoring-surface/defineconfig.md +41 -0
  67. package/docs/reference/authoring-surface/defineevent.md +114 -0
  68. package/docs/reference/authoring-surface/dispatch.md +126 -0
  69. package/docs/reference/authoring-surface/fail.md +83 -0
  70. package/docs/reference/authoring-surface/file.md +122 -0
  71. package/docs/reference/authoring-surface/frompick.md +277 -0
  72. package/docs/reference/authoring-surface/http.md +159 -0
  73. package/docs/reference/authoring-surface/isfailure.md +58 -0
  74. package/docs/reference/authoring-surface/jr2setup.md +237 -0
  75. package/docs/reference/authoring-surface/open.md +74 -0
  76. package/docs/reference/authoring-surface/openbody.md +86 -0
  77. package/docs/reference/authoring-surface/paramsof.md +125 -0
  78. package/docs/reference/authoring-surface/pool.md +186 -0
  79. package/docs/reference/authoring-surface/push.md +112 -0
  80. package/docs/reference/authoring-surface/secret.md +118 -0
  81. package/docs/reference/authoring-surface/sentby.md +83 -0
  82. package/docs/reference/authoring-surface/source.md +51 -0
  83. package/docs/reference/authoring-surface/text.md +62 -0
  84. package/docs/reference/authoring-surface/type-agentdeclaration.md +27 -0
  85. package/docs/reference/authoring-surface/type-agentdefinition.md +80 -0
  86. package/docs/reference/authoring-surface/type-agentlogic.md +15 -0
  87. package/docs/reference/authoring-surface/type-agentslots.md +12 -0
  88. package/docs/reference/authoring-surface/type-answeraction.md +12 -0
  89. package/docs/reference/authoring-surface/type-arg.md +19 -0
  90. package/docs/reference/authoring-surface/type-argnames.md +12 -0
  91. package/docs/reference/authoring-surface/type-binding.md +62 -0
  92. package/docs/reference/authoring-surface/type-bindingof.md +12 -0
  93. package/docs/reference/authoring-surface/type-bodyacceptsdoor.md +15 -0
  94. package/docs/reference/authoring-surface/type-bodyacceptsslots.md +17 -0
  95. package/docs/reference/authoring-surface/type-ceilings.md +41 -0
  96. package/docs/reference/authoring-surface/type-chainlink.md +19 -0
  97. package/docs/reference/authoring-surface/type-chartaction.md +46 -0
  98. package/docs/reference/authoring-surface/type-chartagent.md +28 -0
  99. package/docs/reference/authoring-surface/type-chartevent.md +20 -0
  100. package/docs/reference/authoring-surface/type-chartguard.md +20 -0
  101. package/docs/reference/authoring-surface/type-chartinvoke.md +28 -0
  102. package/docs/reference/authoring-surface/type-chartjson.md +14 -0
  103. package/docs/reference/authoring-surface/type-chartstate.md +46 -0
  104. package/docs/reference/authoring-surface/type-charttext.md +40 -0
  105. package/docs/reference/authoring-surface/type-charttransition.md +22 -0
  106. package/docs/reference/authoring-surface/type-charttransitions.md +14 -0
  107. package/docs/reference/authoring-surface/type-chartvalue.md +15 -0
  108. package/docs/reference/authoring-surface/type-childat.md +12 -0
  109. package/docs/reference/authoring-surface/type-childslots.md +12 -0
  110. package/docs/reference/authoring-surface/type-commandenv.md +15 -0
  111. package/docs/reference/authoring-surface/type-commandinput.md +19 -0
  112. package/docs/reference/authoring-surface/type-commandoptions.md +78 -0
  113. package/docs/reference/authoring-surface/type-commandoutput.md +104 -0
  114. package/docs/reference/authoring-surface/type-customize.md +109 -0
  115. package/docs/reference/authoring-surface/type-dispatchdefinition.md +39 -0
  116. package/docs/reference/authoring-surface/type-dispatchinput.md +14 -0
  117. package/docs/reference/authoring-surface/type-dispatchopenparts.md +14 -0
  118. package/docs/reference/authoring-surface/type-dispatchoutput.md +65 -0
  119. package/docs/reference/authoring-surface/type-duration.md +15 -0
  120. package/docs/reference/authoring-surface/type-effectbinding.md +93 -0
  121. package/docs/reference/authoring-surface/type-effectlogic.md +12 -0
  122. package/docs/reference/authoring-surface/type-envopenparts.md +14 -0
  123. package/docs/reference/authoring-surface/type-eventaudience.md +27 -0
  124. package/docs/reference/authoring-surface/type-eventdef.md +45 -0
  125. package/docs/reference/authoring-surface/type-eventfrom.md +16 -0
  126. package/docs/reference/authoring-surface/type-eventsemantics.md +26 -0
  127. package/docs/reference/authoring-surface/type-execoutcome.md +34 -0
  128. package/docs/reference/authoring-surface/type-failslot.md +14 -0
  129. package/docs/reference/authoring-surface/type-failure.md +19 -0
  130. package/docs/reference/authoring-surface/type-failureinput.md +20 -0
  131. package/docs/reference/authoring-surface/type-filebody.md +41 -0
  132. package/docs/reference/authoring-surface/type-frominput.md +14 -0
  133. package/docs/reference/authoring-surface/type-hostinjectedinput.md +58 -0
  134. package/docs/reference/authoring-surface/type-httpdefinition.md +101 -0
  135. package/docs/reference/authoring-surface/type-httpinput.md +12 -0
  136. package/docs/reference/authoring-surface/type-httpmethod.md +14 -0
  137. package/docs/reference/authoring-surface/type-httpopenparts.md +12 -0
  138. package/docs/reference/authoring-surface/type-httpoutput.md +89 -0
  139. package/docs/reference/authoring-surface/type-httprequestparts.md +33 -0
  140. package/docs/reference/authoring-surface/type-jr2config.md +126 -0
  141. package/docs/reference/authoring-surface/type-lends.md +38 -0
  142. package/docs/reference/authoring-surface/type-logicat.md +12 -0
  143. package/docs/reference/authoring-surface/type-markedif.md +12 -0
  144. package/docs/reference/authoring-surface/type-mergedactors.md +12 -0
  145. package/docs/reference/authoring-surface/type-namedsecret.md +25 -0
  146. package/docs/reference/authoring-surface/type-noslotspec.md +26 -0
  147. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +32 -0
  148. package/docs/reference/authoring-surface/type-openbodydeclaration.md +40 -0
  149. package/docs/reference/authoring-surface/type-openbodylogic.md +14 -0
  150. package/docs/reference/authoring-surface/type-openbodyoutput.md +42 -0
  151. package/docs/reference/authoring-surface/type-openbodyslot.md +18 -0
  152. package/docs/reference/authoring-surface/type-opencommandinput.md +17 -0
  153. package/docs/reference/authoring-surface/type-openeffect.md +12 -0
  154. package/docs/reference/authoring-surface/type-openeffectslots.md +12 -0
  155. package/docs/reference/authoring-surface/type-openof.md +12 -0
  156. package/docs/reference/authoring-surface/type-opensecret.md +23 -0
  157. package/docs/reference/authoring-surface/type-opensecretmark.md +12 -0
  158. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +31 -0
  159. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +32 -0
  160. package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
  161. package/docs/reference/authoring-surface/type-poolspec.md +60 -0
  162. package/docs/reference/authoring-surface/type-pushinput.md +15 -0
  163. package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
  164. package/docs/reference/authoring-surface/type-pushoutput.md +124 -0
  165. package/docs/reference/authoring-surface/type-pushwireoutput.md +12 -0
  166. package/docs/reference/authoring-surface/type-quantity.md +14 -0
  167. package/docs/reference/authoring-surface/type-reached.md +12 -0
  168. package/docs/reference/authoring-surface/type-reposlot.md +70 -0
  169. package/docs/reference/authoring-surface/type-reposlotsof.md +16 -0
  170. package/docs/reference/authoring-surface/type-sandboxoptions.md +77 -0
  171. package/docs/reference/authoring-surface/type-secret.md +25 -0
  172. package/docs/reference/authoring-surface/type-sender.md +19 -0
  173. package/docs/reference/authoring-surface/type-sentbyguards.md +12 -0
  174. package/docs/reference/authoring-surface/type-size.md +85 -0
  175. package/docs/reference/authoring-surface/type-slothandles.md +32 -0
  176. package/docs/reference/authoring-surface/type-sourcenextresult.md +17 -0
  177. package/docs/reference/authoring-surface/type-sourcespec.md +44 -0
  178. package/docs/reference/authoring-surface/type-syncstate.md +12 -0
  179. package/docs/reference/authoring-surface/type-text.md +19 -0
  180. package/docs/reference/authoring-surface/type-textpart.md +24 -0
  181. package/docs/reference/authoring-surface/type-thinkinglevel.md +39 -0
  182. package/docs/reference/authoring-surface/type-toparameterizedobject.md +14 -0
  183. package/docs/reference/authoring-surface/type-toprovidedactor.md +14 -0
  184. package/docs/reference/authoring-surface/type-unknown.md +21 -0
  185. package/docs/reference/authoring-surface/type-usercontainer.md +77 -0
  186. package/docs/reference/authoring-surface/type-withoutemptyindex.md +20 -0
  187. package/docs/reference/authoring-surface/type-workflowevent.md +46 -0
  188. package/docs/reference/authoring-surface/type-workspaceaccess.md +16 -0
  189. package/docs/reference/authoring-surface/type-workspaced.md +94 -0
  190. package/docs/reference/authoring-surface/type-workspacehandles.md +23 -0
  191. package/docs/reference/authoring-surface/type-workspacemachine.md +29 -0
  192. package/docs/reference/authoring-surface/type-workspaceof.md +13 -0
  193. package/docs/reference/authoring-surface/type-workspaceoptions.md +39 -0
  194. package/docs/reference/authoring-surface/type-workspacespec.md +37 -0
  195. package/docs/reference/authoring-surface/workspace.md +259 -0
  196. package/docs/reference/authoring-surface.md +197 -0
  197. package/docs/reference/cli.md +433 -0
  198. package/docs/reference/config.md +756 -0
  199. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +113 -0
  200. package/docs/reference/decisions/0002-duplex-streaming-actor.md +110 -0
  201. package/docs/reference/decisions/0004-worktree-provisioning-model.md +124 -0
  202. package/docs/reference/decisions/0005-sandbox-pod-composition.md +230 -0
  203. package/docs/reference/decisions/0006-agent-control-surface.md +47 -0
  204. package/docs/reference/decisions/0007-durable-machine-state.md +109 -0
  205. package/docs/reference/decisions/0008-orchestrator-as-deployed-app.md +60 -0
  206. package/docs/reference/decisions/0009-cli-and-instance-interface.md +275 -0
  207. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +160 -0
  208. package/docs/reference/decisions/0011-workflow-defined-events.md +141 -0
  209. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +103 -0
  210. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +157 -0
  211. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +95 -0
  212. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +265 -0
  213. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +124 -0
  214. package/docs/reference/decisions/0017-pool-over-a-source-port.md +69 -0
  215. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +111 -0
  216. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +275 -0
  217. package/docs/reference/decisions/0020-private-ca-trust-is-a-named-concept.md +60 -0
  218. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +100 -0
  219. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +103 -0
  220. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +176 -0
  221. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +439 -0
  222. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +65 -0
  223. package/docs/reference/decisions/0026-a-turn-that-is-over-is-never-prompted.md +62 -0
  224. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +237 -0
  225. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +89 -0
  226. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +154 -0
  227. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +100 -0
  228. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +190 -0
  229. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +61 -0
  230. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +127 -0
  231. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +57 -0
  232. package/docs/reference/decisions/0035-a-runaway-turn-is-ended-rerolled-once-then-a-fault.md +92 -0
  233. package/docs/reference/decisions/0036-a-turn-compacts-its-own-context-mid-flight.md +138 -0
  234. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +196 -0
  235. package/docs/reference/decisions/0038-jr2-up-builds-every-image-it-deploys.md +170 -0
  236. package/docs/reference/decisions/0039-image-garbage-collects-by-reachability.md +115 -0
  237. package/docs/reference/decisions/0041-a-build-the-host-already-holds-is-not-spent-again.md +64 -0
  238. package/docs/reference/decisions/0042-ready-is-not-routable-an-admission-retries-its-connection.md +142 -0
  239. package/docs/reference/decisions/0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md +170 -0
  240. package/docs/reference/decisions/0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md +85 -0
  241. package/docs/reference/decisions/0045-the-platform-joins-the-image-address-and-the-cluster-chooses-it.md +82 -0
  242. package/docs/reference/decisions/0046-a-converge-failure-carries-its-diagnosis.md +34 -0
  243. package/docs/reference/decisions/0047-the-git-ssh-key-source-is-the-users-choice.md +47 -0
  244. package/docs/reference/decisions/0048-the-orchestrator-boots-without-its-repos.md +36 -0
  245. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +126 -0
  246. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +73 -0
  247. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +227 -0
  248. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +81 -0
  249. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +111 -0
  250. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +126 -0
  251. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +107 -0
  252. package/docs/reference/decisions/0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md +62 -0
  253. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +152 -0
  254. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +106 -0
  255. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +384 -0
  256. package/docs/reference/decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md +111 -0
  257. package/docs/reference/decisions/0061-a-memory-kill-ends-the-agents-processes-not-the-harness.md +83 -0
  258. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +212 -0
  259. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +83 -0
  260. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +79 -0
  261. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +65 -0
  262. 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
  263. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +430 -0
  264. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +464 -0
  265. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +180 -0
  266. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +323 -0
  267. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +183 -0
  268. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +144 -0
  269. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +122 -0
  270. package/docs/reference/decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md +76 -0
  271. package/docs/reference/decisions.md +83 -0
  272. package/docs/reference/glossary.md +648 -0
  273. package/docs/reference/http-api.md +1348 -0
  274. package/docs/reference/machines.md +190 -0
  275. package/docs/reference/mechanism-events.md +72 -0
  276. package/docs/why.md +206 -0
  277. package/package.json +5 -3
  278. package/src/actor.ts +541 -79
  279. package/src/agent.ts +114 -38
  280. package/src/ambient.ts +2 -1
  281. package/src/assembly-checks.ts +189 -7
  282. package/src/cause.ts +234 -0
  283. package/src/chart.ts +169 -37
  284. package/src/charts.ts +34 -6
  285. package/src/config.ts +430 -178
  286. package/src/custodian.ts +74 -22
  287. package/src/customize.ts +188 -46
  288. package/src/dispatch-effect.ts +945 -0
  289. package/src/door-compat.ts +217 -0
  290. package/src/durability.ts +11 -4
  291. package/src/effect.ts +341 -44
  292. package/src/effector-wire.ts +160 -11
  293. package/src/fail.ts +209 -0
  294. package/src/harness-client.ts +541 -47
  295. package/src/http-effect.ts +495 -53
  296. package/src/http.ts +427 -16
  297. package/src/index.ts +60 -82
  298. package/src/instance.ts +68 -8
  299. package/src/internal.ts +84 -0
  300. package/src/machine-doc.ts +21 -3
  301. package/src/mechanism-routes.ts +168 -0
  302. package/src/model-keys.generated.ts +53 -0
  303. package/src/model-keys.ts +18 -58
  304. package/src/notices.ts +66 -13
  305. package/src/open-body.ts +159 -23
  306. package/src/open-effect.ts +81 -8
  307. package/src/open.ts +59 -6
  308. package/src/parts.ts +184 -13
  309. package/src/party.ts +68 -2
  310. package/src/pick.ts +463 -11
  311. package/src/pool.ts +247 -36
  312. package/src/registration.ts +83 -4
  313. package/src/repos.ts +2 -1
  314. package/src/run-host.ts +565 -31
  315. package/src/run-output.ts +38 -0
  316. package/src/sandbox-effect.ts +273 -38
  317. package/src/sandbox-kube.ts +3 -1
  318. package/src/secret-placement.ts +52 -17
  319. package/src/server.ts +3 -0
  320. package/src/setup.ts +239 -56
  321. package/src/size.ts +121 -9
  322. package/src/snapshot-store.ts +54 -10
  323. package/src/stub-harness.ts +44 -16
  324. package/src/timers.ts +14 -1
  325. package/src/vocabulary.ts +69 -14
  326. package/src/wire.ts +90 -16
  327. package/src/workspace.ts +697 -175
package/src/instance.ts CHANGED
@@ -38,8 +38,17 @@ import {
38
38
  type ResolvedAssembly,
39
39
  } from "./assembly.ts";
40
40
  import { assemblyRefusals, type LoadedWorkflow } from "./assembly-checks.ts";
41
- import { chartName, NamedCharts, readChartFiles, splitChartName, type ChartCounts, type ChartFile } from "./charts.ts";
41
+ import {
42
+ chartName,
43
+ NamedCharts,
44
+ readChartFiles,
45
+ readRuntimeCharts,
46
+ splitChartName,
47
+ type ChartCounts,
48
+ type ChartFile,
49
+ } from "./charts.ts";
42
50
  import type { ImageRefs } from "./images.ts";
51
+ import { partsOf } from "./parts.ts";
43
52
  import { join } from "node:path";
44
53
  import { pathToFileURL } from "node:url";
45
54
  import type { AddressInfo } from "node:net";
@@ -56,6 +65,7 @@ import type { SnapshotStore } from "./snapshot-store.ts";
56
65
  import { createAuthenticator, effectorKey, harnessToken, loadSigningKey, mintInstanceToken } from "./tokens.ts";
57
66
  import type { SandboxPort } from "./workspace.ts";
58
67
  import type { EffectorPlacement, EffectorTarget } from "./http-effect.ts";
68
+ import { WORKFLOW_NAME } from "./effector-wire.ts";
59
69
 
60
70
  export type InstanceOptions = {
61
71
  /** The instance folder: the state volume `.jr2/` (its `state.db` unless `store` is supplied, and
@@ -119,6 +129,10 @@ export type InstanceOptions = {
119
129
  * The entrypoint derives it from the pod's namespace (deterministic Service DNS); absent,
120
130
  * such a Turn without an explicit `endpoint` faults pointedly. */
121
131
  instanceHarness?: string;
132
+ /** The name this Orchestrator gives itself at another Orchestrator's door (ADR-0071): a foreign
133
+ * dispatch's `dispatchedBy.instance`. The entrypoint passes the pod's namespace; absent, a foreign
134
+ * dispatch names an unnamed caller. */
135
+ orchestratorName?: string;
122
136
  /** The Instance Effector's headless Service, `host:port` (ADR-0068) — where an http Effect is sent.
123
137
  * The entrypoint derives it from the pod's namespace; absent, an http Effect faults pointedly. */
124
138
  instanceEffector?: string;
@@ -287,6 +301,7 @@ export async function startInstance(opts: InstanceOptions): Promise<RunningInsta
287
301
  resolveDef,
288
302
  sandbox: opts.sandbox,
289
303
  instanceHarness: opts.instanceHarness,
304
+ ...(opts.orchestratorName !== undefined ? { orchestratorName: opts.orchestratorName } : {}),
290
305
  harnessBearer: (placement) => harnessToken(signingKey, placement),
291
306
  // The Effectors (ADR-0068): each placement's Service, and the key its wire is signed with —
292
307
  // derived from the signing key as the Harness bearer is, so nothing is stored. The Instance
@@ -307,12 +322,15 @@ export async function startInstance(opts: InstanceOptions): Promise<RunningInsta
307
322
  // says why (ADR-0030). stderr, because it is a fault, not the boot's structured result.
308
323
  onRestoreError: (runId, err) =>
309
324
  console.error(`restore failed for run ${runId}: ${err instanceof Error ? err.message : err}`),
325
+ // An in-process dispatch of a named Chart the current Assembly left unrunnable answers 409, as
326
+ // the HTTP door does (ADR-0071). The registry is built after the host; this runs only at a start.
327
+ unrunnableChart: (name): string | undefined => charts.get(name)?.unrunnable,
310
328
  });
311
329
 
312
330
  // The named Charts (ADR-0070). The runtime ones are read from the volume now, and admitted when
313
331
  // the first Assembly binds; with none current, every one waits unrunnable ("not in the current
314
332
  // Assembly") and is never deleted.
315
- const charts = new NamedCharts({ dir: opts.dir, host });
333
+ const charts: NamedCharts = new NamedCharts({ dir: opts.dir, host });
316
334
  await charts.load();
317
335
  // The current Assembly's own Workflow names (ADR-0069, ADR-0070): its Machines and its Chart
318
336
  // files, as its record lists them. `GET /assembly` answers these; the runtime Charts the host
@@ -352,7 +370,8 @@ export async function startInstance(opts: InstanceOptions): Promise<RunningInsta
352
370
  // by name. No current Assembly → no Workflows, and the process still serves (ADR-0048).
353
371
  if (current) {
354
372
  const booted = current;
355
- swap(await importAssembly(booted.hash, booted.root), await readChartFiles(booted.root), booted);
373
+ const loaded = await importAssembly(booted.hash, booted.root);
374
+ swap(loaded, await readChartFiles(booted.root, undefined, contextDirsOf(loaded)), booted);
356
375
  } else {
357
376
  charts.bind({ workflows: [], files: [] });
358
377
  }
@@ -390,11 +409,14 @@ export async function startInstance(opts: InstanceOptions): Promise<RunningInsta
390
409
  const workflows = await importWorkflows(root);
391
410
  // The Assembly's Chart files (ADR-0070): the door admits each against its wrapper, and one
392
411
  // that fails refuses the publish naming the file.
393
- const files = await readChartFiles(root);
412
+ const files = await readChartFiles(root, undefined, contextDirsOf(workflows));
413
+ // The runtime Charts on the volume: a dispatch of a named Chart may resolve to one (ADR-0071).
414
+ const runtimeCharts = (await readRuntimeCharts(opts.dir)).map((c) => ({ name: chartName(c.wrapper, c.name) }));
394
415
  const refusals = await assemblyRefusals({
395
416
  config,
396
417
  workflows,
397
418
  charts: files,
419
+ runtimeCharts,
398
420
  ...(opts.imageMap ? { images: opts.imageMap } : {}),
399
421
  root,
400
422
  });
@@ -615,10 +637,22 @@ export async function loadWorkflows(dir: string): Promise<Array<{ name: string;
615
637
  * `workflows/<wrapper>/<name>.chart.json`, one level below the flat `workflows/*.ts`. The sibling
616
638
  * of {@link loadWorkflows}, for `jr2 up`'s host-side walk, which admits each file against its
617
639
  * wrapper with `chartFileRefusals`, the check the door's `assemblyRefusals({ charts })` runs. Any other file in a
618
- * wrapper's directory is ignored and named to `warn`.
640
+ * wrapper's directory is ignored and named to `warn`, except in a docker context that one of
641
+ * `workflows` names (ADR-0037): jr2 reads that folder too.
619
642
  */
620
- export function loadCharts(root: string, warn?: (line: string) => void): Promise<ChartFile[]> {
621
- return readChartFiles(root, warn);
643
+ export function loadCharts(
644
+ root: string,
645
+ workflows: LoadedWorkflow[],
646
+ warn?: (line: string) => void,
647
+ ): Promise<ChartFile[]> {
648
+ return readChartFiles(root, warn, contextDirsOf(workflows));
649
+ }
650
+
651
+ /** Every docker context folder the registered Machines name (ADR-0037, ADR-0049): a Sandbox
652
+ * Image, a User Container image or an Effector image that jr2 builds. The Chart discovery does not
653
+ * name a file in one (ADR-0070). */
654
+ function contextDirsOf(workflows: LoadedWorkflow[]): string[] {
655
+ return partsOf(workflows.map((w) => w.machine)).images.map((i) => i.dir);
622
656
  }
623
657
 
624
658
  /**
@@ -629,6 +663,11 @@ export function loadCharts(root: string, warn?: (line: string) => void): Promise
629
663
  * and the `images/<name>` dirname scan it was also written for both retired with ADR-0049. An
630
664
  * ABSENT dir is empty; any other readdir failure (EACCES, ENOTDIR, …) throws — a directory that
631
665
  * exists but cannot be read must be loud, never "no modules".
666
+ *
667
+ * A stem is a Workflow name, and a Workflow name has one rule wherever it is written: lowercase
668
+ * letters, digits and "-", the rule a Chart name keeps too (`WORKFLOW_NAME`). So `jr2 run`, the
669
+ * door's path and a `dispatch` slot name the same set of Workflows (ADR-0071), and a stem outside
670
+ * the rule throws here, naming the rename, before anything registers it.
632
671
  */
633
672
  export async function discoverModules(moduleDir: string): Promise<Array<{ name: string; file: string }>> {
634
673
  let entries: string[];
@@ -638,10 +677,31 @@ export async function discoverModules(moduleDir: string): Promise<Array<{ name:
638
677
  if ((err as NodeJS.ErrnoException).code === "ENOENT") return [];
639
678
  throw err;
640
679
  }
641
- return entries
680
+ const modules = entries
642
681
  .filter((f) => f.endsWith(".ts") && !f.endsWith(".d.ts") && !f.startsWith("_"))
643
682
  .sort()
644
683
  .map((f) => ({ name: f.slice(0, -3), file: join(moduleDir, f) }));
684
+ for (const { name } of modules) {
685
+ if (!WORKFLOW_NAME.test(name)) {
686
+ throw new Error(
687
+ `${join(moduleDir, `${name}.ts`)}: a Workflow is named by its filename stem, in lowercase letters, digits ` +
688
+ `and "-", so that \`jr2 run\` and a \`dispatch\` slot name it the same way — rename it to ` +
689
+ `${kebabOf(name)}.ts (ADR-0071)`,
690
+ );
691
+ }
692
+ }
693
+ return modules;
694
+ }
695
+
696
+ /** A stem outside the rule, as the rule would spell it: `reviewLoop` and `review_loop` are
697
+ * `review-loop`. */
698
+ function kebabOf(stem: string): string {
699
+ const out = stem
700
+ .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
701
+ .toLowerCase()
702
+ .replace(/[^a-z0-9]+/g, "-")
703
+ .replace(/^-+|-+$/g, "");
704
+ return out === "" ? "workflow" : out;
645
705
  }
646
706
 
647
707
  /**
@@ -0,0 +1,84 @@
1
+ // @jr2/orchestrator/internal — NOT for workflow authors. Everything the kit's own packages share
2
+ // that is not on the Authoring surface (index.ts): the run host, the HTTP API, the Harness wire,
3
+ // the Custodian, the Effector wire, the cluster names, the load walk. The CLI, the e2e suite,
4
+ // and any other kit package that imports this one use it. No stability promise: any
5
+ // name here can change or go in any release, so a workflow or a `jr2.config.ts` that imports it
6
+ // is wrong, whatever compiles.
7
+ //
8
+ // It is the whole of index.ts plus the rest, so a kit file imports one specifier. The Orchestrator
9
+ // does NOT speak MCP (ADR-0013): the Harness presents the Menu to its model itself, and reaches the
10
+ // surface here through the Custodian in its pod.
11
+
12
+ export * from "./index.ts";
13
+
14
+ // The wire contract (ADR-0011): `defineEvent` is on the surface; the rest is the kit's.
15
+ export * from "@jr2/agent-protocol";
16
+ export * from "./agent.ts";
17
+ export * from "./effect.ts";
18
+ export * from "./http-effect.ts";
19
+ export * from "./sandbox-effect.ts";
20
+ export * from "./dispatch-effect.ts";
21
+ export type { EffectBinding, OpenEffect, OpenEffectPart } from "./open-effect.ts";
22
+ export { openPartsText } from "./open-effect.ts";
23
+ export * from "./effector-wire.ts";
24
+ export * from "./pick.ts";
25
+ export * from "./chart.ts";
26
+ export * from "./open-body.ts";
27
+ export * from "./charts.ts";
28
+ export * from "./parts.ts";
29
+ export * from "./size.ts";
30
+ export * from "./customize.ts";
31
+ export * from "./ambient.ts";
32
+ export * from "./config.ts";
33
+ export * from "./repo-identity.ts";
34
+ export * from "./pool.ts";
35
+ export * from "./setup.ts";
36
+ export { sentBy } from "./party.ts";
37
+ export * from "./vocabulary.ts";
38
+ export * from "./tokens.ts";
39
+ export * from "./durability.ts";
40
+ export * from "./snapshot-store.ts";
41
+ export * from "./actor.ts";
42
+ // The `gate` actor: jr2Setup pre-registers it and a state invokes it as `src: "gate"`, so no
43
+ // author writes the value (ADR-0015).
44
+ export * from "./gate.ts";
45
+ // The Failure's brand stays in fail.ts, and so does the logic that throws it: the kit makes a Failure
46
+ // only through `fail()` and `workspace()`, which imports that logic from fail.ts (ADR-0073).
47
+ export { failurePayload, isFailSlot } from "./fail.ts";
48
+ export type { Failure, FailSlot, FailureInput } from "./fail.ts";
49
+ export * from "./workspace.ts";
50
+ export * from "./images.ts";
51
+ export * from "./content-hash.ts";
52
+ export * from "./assembly-tar.ts";
53
+ export * from "./assembly.ts";
54
+ export * from "./assembly-checks.ts";
55
+ export * from "./mount-digest.ts";
56
+ export * from "./model-keys.ts";
57
+ export * from "./held-secrets.ts";
58
+ export * from "./secret-placement.ts";
59
+ export * from "./git-push.ts";
60
+ export * from "./git-clone.ts";
61
+ export * from "./custodian.ts";
62
+ export * from "./sandbox-kube.ts";
63
+ export {
64
+ effectorManifestKey,
65
+ LABEL_EFFECTOR_POD,
66
+ readSandboxEffectorManifest,
67
+ sandboxEffectorKeySecret,
68
+ sandboxEffectorObjectsOf,
69
+ type ProjectedSecret,
70
+ type SandboxEffectorManifest,
71
+ } from "./sandbox-effector.ts";
72
+ export * from "./repos.ts";
73
+ export * from "./repo-fetch.ts";
74
+ // Type-only: what a workflow event delivery looks like (the registration TABLE stays internal
75
+ // even here — ADR-0011).
76
+ export type { ChildErrorMarker, DeliveredEvent, PlacingMarker, RunMarker, TurnMarker } from "./registration.ts";
77
+ export * from "./machine-doc.ts";
78
+ export * from "./run-host.ts";
79
+ export * from "./http.ts";
80
+ export * from "./harness-client.ts";
81
+ export * from "./stub-harness.ts";
82
+ export * from "./instance.ts";
83
+ export * from "./server.ts";
84
+ export * from "./names.ts";
@@ -19,7 +19,9 @@ import type { AnyStateMachine, StateNode, TransitionDefinition } from "xstate";
19
19
  import { z } from "zod";
20
20
  import type { EventAudience } from "@jr2/agent-protocol";
21
21
  import { argvText, isEffect, targetText, type EffectParts } from "./effect.ts";
22
+ import { isFailSlot } from "./fail.ts";
22
23
  import { loadWarningsOf, seatsOf, vocabularyOf, type MachineSeat } from "./vocabulary.ts";
24
+ import { open } from "./open.ts";
23
25
 
24
26
  /** One transition of the Machine, id-addressed at both ends. */
25
27
  export type MachineTransitionDoc = {
@@ -55,7 +57,11 @@ export type EffectDoc =
55
57
  file?: true;
56
58
  }
57
59
  /** A push (ADR-0067): which options the author set. */
58
- | { kind: "push"; sign: boolean; squash: boolean; force: boolean; pin: boolean; timeoutMs: number; slot?: string };
60
+ | { kind: "push"; sign: boolean; squash: boolean; force: boolean; pin: boolean; timeoutMs: number; slot?: string }
61
+ /** A dispatch (ADR-0071): the Workflow it starts a run of, whether it waits, and the other
62
+ * Orchestrator's origin (`<open>` while a package leaves it Open); no target is this Orchestrator.
63
+ * The token's secret is named on the parts, never here: the target is what a reader routes on. */
64
+ | { kind: "dispatch"; workflow: string; wait: boolean; target?: string; timeoutMs: number };
59
65
 
60
66
  /** One state of the Machine; `states` nests in document order. */
61
67
  export type MachineStateDoc = {
@@ -67,8 +73,9 @@ export type MachineStateDoc = {
67
73
  /** Initial child state id (compound only). */
68
74
  initial?: string;
69
75
  /** Each invoke's id and slot; an Effect's carries its plain-data parts (ADR-0067) — what it runs,
70
- * where, and for how long, read without running anything. */
71
- invoke: Array<{ id: string; src: string; effect?: EffectDoc }>;
76
+ * where, and for how long, read without running anything. `fail`: the slot is a `fail()`, so this
77
+ * state can fail the run (ADR-0073). */
78
+ invoke: Array<{ id: string; src: string; effect?: EffectDoc; fail?: true }>;
72
79
  tags: string[];
73
80
  description?: string;
74
81
  states: MachineStateDoc[];
@@ -326,6 +333,7 @@ function serializeState(node: StateNode<any, any>, into: MachineTransitionDoc[],
326
333
  type: node.type,
327
334
  invoke: node.invoke.map((inv) => {
328
335
  const logic = typeof inv.src === "string" ? scope.actors[inv.src] : undefined;
336
+ if (isFailSlot(logic)) return { id: inv.id, src: srcName(inv.src), fail: true as const };
329
337
  if (!isEffect(logic)) return { id: inv.id, src: srcName(inv.src) };
330
338
  return { id: inv.id, src: srcName(inv.src), effect: effectDoc(logic.effect) };
331
339
  }),
@@ -404,6 +412,16 @@ function effectDoc(parts: EffectParts): EffectDoc {
404
412
  const { sign, squash, force, pin, timeoutMs, slot } = parts;
405
413
  return { kind: "push", sign, squash, force, pin, timeoutMs, ...(slot ? { slot } : {}) };
406
414
  }
415
+ if (parts.kind === "dispatch") {
416
+ const { workflow, wait, timeoutMs, target } = parts;
417
+ return {
418
+ kind: "dispatch",
419
+ workflow,
420
+ wait,
421
+ ...(target === undefined ? {} : { target: target === open ? "<open>" : target.url }),
422
+ timeoutMs,
423
+ };
424
+ }
407
425
  const { argv, timeoutMs, slot, secrets, pin } = parts;
408
426
  return {
409
427
  kind: "command",
@@ -0,0 +1,168 @@
1
+ // A mechanism event must have a route (ADR-0074). jr2 sends `agent.fault` to the Machine that
2
+ // invoked an Agent when the Agent's Turn cannot go on (ADR-0016), and `workspace()` sends
3
+ // `workspace.lost` to its body when the Sandbox is gone (ADR-0012), and an Open body sends a
4
+ // Chart's unrouted `agent.fault` to the Machine that invoked it (ADR-0070). Each is a relayed
5
+ // event, and xstate drops an event that no active state handles. So the walk refuses a state that cannot
6
+ // route one, and the run host faults a run in which none took one.
7
+ //
8
+ // - `refuseUnroutedAgentFault`: a state that invokes an Agent, or an Open body that can run one
9
+ // (it lends an Agent or allows a model), when that state and all its ancestors have no
10
+ // `agent.fault` route. In `jr2Setup().createMachine` and in the load walk (`partsOf`), beside the
11
+ // lost-output refusal, so `jr2 check`, `jr2 up` and the Orchestrator's load show it. Not for a
12
+ // compiled Chart: there the route is optional, and the Open body sends an `agent.fault` that no
13
+ // transition of the Chart takes to the Machine that invoked it (open-body.ts; ADR-0070).
14
+ // - `refuseUnroutedLost`: a non-final leaf state of a `workspace()` body, when that state and all
15
+ // its ancestors have no `workspace.lost` route. In `workspace()`, the one place that knows that a
16
+ // Machine is a body. A compound or parallel state is not judged by itself: an event goes to the
17
+ // active leaves first, so a route on each leaf (or above it) is a route for the compound state.
18
+ // A final state of the root ends the body, so it is not judged. A nested final state is judged
19
+ // when its compound state has no `onDone`: the body then stays in the compound state, and a
20
+ // route on the compound state or above it is the route. The line names the compound state. A
21
+ // final state of a parallel region is not judged: the other regions are active, and their leaves
22
+ // are judged.
23
+ // - `unroutedMechanismEvent`: the runtime half, for a route that the walk cannot judge (a guard
24
+ // that is false when the event comes). The run host reads it off each microstep.
25
+ //
26
+ // A route is any transition for the event: one with actions only counts, because it can be a
27
+ // deliberate `reenter: true` that invokes the Agent again. jr2 does not judge what a route does.
28
+ // A wildcard (`"*"`, `"agent.*"`) that takes the event is a route too, as it is at runtime.
29
+
30
+ import type { AnyStateMachine, StateNode } from "xstate";
31
+ import { isAgent } from "./agent.ts";
32
+ import { isOpenBody, openBodyDeclarationOf } from "./open-body.ts";
33
+
34
+ /** The two mechanism events that must have a route. */
35
+ const ROUTED_MECHANISM_EVENTS = ["agent.fault", "workspace.lost"] as const;
36
+
37
+ type Node = StateNode<any, any>;
38
+
39
+ /** Does a descriptor of a state's `on` take this event type? xstate's own match: the exact type,
40
+ * `"*"`, or a partial wildcard like `"agent.*"`. */
41
+ function takes(descriptor: string, type: string): boolean {
42
+ if (descriptor === type || descriptor === "*") return true;
43
+ return descriptor.endsWith(".*") && type.startsWith(descriptor.slice(0, -1));
44
+ }
45
+
46
+ /** Does this state, or a state above it, have a route for `type`? */
47
+ function routedAt(node: Node, type: string): boolean {
48
+ for (let at: Node | undefined = node; at !== undefined; at = at.parent) {
49
+ for (const descriptor of at.transitions.keys()) if (takes(descriptor, type)) return true;
50
+ }
51
+ return false;
52
+ }
53
+
54
+ /** Every state node of the tree below (and with) `node`, in document order. */
55
+ function statesOf(node: Node): Node[] {
56
+ const out: Node[] = [node];
57
+ const children = Object.values(node.states as Record<string, Node>).sort((a, b) => a.order - b.order);
58
+ for (const child of children) out.push(...statesOf(child));
59
+ return out;
60
+ }
61
+
62
+ /** The text of one or more state ids, quoted. */
63
+ function quoted(ids: readonly string[]): string {
64
+ return ids.map((id) => `"${id}"`).join(", ");
65
+ }
66
+
67
+ /** Is this slot an Open body that can run an Agent whose `agent.fault` comes to the Machine? One
68
+ * that lends an Agent or lets a Chart add its own: the body sends the Machine a Chart's unrouted
69
+ * `agent.fault`. A lent Machine routes its own Agents, and the walk judges it by itself. */
70
+ function runsChartAgents(logic: unknown): boolean {
71
+ if (!isOpenBody(logic)) return false;
72
+ const decl = openBodyDeclarationOf(logic);
73
+ return (decl.lends.agents?.length ?? 0) > 0 || decl.allows.models.length > 0;
74
+ }
75
+
76
+ /**
77
+ * Refuse a Machine with a state that invokes an Agent, or an Open body that can run one, when that
78
+ * state and all its ancestors have no `agent.fault` route (ADR-0074). The slots are the Machine's
79
+ * own `actors`. Throws one line that names each state, the event and the route to write.
80
+ */
81
+ export function refuseUnroutedAgentFault(machine: AnyStateMachine): void {
82
+ const actors = (machine.implementations.actors ?? {}) as Record<string, unknown>;
83
+ const agents: string[] = [];
84
+ const bodies: string[] = [];
85
+ for (const node of statesOf(machine.root as Node)) {
86
+ if (routedAt(node, "agent.fault")) continue;
87
+ const srcs = node.invoke.map((inv) => (typeof inv.src === "string" ? actors[inv.src] : undefined));
88
+ if (srcs.some((logic) => isAgent(logic))) agents.push(node.id);
89
+ else if (srcs.some((logic) => runsChartAgents(logic))) bodies.push(node.id);
90
+ }
91
+ if (!agents.length && !bodies.length) return;
92
+ const clause = (ids: string[], what: string) =>
93
+ `${ids.length > 1 ? "states" : "state"} ${quoted(ids)} ${ids.length > 1 ? "invoke" : "invokes"} ${what}`;
94
+ const what = [
95
+ ...(agents.length ? [clause(agents, "an Agent")] : []),
96
+ ...(bodies.length ? [clause(bodies, "an Open body that can run an Agent")] : []),
97
+ ].join(", and ");
98
+ const many = agents.length + bodies.length > 1;
99
+ throw new Error(
100
+ `machine "${machine.id}": ${what}, and no route for "agent.fault" is on ${many ? "them" : "it"} or on a state ` +
101
+ `above — jr2 sends agent.fault when the Agent's Turn cannot go on, and with no route the run faults: route it ` +
102
+ `on that state or on a state above it, as on: { "agent.fault": "<the state that handles it>" } (ADR-0074)`,
103
+ );
104
+ }
105
+
106
+ /** Does the body stay in this final state? It does when the final state is nested, its compound
107
+ * state has no `onDone`, and that compound state is no region of a parallel state. */
108
+ function staysIn(final: Node): boolean {
109
+ const parent = final.parent;
110
+ if (parent === undefined || parent.parent === undefined) return false;
111
+ if (parent.parent.type === "parallel") return false;
112
+ return !parent.transitions.has(`xstate.done.state.${parent.id}`);
113
+ }
114
+
115
+ /**
116
+ * Refuse a `workspace()` body with a state that is not final, that it can stay in, when that state
117
+ * and all its ancestors have no `workspace.lost` route (ADR-0074). Throws one line that names each
118
+ * state, the event and the route to write.
119
+ */
120
+ export function refuseUnroutedLost(body: AnyStateMachine): void {
121
+ // The leaves: an atomic state, a root with no child state, or a final state that the body can stay
122
+ // in. A history state is no state the body is in.
123
+ const unrouted = statesOf(body.root as Node)
124
+ .filter((node) => Object.keys(node.states).length === 0 && node.type !== "history")
125
+ .filter((node) => node.type !== "final" || staysIn(node))
126
+ .filter((node) => !routedAt(node, "workspace.lost"))
127
+ // The body stays in the compound state of a final state that it can stay in, and that state is
128
+ // not final: the line names it.
129
+ .map((node) => (node.type === "final" ? node.parent!.id : node.id));
130
+ if (!unrouted.length) return;
131
+ const many = unrouted.length > 1;
132
+ throw new Error(
133
+ `workspace(): the body "${body.id}" has ${many ? "states" : "a state"} ${quoted(unrouted)} that ` +
134
+ `${many ? "are" : "is"} not final, and no route for "workspace.lost" is on ${many ? "them" : "it"} or on a ` +
135
+ `state above — jr2 sends workspace.lost to the body when its Sandbox is gone, and with no route the run ` +
136
+ `faults: route it on the body's root, as on: { "workspace.lost": "<the state that handles it>" } (ADR-0074)`,
137
+ );
138
+ }
139
+
140
+ /** The parts of an `@xstate.microstep` inspection event that the runtime check reads. */
141
+ export type MicrostepView = {
142
+ actorRef: { id: string; getSnapshot(): unknown; logic?: unknown };
143
+ event: { type: string; reason?: unknown };
144
+ snapshot?: unknown;
145
+ _transitions?: readonly unknown[];
146
+ };
147
+
148
+ /**
149
+ * The fault of a microstep in which a Machine got `agent.fault` or `workspace.lost` and took no
150
+ * transition for it (ADR-0074), or undefined for every other microstep. The text names the event,
151
+ * the Machine and its state, and the event's `reason`. For `workspace.lost` this is the body's
152
+ * microstep: the wrapper always takes its own transition, to forward the event to the body.
153
+ */
154
+ export function unroutedMechanismEvent(ev: MicrostepView): string | undefined {
155
+ const type = ev.event?.type;
156
+ if (!(ROUTED_MECHANISM_EVENTS as readonly string[]).includes(type)) return undefined;
157
+ if (ev._transitions === undefined || ev._transitions.length > 0) return undefined;
158
+ const snapshot = (ev.snapshot ?? ev.actorRef.getSnapshot()) as { value?: unknown; status?: string } | undefined;
159
+ if (snapshot?.status !== undefined && snapshot.status !== "active") return undefined;
160
+ const machine = (ev.actorRef.logic as { id?: unknown } | undefined)?.id;
161
+ const name = typeof machine === "string" ? machine : ev.actorRef.id;
162
+ const reason = typeof ev.event.reason === "string" && ev.event.reason !== "" ? ev.event.reason : "no reason given";
163
+ return (
164
+ `machine "${name}" got "${type}" in state ${JSON.stringify(snapshot?.value ?? null)}, and no transition took ` +
165
+ `it (${reason}) — no route for "${type}" is on that state or on a state above, or the guard of each route ` +
166
+ `was false: route it with a transition that is always taken (ADR-0074)`
167
+ );
168
+ }
@@ -0,0 +1,53 @@
1
+ // Made from @earendil-works/pi-ai 1.4.0 by `just model-keys` (packages/harness/scripts/model-keys.ts).
2
+ // Do not edit: run the recipe at each pi bump. `model-keys.ts` says what the table means (ADR-0059).
3
+
4
+ import type { ModelKey } from "./model-keys.ts";
5
+
6
+ export const MODEL_KEYS: Readonly<Record<string, ModelKey>> = {
7
+ // No default host: the calls go to more than one host: bedrock-runtime.us-east-1.amazonaws.com, bedrock-runtime.eu-central-1.amazonaws.com.
8
+ AWS_BEARER_TOKEN_BEDROCK: { providers: ["amazon-bedrock"] },
9
+ ANT_LING_API_KEY: { providers: ["ant-ling"], host: "api.ant-ling.com" },
10
+ ANTHROPIC_AUTH_TOKEN: { providers: ["anthropic"], host: "api.anthropic.com" },
11
+ ANTHROPIC_OAUTH_TOKEN: { providers: ["anthropic"], host: "api.anthropic.com" },
12
+ ANTHROPIC_API_KEY: { providers: ["anthropic"], host: "api.anthropic.com" },
13
+ // No default host: the catalog names no base URL.
14
+ AZURE_OPENAI_API_KEY: { providers: ["azure"] },
15
+ BASETEN_API_KEY: { providers: ["baseten"], host: "inference.baseten.co" },
16
+ CEREBRAS_API_KEY: { providers: ["cerebras"], host: "api.cerebras.ai" },
17
+ // No default host: a base URL is a template.
18
+ CLOUDFLARE_API_KEY: { providers: ["cloudflare-ai-gateway", "cloudflare-workers-ai"] },
19
+ DEEPSEEK_API_KEY: { providers: ["deepseek"], host: "api.deepseek.com" },
20
+ FIREWORKS_API_KEY: { providers: ["fireworks"], host: "api.fireworks.ai" },
21
+ COPILOT_GITHUB_TOKEN: { providers: ["github-copilot"], host: "api.individual.githubcopilot.com" },
22
+ GEMINI_API_KEY: { providers: ["google"], host: "generativelanguage.googleapis.com" },
23
+ // No default host: a base URL is a template.
24
+ GOOGLE_CLOUD_API_KEY: { providers: ["google-vertex"] },
25
+ GROQ_API_KEY: { providers: ["groq"], host: "api.groq.com" },
26
+ HF_TOKEN: { providers: ["huggingface"], host: "router.huggingface.co" },
27
+ KIMI_API_KEY: { providers: ["kimi-coding"], host: "api.kimi.com" },
28
+ META_API_KEY: { providers: ["meta"], host: "api.meta.ai" },
29
+ MINIMAX_API_KEY: { providers: ["minimax"], host: "api.minimax.io" },
30
+ MINIMAX_CN_API_KEY: { providers: ["minimax-cn"], host: "api.minimaxi.com" },
31
+ MISTRAL_API_KEY: { providers: ["mistral"], host: "api.mistral.ai" },
32
+ // No default host: the calls go to more than one host: api.moonshot.ai, api.moonshot.cn.
33
+ MOONSHOT_API_KEY: { providers: ["moonshotai", "moonshotai-cn"] },
34
+ NVIDIA_API_KEY: { providers: ["nvidia"], host: "integrate.api.nvidia.com" },
35
+ OPENAI_API_KEY: { providers: ["openai"], host: "api.openai.com" },
36
+ OPENCODE_API_KEY: { providers: ["opencode", "opencode-go"], host: "opencode.ai" },
37
+ OPENROUTER_API_KEY: { providers: ["openrouter"], host: "openrouter.ai" },
38
+ QWEN_TOKEN_PLAN_API_KEY: {
39
+ providers: ["qwen-token-plan", "qwen-token-plan-individual"],
40
+ host: "token-plan.ap-southeast-1.maas.aliyuncs.com",
41
+ },
42
+ QWEN_TOKEN_PLAN_CN_API_KEY: { providers: ["qwen-token-plan-cn"], host: "token-plan.cn-beijing.maas.aliyuncs.com" },
43
+ RADIUS_API_KEY: { providers: ["radius"], host: "radius.pi.dev" },
44
+ TOGETHER_API_KEY: { providers: ["together"], host: "api.together.ai" },
45
+ AI_GATEWAY_API_KEY: { providers: ["vercel-ai-gateway"], host: "ai-gateway.vercel.sh" },
46
+ XAI_API_KEY: { providers: ["xai"], host: "api.x.ai" },
47
+ XIAOMI_API_KEY: { providers: ["xiaomi"], host: "api.xiaomimimo.com" },
48
+ XIAOMI_TOKEN_PLAN_AMS_API_KEY: { providers: ["xiaomi-token-plan-ams"], host: "token-plan-ams.xiaomimimo.com" },
49
+ XIAOMI_TOKEN_PLAN_CN_API_KEY: { providers: ["xiaomi-token-plan-cn"], host: "token-plan-cn.xiaomimimo.com" },
50
+ XIAOMI_TOKEN_PLAN_SGP_API_KEY: { providers: ["xiaomi-token-plan-sgp"], host: "token-plan-sgp.xiaomimimo.com" },
51
+ ZAI_API_KEY: { providers: ["zai"], host: "api.z.ai" },
52
+ ZAI_CODING_CN_API_KEY: { providers: ["zai-coding-cn"], host: "open.bigmodel.cn" },
53
+ };
package/src/model-keys.ts CHANGED
@@ -1,17 +1,29 @@
1
1
  // The known model keys (ADR-0059): every env var pi's catalog reads a provider's credential from,
2
2
  // the catalog provider(s) that read it, and the one host those providers' calls go to by default.
3
3
  //
4
- // A COPY of pi's data at the exact pin `@jr2/harness` holds, because this package has no pi
5
- // dependency and must not grow one: `jr2 up` reads this table host-side (R1, and the default
6
- // `hosts` of a held secret), and the in-cluster Orchestrator never touches pi. The copy cannot
7
- // drift silently — `@jr2/harness`'s `model-keys.test.ts` checks it against pi itself, env names per
8
- // provider and every provider's default host, in the default gate. A pi bump that adds or moves a
9
- // provider fails there first, the same canary role Harness conformance plays (ADR-0027).
4
+ // pi's data at the exact pin `@jr2/harness` holds, made into plain data by a script, because this
5
+ // package has no pi dependency and must not grow one: `jr2 up` reads this table host-side (R1, and
6
+ // the default `hosts` of a held secret), and the in-cluster Orchestrator never touches pi. The table
7
+ // is `model-keys.generated.ts`, and `just model-keys` writes it from pi-ai at each pi bump
8
+ // (`packages/harness/scripts/model-keys.ts`). It cannot drift silently: `@jr2/harness`'s
9
+ // `model-keys.test.ts` checks it against pi itself, env names per provider and every provider's
10
+ // default host, in the default gate. A pi bump that adds or moves a provider fails there first, the
11
+ // same canary role Harness conformance plays (ADR-0027).
10
12
  //
11
13
  // `host` is present only when every model of every provider that reads the name goes to ONE static
12
14
  // host. It is absent when there is none: the base URL is a template (`{location}`, an account id),
13
15
  // the providers disagree (MOONSHOT_API_KEY serves two regions), or the host is regional (Bedrock).
14
16
  // A held secret with such a name must then say its `hosts` itself (§3.4 of the spec, R5).
17
+ //
18
+ // Two facts the data does not say. anthropic reads three names: AUTH_TOKEN goes as `Authorization:
19
+ // Bearer`, the other two as `x-api-key` (an `sk-ant-oat` value is an OAuth token, which a Stand-in
20
+ // never is). AWS_BEARER_TOKEN_BEDROCK is the bearer form of Bedrock auth, which pi reads through
21
+ // its credential chain; SigV4 credentials cannot be held at all — a signature is not a header a
22
+ // Stand-in can be swapped into — and `jr2 up` warns on them (`SIGNING_CREDENTIALS`).
23
+
24
+ import { MODEL_KEYS } from "./model-keys.generated.ts";
25
+
26
+ export { MODEL_KEYS };
15
27
 
16
28
  /** One known model-key env name. */
17
29
  export type ModelKey = {
@@ -21,58 +33,6 @@ export type ModelKey = {
21
33
  host?: string;
22
34
  };
23
35
 
24
- export const MODEL_KEYS: Readonly<Record<string, ModelKey>> = {
25
- // anthropic reads three names. AUTH_TOKEN goes as `Authorization: Bearer`, the other two as
26
- // `x-api-key` (an `sk-ant-oat` value is an OAuth token, which a Stand-in never is).
27
- ANTHROPIC_API_KEY: { providers: ["anthropic"], host: "api.anthropic.com" },
28
- ANTHROPIC_AUTH_TOKEN: { providers: ["anthropic"], host: "api.anthropic.com" },
29
- ANTHROPIC_OAUTH_TOKEN: { providers: ["anthropic"], host: "api.anthropic.com" },
30
- OPENAI_API_KEY: { providers: ["openai"], host: "api.openai.com" },
31
- GEMINI_API_KEY: { providers: ["google"], host: "generativelanguage.googleapis.com" },
32
- // `https://{location}-aiplatform.googleapis.com` — regional, so no default.
33
- GOOGLE_CLOUD_API_KEY: { providers: ["google-vertex"] },
34
- COPILOT_GITHUB_TOKEN: { providers: ["github-copilot"], host: "api.individual.githubcopilot.com" },
35
- // The bearer form of Bedrock auth. Regional, so no default. (SigV4 credentials cannot be held at
36
- // all — a signature is not a header a Stand-in can be swapped into — and `jr2 up` warns on them.)
37
- AWS_BEARER_TOKEN_BEDROCK: { providers: ["amazon-bedrock"] },
38
- ANT_LING_API_KEY: { providers: ["ant-ling"], host: "api.ant-ling.com" },
39
- QWEN_TOKEN_PLAN_API_KEY: {
40
- providers: ["qwen-token-plan"],
41
- host: "token-plan.ap-southeast-1.maas.aliyuncs.com",
42
- },
43
- QWEN_TOKEN_PLAN_CN_API_KEY: { providers: ["qwen-token-plan-cn"], host: "token-plan.cn-beijing.maas.aliyuncs.com" },
44
- // The catalog names no base URL: an Azure deployment is the user's own resource.
45
- AZURE_OPENAI_API_KEY: { providers: ["azure-openai-responses"] },
46
- NVIDIA_API_KEY: { providers: ["nvidia"], host: "integrate.api.nvidia.com" },
47
- DEEPSEEK_API_KEY: { providers: ["deepseek"], host: "api.deepseek.com" },
48
- GROQ_API_KEY: { providers: ["groq"], host: "api.groq.com" },
49
- CEREBRAS_API_KEY: { providers: ["cerebras"], host: "api.cerebras.ai" },
50
- XAI_API_KEY: { providers: ["xai"], host: "api.x.ai" },
51
- // pi reads the name, and its catalog lists no model for the provider at this pin.
52
- RADIUS_API_KEY: { providers: ["radius"] },
53
- OPENROUTER_API_KEY: { providers: ["openrouter"], host: "openrouter.ai" },
54
- AI_GATEWAY_API_KEY: { providers: ["vercel-ai-gateway"], host: "ai-gateway.vercel.sh" },
55
- ZAI_API_KEY: { providers: ["zai"], host: "api.z.ai" },
56
- ZAI_CODING_CN_API_KEY: { providers: ["zai-coding-cn"], host: "open.bigmodel.cn" },
57
- MISTRAL_API_KEY: { providers: ["mistral"], host: "api.mistral.ai" },
58
- MINIMAX_API_KEY: { providers: ["minimax"], host: "api.minimax.io" },
59
- MINIMAX_CN_API_KEY: { providers: ["minimax-cn"], host: "api.minimaxi.com" },
60
- // Two providers, two hosts (`.ai` and `.cn`): no single default.
61
- MOONSHOT_API_KEY: { providers: ["moonshotai", "moonshotai-cn"] },
62
- HF_TOKEN: { providers: ["huggingface"], host: "router.huggingface.co" },
63
- FIREWORKS_API_KEY: { providers: ["fireworks"], host: "api.fireworks.ai" },
64
- TOGETHER_API_KEY: { providers: ["together"], host: "api.together.ai" },
65
- // Two providers, one host.
66
- OPENCODE_API_KEY: { providers: ["opencode", "opencode-go"], host: "opencode.ai" },
67
- KIMI_API_KEY: { providers: ["kimi-coding"], host: "api.kimi.com" },
68
- // Account-templated base URLs.
69
- CLOUDFLARE_API_KEY: { providers: ["cloudflare-workers-ai", "cloudflare-ai-gateway"] },
70
- XIAOMI_API_KEY: { providers: ["xiaomi"], host: "api.xiaomimimo.com" },
71
- XIAOMI_TOKEN_PLAN_CN_API_KEY: { providers: ["xiaomi-token-plan-cn"], host: "token-plan-cn.xiaomimimo.com" },
72
- XIAOMI_TOKEN_PLAN_AMS_API_KEY: { providers: ["xiaomi-token-plan-ams"], host: "token-plan-ams.xiaomimimo.com" },
73
- XIAOMI_TOKEN_PLAN_SGP_API_KEY: { providers: ["xiaomi-token-plan-sgp"], host: "token-plan-sgp.xiaomimimo.com" },
74
- };
75
-
76
36
  /** Every catalog provider id some known model key belongs to — the set `harness.catalog` may name. */
77
37
  export const MODEL_KEY_PROVIDERS: ReadonlySet<string> = new Set(
78
38
  Object.values(MODEL_KEYS).flatMap((key) => key.providers),