@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
@@ -28,9 +28,11 @@ import { getStateNodes } from "xstate";
28
28
  import type { ActorSystem, AnyActorRef, AnyEventObject, AnyMachineSnapshot, AnyStateMachine } from "xstate";
29
29
  import { SENDER_KEY, type EventDef, type Sender } from "@jr2/agent-protocol";
30
30
  import type { AgentAdmission } from "./actor.ts";
31
+ import type { CallerRecord } from "./callers.ts";
32
+ import type { AnyHandler, GateSlotParts } from "./gate.ts";
31
33
  import type { EffectMarker, EffectRow } from "./effect.ts";
32
34
  import type { EffectorPlacement, EffectorTarget } from "./http-effect.ts";
33
- import type { NoticeScope, RaiseOptions, TakenNotices } from "./notices.ts";
35
+ import type { LedgerBody, NoticeScope, RaiseOptions, TakenNotices } from "./notices.ts";
34
36
  import type { AnswerSeat } from "./pick.ts";
35
37
  import type { Notice, PickAnswer } from "./wire.ts";
36
38
  import type { SandboxPort } from "./workspace.ts";
@@ -92,7 +94,7 @@ export type Registration = {
92
94
  slot?: string;
93
95
  /** An Agent's: the step boundary's read of its conversation's pending notices (ADR-0062), each
94
96
  * reserved for this Turn until {@link noticesHeard} or the Turn's end releases it. */
95
- boundaryNotices?: () => Promise<Array<{ id: string; notice: Notice }>>;
97
+ boundaryNotices?: () => Promise<Array<{ id: string; notice: LedgerBody }>>;
96
98
  /** An Agent's: the Harness appended these step-boundary notices — they are heard, ledgered in one
97
99
  * save. Answers how many were this Turn's reservations. */
98
100
  noticesHeard?: (ids: readonly string[]) => number;
@@ -124,6 +126,57 @@ export type Registration = {
124
126
  * answer has nowhere to go.
125
127
  */
126
128
  conversation?: string;
129
+ /**
130
+ * A Gate slot's (ADR-0011): its handlers by Hook kind id and event key, its `reply`, and the names
131
+ * of its `delivers`. What the Hook's door reads to fan a delivery out to this Gate. Absent on a
132
+ * plain Gate and on an Agent.
133
+ */
134
+ hooked?: {
135
+ hooks: Readonly<Record<string, Readonly<Record<string, AnyHandler>>>>;
136
+ reply?: GateSlotParts["reply"];
137
+ delivers: readonly string[];
138
+ };
139
+ /**
140
+ * A Pool's `source` Gate (ADR-0017): the push half of its Source. `hears` says whether a handler
141
+ * of the Source hears a Hook kind and event key; `hear` runs that handler and answers the
142
+ * Source's verdict. What the Hook's door reads to bring a delivery to the Pool. Absent on every
143
+ * other Gate and on an Agent.
144
+ */
145
+ source?: SourceSeat;
146
+ };
147
+
148
+ /** One Hook delivery as a Source's handler hears it (ADR-0017): the Hook, its kind and event key,
149
+ * the delivery id, the parsed payload and the Hook as a Caller. */
150
+ export type SourceDelivery = {
151
+ hook: string;
152
+ kind: string;
153
+ key: string;
154
+ delivery: string;
155
+ payload: unknown;
156
+ caller: CallerRecord;
157
+ };
158
+
159
+ /** The verdicts of a Source on one delivery (ADR-0017). */
160
+ export const SOURCE_VERDICTS = ["held", "existing", "hold-full", "reread", "skipped", "paused"] as const;
161
+ export type SourceVerdictName = (typeof SOURCE_VERDICTS)[number];
162
+
163
+ /** A Source's verdict on one delivery (ADR-0017): `key` for a carried item, `run` when a run holds
164
+ * that key, `reason` for a skip. */
165
+ export type SourceVerdictLine = { verdict: SourceVerdictName; key?: string; run?: string; reason?: string };
166
+
167
+ /** A Source's verdict as its seat gives it: the line, and the carried item, which only the
168
+ * Source's `reply` reads (ADR-0011). The item never goes in the outcome. */
169
+ export type SourceHeard = SourceVerdictLine & { item?: unknown };
170
+
171
+ /** The push half of a Pool's Source, on its `source` Gate's registration (ADR-0017). */
172
+ export type SourceSeat = {
173
+ hears(kind: string, key: string): boolean;
174
+ /** Run the handler, check the key and hold the item. The answer is the verdict; a `held` item is
175
+ * in the Pool's context when it resolves, and the caller saves the run before it answers. */
176
+ hear(d: SourceDelivery): Promise<SourceHeard>;
177
+ /** The Source's `reply`, when it has one: the text that the person who acted reads, from the
178
+ * verdict (ADR-0011). Pure; a throw is the caller's to log. */
179
+ reply?(verdict: SourceHeard, caller: CallerRecord): unknown;
127
180
  };
128
181
 
129
182
  /**
@@ -280,6 +333,9 @@ export class OutstandingError extends Error {
280
333
  }
281
334
  }
282
335
 
336
+ /** The Caller that a Gate's event carries on its {@link Sender} (ADR-0011, ADR-0078). */
337
+ export type SenderCaller = Pick<Sender, "via" | "name" | "onBehalfOf" | "checked">;
338
+
283
339
  /** Refuse a caller's body that carries the sender (ADR-0013, ADR-0015): only the Orchestrator writes
284
340
  * it, from the registration, so neither the Agent's wire nor the gates route takes it. */
285
341
  export function refuseSenderField(payload: unknown): void {
@@ -328,10 +384,42 @@ export function actorPath(ref: AnyActorRef): string[] {
328
384
  * names the conversation it burns with it.
329
385
  */
330
386
  /** An Agent's slot (ADR-0076): its continued conversation's Instance ID without the run id —
331
- * `<machine actor path>/<agent>`. What the Harness states and the registration records. */
387
+ * `<machine actor path>/<agent>`. What the Harness states and the registration records. Each
388
+ * segment of the path is encoded ({@link slotSegment}) before the segments are joined with `.`,
389
+ * so two paths never have one spelling (ADR-0057). The root Machine's path is `root`, a spelling
390
+ * that no encoded path has: a segment `root` and the empty segment are escaped. */
332
391
  export function agentSlot(machine: AnyActorRef | undefined, agentName: string): string {
333
- const path = (machine ? actorPath(machine).join(".") : "") || "root";
334
- return `${path}/${agentName}`;
392
+ return `${slotPath(machine)}/${agentName}`;
393
+ }
394
+
395
+ /** The Machine part of a slot ({@link agentSlot}): the encoded actor path of the Machine instance,
396
+ * `root` for the run's root. A Steering message held for a slot ends with this Machine (ADR-0002). */
397
+ export function slotPath(machine: AnyActorRef | undefined): string {
398
+ return (machine ? actorPath(machine).map(slotSegment).join(".") : "") || "root";
399
+ }
400
+
401
+ /**
402
+ * One actor id as a segment of a slot's path (ADR-0057). A letter, a digit, `-` and `_` stay as
403
+ * they are; each other character becomes `~` and two upper-case hex digits for each of its UTF-8
404
+ * bytes. So a segment has no `.` (the joint), no `/` (the iid's joint) and nothing that a URL or
405
+ * a header must escape, and the encoding is one to one: `~` occurs only as an escape. A Pool's
406
+ * worker has its item's id as its actor id, and an item id can be any text. Two segments are
407
+ * escaped although each character in them may stay: `root` is `~72oot` and the empty id is `~`
408
+ * (a `~` that no hex follows), so no encoded path spells `root`, the root Machine's path, or is
409
+ * empty, which the root's path also was.
410
+ */
411
+ export function slotSegment(id: string): string {
412
+ if (id === "") return "~";
413
+ if (id === "root") return "~72oot";
414
+ let out = "";
415
+ for (const ch of id) {
416
+ if (/^[A-Za-z0-9_-]$/.test(ch)) {
417
+ out += ch;
418
+ continue;
419
+ }
420
+ for (const byte of new TextEncoder().encode(ch)) out += `~${byte.toString(16).toUpperCase().padStart(2, "0")}`;
421
+ }
422
+ return out;
335
423
  }
336
424
 
337
425
  export function continuedIid(machine: AnyActorRef | undefined, runId: string, agentName: string): string {
@@ -348,11 +436,23 @@ export function agentAddress(instanceId: string): string {
348
436
  return `agent/${instanceId}`;
349
437
  }
350
438
 
439
+ /** What the run host hears of the table: a surface that opens, and one that closes. A Gate's
440
+ * `openedAt` is recorded here (ADR-0011). */
441
+ export type RegistrationWatch = {
442
+ opened?: (reg: Registration) => void;
443
+ closed?: (reg: Registration) => void;
444
+ };
445
+
351
446
  export class RegistrationTable {
352
447
  private readonly byAddress = new Map<string, Registration>();
353
448
  /** The same registrations by run, so a run's are read without a scan of every run's: the run host
354
449
  * reads them on every microstep of the run (`cause.ts`). */
355
450
  private readonly ofRun = new Map<string, Set<Registration>>();
451
+ private readonly watch: RegistrationWatch;
452
+
453
+ constructor(watch: RegistrationWatch = {}) {
454
+ this.watch = watch;
455
+ }
356
456
 
357
457
  /** Register a live surface; returns its disposer (actors call it from their stop cleanup). */
358
458
  register(reg: Registration): () => void {
@@ -365,6 +465,8 @@ export class RegistrationTable {
365
465
  const ofRun = this.ofRun.get(reg.runId) ?? new Set<Registration>();
366
466
  ofRun.add(reg);
367
467
  this.ofRun.set(reg.runId, ofRun);
468
+ this.watch.opened?.(reg);
469
+ let disposed = false;
368
470
  return () => {
369
471
  // Dispose only our own entry — a re-registration under the same address must not be
370
472
  // clobbered by a stale disposer running late (dev reload, restore races).
@@ -372,6 +474,8 @@ export class RegistrationTable {
372
474
  const runRegs = this.ofRun.get(reg.runId);
373
475
  runRegs?.delete(reg);
374
476
  if (runRegs?.size === 0) this.ofRun.delete(reg.runId);
477
+ if (!disposed) this.watch.closed?.(reg);
478
+ disposed = true;
375
479
  };
376
480
  }
377
481
 
@@ -391,9 +495,10 @@ export class RegistrationTable {
391
495
  * to their wire (404 / 400, MCP tool errors). The payload is parsed by the named def's
392
496
  * schema, so what lands in the Machine is exactly the validated shape — plus the registration's
393
497
  * sender, written here and nowhere else (ADR-0015). A delivery that would run another party's
394
- * writing throws {@link NotTakenError}.
498
+ * writing throws {@link NotTakenError}. `caller` is the Caller that answers a Gate (ADR-0011,
499
+ * ADR-0078): a Gate's Sender carries it, and an Agent's never does.
395
500
  */
396
- deliver(address: string, type: string, payload: unknown): void {
501
+ deliver(address: string, type: string, payload: unknown, caller?: SenderCaller): void {
397
502
  const reg = this.byAddress.get(address);
398
503
  if (!reg) throw new UnknownAddressError(`no live registration at "${address}"`);
399
504
  refuseSenderField(payload);
@@ -408,10 +513,11 @@ export class RegistrationTable {
408
513
  throw new EventValidationError(`invalid "${type}" payload: ${parsed.error.message}`);
409
514
  }
410
515
  // `type` after the payload: a payload never names the event the Machine receives (ADR-0011).
516
+ const sender = reg.sender && caller && reg.sender.party === "gate" ? { ...reg.sender, ...caller } : reg.sender;
411
517
  const event: DeliveredEvent = {
412
518
  ...parsed.data,
413
519
  type: def.name,
414
- ...(reg.sender ? { [SENDER_KEY]: reg.sender } : {}),
520
+ ...(sender ? { [SENDER_KEY]: sender } : {}),
415
521
  };
416
522
  // Exact, with the real payload (ADR-0015): a delivery whose selection holds a writing that is
417
523
  // not this sender's never reaches the Machine.
@@ -437,12 +543,34 @@ export class RegistrationTable {
437
543
  * and on the row. `instance` is set for a foreign caller, whose Orchestrator this one cannot
438
544
  * verify: opaque text, never resolved.
439
545
  */
440
- export type DispatchedBy = { run: string; slot: string; effect: string; depth: number; instance?: string };
546
+ export type DispatchedBy = {
547
+ run: string;
548
+ slot: string;
549
+ effect: string;
550
+ depth: number;
551
+ instance?: string;
552
+ /** The idempotency key when a Pool of runs started the run (ADR-0017): the item's id. The store
553
+ * scopes it to the callee Workflow, as it scopes `effect`. Absent: the key is `effect`. */
554
+ key?: string;
555
+ /** The Hook delivery that carried the item to the Pool (ADR-0017): the Hook's name, the delivery
556
+ * id, and the party that the signed payload names. Absent for an item that a read or `next` gave. */
557
+ hook?: { name: string; delivery: string; onBehalfOf?: string };
558
+ };
559
+
560
+ /** The idempotency key that a start carries (ADR-0017, ADR-0071): a Pool's `key`, else the caller's
561
+ * ledger row id. */
562
+ export const dispatchKeyOf = (by: Pick<DispatchedBy, "effect" | "key">): string => by.key ?? by.effect;
441
563
 
442
564
  /** The header a foreign `dispatch` carries its {@link DispatchedBy} in, as JSON (ADR-0071): out of
443
565
  * band, so the callee's door reserves no body field for it. */
444
566
  export const DISPATCHED_BY_HEADER = "jr2-dispatched-by";
445
567
 
568
+ /** The header that makes a foreign start a key-only ask (ADR-0071), with the value `true`: the door
569
+ * answers the run under the key of {@link DISPATCHED_BY_HEADER}, or `200 { existing: false }`, and
570
+ * starts nothing. The Instance Effector sends it, with no body, when the slot's schema refuses the
571
+ * body. */
572
+ export const KEY_ONLY_HEADER = "jr2-key-only";
573
+
446
574
  /**
447
575
  * A dispatch's start as the run host answers it in-process (ADR-0071): the run, and whether the key
448
576
  * found one that already existed; or the door's refusal, with the status `POST /workflows/:name/runs`
@@ -467,16 +595,24 @@ export type RunBinding = {
467
595
  /** The run's depth in a chain of dispatches (ADR-0071): 0 for a run nothing dispatched. Absent
468
596
  * on a bare unit-test binding, which reads 0. */
469
597
  depth?: number;
598
+ /** The first Caller of the run's chain of starts (ADR-0078): the Caller that started this run,
599
+ * or the run that dispatched it, and so on. A foreign `dispatch` sends it to the other door as
600
+ * `onBehalfOf` (ADR-0071). Undefined when jr2 started the chain. Absent on a bare binding. */
601
+ firstCaller?: () => Promise<{ via: string; name: string; shown?: string; onBehalfOf?: string } | undefined>;
470
602
  /**
471
603
  * The in-process half of a `dispatch` Effect (ADR-0071): the run host, reached directly, with no
472
604
  * wire and no token — the Orchestrator is the single writer of runs. `start` answers a refusal as
473
605
  * {@link DispatchStart}, never by throwing; `settled` resolves with the run's status once it is no
474
606
  * longer live — at once for a run already settled, and across a restore in either order — and
475
- * rejects when `signal` aborts. Absent outside a RunHost: a dispatch with no target faults.
607
+ * rejects when `signal` aborts. `lookup` is the key check alone: the run of `workflow` that
608
+ * carries the key `key`, in any status, while its row lives; the Effect asks it before it reads
609
+ * its input, so a run that exists is answered whatever the slot's schema says now. Absent
610
+ * outside a RunHost: a dispatch with no target faults.
476
611
  */
477
612
  dispatch?: {
478
613
  start(workflow: string, input: Record<string, unknown>, by: DispatchedBy): Promise<DispatchStart>;
479
614
  settled(runId: string, signal: AbortSignal): Promise<RunStatus>;
615
+ lookup?(workflow: string, key: string): Promise<{ runId: string } | undefined>;
480
616
  };
481
617
  /** The hash of the Assembly the run holds (ADR-0069): the one it started under, through
482
618
  * restarts. Absent for a run no Assembly started. */
@@ -554,14 +690,18 @@ export type RunBinding = {
554
690
  /** Put back a notice an admission carried that no model heard, WITH its source (`NoticeLedger.restore`):
555
691
  * a raise would drop it, its source being seen. A Workspace notice is pending again for `taker`,
556
692
  * the conversation that did not hear it. Persisted at the write. */
557
- restoreNotice?: (notice: Notice, to: string, source?: string, taker?: string) => void;
693
+ restoreNotice?: (notice: LedgerBody, to: string, source?: string, taker?: string) => void;
558
694
  /** Reserve the pending notices in one admission's scope, for its conversation (`scope.taker`).
559
695
  * They are delivered by the `recordAdmission` that ledgers it (its `delivered` ids and `taker`),
560
696
  * or released for that conversation if the admission fails. */
561
697
  takeNotices?: (scope: NoticeScope) => TakenNotices;
562
698
  releaseNotices?: (ids: readonly string[], taker?: string) => void;
563
699
  /** What `takeNotices` would reserve in this scope, reserving nothing (`NoticeLedger.peek`). */
564
- peekNotices?: (scope: NoticeScope) => Array<{ id: string; notice: Notice; source?: string }>;
700
+ peekNotices?: (scope: NoticeScope) => Array<{ id: string; notice: LedgerBody; source?: string }>;
701
+ /** The Machine's `steer` action (ADR-0002): hold a Steering message for the Agent `agentName` of
702
+ * `machine`, with the Sender whose words it relays. Throws when `machine` carries no Agent of
703
+ * that name. Absent outside a RunHost: the action then does nothing. */
704
+ steer?: (machine: AnyActorRef, agentName: string, text: string, from?: Sender) => void;
565
705
  /** The Harness acknowledged a step boundary's notices (ADR-0062): each that `taker` reserved is
566
706
  * heard there, persisted at the write. Answers how many were. */
567
707
  acknowledgeNotices?: (ids: readonly string[], taker: string) => number;
@@ -727,7 +867,57 @@ export type ChildErrorMarker = { kind: "child-error"; agent: string; pick: strin
727
867
 
728
868
  /** Every marker a run's mechanism puts on its per-run feed — an Effect's lines among them
729
869
  * (ADR-0067): its start, its stream, its output, its abort. */
730
- export type RunMarker = TurnMarker | PlacingMarker | ChildErrorMarker | EffectMarker;
870
+ export type RunMarker =
871
+ | TurnMarker
872
+ | PlacingMarker
873
+ | ChildErrorMarker
874
+ | EffectMarker
875
+ | GateMarker
876
+ | HookRefusedMarker
877
+ | PoolMarker;
878
+
879
+ /**
880
+ * What a Pool of runs did with one item (ADR-0017): `started` (its run, and `existing` when the key
881
+ * found a run that was there before), `settled` (the run ended, with its status, or the start
882
+ * ended with no run: `start-refused` with the door's report, `dispatch-error` with the error), and
883
+ * `carry-refused` (a held item that the Source's `item` schema does not parse), and `skipped` (an
884
+ * item of a read whose `itemId` threw or gave no id: it has no `key`, and `reason` says which item
885
+ * and why; one mark per item in a run's life). `pool` is the Pool's Machine id. Instance-token class
886
+ * like the Turn markers: a key is the workflow's data.
887
+ */
888
+ export type PoolMarker = {
889
+ kind: "pool";
890
+ pool: string;
891
+ mark: "started" | "settled" | "carry-refused" | "skipped";
892
+ /** The item's id: absent on `skipped`, whose item has none. */
893
+ key?: string;
894
+ run?: string;
895
+ existing?: boolean;
896
+ status?: string;
897
+ reason?: string;
898
+ };
899
+
900
+ /**
901
+ * A Gate's look gave its event, and the run took it (ADR-0011): the Gate's id and the event's name.
902
+ * The event's Sender is the Gate, and no Caller answered it. Instance-token class like the Turn
903
+ * markers: a Gate id can be one that the workflow computed from its data.
904
+ */
905
+ export type GateMarker = { kind: "looked"; gate: string; event: string };
906
+
907
+ /**
908
+ * A Hook's delivery reached an open Gate of the run, and the Gate did not take it (ADR-0011): its
909
+ * handler threw, gave an event that is not in its `delivers`, or gave one that the run does not take
910
+ * now. The run goes on. The Hook's name, the delivery id, the Gate, the event when there was one,
911
+ * and why. Instance-token class like the Turn markers: the reason can be a handler's own words.
912
+ */
913
+ export type HookRefusedMarker = {
914
+ kind: "hook-refused";
915
+ hook: string;
916
+ delivery: string;
917
+ gate: string;
918
+ event?: string;
919
+ reason: string;
920
+ };
731
921
 
732
922
  const bindings = new WeakMap<AnyActorSystem, RunBinding>();
733
923