@jr2/orchestrator 0.5.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/console/components/app.ts +55 -15
- package/console/components/bottom-drawer.ts +259 -0
- package/console/components/drawer.ts +14 -6
- package/console/components/fleet.ts +36 -13
- package/console/components/machine-pane.ts +61 -5
- package/console/components/nav.ts +54 -8
- package/console/conversation.ts +161 -0
- package/console/login.ts +123 -0
- package/console/main.ts +600 -60
- package/console/store.ts +611 -64
- package/console/style.css +231 -0
- package/docs/agents-block.md +2 -2
- package/docs/concepts/agents-and-turns.md +142 -29
- package/docs/concepts/architecture.md +60 -34
- package/docs/concepts/callers-and-hooks.md +205 -0
- package/docs/concepts/charts.md +13 -8
- package/docs/concepts/composition.md +10 -1
- package/docs/concepts/converge.md +7 -2
- package/docs/concepts/durable-runs.md +43 -16
- package/docs/concepts/effects.md +133 -50
- package/docs/concepts/gates.md +95 -25
- package/docs/concepts/machines-and-workflows.md +14 -4
- package/docs/concepts/pools-and-sources.md +193 -17
- package/docs/concepts/secrets-and-trust.md +186 -62
- package/docs/concepts/the-menu.md +14 -0
- package/docs/concepts/workspaces.md +135 -56
- package/docs/get-started/a-model-decides.md +14 -13
- package/docs/get-started/an-agent-in-a-sandbox.md +46 -35
- package/docs/get-started/connect-a-model.md +1 -1
- package/docs/get-started/create-an-instance.md +17 -14
- package/docs/get-started/have-your-assistant-write-a-machine.md +111 -40
- package/docs/get-started/install.md +4 -0
- package/docs/guides/add-tools-to-the-sandbox.md +72 -15
- package/docs/guides/answer-a-gate-from-a-forge.md +304 -0
- package/docs/guides/answer-a-gate.md +74 -37
- package/docs/guides/bind-open-parts.md +29 -12
- package/docs/guides/call-an-mcp-tool-from-a-machine.md +572 -0
- package/docs/guides/cancel-a-run.md +26 -16
- package/docs/guides/cut-a-worktree.md +560 -0
- package/docs/guides/fix-a-failed-converge.md +19 -7
- package/docs/guides/give-a-caller-a-scope.md +203 -0
- package/docs/guides/give-an-agent-mcp-tools.md +422 -0
- package/docs/guides/iterate-on-a-machine-on-kind.md +10 -9
- package/docs/guides/let-an-agent-write-a-chart.md +14 -7
- package/docs/guides/look-inside-a-sandbox.md +115 -42
- package/docs/guides/move-from-kind-to-a-shared-cluster.md +16 -0
- package/docs/guides/open-the-console.md +63 -14
- package/docs/guides/push-and-sign-a-branch.md +67 -21
- package/docs/guides/read-a-conversation.md +225 -0
- package/docs/guides/run-a-chart-by-name.md +17 -16
- package/docs/guides/run-a-command-or-call-a-url.md +84 -41
- package/docs/guides/run-one-worker-per-item.md +34 -27
- package/docs/guides/set-an-agents-access-and-dials.md +69 -22
- package/docs/guides/size-a-workspace.md +19 -6
- package/docs/guides/start-a-run-for-each-alert.md +290 -0
- package/docs/guides/start-a-run-from-a-run.md +18 -16
- package/docs/guides/start-a-run.md +24 -18
- package/docs/guides/steer-an-agent.md +332 -0
- package/docs/guides/use-a-hosted-provider.md +7 -1
- package/docs/guides/watch-a-run.md +146 -117
- package/docs/guides/write-a-hook-kind.md +238 -0
- package/docs/guides/write-a-tool-as-an-mcp-server.md +351 -0
- package/docs/guides/write-a-workflow.md +18 -8
- package/docs/llms.txt +110 -23
- package/docs/reference/authoring-surface/agent.md +35 -12
- package/docs/reference/authoring-surface/answer.md +20 -4
- package/docs/reference/authoring-surface/arg.md +28 -6
- package/docs/reference/authoring-surface/basic.md +1 -1
- package/docs/reference/authoring-surface/bearer.md +1 -1
- package/docs/reference/authoring-surface/branchname.md +15 -0
- package/docs/reference/authoring-surface/carry.md +23 -0
- package/docs/reference/authoring-surface/chartschema.md +1 -1
- package/docs/reference/authoring-surface/chartschemafor.md +1 -1
- package/docs/reference/authoring-surface/command.md +37 -22
- package/docs/reference/authoring-surface/customize.md +4 -1
- package/docs/reference/authoring-surface/defineconfig.md +1 -1
- package/docs/reference/authoring-surface/defineevent.md +1 -1
- package/docs/reference/authoring-surface/dispatch.md +5 -4
- package/docs/reference/authoring-surface/fail.md +18 -5
- package/docs/reference/authoring-surface/file.md +37 -17
- package/docs/reference/authoring-surface/frompick.md +1 -1
- package/docs/reference/authoring-surface/gate.md +118 -0
- package/docs/reference/authoring-surface/group-agents.md +15 -0
- package/docs/reference/authoring-surface/group-charts.md +16 -0
- package/docs/reference/authoring-surface/group-composition.md +25 -0
- package/docs/reference/authoring-surface/group-config.md +17 -0
- package/docs/reference/authoring-surface/group-effects.md +48 -0
- package/docs/reference/authoring-surface/group-failure.md +13 -0
- package/docs/reference/authoring-surface/group-gates.md +16 -0
- package/docs/reference/authoring-surface/group-machine-and-events.md +15 -0
- package/docs/reference/authoring-surface/group-steering.md +12 -0
- package/docs/reference/authoring-surface/group-types-these-signatures-name.md +137 -0
- package/docs/reference/authoring-surface/group-workspaces-and-repos.md +15 -0
- package/docs/reference/authoring-surface/http.md +109 -9
- package/docs/reference/authoring-surface/isfailure.md +1 -1
- package/docs/reference/authoring-surface/isready.md +14 -0
- package/docs/reference/authoring-surface/jr2setup.md +14 -4
- package/docs/reference/authoring-surface/mcp.md +107 -0
- package/docs/reference/authoring-surface/open.md +41 -8
- package/docs/reference/authoring-surface/openbody.md +1 -1
- package/docs/reference/authoring-surface/paramsof.md +23 -5
- package/docs/reference/authoring-surface/pool.md +79 -11
- package/docs/reference/authoring-surface/push.md +31 -13
- package/docs/reference/authoring-surface/readyof.md +21 -0
- package/docs/reference/authoring-surface/reread.md +19 -0
- package/docs/reference/authoring-surface/secret.md +26 -5
- package/docs/reference/authoring-surface/sentby.md +1 -1
- package/docs/reference/authoring-surface/skip.md +22 -0
- package/docs/reference/authoring-surface/source.md +59 -4
- package/docs/reference/authoring-surface/steer.md +93 -0
- package/docs/reference/authoring-surface/text.md +1 -1
- package/docs/reference/authoring-surface/type-agentdefinition.md +16 -3
- package/docs/reference/authoring-surface/type-agentmcpserver.md +54 -0
- package/docs/reference/authoring-surface/type-agentslots.md +4 -1
- package/docs/reference/authoring-surface/type-anyframepath.md +14 -0
- package/docs/reference/authoring-surface/type-anyworktreepath.md +15 -0
- package/docs/reference/authoring-surface/type-bindactors.md +14 -0
- package/docs/reference/authoring-surface/type-binding.md +6 -7
- package/docs/reference/authoring-surface/type-caller.md +40 -0
- package/docs/reference/authoring-surface/type-callerrecord.md +32 -0
- package/docs/reference/authoring-surface/type-carry.md +19 -0
- package/docs/reference/authoring-surface/type-chainlink.md +3 -3
- package/docs/reference/authoring-surface/type-commandinput.md +4 -3
- package/docs/reference/authoring-surface/type-commandoptions.md +0 -22
- package/docs/reference/authoring-surface/type-commandoutput.md +23 -5
- package/docs/reference/authoring-surface/type-customize.md +20 -3
- package/docs/reference/authoring-surface/type-delivered.md +17 -0
- package/docs/reference/authoring-surface/type-deliveredevent.md +18 -0
- package/docs/reference/authoring-surface/type-dispatchdefinition.md +4 -3
- package/docs/reference/authoring-surface/type-dispatchoutput.md +9 -2
- package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
- package/docs/reference/authoring-surface/type-effectbinding.md +12 -2
- package/docs/reference/authoring-surface/type-filebody.md +4 -8
- package/docs/reference/authoring-surface/type-framepath.md +14 -0
- package/docs/reference/authoring-surface/type-gatebinding.md +15 -0
- package/docs/reference/authoring-surface/type-gatehooks.md +12 -0
- package/docs/reference/authoring-surface/type-gatelook.md +37 -0
- package/docs/reference/authoring-surface/type-gateoutcome.md +32 -0
- package/docs/reference/authoring-surface/type-gatereply.md +19 -0
- package/docs/reference/authoring-surface/type-gateslot.md +21 -0
- package/docs/reference/authoring-surface/type-gateslotinput.md +13 -0
- package/docs/reference/authoring-surface/type-gateslotparts.md +18 -0
- package/docs/reference/authoring-surface/type-gateslots.md +12 -0
- package/docs/reference/authoring-surface/type-gatetypesat.md +13 -0
- package/docs/reference/authoring-surface/type-hostinjectedinput.md +3 -5
- package/docs/reference/authoring-surface/type-httpdefinition.md +6 -3
- package/docs/reference/authoring-surface/type-httpfileinput.md +20 -0
- package/docs/reference/authoring-surface/type-httpoutput.md +8 -2
- package/docs/reference/authoring-surface/type-jr2config.md +37 -2
- package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
- package/docs/reference/authoring-surface/type-lends.md +5 -1
- package/docs/reference/authoring-surface/type-lookoutput.md +14 -0
- package/docs/reference/authoring-surface/type-mcpcalloutput.md +78 -0
- package/docs/reference/authoring-surface/type-mcpnotreturned.md +42 -0
- package/docs/reference/authoring-surface/type-mcpoptions.md +47 -0
- package/docs/reference/authoring-surface/type-mcpoutput.md +50 -0
- package/docs/reference/authoring-surface/type-mcpreadoutput.md +53 -0
- package/docs/reference/authoring-surface/type-mcpremoteserver.md +50 -0
- package/docs/reference/authoring-surface/type-mcpserver.md +41 -0
- package/docs/reference/authoring-surface/type-mcpstdioserver.md +42 -0
- package/docs/reference/authoring-surface/type-namedsecret.md +4 -4
- package/docs/reference/authoring-surface/type-namestart.md +77 -0
- package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +1 -6
- package/docs/reference/authoring-surface/type-notworktreekeys.md +12 -0
- package/docs/reference/authoring-surface/type-openbodydeclaration.md +1 -1
- package/docs/reference/authoring-surface/type-openbodyoutput.md +1 -1
- package/docs/reference/authoring-surface/type-opencommandinput.md +7 -2
- package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +1 -6
- package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +5 -10
- package/docs/reference/authoring-surface/type-poolnextinput.md +22 -0
- package/docs/reference/authoring-surface/type-poolspec.md +11 -6
- package/docs/reference/authoring-surface/type-pushinput.md +22 -3
- package/docs/reference/authoring-surface/type-pushoutput.md +33 -8
- package/docs/reference/authoring-surface/type-pushsourcespec.md +38 -0
- package/docs/reference/authoring-surface/type-readsourcespec.md +47 -0
- package/docs/reference/authoring-surface/type-reposlot.md +6 -6
- package/docs/reference/authoring-surface/type-reread.md +18 -0
- package/docs/reference/authoring-surface/type-runsconfig.md +41 -0
- package/docs/reference/authoring-surface/type-sandboxoptions.md +11 -4
- package/docs/reference/authoring-surface/type-sender.md +26 -1
- package/docs/reference/authoring-surface/type-sendercallerfield.md +14 -0
- package/docs/reference/authoring-surface/type-size.md +1 -1
- package/docs/reference/authoring-surface/type-skip.md +19 -0
- package/docs/reference/authoring-surface/type-slothandles.md +5 -13
- package/docs/reference/authoring-surface/type-sourcehooks.md +23 -0
- package/docs/reference/authoring-surface/type-sourcein.md +25 -0
- package/docs/reference/authoring-surface/type-sourcereply.md +18 -0
- package/docs/reference/authoring-surface/type-sourcespec.md +26 -17
- package/docs/reference/authoring-surface/type-sourceverdict.md +49 -0
- package/docs/reference/authoring-surface/type-standingpoolmachine.md +12 -0
- package/docs/reference/authoring-surface/type-standingpoolspec.md +51 -0
- package/docs/reference/authoring-surface/type-steerargs.md +20 -0
- package/docs/reference/authoring-surface/type-thinkinglevel.md +1 -1
- package/docs/reference/authoring-surface/type-usercontainer.md +1 -1
- package/docs/reference/authoring-surface/type-workspaced.md +30 -9
- package/docs/reference/authoring-surface/type-workspacehandles.md +5 -6
- package/docs/reference/authoring-surface/type-workspaceoptions.md +1 -13
- package/docs/reference/authoring-surface/type-worktreefound.md +16 -0
- package/docs/reference/authoring-surface/type-worktreeinput.md +56 -0
- package/docs/reference/authoring-surface/type-worktreekey.md +14 -0
- package/docs/reference/authoring-surface/type-worktreeoptions.md +29 -0
- package/docs/reference/authoring-surface/type-worktreeoutput.md +45 -0
- package/docs/reference/authoring-surface/type-worktreepath.md +16 -0
- package/docs/reference/authoring-surface/type-worktreeready.md +37 -0
- package/docs/reference/authoring-surface/type-worktreerefused.md +21 -0
- package/docs/reference/authoring-surface/workspace.md +66 -38
- package/docs/reference/authoring-surface/worktree.md +71 -0
- package/docs/reference/authoring-surface.md +88 -147
- package/docs/reference/cli.md +413 -74
- package/docs/reference/config.md +248 -6
- package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +10 -9
- package/docs/reference/decisions/0002-duplex-streaming-actor.md +81 -14
- package/docs/reference/decisions/0004-worktree-provisioning-model.md +27 -21
- package/docs/reference/decisions/0005-sandbox-pod-composition.md +39 -25
- package/docs/reference/decisions/0006-agent-control-surface.md +6 -3
- package/docs/reference/decisions/0007-durable-machine-state.md +1 -1
- package/docs/reference/decisions/0009-cli-and-instance-interface.md +150 -49
- package/docs/reference/decisions/0010-bdd-acceptance-tests.md +5 -3
- package/docs/reference/decisions/0011-workflow-defined-events.md +256 -12
- package/docs/reference/decisions/0012-workspace-wrapper-machine.md +39 -32
- package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +54 -30
- package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +53 -27
- package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +26 -16
- package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +9 -7
- package/docs/reference/decisions/0017-pool-over-a-source-port.md +226 -10
- package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +15 -7
- package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +53 -27
- package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +16 -2
- package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +4 -4
- package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +124 -43
- package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +36 -38
- package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +10 -5
- package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +16 -7
- package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +36 -19
- package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +18 -10
- package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +3 -2
- package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +23 -15
- package/docs/reference/decisions/0032-the-console-unlocks-with-a-callers-credential.md +101 -0
- package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +22 -22
- package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +6 -6
- package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +13 -12
- package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +23 -23
- package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +6 -1
- package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +57 -46
- package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +5 -0
- package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +20 -8
- package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +55 -40
- package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +14 -13
- package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +31 -24
- package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +18 -10
- package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +137 -86
- package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +165 -91
- package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +7 -7
- package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +2 -2
- package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md +109 -0
- package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +53 -23
- package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +221 -153
- package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +161 -96
- package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +32 -9
- package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +48 -33
- package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +56 -30
- package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +35 -25
- package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +1 -1
- package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +296 -0
- package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +347 -0
- package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
- package/docs/reference/decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md +285 -0
- 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
- package/docs/reference/decisions.md +9 -4
- package/docs/reference/glossary.md +138 -45
- package/docs/reference/hooks.md +1077 -0
- package/docs/reference/http-api.md +988 -123
- package/docs/reference/machines.md +0 -1
- package/docs/why.md +7 -5
- package/package.json +10 -3
- package/src/actor.ts +194 -42
- package/src/agent.ts +56 -6
- package/src/ambient.ts +3 -5
- package/src/assembly-checks.ts +530 -4
- package/src/callers.ts +245 -0
- package/src/chart.ts +238 -26
- package/src/charts.ts +9 -2
- package/src/config.ts +566 -7
- package/src/conversations.ts +453 -0
- package/src/custodian.lua +17 -5
- package/src/custodian.ts +257 -77
- package/src/customize.ts +154 -7
- package/src/dispatch-effect.ts +142 -26
- package/src/durability.ts +2 -0
- package/src/duration.ts +20 -0
- package/src/effect-checks.ts +52 -4
- package/src/effect.ts +401 -132
- package/src/effector-wire.ts +278 -30
- package/src/fail.ts +17 -4
- package/src/gate-brand.ts +16 -0
- package/src/gate.ts +634 -48
- package/src/harness-client.ts +84 -35
- package/src/harness-env.ts +33 -0
- package/src/held-secrets.ts +197 -39
- package/src/holds.ts +5 -2
- package/src/hook-kinds/discord.ts +92 -0
- package/src/hook-kinds/door.ts +107 -0
- package/src/hook-kinds/github.ts +89 -0
- package/src/hook-kinds/gitlab.ts +72 -0
- package/src/hook-kinds/hook.ts +7 -0
- package/src/hook-kinds/index.ts +465 -0
- package/src/hook-kinds/slack.ts +151 -0
- package/src/hook-kinds/webhook.ts +113 -0
- package/src/hooks.ts +506 -0
- package/src/http-effect.ts +164 -70
- package/src/http.ts +933 -99
- package/src/index.ts +30 -5
- package/src/instance.ts +196 -26
- package/src/internal.ts +23 -3
- package/src/kube-client.ts +6 -3
- package/src/machine-doc.ts +29 -5
- package/src/mcp-effect.ts +424 -0
- package/src/model-keys.ts +24 -0
- package/src/mount-digest.ts +41 -7
- package/src/names.ts +82 -7
- package/src/notices.ts +576 -59
- package/src/open-body.ts +110 -30
- package/src/open-effect.ts +17 -4
- package/src/open.ts +40 -7
- package/src/parts.ts +125 -35
- package/src/party.ts +35 -1
- package/src/pick.ts +43 -9
- package/src/pool.ts +1172 -172
- package/src/registration.ts +249 -17
- package/src/repo-identity.ts +7 -3
- package/src/repos.ts +20 -2
- package/src/run-host.ts +1614 -190
- package/src/run-list.ts +191 -0
- package/src/sandbox-effect.ts +217 -66
- package/src/sandbox-kube.ts +116 -21
- package/src/secret-placement.ts +248 -35
- package/src/server.ts +53 -4
- package/src/setup.ts +158 -38
- package/src/snapshot-store.ts +565 -48
- package/src/steer.ts +111 -0
- package/src/stub-harness.ts +6 -2
- package/src/tokens.ts +362 -46
- package/src/vocabulary.ts +23 -4
- package/src/wire.ts +27 -10
- package/src/workspace.ts +314 -259
- package/src/worktree.ts +249 -0
- package/docs/reference/authoring-surface/type-noslotspec.md +0 -26
- package/docs/reference/authoring-surface/type-workspacespec.md +0 -37
- package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +0 -61
- package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +0 -65
package/src/registration.ts
CHANGED
|
@@ -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, 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";
|
|
@@ -82,6 +84,20 @@ export type Registration = {
|
|
|
82
84
|
* Harness runs on the host, in no Sandbox at all): those are the Instance token's business.
|
|
83
85
|
*/
|
|
84
86
|
sandbox?: string;
|
|
87
|
+
/**
|
|
88
|
+
* An Agent's: its SLOT (ADR-0076) — the actor path of its Machine and its agent name, which is its
|
|
89
|
+
* continued conversation's Instance ID without the run id ({@link continuedIid}). The Harness
|
|
90
|
+
* states it on every control request, from its own record of the conversation, and the
|
|
91
|
+
* Orchestrator speaks here only for that slot. Two Agents with one key in two child Machines are
|
|
92
|
+
* two slots. Absent on gates.
|
|
93
|
+
*/
|
|
94
|
+
slot?: string;
|
|
95
|
+
/** An Agent's: the step boundary's read of its conversation's pending notices (ADR-0062), each
|
|
96
|
+
* reserved for this Turn until {@link noticesHeard} or the Turn's end releases it. */
|
|
97
|
+
boundaryNotices?: () => Promise<Array<{ id: string; notice: LedgerBody }>>;
|
|
98
|
+
/** An Agent's: the Harness appended these step-boundary notices — they are heard, ledgered in one
|
|
99
|
+
* save. Answers how many were this Turn's reservations. */
|
|
100
|
+
noticesHeard?: (ids: readonly string[]) => number;
|
|
85
101
|
/** Close over the invoking state's `sendBack`; delivery lands where the actor was invoked. */
|
|
86
102
|
deliver: (event: DeliveredEvent) => void;
|
|
87
103
|
/** An Agent's: the Harness read this surface (`GET /agents/:iid/surface`), so it has the Turn in
|
|
@@ -110,6 +126,57 @@ export type Registration = {
|
|
|
110
126
|
* answer has nowhere to go.
|
|
111
127
|
*/
|
|
112
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;
|
|
113
180
|
};
|
|
114
181
|
|
|
115
182
|
/**
|
|
@@ -266,6 +333,9 @@ export class OutstandingError extends Error {
|
|
|
266
333
|
}
|
|
267
334
|
}
|
|
268
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
|
+
|
|
269
339
|
/** Refuse a caller's body that carries the sender (ADR-0013, ADR-0015): only the Orchestrator writes
|
|
270
340
|
* it, from the registration, so neither the Agent's wire nor the gates route takes it. */
|
|
271
341
|
export function refuseSenderField(payload: unknown): void {
|
|
@@ -313,9 +383,47 @@ export function actorPath(ref: AnyActorRef): string[] {
|
|
|
313
383
|
* Turn adds a random suffix, a burned conversation's successor its epoch), and the Agent's Actor
|
|
314
384
|
* names the conversation it burns with it.
|
|
315
385
|
*/
|
|
386
|
+
/** An Agent's slot (ADR-0076): its continued conversation's Instance ID without the run id —
|
|
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. */
|
|
391
|
+
export function agentSlot(machine: AnyActorRef | undefined, agentName: string): string {
|
|
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;
|
|
423
|
+
}
|
|
424
|
+
|
|
316
425
|
export function continuedIid(machine: AnyActorRef | undefined, runId: string, agentName: string): string {
|
|
317
|
-
|
|
318
|
-
return `${runId}/${path}/${agentName}`;
|
|
426
|
+
return `${runId}/${agentSlot(machine, agentName)}`;
|
|
319
427
|
}
|
|
320
428
|
|
|
321
429
|
/** The address of a run's gate: gate ids are run-scoped by construction (ADR-0011). */
|
|
@@ -328,11 +436,23 @@ export function agentAddress(instanceId: string): string {
|
|
|
328
436
|
return `agent/${instanceId}`;
|
|
329
437
|
}
|
|
330
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
|
+
|
|
331
446
|
export class RegistrationTable {
|
|
332
447
|
private readonly byAddress = new Map<string, Registration>();
|
|
333
448
|
/** The same registrations by run, so a run's are read without a scan of every run's: the run host
|
|
334
449
|
* reads them on every microstep of the run (`cause.ts`). */
|
|
335
450
|
private readonly ofRun = new Map<string, Set<Registration>>();
|
|
451
|
+
private readonly watch: RegistrationWatch;
|
|
452
|
+
|
|
453
|
+
constructor(watch: RegistrationWatch = {}) {
|
|
454
|
+
this.watch = watch;
|
|
455
|
+
}
|
|
336
456
|
|
|
337
457
|
/** Register a live surface; returns its disposer (actors call it from their stop cleanup). */
|
|
338
458
|
register(reg: Registration): () => void {
|
|
@@ -345,6 +465,8 @@ export class RegistrationTable {
|
|
|
345
465
|
const ofRun = this.ofRun.get(reg.runId) ?? new Set<Registration>();
|
|
346
466
|
ofRun.add(reg);
|
|
347
467
|
this.ofRun.set(reg.runId, ofRun);
|
|
468
|
+
this.watch.opened?.(reg);
|
|
469
|
+
let disposed = false;
|
|
348
470
|
return () => {
|
|
349
471
|
// Dispose only our own entry — a re-registration under the same address must not be
|
|
350
472
|
// clobbered by a stale disposer running late (dev reload, restore races).
|
|
@@ -352,6 +474,8 @@ export class RegistrationTable {
|
|
|
352
474
|
const runRegs = this.ofRun.get(reg.runId);
|
|
353
475
|
runRegs?.delete(reg);
|
|
354
476
|
if (runRegs?.size === 0) this.ofRun.delete(reg.runId);
|
|
477
|
+
if (!disposed) this.watch.closed?.(reg);
|
|
478
|
+
disposed = true;
|
|
355
479
|
};
|
|
356
480
|
}
|
|
357
481
|
|
|
@@ -371,9 +495,10 @@ export class RegistrationTable {
|
|
|
371
495
|
* to their wire (404 / 400, MCP tool errors). The payload is parsed by the named def's
|
|
372
496
|
* schema, so what lands in the Machine is exactly the validated shape — plus the registration's
|
|
373
497
|
* sender, written here and nowhere else (ADR-0015). A delivery that would run another party's
|
|
374
|
-
* 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.
|
|
375
500
|
*/
|
|
376
|
-
deliver(address: string, type: string, payload: unknown): void {
|
|
501
|
+
deliver(address: string, type: string, payload: unknown, caller?: SenderCaller): void {
|
|
377
502
|
const reg = this.byAddress.get(address);
|
|
378
503
|
if (!reg) throw new UnknownAddressError(`no live registration at "${address}"`);
|
|
379
504
|
refuseSenderField(payload);
|
|
@@ -388,10 +513,11 @@ export class RegistrationTable {
|
|
|
388
513
|
throw new EventValidationError(`invalid "${type}" payload: ${parsed.error.message}`);
|
|
389
514
|
}
|
|
390
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;
|
|
391
517
|
const event: DeliveredEvent = {
|
|
392
518
|
...parsed.data,
|
|
393
519
|
type: def.name,
|
|
394
|
-
...(
|
|
520
|
+
...(sender ? { [SENDER_KEY]: sender } : {}),
|
|
395
521
|
};
|
|
396
522
|
// Exact, with the real payload (ADR-0015): a delivery whose selection holds a writing that is
|
|
397
523
|
// not this sender's never reaches the Machine.
|
|
@@ -417,12 +543,34 @@ export class RegistrationTable {
|
|
|
417
543
|
* and on the row. `instance` is set for a foreign caller, whose Orchestrator this one cannot
|
|
418
544
|
* verify: opaque text, never resolved.
|
|
419
545
|
*/
|
|
420
|
-
export type DispatchedBy = {
|
|
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;
|
|
421
563
|
|
|
422
564
|
/** The header a foreign `dispatch` carries its {@link DispatchedBy} in, as JSON (ADR-0071): out of
|
|
423
565
|
* band, so the callee's door reserves no body field for it. */
|
|
424
566
|
export const DISPATCHED_BY_HEADER = "jr2-dispatched-by";
|
|
425
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
|
+
|
|
426
574
|
/**
|
|
427
575
|
* A dispatch's start as the run host answers it in-process (ADR-0071): the run, and whether the key
|
|
428
576
|
* found one that already existed; or the door's refusal, with the status `POST /workflows/:name/runs`
|
|
@@ -447,16 +595,24 @@ export type RunBinding = {
|
|
|
447
595
|
/** The run's depth in a chain of dispatches (ADR-0071): 0 for a run nothing dispatched. Absent
|
|
448
596
|
* on a bare unit-test binding, which reads 0. */
|
|
449
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>;
|
|
450
602
|
/**
|
|
451
603
|
* The in-process half of a `dispatch` Effect (ADR-0071): the run host, reached directly, with no
|
|
452
604
|
* wire and no token — the Orchestrator is the single writer of runs. `start` answers a refusal as
|
|
453
605
|
* {@link DispatchStart}, never by throwing; `settled` resolves with the run's status once it is no
|
|
454
606
|
* longer live — at once for a run already settled, and across a restore in either order — and
|
|
455
|
-
* rejects when `signal` aborts.
|
|
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.
|
|
456
611
|
*/
|
|
457
612
|
dispatch?: {
|
|
458
613
|
start(workflow: string, input: Record<string, unknown>, by: DispatchedBy): Promise<DispatchStart>;
|
|
459
614
|
settled(runId: string, signal: AbortSignal): Promise<RunStatus>;
|
|
615
|
+
lookup?(workflow: string, key: string): Promise<{ runId: string } | undefined>;
|
|
460
616
|
};
|
|
461
617
|
/** The hash of the Assembly the run holds (ADR-0069): the one it started under, through
|
|
462
618
|
* restarts. Absent for a run no Assembly started. */
|
|
@@ -488,9 +644,18 @@ export type RunBinding = {
|
|
|
488
644
|
* Record an Agent invocation's durable admission in the host ledger (ADR-0016): persisted
|
|
489
645
|
* beside the snapshot in the same RunBlob save, keyed by iid (globally unique, so the map is
|
|
490
646
|
* flat). Optional so a bare unit-test binding can omit it — then admissions simply are not
|
|
491
|
-
* durable.
|
|
647
|
+
* durable. `delivered` are the notices the admission carried, delivered by this save in `taker`'s
|
|
648
|
+
* conversation — the Instance ID the admission rides (ADR-0062). `member`: the Workspace the
|
|
649
|
+
* conversation was admitted in, its slot and whether it continues — so it is in the audience of
|
|
650
|
+
* every kill there from now on, in the same save.
|
|
492
651
|
*/
|
|
493
|
-
recordAdmission?: (
|
|
652
|
+
recordAdmission?: (
|
|
653
|
+
instanceId: string,
|
|
654
|
+
admission: AgentAdmission,
|
|
655
|
+
delivered?: readonly string[],
|
|
656
|
+
taker?: string,
|
|
657
|
+
member?: { workspace: string; slot: string; continued: boolean },
|
|
658
|
+
) => void;
|
|
494
659
|
/**
|
|
495
660
|
* The admission ledger's READ half (ADR-0016): the last admission ledgered under this iid. A
|
|
496
661
|
* continued Turn asks it whether its conversation was ever admitted — only then can the Harness
|
|
@@ -521,14 +686,31 @@ export type RunBinding = {
|
|
|
521
686
|
* `older` names earlier events this one tells the same as, marked raised with it (`NoticeLedger`).
|
|
522
687
|
* A burned conversation's notice is raised by `bumpEpoch` itself, in the same save.
|
|
523
688
|
*/
|
|
524
|
-
raiseNotice?: (notice: Notice, to: string, source?: string, older?: readonly string[]) => void;
|
|
689
|
+
raiseNotice?: (notice: Notice, to: string, source?: string, older?: readonly string[], opts?: RaiseOptions) => void;
|
|
525
690
|
/** Put back a notice an admission carried that no model heard, WITH its source (`NoticeLedger.restore`):
|
|
526
|
-
* a raise would drop it, its source being seen.
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
691
|
+
* a raise would drop it, its source being seen. A Workspace notice is pending again for `taker`,
|
|
692
|
+
* the conversation that did not hear it. Persisted at the write. */
|
|
693
|
+
restoreNotice?: (notice: LedgerBody, to: string, source?: string, taker?: string) => void;
|
|
694
|
+
/** Reserve the pending notices in one admission's scope, for its conversation (`scope.taker`).
|
|
695
|
+
* They are delivered by the `recordAdmission` that ledgers it (its `delivered` ids and `taker`),
|
|
696
|
+
* or released for that conversation if the admission fails. */
|
|
530
697
|
takeNotices?: (scope: NoticeScope) => TakenNotices;
|
|
531
|
-
releaseNotices?: (ids: readonly string[]) => void;
|
|
698
|
+
releaseNotices?: (ids: readonly string[], taker?: string) => void;
|
|
699
|
+
/** What `takeNotices` would reserve in this scope, reserving nothing (`NoticeLedger.peek`). */
|
|
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;
|
|
705
|
+
/** The Harness acknowledged a step boundary's notices (ADR-0062): each that `taker` reserved is
|
|
706
|
+
* heard there, persisted at the write. Answers how many were. */
|
|
707
|
+
acknowledgeNotices?: (ids: readonly string[], taker: string) => number;
|
|
708
|
+
/** The conversations each kill's `bash` answer told (ADR-0062, D4 rule 3), off the pod's list:
|
|
709
|
+
* they leave that kill's audience. Persisted at the write when anything changed. */
|
|
710
|
+
noticesTold?: (told: ReadonlyArray<{ source: string; takers: readonly string[] }>) => void;
|
|
711
|
+
/** A Turn of these conversations in `workspace` ended: a fresh one leaves every audience there,
|
|
712
|
+
* a continued one stays for its next Turn (ADR-0062). Persisted at the write. */
|
|
713
|
+
noticeTurnEnded?: (workspace: string, takers: readonly string[]) => void;
|
|
532
714
|
/**
|
|
533
715
|
* The pick the Turn now at this address has outstanding, or answered and not yet read (ADR-0024):
|
|
534
716
|
* what the Agent's Actor asks when its Harness restarted under a Turn — a Turn whose pick is still
|
|
@@ -685,7 +867,57 @@ export type ChildErrorMarker = { kind: "child-error"; agent: string; pick: strin
|
|
|
685
867
|
|
|
686
868
|
/** Every marker a run's mechanism puts on its per-run feed — an Effect's lines among them
|
|
687
869
|
* (ADR-0067): its start, its stream, its output, its abort. */
|
|
688
|
-
export type RunMarker =
|
|
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
|
+
};
|
|
689
921
|
|
|
690
922
|
const bindings = new WeakMap<AnyActorSystem, RunBinding>();
|
|
691
923
|
|
package/src/repo-identity.ts
CHANGED
|
@@ -18,7 +18,8 @@ export type RepoIdentity = {
|
|
|
18
18
|
* absolute path for a local repository. Path case is KEPT: hosts are case-insensitive, paths on
|
|
19
19
|
* most forges are not. */
|
|
20
20
|
identity: string;
|
|
21
|
-
/** `<slug>-<sha256(identity)[:
|
|
21
|
+
/** `<slug>-<sha256(identity)[:32]>` — a DNS-1123 label of at most 49 characters: a slug of at
|
|
22
|
+
* most 16 characters and 128 bits of the hash, so no one finds two identities with one key. */
|
|
22
23
|
key: string;
|
|
23
24
|
scheme: "https" | "http" | "ssh" | "git" | "local";
|
|
24
25
|
};
|
|
@@ -139,7 +140,10 @@ function keyOf(identity: string): string {
|
|
|
139
140
|
.toLowerCase()
|
|
140
141
|
.replace(/[^a-z0-9]+/g, "-")
|
|
141
142
|
.replace(/^-+|-+$/g, "")
|
|
142
|
-
.slice(0,
|
|
143
|
+
.slice(0, 16)
|
|
143
144
|
.replace(/-+$/, "") || "repo";
|
|
144
|
-
|
|
145
|
+
// 128 bits (ADR-0051): the key names the cache that a Sandbox mounts, and a per-run url is the
|
|
146
|
+
// Agent's text. With 32 bits a search found a url of another repository with the same key in
|
|
147
|
+
// seconds, and that url mounted the other repository's cache.
|
|
148
|
+
return `${slug}-${createHash("sha256").update(identity).digest("hex").slice(0, 32)}`;
|
|
145
149
|
}
|
package/src/repos.ts
CHANGED
|
@@ -192,10 +192,28 @@ export function kubeRepos(opts: KubeReposOptions): RepoResources {
|
|
|
192
192
|
return item;
|
|
193
193
|
};
|
|
194
194
|
|
|
195
|
+
/** The resource that stands under `repo.key`, when it is `repo`'s identity; else a throw that names
|
|
196
|
+
* both urls (ADR-0051). The key is collision-resistant, so another identity under one key is a
|
|
197
|
+
* defect or an attack, and no statement of this port writes a resource of another identity: not
|
|
198
|
+
* its url, not its `secretRef`, not its clock. */
|
|
199
|
+
const sameIdentity = async (repo: { url: string; identity: string; key: string }): Promise<RepoItem> => {
|
|
200
|
+
const item = await get(repo.key);
|
|
201
|
+
const stands = item.metadata?.annotations?.[ANNOTATION_REPO_IDENTITY];
|
|
202
|
+
if (stands !== repo.identity) {
|
|
203
|
+
throw new Error(
|
|
204
|
+
`Repo "${repo.key}" stands for ${stands === undefined ? "no stated identity" : `"${stands}"`} ` +
|
|
205
|
+
`(url ${item.spec?.url ?? "unknown"}), not for "${repo.identity}" (url ${repo.url}): one key, two ` +
|
|
206
|
+
"repositories, so jr2 changes nothing on it (ADR-0051). Delete that Repo if it is stale.",
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
return item;
|
|
210
|
+
};
|
|
211
|
+
|
|
195
212
|
return {
|
|
196
213
|
async bind(repo) {
|
|
197
214
|
const { secretRef, cr } = await resourceFor({ ...repo, bound: true });
|
|
198
215
|
if (await create(cr)) return;
|
|
216
|
+
await sameIdentity(repo);
|
|
199
217
|
// Already there from an earlier deploy: ONE merge patch says what the Machine resolves now —
|
|
200
218
|
// url, credential, and the label `jr2 gc` honors. `secretRef: null` clears a credential the
|
|
201
219
|
// config no longer names, so a dropped entry does not linger. The clock is not touched: it
|
|
@@ -216,12 +234,12 @@ export function kubeRepos(opts: KubeReposOptions): RepoResources {
|
|
|
216
234
|
// Already there — the boot's statement, or an earlier run's. The eviction clock moves: the
|
|
217
235
|
// run attached it now. The url stays as stated, whatever spelling this run brought.
|
|
218
236
|
const clock = { metadata: { annotations: { [ANNOTATION_REPO_LAST_ATTACHED]: attached } } };
|
|
237
|
+
const standing = await sameIdentity(repo);
|
|
219
238
|
// A Machine's slot binds it: the boot is its writer, and restates the credential at the next
|
|
220
|
-
// deploy.
|
|
239
|
+
// deploy.
|
|
221
240
|
if (repo.bound) return patch(repo.key, clock);
|
|
222
241
|
// This run's slot is per-run, but another registered Machine may bind the identity — the
|
|
223
242
|
// label says so, and then the boot is still the writer.
|
|
224
|
-
const standing = await get(repo.key);
|
|
225
243
|
if (standing.metadata?.labels?.[LABEL_REPO_BOUND] === "true") return patch(repo.key, clock);
|
|
226
244
|
// Nothing binds it: no boot ever restates it, so this attach does — the credential resolved
|
|
227
245
|
// against the url that STANDS, since the scheme picks the Secret's kind (config.ts) and the
|