@jr2/orchestrator 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. package/console/components/app.ts +55 -15
  2. package/console/components/bottom-drawer.ts +259 -0
  3. package/console/components/drawer.ts +14 -6
  4. package/console/components/fleet.ts +36 -13
  5. package/console/components/machine-pane.ts +61 -5
  6. package/console/components/nav.ts +54 -8
  7. package/console/conversation.ts +161 -0
  8. package/console/login.ts +123 -0
  9. package/console/main.ts +600 -60
  10. package/console/store.ts +611 -64
  11. package/console/style.css +231 -0
  12. package/docs/agents-block.md +2 -2
  13. package/docs/concepts/agents-and-turns.md +86 -9
  14. package/docs/concepts/architecture.md +41 -28
  15. package/docs/concepts/callers-and-hooks.md +205 -0
  16. package/docs/concepts/charts.md +7 -5
  17. package/docs/concepts/composition.md +10 -1
  18. package/docs/concepts/converge.md +4 -2
  19. package/docs/concepts/durable-runs.md +43 -16
  20. package/docs/concepts/effects.md +50 -22
  21. package/docs/concepts/gates.md +95 -25
  22. package/docs/concepts/machines-and-workflows.md +5 -4
  23. package/docs/concepts/pools-and-sources.md +187 -11
  24. package/docs/concepts/secrets-and-trust.md +73 -33
  25. package/docs/concepts/the-menu.md +11 -0
  26. package/docs/concepts/workspaces.md +6 -5
  27. package/docs/get-started/a-model-decides.md +14 -13
  28. package/docs/get-started/an-agent-in-a-sandbox.md +43 -34
  29. package/docs/get-started/connect-a-model.md +1 -1
  30. package/docs/get-started/create-an-instance.md +17 -14
  31. package/docs/guides/add-tools-to-the-sandbox.md +55 -11
  32. package/docs/guides/answer-a-gate-from-a-forge.md +304 -0
  33. package/docs/guides/answer-a-gate.md +74 -37
  34. package/docs/guides/bind-open-parts.md +24 -8
  35. package/docs/guides/call-an-mcp-tool-from-a-machine.md +572 -0
  36. package/docs/guides/cancel-a-run.md +26 -16
  37. package/docs/guides/fix-a-failed-converge.md +18 -6
  38. package/docs/guides/give-a-caller-a-scope.md +203 -0
  39. package/docs/guides/give-an-agent-mcp-tools.md +422 -0
  40. package/docs/guides/iterate-on-a-machine-on-kind.md +10 -9
  41. package/docs/guides/let-an-agent-write-a-chart.md +9 -6
  42. package/docs/guides/look-inside-a-sandbox.md +101 -31
  43. package/docs/guides/open-the-console.md +63 -14
  44. package/docs/guides/read-a-conversation.md +225 -0
  45. package/docs/guides/run-a-chart-by-name.md +17 -16
  46. package/docs/guides/run-one-worker-per-item.md +15 -9
  47. package/docs/guides/set-an-agents-access-and-dials.md +2 -0
  48. package/docs/guides/start-a-run-for-each-alert.md +290 -0
  49. package/docs/guides/start-a-run-from-a-run.md +18 -16
  50. package/docs/guides/start-a-run.md +24 -18
  51. package/docs/guides/steer-an-agent.md +332 -0
  52. package/docs/guides/use-a-hosted-provider.md +1 -1
  53. package/docs/guides/watch-a-run.md +127 -121
  54. package/docs/guides/write-a-hook-kind.md +238 -0
  55. package/docs/guides/write-a-tool-as-an-mcp-server.md +351 -0
  56. package/docs/guides/write-a-workflow.md +17 -7
  57. package/docs/llms.txt +81 -15
  58. package/docs/reference/authoring-surface/agent.md +1 -1
  59. package/docs/reference/authoring-surface/answer.md +1 -1
  60. package/docs/reference/authoring-surface/arg.md +1 -1
  61. package/docs/reference/authoring-surface/basic.md +1 -1
  62. package/docs/reference/authoring-surface/bearer.md +1 -1
  63. package/docs/reference/authoring-surface/branchname.md +1 -1
  64. package/docs/reference/authoring-surface/carry.md +23 -0
  65. package/docs/reference/authoring-surface/chartschema.md +1 -1
  66. package/docs/reference/authoring-surface/chartschemafor.md +1 -1
  67. package/docs/reference/authoring-surface/command.md +1 -1
  68. package/docs/reference/authoring-surface/customize.md +4 -1
  69. package/docs/reference/authoring-surface/defineconfig.md +1 -1
  70. package/docs/reference/authoring-surface/defineevent.md +1 -1
  71. package/docs/reference/authoring-surface/dispatch.md +5 -4
  72. package/docs/reference/authoring-surface/fail.md +1 -1
  73. package/docs/reference/authoring-surface/file.md +1 -1
  74. package/docs/reference/authoring-surface/frompick.md +1 -1
  75. package/docs/reference/authoring-surface/gate.md +118 -0
  76. package/docs/reference/authoring-surface/group-agents.md +15 -0
  77. package/docs/reference/authoring-surface/group-charts.md +16 -0
  78. package/docs/reference/authoring-surface/group-composition.md +25 -0
  79. package/docs/reference/authoring-surface/group-config.md +17 -0
  80. package/docs/reference/authoring-surface/group-effects.md +48 -0
  81. package/docs/reference/authoring-surface/group-failure.md +13 -0
  82. package/docs/reference/authoring-surface/group-gates.md +16 -0
  83. package/docs/reference/authoring-surface/group-machine-and-events.md +15 -0
  84. package/docs/reference/authoring-surface/group-steering.md +12 -0
  85. package/docs/reference/authoring-surface/group-types-these-signatures-name.md +137 -0
  86. package/docs/reference/authoring-surface/group-workspaces-and-repos.md +15 -0
  87. package/docs/reference/authoring-surface/http.md +1 -1
  88. package/docs/reference/authoring-surface/isfailure.md +1 -1
  89. package/docs/reference/authoring-surface/isready.md +1 -1
  90. package/docs/reference/authoring-surface/jr2setup.md +13 -3
  91. package/docs/reference/authoring-surface/mcp.md +107 -0
  92. package/docs/reference/authoring-surface/open.md +5 -3
  93. package/docs/reference/authoring-surface/openbody.md +1 -1
  94. package/docs/reference/authoring-surface/paramsof.md +1 -1
  95. package/docs/reference/authoring-surface/pool.md +77 -9
  96. package/docs/reference/authoring-surface/push.md +1 -1
  97. package/docs/reference/authoring-surface/readyof.md +1 -1
  98. package/docs/reference/authoring-surface/reread.md +19 -0
  99. package/docs/reference/authoring-surface/secret.md +1 -1
  100. package/docs/reference/authoring-surface/sentby.md +1 -1
  101. package/docs/reference/authoring-surface/skip.md +22 -0
  102. package/docs/reference/authoring-surface/source.md +56 -1
  103. package/docs/reference/authoring-surface/steer.md +93 -0
  104. package/docs/reference/authoring-surface/text.md +1 -1
  105. package/docs/reference/authoring-surface/type-agentdefinition.md +14 -1
  106. package/docs/reference/authoring-surface/type-agentmcpserver.md +54 -0
  107. package/docs/reference/authoring-surface/type-anyframepath.md +1 -1
  108. package/docs/reference/authoring-surface/type-anyworktreepath.md +1 -1
  109. package/docs/reference/authoring-surface/type-binding.md +1 -1
  110. package/docs/reference/authoring-surface/type-caller.md +40 -0
  111. package/docs/reference/authoring-surface/type-callerrecord.md +32 -0
  112. package/docs/reference/authoring-surface/type-carry.md +19 -0
  113. package/docs/reference/authoring-surface/type-chainlink.md +3 -3
  114. package/docs/reference/authoring-surface/type-commandoutput.md +1 -1
  115. package/docs/reference/authoring-surface/type-customize.md +16 -1
  116. package/docs/reference/authoring-surface/type-delivered.md +17 -0
  117. package/docs/reference/authoring-surface/type-deliveredevent.md +18 -0
  118. package/docs/reference/authoring-surface/type-dispatchdefinition.md +4 -3
  119. package/docs/reference/authoring-surface/type-dispatchoutput.md +1 -1
  120. package/docs/reference/authoring-surface/type-effectbinding.md +12 -2
  121. package/docs/reference/authoring-surface/type-framepath.md +1 -1
  122. package/docs/reference/authoring-surface/type-gatebinding.md +15 -0
  123. package/docs/reference/authoring-surface/type-gatehooks.md +12 -0
  124. package/docs/reference/authoring-surface/type-gatelook.md +37 -0
  125. package/docs/reference/authoring-surface/type-gateoutcome.md +32 -0
  126. package/docs/reference/authoring-surface/type-gatereply.md +19 -0
  127. package/docs/reference/authoring-surface/type-gateslot.md +21 -0
  128. package/docs/reference/authoring-surface/type-gateslotinput.md +13 -0
  129. package/docs/reference/authoring-surface/type-gateslotparts.md +18 -0
  130. package/docs/reference/authoring-surface/type-gateslots.md +12 -0
  131. package/docs/reference/authoring-surface/type-gatetypesat.md +13 -0
  132. package/docs/reference/authoring-surface/type-hostinjectedinput.md +1 -1
  133. package/docs/reference/authoring-surface/type-httpoutput.md +1 -1
  134. package/docs/reference/authoring-surface/type-jr2config.md +37 -2
  135. package/docs/reference/authoring-surface/type-lateroutcome.md +1 -1
  136. package/docs/reference/authoring-surface/type-lends.md +5 -1
  137. package/docs/reference/authoring-surface/type-lookoutput.md +14 -0
  138. package/docs/reference/authoring-surface/type-mcpcalloutput.md +78 -0
  139. package/docs/reference/authoring-surface/type-mcpnotreturned.md +42 -0
  140. package/docs/reference/authoring-surface/type-mcpoptions.md +47 -0
  141. package/docs/reference/authoring-surface/type-mcpoutput.md +50 -0
  142. package/docs/reference/authoring-surface/type-mcpreadoutput.md +53 -0
  143. package/docs/reference/authoring-surface/type-mcpremoteserver.md +50 -0
  144. package/docs/reference/authoring-surface/type-mcpserver.md +41 -0
  145. package/docs/reference/authoring-surface/type-mcpstdioserver.md +42 -0
  146. package/docs/reference/authoring-surface/type-namedsecret.md +4 -4
  147. package/docs/reference/authoring-surface/type-openbodydeclaration.md +1 -1
  148. package/docs/reference/authoring-surface/type-openbodyoutput.md +1 -1
  149. package/docs/reference/authoring-surface/type-poolnextinput.md +1 -1
  150. package/docs/reference/authoring-surface/type-poolspec.md +11 -6
  151. package/docs/reference/authoring-surface/type-pushoutput.md +1 -1
  152. package/docs/reference/authoring-surface/type-pushsourcespec.md +38 -0
  153. package/docs/reference/authoring-surface/type-readsourcespec.md +47 -0
  154. package/docs/reference/authoring-surface/type-reposlot.md +1 -1
  155. package/docs/reference/authoring-surface/type-reread.md +18 -0
  156. package/docs/reference/authoring-surface/type-runsconfig.md +41 -0
  157. package/docs/reference/authoring-surface/type-sender.md +26 -1
  158. package/docs/reference/authoring-surface/type-sendercallerfield.md +14 -0
  159. package/docs/reference/authoring-surface/type-size.md +1 -1
  160. package/docs/reference/authoring-surface/type-skip.md +19 -0
  161. package/docs/reference/authoring-surface/type-sourcehooks.md +23 -0
  162. package/docs/reference/authoring-surface/type-sourcein.md +25 -0
  163. package/docs/reference/authoring-surface/type-sourcereply.md +18 -0
  164. package/docs/reference/authoring-surface/type-sourcespec.md +16 -7
  165. package/docs/reference/authoring-surface/type-sourceverdict.md +49 -0
  166. package/docs/reference/authoring-surface/type-standingpoolmachine.md +12 -0
  167. package/docs/reference/authoring-surface/type-standingpoolspec.md +51 -0
  168. package/docs/reference/authoring-surface/type-steerargs.md +20 -0
  169. package/docs/reference/authoring-surface/type-thinkinglevel.md +1 -1
  170. package/docs/reference/authoring-surface/type-usercontainer.md +1 -1
  171. package/docs/reference/authoring-surface/type-workspaced.md +1 -1
  172. package/docs/reference/authoring-surface/type-worktreefound.md +3 -3
  173. package/docs/reference/authoring-surface/type-worktreeinput.md +1 -1
  174. package/docs/reference/authoring-surface/type-worktreeoptions.md +1 -1
  175. package/docs/reference/authoring-surface/type-worktreeoutput.md +1 -1
  176. package/docs/reference/authoring-surface/type-worktreepath.md +1 -1
  177. package/docs/reference/authoring-surface/type-worktreeready.md +1 -1
  178. package/docs/reference/authoring-surface/type-worktreerefused.md +4 -4
  179. package/docs/reference/authoring-surface/workspace.md +1 -1
  180. package/docs/reference/authoring-surface/worktree.md +1 -1
  181. package/docs/reference/authoring-surface.md +88 -167
  182. package/docs/reference/cli.md +413 -74
  183. package/docs/reference/config.md +235 -2
  184. package/docs/reference/decisions/0002-duplex-streaming-actor.md +71 -11
  185. package/docs/reference/decisions/0005-sandbox-pod-composition.md +14 -10
  186. package/docs/reference/decisions/0006-agent-control-surface.md +4 -4
  187. package/docs/reference/decisions/0007-durable-machine-state.md +1 -1
  188. package/docs/reference/decisions/0009-cli-and-instance-interface.md +150 -49
  189. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +5 -3
  190. package/docs/reference/decisions/0011-workflow-defined-events.md +256 -12
  191. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +9 -2
  192. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +20 -14
  193. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +53 -27
  194. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +18 -14
  195. package/docs/reference/decisions/0017-pool-over-a-source-port.md +226 -12
  196. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +15 -7
  197. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +32 -18
  198. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +14 -1
  199. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +4 -4
  200. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +116 -34
  201. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +38 -37
  202. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +10 -5
  203. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +7 -6
  204. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +23 -7
  205. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +18 -10
  206. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +3 -2
  207. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +23 -18
  208. package/docs/reference/decisions/0032-the-console-unlocks-with-a-callers-credential.md +101 -0
  209. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +4 -3
  210. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +6 -6
  211. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +5 -4
  212. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +19 -19
  213. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +6 -1
  214. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +55 -40
  215. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +14 -13
  216. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +7 -0
  217. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +13 -7
  218. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +38 -30
  219. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +64 -17
  220. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +2 -1
  221. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +2 -2
  222. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md +109 -0
  223. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +51 -23
  224. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +94 -54
  225. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +101 -47
  226. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +32 -9
  227. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +17 -13
  228. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +56 -30
  229. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +35 -25
  230. package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +4 -3
  231. package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +37 -10
  232. package/docs/reference/decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md +285 -0
  233. package/docs/reference/decisions/0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md +198 -0
  234. package/docs/reference/decisions.md +6 -4
  235. package/docs/reference/glossary.md +96 -42
  236. package/docs/reference/hooks.md +1077 -0
  237. package/docs/reference/http-api.md +976 -149
  238. package/docs/why.md +2 -1
  239. package/package.json +10 -3
  240. package/src/actor.ts +36 -10
  241. package/src/agent.ts +50 -0
  242. package/src/assembly-checks.ts +530 -4
  243. package/src/callers.ts +245 -0
  244. package/src/chart.ts +52 -7
  245. package/src/config.ts +550 -1
  246. package/src/conversations.ts +453 -0
  247. package/src/custodian.ts +8 -2
  248. package/src/customize.ts +143 -4
  249. package/src/dispatch-effect.ts +142 -26
  250. package/src/durability.ts +2 -0
  251. package/src/duration.ts +20 -0
  252. package/src/effect-checks.ts +2 -1
  253. package/src/effect.ts +60 -7
  254. package/src/effector-wire.ts +272 -27
  255. package/src/gate-brand.ts +16 -0
  256. package/src/gate.ts +634 -48
  257. package/src/harness-client.ts +10 -4
  258. package/src/held-secrets.ts +72 -23
  259. package/src/holds.ts +2 -2
  260. package/src/hook-kinds/discord.ts +92 -0
  261. package/src/hook-kinds/door.ts +107 -0
  262. package/src/hook-kinds/github.ts +89 -0
  263. package/src/hook-kinds/gitlab.ts +72 -0
  264. package/src/hook-kinds/hook.ts +7 -0
  265. package/src/hook-kinds/index.ts +465 -0
  266. package/src/hook-kinds/slack.ts +151 -0
  267. package/src/hook-kinds/webhook.ts +113 -0
  268. package/src/hooks.ts +506 -0
  269. package/src/http-effect.ts +76 -38
  270. package/src/http.ts +889 -94
  271. package/src/index.ts +16 -5
  272. package/src/instance.ts +196 -26
  273. package/src/internal.ts +22 -3
  274. package/src/machine-doc.ts +26 -1
  275. package/src/mcp-effect.ts +424 -0
  276. package/src/mount-digest.ts +41 -7
  277. package/src/names.ts +64 -2
  278. package/src/notices.ts +138 -10
  279. package/src/open-body.ts +25 -11
  280. package/src/open-effect.ts +17 -4
  281. package/src/open.ts +4 -2
  282. package/src/parts.ts +87 -9
  283. package/src/party.ts +2 -1
  284. package/src/pick.ts +2 -2
  285. package/src/pool.ts +1142 -163
  286. package/src/registration.ts +203 -13
  287. package/src/run-host.ts +1509 -171
  288. package/src/run-list.ts +191 -0
  289. package/src/sandbox-kube.ts +53 -5
  290. package/src/secret-placement.ts +239 -33
  291. package/src/server.ts +53 -4
  292. package/src/setup.ts +72 -32
  293. package/src/snapshot-store.ts +565 -48
  294. package/src/steer.ts +111 -0
  295. package/src/stub-harness.ts +6 -2
  296. package/src/tokens.ts +263 -22
  297. package/src/wire.ts +17 -0
  298. package/src/workspace.ts +50 -6
  299. package/src/worktree.ts +3 -3
  300. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +0 -61
  301. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +0 -65
package/src/setup.ts CHANGED
@@ -50,7 +50,8 @@ import { isAgent } from "./agent.ts";
50
50
  import { isEffect } from "./effect.ts";
51
51
  import type { AnyFramePath, FramePath, WorktreePath, WorktreeReady } from "./worktree.ts";
52
52
  import { effectChecks, refuseUnroutedOutcome } from "./effect-checks.ts";
53
- import { gate } from "./gate.ts";
53
+ import { isGateInvoke } from "./gate-brand.ts";
54
+ import { isGateSlot, plainGate, settlesOnItsOwn, type PlainGate } from "./gate.ts";
54
55
  import { attachLent, isOpenBody, openBodyDeclarationOf, type LentParts } from "./open-body.ts";
55
56
  import { refuseInlineParts } from "./parts.ts";
56
57
  import { partyWarnings, refuseSlotActiveTwice, refuseUnknownSentBy, SENT_BY, sentByGuard } from "./party.ts";
@@ -112,7 +113,7 @@ type SentByGuards = { [SENT_BY]: { id: string } };
112
113
  /** The jr2 actor every workflow can invoke by name without listing it (ADR-0015). `gate` is the
113
114
  * whole set: an Agent is not pre-registered, because it is not one logic — it is the slot the
114
115
  * Machine declares, `actors: { coder: agent(def) }` (ADR-0049). */
115
- const jr2Actors = { gate };
116
+ const jr2Actors: { gate: PlainGate } = { gate: plainGate };
116
117
  type JR2Actors = typeof jr2Actors;
117
118
 
118
119
  /**
@@ -212,7 +213,9 @@ type BindActors<A extends Record<string, UnknownActorLogic>, TInput, TContext> =
212
213
  * `defineEvent` events you list, plus the events jr2 itself sends (`agent.fault`,
213
214
  * `workspace.lost`). An event that a party sent has `event.sender`. A Machine that takes no
214
215
  * workflow event leaves out `events`.
215
- * - The `gate` actor is ready to invoke by name: `invoke: { src: "gate" }`.
216
+ * - The `gate` actor is ready to invoke by name: `invoke: { src: "gate" }`. A Gate slot that hears a
217
+ * Hook (`actors: { review: gate({ … }) }`) is invoked by its key. An event in its `delivers` whose
218
+ * def leaves `audience` unset is `audience: "external"` in this Machine.
216
219
  * - Each Agent slot (`actors: { coder: agent(…) }`) gets its Menu from the states that invoke it.
217
220
  * - The `sentBy(id)` guard is ready to use.
218
221
  * - `createMachine` also takes `input`: a zod object for the input that starts a run. jr2 checks
@@ -269,6 +272,13 @@ type BindActors<A extends Record<string, UnknownActorLogic>, TInput, TContext> =
269
272
  * run an Agent`: the body sends it a Chart's unrouted `agent.fault`. When both kinds have no route,
270
273
  * one line names both, joined by `, and`. One route on the root is a route for each state. A
271
274
  * Chart's Machine does not get this refusal: in a Chart the route is optional (ADR-0070).
275
+ * - an event in a Gate slot's `delivers` that `events` does not list: ``machine "<id>": Gate slot
276
+ * "<slot>" delivers "<name>", which no def in jr2Setup({ events }) declares (declared: <names>) —
277
+ * list it in events (ADR-0011)``
278
+ * - an event in a Gate slot's `delivers` that the Gate does not accept at a state that invokes it:
279
+ * ``machine "<id>": state "<state>" invokes Gate slot "<slot>", which delivers "<name>", and its
280
+ * Gate does not accept it (accepts: <names>) — no handler or look could deliver it: handle it on
281
+ * that state, a state above it or a plain child, or drop it from delivers (ADR-0011)``
272
282
  *
273
283
  * It also refuses a `sentBy` that names no party (see `sentBy`). It refuses a pick param that
274
284
  * nothing reads, where the pick enters a state that invokes an Effect (see `fromPick`). Shapes that
@@ -358,7 +368,8 @@ export function jr2Setup<
358
368
  * listed, if it is not a jr2 or xstate event (an event with a `.` in its name). */
359
369
  events?: TDefs;
360
370
  /** The actors the states invoke by name, as in `setup()`: Agent slots (`agent(…)`), Effects
361
- * (`command(…)`, `http(…)`, `push(…)`), child Machines, `workspace(…)` and plain xstate logic.
371
+ * (`command(…)`, `http(…)`, `push(…)`), Gate slots (`gate({ … })`), child Machines, `workspace(…)`
372
+ * and plain xstate logic.
362
373
  * The key is the slot's name. An actor named `gate` replaces jr2's Gate. */
363
374
  actors?: TActors;
364
375
  /** Named actions, as in `setup()`. Their `event` has the type of this Machine's events. */
@@ -429,6 +440,10 @@ export function jr2Setup<
429
440
  // Resolve the defs first (duplicates and reserved semantics fail HERE, naming the machine —
430
441
  // the same loud failure `eventMap` gave the manifest, moved to machine-build time)…
431
442
  const defs = eventMap((machineConfig as { id?: string }).id ?? "(machine)", def.events ?? []);
443
+ // A Gate slot's `delivers` (ADR-0011, ADR-0015): each name is one of this Machine's events, and
444
+ // one whose def leaves the audience unset reads as `external`, so no Agent has a Hook's event on
445
+ // its Menu. Before the Menus derive, so the walk, the Menu and the runtime read one vocabulary.
446
+ externalDelivers((machineConfig as { id?: string }).id ?? "(machine)", defs, def.actors ?? {});
432
447
 
433
448
  // Every Open body slot (ADR-0070), found by its brand: each name it lends resolves against THIS
434
449
  // setup's own parts, or the Machine is refused naming it. The resolved set is stamped on the slot,
@@ -441,6 +456,12 @@ export function jr2Setup<
441
456
  // ADR-0007 restore path and invoke-time `resolveAccepts` validation are unchanged.
442
457
  const derived = deriveMenus(machineConfig, defs, def.actors ?? {});
443
458
  const machine = (inner.createMachine as unknown as (c: never) => AnyStateMachine)(derived.config as never);
459
+ // A Gate slot's event that its state's Gate does not accept (ADR-0011): no handler or look could
460
+ // ever deliver it.
461
+ if (derived.refused.length) {
462
+ const idOf = (path: readonly string[]): string => stateAt(machine, path).id;
463
+ throw new Error(`machine "${machine.id}": ${derived.refused.map((r) => r(idOf)).join("; ")}`);
464
+ }
444
465
 
445
466
  // Close the nested-`on` typo hole (ADR-0015): with the manifest dead, a typo'd key would
446
467
  // silently become vocabulary — so every non-dotted key the machine handles anywhere must map
@@ -603,7 +624,7 @@ function deriveMenus(
603
624
  config: unknown,
604
625
  defs: Map<string, EventDef>,
605
626
  actors: Record<string, UnknownActorLogic>,
606
- ): { config: unknown; seats: Seat[]; warnings: Warning[]; derivedGateIds: Set<string> } {
627
+ ): { config: unknown; seats: Seat[]; warnings: Warning[]; derivedGateIds: Set<string>; refused: Warning[] } {
607
628
  const pick = (reach: Reach, kind: "agent" | "external", self: string): string[] =>
608
629
  [...reach]
609
630
  .filter(([name, owners]) => {
@@ -623,6 +644,8 @@ function deriveMenus(
623
644
  const parties = new Map<string, { agents: string[]; gate: boolean }>();
624
645
  // The Gate invoke ids this walk names: a party warning says how to name one instead.
625
646
  const derivedGateIds = new Set<string>();
647
+ // A Gate slot's `delivers` that its state's Gate does not accept (ADR-0011): refusals.
648
+ const refused: Warning[] = [];
626
649
 
627
650
  const walk = (
628
651
  node: LooseState,
@@ -645,7 +668,7 @@ function deriveMenus(
645
668
  const invokes = Array.isArray(node.invoke) ? node.invoke : [node.invoke];
646
669
  // >1 unnamed gate in one state would collide on the state-key id; suffix ONLY then, so
647
670
  // the common case (one gate per state) keeps the clean name.
648
- const unnamedGates = invokes.filter((inv) => inv?.src === "gate" && inv.id == null).length;
671
+ const unnamedGates = invokes.filter((inv) => isGateInvoke(inv, actors) && inv.id == null).length;
649
672
  let ordinal = 0;
650
673
  const wrapOne = (inv: LooseInvoke): LooseInvoke => {
651
674
  // The slot key IS the Agent name (ADR-0049) — read off the declaration, never authored.
@@ -678,7 +701,7 @@ function deriveMenus(
678
701
  seats.push({ path, party: "agent", agent: inv.src, names: [...derived].sort() });
679
702
  return { ...inv, input: wrapAgentInput(inv.input, derived, inv.src, menuOf) };
680
703
  }
681
- if (inv?.src === "gate") {
704
+ if (isGateInvoke(inv, actors)) {
682
705
  // A Gate's plain children — below it, with no party-invoking state between — are its own
683
706
  // by position, as an Agent's modes are its (ADR-0015).
684
707
  const reach: Reach = new Map([...names].map(([name, owners]) => [name, new Set(owners)]));
@@ -686,6 +709,20 @@ function deriveMenus(
686
709
  reach.set(name, (reach.get(name) ?? new Set<string>()).add(owner));
687
710
  const accepts = pick(reach, "external", owner);
688
711
  seats.push({ path, party: "gate", names: [...accepts].sort() });
712
+ const slot = typeof inv.src === "string" ? actors[inv.src] : undefined;
713
+ if (isGateSlot(slot)) {
714
+ const outside = slot.gateSlot.delivers.map((d) => d.name).filter((name) => !accepts.includes(name));
715
+ if (outside.length) {
716
+ const name = inv.src as string;
717
+ refused.push(
718
+ (idOf) =>
719
+ `state "${idOf(path)}" invokes Gate slot "${name}", which delivers ${outside.map((n) => `"${n}"`).join(", ")}, ` +
720
+ `and its Gate does not accept ${outside.length > 1 ? "them" : "it"} (accepts: ${accepts.join(", ") || "nothing"}) — ` +
721
+ "no handler or look could deliver it: handle it on that state, a state above it or a plain child, " +
722
+ "or drop it from delivers (ADR-0011)",
723
+ );
724
+ }
725
+ }
689
726
  const wrapped: LooseInvoke = { ...inv, input: wrapGateInput(inv.input, accepts) };
690
727
  // A machine-root gate (empty path) is left to xstate's default id: a `""` id would be
691
728
  // worse than a noisy one, and the derived gate id still works.
@@ -710,7 +747,7 @@ function deriveMenus(
710
747
  };
711
748
 
712
749
  const out = walk(config as LooseState, new Map(), new Map(), [], NOBODY);
713
- return { config: out, seats, warnings: [...warnings.values()], derivedGateIds };
750
+ return { config: out, seats, warnings: [...warnings.values()], derivedGateIds, refused };
714
751
  }
715
752
 
716
753
  /**
@@ -845,7 +882,7 @@ function unenteredModes(machine: AnyStateMachine, actors: Record<string, Unknown
845
882
  continue;
846
883
  }
847
884
  const party = child.invoke.some(
848
- (inv) => inv.src === "gate" || (typeof inv.src === "string" && isAgent(actors[inv.src])),
885
+ (inv) => isGateInvoke(inv, actors) || (typeof inv.src === "string" && isAgent(actors[inv.src])),
849
886
  );
850
887
  if (!party) descend(child);
851
888
  continue;
@@ -868,27 +905,6 @@ function unenteredModes(machine: AnyStateMachine, actors: Record<string, Unknown
868
905
  return out;
869
906
  }
870
907
 
871
- /**
872
- * Does this logic settle on its own — a promise, the one plain xstate actor that ends without being
873
- * stopped? Asked of its own transition: a promise logic's initial snapshot, sent xstate's resolve
874
- * event, is `done`. That event name is an xstate internal (5.32.2); a bump that renamed it would
875
- * make every promise read long-lived, and setup.test.ts pins it against the real constructors.
876
- */
877
- function settlesOnItsOwn(logic: unknown): boolean {
878
- const l = logic as {
879
- getInitialSnapshot?: (scope: unknown, input: unknown) => unknown;
880
- transition?: (snapshot: unknown, event: unknown, scope: unknown) => { status?: string } | undefined;
881
- };
882
- if (typeof l?.getInitialSnapshot !== "function" || typeof l.transition !== "function") return false;
883
- try {
884
- const scope = { self: {}, defer: () => {}, emit: () => {} };
885
- const snapshot = l.getInitialSnapshot(scope, undefined);
886
- return l.transition(snapshot, { type: "xstate.promise.resolve", data: undefined }, scope)?.status === "done";
887
- } catch {
888
- return false;
889
- }
890
- }
891
-
892
908
  /**
893
909
  * The load walk's Rest warnings (ADR-0024): shapes below an Agent's state that keep it from Rest, so
894
910
  * every pick of the Agent is refused for longer than the author likely meant. Warnings, never
@@ -918,7 +934,7 @@ function restWarnings(machine: AnyStateMachine, actors: Record<string, UnknownAc
918
934
  for (const inv of child.invoke) {
919
935
  const logic = logicOf(inv.src);
920
936
  const name = typeof inv.src === "string" ? inv.src : inv.id;
921
- if (inv.src === "gate") {
937
+ if (isGateInvoke(inv, actors)) {
922
938
  for (const [event, candidates] of child.transitions) {
923
939
  if (event.includes(".") || event === "*") continue;
924
940
  if (candidates.some((t) => (t.target ?? []).length > 0)) continue;
@@ -982,6 +998,7 @@ function stampOpenBodies(
982
998
  ["agents", "an Agent slot (agent(...))", isAgent],
983
999
  ["effects", "an Effect slot", isEffect],
984
1000
  ["machines", "a Machine", (l: unknown) => (l as { root?: unknown } | undefined)?.root !== undefined],
1001
+ ["gates", "a Gate slot (gate({ … }))", isGateSlot],
985
1002
  ] as const;
986
1003
  for (const [kind, what, test] of actorKinds) {
987
1004
  for (const name of decl.lends[kind] ?? []) {
@@ -1052,6 +1069,29 @@ function stateAt(machine: AnyStateMachine, path: readonly string[]): AnyStateMac
1052
1069
  return node;
1053
1070
  }
1054
1071
 
1072
+ /**
1073
+ * The `delivers` of each Gate slot this setup declares (ADR-0011, ADR-0015). Each name must be an
1074
+ * event of the Machine: a handler or a look gives a plain event, which the Machine's own def parses.
1075
+ * An event whose def leaves the audience unset reads as `audience: "external"` in this Machine's
1076
+ * vocabulary, so no Agent of the Machine has it on its Menu, and the author does not have to know
1077
+ * this. An explicit audience stays.
1078
+ */
1079
+ function externalDelivers(machineId: string, defs: Map<string, EventDef>, actors: Record<string, unknown>): void {
1080
+ for (const [slot, logic] of Object.entries(actors)) {
1081
+ if (!isGateSlot(logic)) continue;
1082
+ for (const d of logic.gateSlot.delivers) {
1083
+ const own = defs.get(d.name);
1084
+ if (!own) {
1085
+ throw new Error(
1086
+ `machine "${machineId}": Gate slot "${slot}" delivers "${d.name}", which no def in jr2Setup({ events }) ` +
1087
+ `declares (declared: ${[...defs.keys()].join(", ") || "none"}) — list it in events (ADR-0011)`,
1088
+ );
1089
+ }
1090
+ if (own.audience === undefined) defs.set(d.name, Object.freeze({ ...own, audience: "external" as const }));
1091
+ }
1092
+ }
1093
+ }
1094
+
1055
1095
  /** A state's own workflow-event keys: dotted names are jr2's or xstate's, `*` is the wildcard. */
1056
1096
  function ownPicks(node: LooseState): string[] {
1057
1097
  return Object.keys(node.on ?? {}).filter((key) => !key.includes(".") && key !== "*");
@@ -1062,7 +1102,7 @@ function partiesOf(node: LooseState, actors: Record<string, UnknownActorLogic>):
1062
1102
  const invokes = node.invoke ? (Array.isArray(node.invoke) ? node.invoke : [node.invoke]) : [];
1063
1103
  return {
1064
1104
  agents: invokes.flatMap((inv) => (typeof inv?.src === "string" && isAgent(actors[inv.src]) ? [inv.src] : [])),
1065
- gate: invokes.some((inv) => inv?.src === "gate"),
1105
+ gate: invokes.some((inv) => isGateInvoke(inv, actors)),
1066
1106
  };
1067
1107
  }
1068
1108