@jr2/orchestrator 0.3.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -2
- package/bin/server.ts +7 -4
- package/console/canvas.ts +27 -14
- package/console/components/app.ts +12 -1
- package/console/components/fleet.ts +52 -6
- package/console/components/machine-pane.ts +128 -2
- package/console/components/nav.ts +1 -1
- package/console/components/schema-form.ts +12 -4
- package/console/json-field.ts +23 -0
- package/console/main.ts +122 -39
- package/console/store.ts +321 -21
- package/console/style.css +111 -0
- package/docs/agents-block.md +5 -0
- package/docs/concepts/agents-and-turns.md +442 -0
- package/docs/concepts/architecture.md +230 -0
- package/docs/concepts/charts.md +143 -0
- package/docs/concepts/composition.md +357 -0
- package/docs/concepts/converge.md +254 -0
- package/docs/concepts/durable-runs.md +189 -0
- package/docs/concepts/effects.md +603 -0
- package/docs/concepts/gates.md +208 -0
- package/docs/concepts/machines-and-workflows.md +289 -0
- package/docs/concepts/pools-and-sources.md +230 -0
- package/docs/concepts/secrets-and-trust.md +222 -0
- package/docs/concepts/the-menu.md +378 -0
- package/docs/concepts/workspaces.md +678 -0
- package/docs/get-started/a-model-decides.md +563 -0
- package/docs/get-started/an-agent-in-a-sandbox.md +381 -0
- package/docs/get-started/connect-a-model.md +248 -0
- package/docs/get-started/create-an-instance.md +194 -0
- package/docs/get-started/have-your-assistant-write-a-machine.md +922 -0
- package/docs/get-started/install.md +126 -0
- package/docs/guides/add-tools-to-the-sandbox.md +295 -0
- package/docs/guides/answer-a-gate.md +304 -0
- package/docs/guides/bind-open-parts.md +223 -0
- package/docs/guides/cancel-a-run.md +146 -0
- package/docs/guides/clean-up.md +206 -0
- package/docs/guides/fix-a-failed-converge.md +361 -0
- package/docs/guides/give-the-cluster-git-credentials.md +222 -0
- package/docs/guides/iterate-on-a-machine-on-kind.md +190 -0
- package/docs/guides/let-an-agent-write-a-chart.md +226 -0
- package/docs/guides/look-inside-a-sandbox.md +174 -0
- package/docs/guides/mirror-the-kit-images.md +176 -0
- package/docs/guides/move-from-kind-to-a-shared-cluster.md +302 -0
- package/docs/guides/open-the-console.md +91 -0
- package/docs/guides/publish-a-machine-as-an-npm-package.md +350 -0
- package/docs/guides/push-and-sign-a-branch.md +486 -0
- package/docs/guides/run-a-chart-by-name.md +270 -0
- package/docs/guides/run-a-command-or-call-a-url.md +544 -0
- package/docs/guides/run-one-worker-per-item.md +321 -0
- package/docs/guides/set-an-agents-access-and-dials.md +375 -0
- package/docs/guides/size-a-workspace.md +204 -0
- package/docs/guides/start-a-run-from-a-run.md +237 -0
- package/docs/guides/start-a-run.md +207 -0
- package/docs/guides/upgrade-the-kit-version.md +240 -0
- package/docs/guides/use-a-hosted-provider.md +208 -0
- package/docs/guides/use-a-local-model.md +313 -0
- package/docs/guides/watch-a-run.md +326 -0
- package/docs/guides/write-a-workflow.md +318 -0
- package/docs/index.md +44 -0
- package/docs/llms.txt +216 -0
- package/docs/reference/authoring-surface/agent.md +128 -0
- package/docs/reference/authoring-surface/answer.md +140 -0
- package/docs/reference/authoring-surface/arg.md +60 -0
- package/docs/reference/authoring-surface/basic.md +62 -0
- package/docs/reference/authoring-surface/bearer.md +53 -0
- package/docs/reference/authoring-surface/chartschema.md +23 -0
- package/docs/reference/authoring-surface/chartschemafor.md +48 -0
- package/docs/reference/authoring-surface/command.md +191 -0
- package/docs/reference/authoring-surface/customize.md +64 -0
- package/docs/reference/authoring-surface/defineconfig.md +41 -0
- package/docs/reference/authoring-surface/defineevent.md +114 -0
- package/docs/reference/authoring-surface/dispatch.md +126 -0
- package/docs/reference/authoring-surface/fail.md +83 -0
- package/docs/reference/authoring-surface/file.md +122 -0
- package/docs/reference/authoring-surface/frompick.md +277 -0
- package/docs/reference/authoring-surface/http.md +159 -0
- package/docs/reference/authoring-surface/isfailure.md +58 -0
- package/docs/reference/authoring-surface/jr2setup.md +237 -0
- package/docs/reference/authoring-surface/open.md +74 -0
- package/docs/reference/authoring-surface/openbody.md +86 -0
- package/docs/reference/authoring-surface/paramsof.md +125 -0
- package/docs/reference/authoring-surface/pool.md +186 -0
- package/docs/reference/authoring-surface/push.md +112 -0
- package/docs/reference/authoring-surface/secret.md +118 -0
- package/docs/reference/authoring-surface/sentby.md +83 -0
- package/docs/reference/authoring-surface/source.md +51 -0
- package/docs/reference/authoring-surface/text.md +62 -0
- package/docs/reference/authoring-surface/type-agentdeclaration.md +27 -0
- package/docs/reference/authoring-surface/type-agentdefinition.md +80 -0
- package/docs/reference/authoring-surface/type-agentlogic.md +15 -0
- package/docs/reference/authoring-surface/type-agentslots.md +12 -0
- package/docs/reference/authoring-surface/type-answeraction.md +12 -0
- package/docs/reference/authoring-surface/type-arg.md +19 -0
- package/docs/reference/authoring-surface/type-argnames.md +12 -0
- package/docs/reference/authoring-surface/type-binding.md +62 -0
- package/docs/reference/authoring-surface/type-bindingof.md +12 -0
- package/docs/reference/authoring-surface/type-bodyacceptsdoor.md +15 -0
- package/docs/reference/authoring-surface/type-bodyacceptsslots.md +17 -0
- package/docs/reference/authoring-surface/type-ceilings.md +41 -0
- package/docs/reference/authoring-surface/type-chainlink.md +19 -0
- package/docs/reference/authoring-surface/type-chartaction.md +46 -0
- package/docs/reference/authoring-surface/type-chartagent.md +28 -0
- package/docs/reference/authoring-surface/type-chartevent.md +20 -0
- package/docs/reference/authoring-surface/type-chartguard.md +20 -0
- package/docs/reference/authoring-surface/type-chartinvoke.md +28 -0
- package/docs/reference/authoring-surface/type-chartjson.md +14 -0
- package/docs/reference/authoring-surface/type-chartstate.md +46 -0
- package/docs/reference/authoring-surface/type-charttext.md +40 -0
- package/docs/reference/authoring-surface/type-charttransition.md +22 -0
- package/docs/reference/authoring-surface/type-charttransitions.md +14 -0
- package/docs/reference/authoring-surface/type-chartvalue.md +15 -0
- package/docs/reference/authoring-surface/type-childat.md +12 -0
- package/docs/reference/authoring-surface/type-childslots.md +12 -0
- package/docs/reference/authoring-surface/type-commandenv.md +15 -0
- package/docs/reference/authoring-surface/type-commandinput.md +19 -0
- package/docs/reference/authoring-surface/type-commandoptions.md +78 -0
- package/docs/reference/authoring-surface/type-commandoutput.md +104 -0
- package/docs/reference/authoring-surface/type-customize.md +109 -0
- package/docs/reference/authoring-surface/type-dispatchdefinition.md +39 -0
- package/docs/reference/authoring-surface/type-dispatchinput.md +14 -0
- package/docs/reference/authoring-surface/type-dispatchopenparts.md +14 -0
- package/docs/reference/authoring-surface/type-dispatchoutput.md +65 -0
- package/docs/reference/authoring-surface/type-duration.md +15 -0
- package/docs/reference/authoring-surface/type-effectbinding.md +93 -0
- package/docs/reference/authoring-surface/type-effectlogic.md +12 -0
- package/docs/reference/authoring-surface/type-envopenparts.md +14 -0
- package/docs/reference/authoring-surface/type-eventaudience.md +27 -0
- package/docs/reference/authoring-surface/type-eventdef.md +45 -0
- package/docs/reference/authoring-surface/type-eventfrom.md +16 -0
- package/docs/reference/authoring-surface/type-eventsemantics.md +26 -0
- package/docs/reference/authoring-surface/type-execoutcome.md +34 -0
- package/docs/reference/authoring-surface/type-failslot.md +14 -0
- package/docs/reference/authoring-surface/type-failure.md +19 -0
- package/docs/reference/authoring-surface/type-failureinput.md +20 -0
- package/docs/reference/authoring-surface/type-filebody.md +41 -0
- package/docs/reference/authoring-surface/type-frominput.md +14 -0
- package/docs/reference/authoring-surface/type-hostinjectedinput.md +58 -0
- package/docs/reference/authoring-surface/type-httpdefinition.md +101 -0
- package/docs/reference/authoring-surface/type-httpinput.md +12 -0
- package/docs/reference/authoring-surface/type-httpmethod.md +14 -0
- package/docs/reference/authoring-surface/type-httpopenparts.md +12 -0
- package/docs/reference/authoring-surface/type-httpoutput.md +89 -0
- package/docs/reference/authoring-surface/type-httprequestparts.md +33 -0
- package/docs/reference/authoring-surface/type-jr2config.md +126 -0
- package/docs/reference/authoring-surface/type-lends.md +38 -0
- package/docs/reference/authoring-surface/type-logicat.md +12 -0
- package/docs/reference/authoring-surface/type-markedif.md +12 -0
- package/docs/reference/authoring-surface/type-mergedactors.md +12 -0
- package/docs/reference/authoring-surface/type-namedsecret.md +25 -0
- package/docs/reference/authoring-surface/type-noslotspec.md +26 -0
- package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +32 -0
- package/docs/reference/authoring-surface/type-openbodydeclaration.md +40 -0
- package/docs/reference/authoring-surface/type-openbodylogic.md +14 -0
- package/docs/reference/authoring-surface/type-openbodyoutput.md +42 -0
- package/docs/reference/authoring-surface/type-openbodyslot.md +18 -0
- package/docs/reference/authoring-surface/type-opencommandinput.md +17 -0
- package/docs/reference/authoring-surface/type-openeffect.md +12 -0
- package/docs/reference/authoring-surface/type-openeffectslots.md +12 -0
- package/docs/reference/authoring-surface/type-openof.md +12 -0
- package/docs/reference/authoring-surface/type-opensecret.md +23 -0
- package/docs/reference/authoring-surface/type-opensecretmark.md +12 -0
- package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +31 -0
- package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +32 -0
- package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
- package/docs/reference/authoring-surface/type-poolspec.md +60 -0
- package/docs/reference/authoring-surface/type-pushinput.md +15 -0
- package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
- package/docs/reference/authoring-surface/type-pushoutput.md +124 -0
- package/docs/reference/authoring-surface/type-pushwireoutput.md +12 -0
- package/docs/reference/authoring-surface/type-quantity.md +14 -0
- package/docs/reference/authoring-surface/type-reached.md +12 -0
- package/docs/reference/authoring-surface/type-reposlot.md +70 -0
- package/docs/reference/authoring-surface/type-reposlotsof.md +16 -0
- package/docs/reference/authoring-surface/type-sandboxoptions.md +77 -0
- package/docs/reference/authoring-surface/type-secret.md +25 -0
- package/docs/reference/authoring-surface/type-sender.md +19 -0
- package/docs/reference/authoring-surface/type-sentbyguards.md +12 -0
- package/docs/reference/authoring-surface/type-size.md +85 -0
- package/docs/reference/authoring-surface/type-slothandles.md +32 -0
- package/docs/reference/authoring-surface/type-sourcenextresult.md +17 -0
- package/docs/reference/authoring-surface/type-sourcespec.md +44 -0
- package/docs/reference/authoring-surface/type-syncstate.md +12 -0
- package/docs/reference/authoring-surface/type-text.md +19 -0
- package/docs/reference/authoring-surface/type-textpart.md +24 -0
- package/docs/reference/authoring-surface/type-thinkinglevel.md +39 -0
- package/docs/reference/authoring-surface/type-toparameterizedobject.md +14 -0
- package/docs/reference/authoring-surface/type-toprovidedactor.md +14 -0
- package/docs/reference/authoring-surface/type-unknown.md +21 -0
- package/docs/reference/authoring-surface/type-usercontainer.md +77 -0
- package/docs/reference/authoring-surface/type-withoutemptyindex.md +20 -0
- package/docs/reference/authoring-surface/type-workflowevent.md +46 -0
- package/docs/reference/authoring-surface/type-workspaceaccess.md +16 -0
- package/docs/reference/authoring-surface/type-workspaced.md +94 -0
- package/docs/reference/authoring-surface/type-workspacehandles.md +23 -0
- package/docs/reference/authoring-surface/type-workspacemachine.md +29 -0
- package/docs/reference/authoring-surface/type-workspaceof.md +13 -0
- package/docs/reference/authoring-surface/type-workspaceoptions.md +39 -0
- package/docs/reference/authoring-surface/type-workspacespec.md +37 -0
- package/docs/reference/authoring-surface/workspace.md +259 -0
- package/docs/reference/authoring-surface.md +197 -0
- package/docs/reference/cli.md +433 -0
- package/docs/reference/config.md +756 -0
- package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +113 -0
- package/docs/reference/decisions/0002-duplex-streaming-actor.md +110 -0
- package/docs/reference/decisions/0004-worktree-provisioning-model.md +124 -0
- package/docs/reference/decisions/0005-sandbox-pod-composition.md +230 -0
- package/docs/reference/decisions/0006-agent-control-surface.md +47 -0
- package/docs/reference/decisions/0007-durable-machine-state.md +109 -0
- package/docs/reference/decisions/0008-orchestrator-as-deployed-app.md +60 -0
- package/docs/reference/decisions/0009-cli-and-instance-interface.md +275 -0
- package/docs/reference/decisions/0010-bdd-acceptance-tests.md +160 -0
- package/docs/reference/decisions/0011-workflow-defined-events.md +141 -0
- package/docs/reference/decisions/0012-workspace-wrapper-machine.md +103 -0
- package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +157 -0
- package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +95 -0
- package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +265 -0
- package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +124 -0
- package/docs/reference/decisions/0017-pool-over-a-source-port.md +69 -0
- package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +111 -0
- package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +275 -0
- package/docs/reference/decisions/0020-private-ca-trust-is-a-named-concept.md +60 -0
- package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +100 -0
- package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +103 -0
- package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +176 -0
- package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +439 -0
- package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +65 -0
- package/docs/reference/decisions/0026-a-turn-that-is-over-is-never-prompted.md +62 -0
- package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +237 -0
- package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +89 -0
- package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +154 -0
- package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +100 -0
- package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +190 -0
- package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +61 -0
- package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +127 -0
- package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +57 -0
- package/docs/reference/decisions/0035-a-runaway-turn-is-ended-rerolled-once-then-a-fault.md +92 -0
- package/docs/reference/decisions/0036-a-turn-compacts-its-own-context-mid-flight.md +138 -0
- package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +196 -0
- package/docs/reference/decisions/0038-jr2-up-builds-every-image-it-deploys.md +170 -0
- package/docs/reference/decisions/0039-image-garbage-collects-by-reachability.md +115 -0
- package/docs/reference/decisions/0041-a-build-the-host-already-holds-is-not-spent-again.md +64 -0
- package/docs/reference/decisions/0042-ready-is-not-routable-an-admission-retries-its-connection.md +142 -0
- package/docs/reference/decisions/0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md +170 -0
- package/docs/reference/decisions/0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md +85 -0
- package/docs/reference/decisions/0045-the-platform-joins-the-image-address-and-the-cluster-chooses-it.md +82 -0
- package/docs/reference/decisions/0046-a-converge-failure-carries-its-diagnosis.md +34 -0
- package/docs/reference/decisions/0047-the-git-ssh-key-source-is-the-users-choice.md +47 -0
- package/docs/reference/decisions/0048-the-orchestrator-boots-without-its-repos.md +36 -0
- package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +126 -0
- package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +73 -0
- package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +227 -0
- package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +81 -0
- package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +111 -0
- package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +126 -0
- package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +107 -0
- package/docs/reference/decisions/0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md +62 -0
- package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +152 -0
- package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +106 -0
- package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +384 -0
- package/docs/reference/decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md +111 -0
- package/docs/reference/decisions/0061-a-memory-kill-ends-the-agents-processes-not-the-harness.md +83 -0
- package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +212 -0
- package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +83 -0
- package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +79 -0
- package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +65 -0
- package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +550 -0
- package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +430 -0
- package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +464 -0
- package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +180 -0
- package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +323 -0
- package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +183 -0
- package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +144 -0
- package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +122 -0
- package/docs/reference/decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md +76 -0
- package/docs/reference/decisions.md +83 -0
- package/docs/reference/glossary.md +648 -0
- package/docs/reference/http-api.md +1348 -0
- package/docs/reference/machines.md +190 -0
- package/docs/reference/mechanism-events.md +72 -0
- package/docs/why.md +206 -0
- package/package.json +6 -4
- package/src/actor.ts +976 -61
- package/src/agent.ts +114 -38
- package/src/ambient.ts +4 -1
- package/src/assembly-checks.ts +347 -0
- package/src/assembly-tar.ts +494 -0
- package/src/assembly.ts +721 -0
- package/src/cause.ts +234 -0
- package/src/chart.ts +2182 -0
- package/src/charts.ts +748 -0
- package/src/config.ts +520 -162
- package/src/content-hash.ts +105 -0
- package/src/custodian.lua +6 -5
- package/src/custodian.ts +143 -39
- package/src/customize.ts +388 -55
- package/src/dispatch-effect.ts +945 -0
- package/src/door-compat.ts +217 -0
- package/src/durability.ts +11 -4
- package/src/effect-checks.ts +557 -0
- package/src/effect.ts +1202 -0
- package/src/effector-wire.ts +674 -0
- package/src/fail.ts +209 -0
- package/src/fingerprint.ts +4 -3
- package/src/gate.ts +8 -0
- package/src/git-clone.ts +65 -0
- package/src/git-push.ts +336 -0
- package/src/harness-client.ts +764 -48
- package/src/held-secrets.ts +82 -26
- package/src/holds.ts +156 -0
- package/src/http-effect.ts +1423 -0
- package/src/http.ts +916 -113
- package/src/images.ts +47 -21
- package/src/index.ts +62 -43
- package/src/instance.ts +505 -59
- package/src/internal.ts +84 -0
- package/src/kube-client.ts +333 -0
- package/src/machine-doc.ts +106 -6
- package/src/mechanism-routes.ts +168 -0
- package/src/model-keys.generated.ts +53 -0
- package/src/model-keys.ts +18 -58
- package/src/mount-digest.ts +53 -0
- package/src/names.ts +66 -8
- package/src/notices.ts +141 -0
- package/src/open-body.ts +1580 -0
- package/src/open-effect.ts +217 -0
- package/src/open.ts +59 -6
- package/src/parts.ts +709 -41
- package/src/party.ts +534 -0
- package/src/pick.ts +761 -0
- package/src/pool.ts +248 -37
- package/src/registration.ts +382 -19
- package/src/repo-fetch.ts +60 -85
- package/src/repos.ts +65 -75
- package/src/rest.ts +133 -0
- package/src/run-host.ts +2383 -115
- package/src/run-output.ts +38 -0
- package/src/sandbox-effect.ts +1126 -0
- package/src/sandbox-effector.ts +111 -0
- package/src/sandbox-kube.ts +1721 -0
- package/src/sandbox-watch.ts +260 -0
- package/src/secret-placement.ts +529 -0
- package/src/server.ts +284 -84
- package/src/setup.ts +819 -117
- package/src/size.ts +454 -0
- package/src/snapshot-store.ts +115 -20
- package/src/stub-harness.ts +49 -16
- package/src/sync-client.ts +54 -0
- package/src/timers.ts +191 -0
- package/src/tokens.ts +35 -4
- package/src/vocabulary.ts +135 -15
- package/src/wire.ts +238 -10
- package/src/workspace.ts +1195 -338
- package/src/sandbox-kubectl.ts +0 -1108
package/src/held-secrets.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
// Held secrets (ADR-0059): credentials the Harness uses and never holds
|
|
1
|
+
// Held secrets (ADR-0059): credentials the Harness uses and never holds — the entries of the
|
|
2
|
+
// `secrets` list that a Harness pod claims: a `workspace()` lists it under `secrets`, or a provider
|
|
3
|
+
// an Agent there names reads it (secret-placement.ts places each one, ADR-0068).
|
|
2
4
|
//
|
|
3
5
|
// The Agent executes code in the Harness container (ADR-0005), so a key in that container's env is
|
|
4
6
|
// a key it can take out of the pod. A held secret is not there: the Harness's env carries a
|
|
@@ -8,8 +10,9 @@
|
|
|
8
10
|
// This module is the pure half, shared by every seat that needs it:
|
|
9
11
|
//
|
|
10
12
|
// standIn(name) the value the Harness sees — deterministic and public
|
|
11
|
-
// heldSecretsOf(...) `jr2 up`'s normalization: every refusal (R1–R13),
|
|
12
|
-
// engine-neutral
|
|
13
|
+
// heldSecretsOf(...) `jr2 up`'s normalization of the claimed entries: every refusal (R1–R13),
|
|
14
|
+
// every warning, and the engine-neutral bindings each pod's manifest
|
|
15
|
+
// (`held-<pod>.json`) takes its own from
|
|
13
16
|
//
|
|
14
17
|
// It runs HOST-SIDE, in `jr2 up`, because only there does the config carry values: in-cluster,
|
|
15
18
|
// `process.env` has no `.env`, so a `value` reads `undefined` and a `hosts` entry built from `.env`
|
|
@@ -21,11 +24,50 @@ import { readFile } from "node:fs/promises";
|
|
|
21
24
|
import { isIP } from "node:net";
|
|
22
25
|
import type { JR2Config, HarnessEnvVar } from "./config.ts";
|
|
23
26
|
import { MODEL_KEYS, MODEL_KEY_PROVIDERS, SIGNING_CREDENTIALS } from "./model-keys.ts";
|
|
24
|
-
import { HELD_CA_SECRET, HELD_SECRETS_SECRET, HELD_TLS_SECRET, INSTANCE_SECRET } from "./names.ts";
|
|
27
|
+
import { HELD_CA_SECRET, HELD_SECRETS_SECRET, HELD_TLS_SECRET, INSTANCE_EFFECTOR, INSTANCE_SECRET } from "./names.ts";
|
|
28
|
+
import type { PlacementRoute } from "./parts.ts";
|
|
29
|
+
|
|
30
|
+
/** The kit's Secrets no Harness container may read (R13): the Instance token and signing key, each
|
|
31
|
+
* pod's literal held values, the leaves, the CA, and every Effector's (ADR-0068) — the Instance
|
|
32
|
+
* Effector's Effect secrets and key, each Workspace's Sandbox Effector Secret (`jr2-effector-<pod>`),
|
|
33
|
+
* each Sandbox's Effector key (`<sandbox>-effector`), and each `git.credentials` entry's clone and
|
|
34
|
+
* push Secrets (`jr2-git-<hash>`, `jr2-git-push-<hash>`, ADR-0051). */
|
|
35
|
+
const KIT_SECRETS: readonly string[] = [INSTANCE_SECRET, HELD_TLS_SECRET, HELD_CA_SECRET, INSTANCE_EFFECTOR];
|
|
36
|
+
const KIT_SECRET_PREFIXES: readonly string[] = [`${HELD_SECRETS_SECRET}-`, "jr2-effector-", "jr2-git-"];
|
|
37
|
+
const isKitSecret = (name: string): boolean =>
|
|
38
|
+
KIT_SECRETS.includes(name) ||
|
|
39
|
+
name === HELD_SECRETS_SECRET ||
|
|
40
|
+
KIT_SECRET_PREFIXES.some((p) => name.startsWith(p)) ||
|
|
41
|
+
// A Sandbox's own Effector key: every Sandbox's name starts `ws-` (`workspaceName`).
|
|
42
|
+
(name.startsWith("ws-") && name.endsWith("-effector"));
|
|
25
43
|
|
|
26
|
-
/**
|
|
27
|
-
*
|
|
28
|
-
|
|
44
|
+
/**
|
|
45
|
+
* The pod key of one Workspace (ADR-0059): its registered workflow and the placement route to its
|
|
46
|
+
* `workspace()` (`PlacementRoute`) — what `jr2 up` reads off the walk and the run host off the live
|
|
47
|
+
* actor (`actorRoute`), so both name one pod alike. Every Workspace has a route of its own, an
|
|
48
|
+
* inline-invoked one included, so two Workspaces never share a key. A digest, so it is a name a
|
|
49
|
+
* Secret and a ConfigMap key can carry; `ws-` keeps it apart from the Instance Harness's.
|
|
50
|
+
*/
|
|
51
|
+
export function workspaceHeldPod(at: { workflow: string; route: PlacementRoute }): string {
|
|
52
|
+
const key = JSON.stringify([at.workflow, at.route]);
|
|
53
|
+
return `ws-${createHash("sha256").update(key).digest("hex").slice(0, 16)}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The names a provider claims for a pod (ADR-0059): the `secrets` entries that are a known model key
|
|
58
|
+
* the provider of one of `models` reads, and the custom provider's key when an Agent there names
|
|
59
|
+
* that provider. A model key is claimed by the PROVIDER — the Agent names its model, jr2 knows which
|
|
60
|
+
* key that provider reads — so it is not listed under a `workspace()`'s `secrets`.
|
|
61
|
+
*/
|
|
62
|
+
export function providerClaims(config: JR2Config, models: readonly string[]): string[] {
|
|
63
|
+
const providers = new Set(models.map((m) => m.split("/")[0] ?? ""));
|
|
64
|
+
const claims = (config.secrets ?? [])
|
|
65
|
+
.filter((s) => MODEL_KEYS[s.name]?.providers.some((p) => providers.has(p)))
|
|
66
|
+
.map((s) => s.name);
|
|
67
|
+
const custom = config.harness?.provider;
|
|
68
|
+
if (custom?.apiKey && providers.has(custom.id)) claims.push(PROVIDER_KEY_NAME);
|
|
69
|
+
return claims;
|
|
70
|
+
}
|
|
29
71
|
|
|
30
72
|
/**
|
|
31
73
|
* The Stand-in for one held secret: what its env var holds in the Harness container.
|
|
@@ -79,7 +121,7 @@ export const MECHANISM_ENV: readonly string[] = [
|
|
|
79
121
|
"GIT_SSL_CAINFO",
|
|
80
122
|
];
|
|
81
123
|
|
|
82
|
-
/** What `jr2 up`
|
|
124
|
+
/** What `jr2 up` placed in one pod; what its composition reads (`held-<pod>.json` in the `jr2-held`
|
|
83
125
|
* ConfigMap). Values never appear here. */
|
|
84
126
|
export type HeldManifest = { version: 1; secrets: HeldBinding[] };
|
|
85
127
|
|
|
@@ -87,7 +129,7 @@ export type HeldManifest = { version: 1; secrets: HeldBinding[] };
|
|
|
87
129
|
export type HeldBinding = {
|
|
88
130
|
/** The env var; the Harness sees `standIn(name)`. */
|
|
89
131
|
name: string;
|
|
90
|
-
/** A literal lands in
|
|
132
|
+
/** A literal lands in its pod's `jr2-held-secrets-<pod>` Secret under `name`; a `secret` is the user's own
|
|
91
133
|
* Secret, mounted by reference so a rotation reaches new pods without a `jr2 up`. */
|
|
92
134
|
source: { kind: "literal" } | { kind: "secret"; secret: string; key: string };
|
|
93
135
|
/** `leaf` is the file stem of this host's certificate in `jr2-held-tls` (`leafStem`). */
|
|
@@ -118,13 +160,18 @@ export type HeldLookups = {
|
|
|
118
160
|
/** The key names of a Secret a `valueFrom.secretKeyRef` names, or undefined when it does not
|
|
119
161
|
* exist in the namespace (R10). */
|
|
120
162
|
secretKeys?: (name: string) => readonly string[] | undefined;
|
|
163
|
+
/** The length in characters of one key's value in a Secret a `valueFrom.secretKeyRef` names —
|
|
164
|
+
* never the value — or undefined when it is not known. An Effect secret shorter than 8 characters
|
|
165
|
+
* is refused by it as a literal is (ADR-0068). */
|
|
166
|
+
valueLength?: (name: string, key: string) => number | undefined;
|
|
121
167
|
/** Every model specifier a carried Agent definition names (`anthropic/claude-sonnet-4-6`) — R9's
|
|
122
168
|
* evidence that a provider's turns will reach its host. */
|
|
123
169
|
models?: readonly string[];
|
|
124
170
|
};
|
|
125
171
|
|
|
126
|
-
/** `heldSecretsOf`'s answer:
|
|
127
|
-
* `jr2-held-secrets
|
|
172
|
+
/** `heldSecretsOf`'s answer: every claimed binding, the literal values (host-side only — each goes
|
|
173
|
+
* into the `jr2-held-secrets-<pod>` Secret of a pod that claims it and nowhere else), and what
|
|
174
|
+
* `jr2 up` should warn about. */
|
|
128
175
|
export type HeldResolution = {
|
|
129
176
|
manifest: HeldManifest;
|
|
130
177
|
values: Record<string, string>;
|
|
@@ -146,11 +193,17 @@ const FRAMING_HEADERS = new Set([
|
|
|
146
193
|
]);
|
|
147
194
|
|
|
148
195
|
/**
|
|
149
|
-
* Normalize
|
|
150
|
-
*
|
|
151
|
-
*
|
|
196
|
+
* Normalize the Held secrets — the entries of the `secrets` list some pod claims, and the implicit
|
|
197
|
+
* `JR2_PROVIDER_API_KEY` when one claims it (`claimed`, from secret-placement.ts) — into bindings, or
|
|
198
|
+
* throw the FIRST refusal. Every message names the config path and never a value, and says what to
|
|
199
|
+
* write instead — the `checkGit` style. An entry no pod claims is not this function's: `hosts`,
|
|
200
|
+
* `headers` and `paths` are the binding, never the placement, so R5 fires only for a claimed one.
|
|
152
201
|
*/
|
|
153
|
-
export function heldSecretsOf(
|
|
202
|
+
export function heldSecretsOf(
|
|
203
|
+
config: JR2Config,
|
|
204
|
+
claimed: ReadonlySet<string>,
|
|
205
|
+
lookups: HeldLookups = {},
|
|
206
|
+
): HeldResolution {
|
|
154
207
|
const harness = config.harness ?? {};
|
|
155
208
|
const warnings: string[] = [];
|
|
156
209
|
const env = harness.env ?? [];
|
|
@@ -185,7 +238,7 @@ export function heldSecretsOf(config: JR2Config, lookups: HeldLookups = {}): Hel
|
|
|
185
238
|
if (!(v.name in MODEL_KEYS)) continue;
|
|
186
239
|
throw new Error(
|
|
187
240
|
`harness.env[${i}] sets ${v.name}, a model key the Agent could read and take out of the pod — hold it ` +
|
|
188
|
-
`instead:
|
|
241
|
+
`instead: secrets: [${heldLine(v)}] (ADR-0059)`,
|
|
189
242
|
);
|
|
190
243
|
}
|
|
191
244
|
for (const [i, ref] of (harness.envFrom ?? []).entries()) {
|
|
@@ -201,7 +254,7 @@ export function heldSecretsOf(config: JR2Config, lookups: HeldLookups = {}): Hel
|
|
|
201
254
|
const where = ref.secretRef ? `Secret "${ref.secretRef.name}"` : `ConfigMap "${ref.configMapRef?.name}"`;
|
|
202
255
|
throw new Error(
|
|
203
256
|
`harness.envFrom[${i}] loads ${where}, which carries ${known} — a model key the Agent could read. Hold it ` +
|
|
204
|
-
`instead:
|
|
257
|
+
`instead: secrets: [{ name: "${known}", valueFrom: { secretKeyRef: { name: "${ref.secretRef?.name ?? "<secret>"}", key: "${known}" } } }], ` +
|
|
205
258
|
`and drop the key from that ${ref.secretRef ? "Secret" : "ConfigMap"} (ADR-0059)`,
|
|
206
259
|
);
|
|
207
260
|
}
|
|
@@ -224,7 +277,9 @@ export function heldSecretsOf(config: JR2Config, lookups: HeldLookups = {}): Hel
|
|
|
224
277
|
headers?: readonly string[];
|
|
225
278
|
paths?: readonly string[];
|
|
226
279
|
};
|
|
227
|
-
const entries: Entry[] = (
|
|
280
|
+
const entries: Entry[] = (config.secrets ?? []).flatMap((s, i) =>
|
|
281
|
+
claimed.has(s.name) ? [{ at: `secrets[${i}]`, ...s }] : [],
|
|
282
|
+
);
|
|
228
283
|
|
|
229
284
|
// R2, for the user's own entries — before the implicit one joins them.
|
|
230
285
|
for (const e of entries) {
|
|
@@ -241,7 +296,7 @@ export function heldSecretsOf(config: JR2Config, lookups: HeldLookups = {}): Hel
|
|
|
241
296
|
|
|
242
297
|
// The custom provider's key: held since ADR-0059, bound to its own endpoint.
|
|
243
298
|
const provider = harness.provider;
|
|
244
|
-
if (provider?.apiKey) {
|
|
299
|
+
if (provider?.apiKey && claimed.has(PROVIDER_KEY_NAME)) {
|
|
245
300
|
const target = httpsTarget(provider.baseUrl);
|
|
246
301
|
if (!target) {
|
|
247
302
|
throw new Error(
|
|
@@ -335,7 +390,7 @@ export function heldSecretsOf(config: JR2Config, lookups: HeldLookups = {}): Hel
|
|
|
335
390
|
)?.name;
|
|
336
391
|
for (const [i, v] of env.entries()) {
|
|
337
392
|
const ref = (v.valueFrom as { secretKeyRef?: { name?: string; key?: string } } | undefined)?.secretKeyRef;
|
|
338
|
-
if (ref?.name !== undefined &&
|
|
393
|
+
if (ref?.name !== undefined && isKitSecret(ref.name)) {
|
|
339
394
|
throw new Error(
|
|
340
395
|
`harness.env[${i}] (${v.name}) reads Secret "${ref.name}", which is the kit's and never the Harness's — the ` +
|
|
341
396
|
"Agent would read it. Drop the entry (ADR-0059)",
|
|
@@ -359,7 +414,7 @@ export function heldSecretsOf(config: JR2Config, lookups: HeldLookups = {}): Hel
|
|
|
359
414
|
for (const [i, ref] of (harness.envFrom ?? []).entries()) {
|
|
360
415
|
const secret = ref.secretRef?.name;
|
|
361
416
|
if (secret === undefined) continue;
|
|
362
|
-
if (
|
|
417
|
+
if (isKitSecret(secret)) {
|
|
363
418
|
throw new Error(
|
|
364
419
|
`harness.envFrom[${i}] loads Secret "${secret}", which is the kit's and never the Harness's — the Agent ` +
|
|
365
420
|
"would read every key of it. Drop the entry (ADR-0059)",
|
|
@@ -396,7 +451,7 @@ export function heldSecretsOf(config: JR2Config, lookups: HeldLookups = {}): Hel
|
|
|
396
451
|
}
|
|
397
452
|
|
|
398
453
|
/** R4 and R10: exactly one source, and a literal that can ride a header. */
|
|
399
|
-
function sourceOf(
|
|
454
|
+
export function sourceOf(
|
|
400
455
|
e: { at: string; name: string; value?: string; valueFrom?: { secretKeyRef: { name: string; key: string } } },
|
|
401
456
|
lookups: HeldLookups,
|
|
402
457
|
): HeldBinding["source"] {
|
|
@@ -539,7 +594,7 @@ export function normalHost(raw: string): string {
|
|
|
539
594
|
return bare;
|
|
540
595
|
}
|
|
541
596
|
|
|
542
|
-
/** The `
|
|
597
|
+
/** The `secrets` line R1 hands back, built from the entry it refuses. */
|
|
543
598
|
function heldLine(v: HarnessEnvVar): string {
|
|
544
599
|
const ref = (v.valueFrom as { secretKeyRef?: { name?: string; key?: string } } | undefined)?.secretKeyRef;
|
|
545
600
|
if (ref?.name && ref.key) {
|
|
@@ -549,7 +604,8 @@ function heldLine(v: HarnessEnvVar): string {
|
|
|
549
604
|
}
|
|
550
605
|
|
|
551
606
|
/**
|
|
552
|
-
* Read the manifest `jr2 up` wrote (`held
|
|
607
|
+
* Read the manifest `jr2 up` wrote for one pod (`held-<pod>.json`, mounted from the `jr2-held`
|
|
608
|
+
* ConfigMap), per call —
|
|
553
609
|
* the `readImageRefs` stance (ADR-0038): no cache, so a `jr2 up` that changes a held secret reaches
|
|
554
610
|
* the next provision without rolling the Orchestrator. Absent or malformed is a pointed error naming
|
|
555
611
|
* `jr2 up`, never "nothing held": a Sandbox composed on a guess would put a key where the converge
|
|
@@ -562,8 +618,8 @@ export async function readHeldManifest(path: string): Promise<HeldManifest> {
|
|
|
562
618
|
} catch (err) {
|
|
563
619
|
throw new Error(
|
|
564
620
|
`no held-secret manifest at ${path} (${(err as NodeJS.ErrnoException).code ?? "read failed"}) — every ` +
|
|
565
|
-
"Harness pod's Custodian is composed from the `jr2-held` ConfigMap, which `jr2 up`
|
|
566
|
-
"Converge this instance with `jr2 up`.",
|
|
621
|
+
"Harness pod's Custodian is composed from its own manifest in the `jr2-held` ConfigMap, which `jr2 up` " +
|
|
622
|
+
"writes for each Workspace it walked (ADR-0059). Converge this instance with `jr2 up`.",
|
|
567
623
|
);
|
|
568
624
|
}
|
|
569
625
|
const bad = (why: string): never => {
|
package/src/holds.ts
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// The held answer (ADR-0024, ADR-0013): a pick that moves the workflow to a state inside the Agent's
|
|
2
|
+
// state is answered when the workflow is back at Rest there (rest.ts). Until then the pick is
|
|
3
|
+
// OUTSTANDING: its POST answered at once with a delivery id, and the Harness long-polls
|
|
4
|
+
// `GET /agents/:iid/deliveries/:id` for the answer, inside the one tool call.
|
|
5
|
+
//
|
|
6
|
+
// A hold is plain data on the run (`RunBlob.holds`), saved with the snapshot, so a park or an
|
|
7
|
+
// Orchestrator restart is a longer wait in the same Turn: the restored host reads the same Rest off
|
|
8
|
+
// the restored Machine, and answers the same poll. What a hold keeps beside the delivery id is EVERY
|
|
9
|
+
// reply from below the Agent's state since the pick (`chain`) — the last is the answer's default —
|
|
10
|
+
// because the events that carried them are gone after a restart.
|
|
11
|
+
//
|
|
12
|
+
// The answer is the last reply since the pick, rendered by the Harness: a party's event (a Gate's
|
|
13
|
+
// answer, a nested Agent's pick) with its payload, or a child's output (a child Machine, an Effect
|
|
14
|
+
// stand-in). A chain link is a reply from below the Agent's state — from the set of active states
|
|
15
|
+
// under it, never from a sibling region or the root; the events the Machine sent itself (`raise`,
|
|
16
|
+
// `sendTo(self)`, a timer) are skipped, and so is a child's error: every outcome of work is its
|
|
17
|
+
// output, and an error is a jr2 or config defect, never an answer. With no link, the answer carries
|
|
18
|
+
// no reply — it does not say whether work ran.
|
|
19
|
+
|
|
20
|
+
import type { AnyActorRef, AnyEventObject, AnyMachineSnapshot, AnyStateMachine } from "xstate";
|
|
21
|
+
import { SENDER_KEY, type Sender } from "@jr2/agent-protocol";
|
|
22
|
+
import { activeBelow } from "./rest.ts";
|
|
23
|
+
import type { PickAnswer } from "./wire.ts";
|
|
24
|
+
|
|
25
|
+
type Node = AnyStateMachine["root"];
|
|
26
|
+
|
|
27
|
+
/** One outstanding pick, or its answer not yet superseded. JSON-safe: it rides the run blob. */
|
|
28
|
+
export type Hold = {
|
|
29
|
+
/** The Agent address the pick was posted to. */
|
|
30
|
+
instanceId: string;
|
|
31
|
+
deliveryId: string;
|
|
32
|
+
/** The pick's event name. */
|
|
33
|
+
pick: string;
|
|
34
|
+
/** The Agent slot that picked — the Console's "held since" names it. */
|
|
35
|
+
agent: string;
|
|
36
|
+
/** ISO time the pick was delivered: the Console's "held since". */
|
|
37
|
+
since: string;
|
|
38
|
+
/** The Sandbox whose token may read the answer (ADR-0013) — the registration's, kept because the
|
|
39
|
+
* answer outlives a registration that a route out of the Agent's state ends. */
|
|
40
|
+
sandbox?: string;
|
|
41
|
+
/** The invoke id of the Agent's state when the pick was made: a re-registration of the same
|
|
42
|
+
* address by another state is another Turn. */
|
|
43
|
+
invokeId?: string;
|
|
44
|
+
/** The continued conversation (ADR-0057) the picking Turn is part of: where a route out of its
|
|
45
|
+
* state leaves the answer as a notice for its next Turn. Absent for a fresh Turn: no notice. */
|
|
46
|
+
conversation?: string;
|
|
47
|
+
/** Every reply since the pick, oldest first (ADR-0024): the last is the answer's default, and
|
|
48
|
+
* the whole is the `chain` an `answer` action reads (ADR-0067). Each link whole, a command's tail
|
|
49
|
+
* included, because the `answer` action may read it after a restore; dropped once `answer` is
|
|
50
|
+
* set. So a loop below the Agent's state grows the blob by one tail per pass while the pick is
|
|
51
|
+
* outstanding. */
|
|
52
|
+
chain?: PickAnswer[];
|
|
53
|
+
/** What the Machine's `answer` action set since the pick (ADR-0067): it replaces the default.
|
|
54
|
+
* Wrapped, so an answer of `null` is still one. */
|
|
55
|
+
shaped?: { value: unknown };
|
|
56
|
+
/** The last event the Machine took while the Agent's state was the hold's, with work still running
|
|
57
|
+
* below it, was no reply from below and no event a Machine sent itself at once: a timer the Machine
|
|
58
|
+
* set, an event from outside the states below, a party's event, the Agent's own end. If the Machine
|
|
59
|
+
* then left the Agent's state, the pick's route did not take it out: the exit stopped the work
|
|
60
|
+
* below (`HeldAnswer.stopped`). An event a Machine sends itself at once (`sendTo(self)`; a `raise`
|
|
61
|
+
* is never relayed) is part of the route of the event whose actions sent it and changes nothing
|
|
62
|
+
* here; a reply clears it. */
|
|
63
|
+
cutShort?: true;
|
|
64
|
+
/** The answer, once the Machine is back at Rest or has left the Agent's state. */
|
|
65
|
+
answer?: HeldAnswer;
|
|
66
|
+
/** The answer reached the model: the poll read it, or a re-admission carried it (ADR-0031). */
|
|
67
|
+
fetched?: boolean;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/** A pick's answer from what the Machine did since it: the author's `answer` when one ran, else the
|
|
71
|
+
* last reply; undefined when neither — the answer then carries no reply. */
|
|
72
|
+
export function answerOf(record: {
|
|
73
|
+
chain?: readonly PickAnswer[];
|
|
74
|
+
shaped?: { value: unknown };
|
|
75
|
+
}): PickAnswer | undefined {
|
|
76
|
+
if (record.shaped !== undefined) return { kind: "answer", value: record.shaped.value };
|
|
77
|
+
return record.chain?.at(-1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** The answer to an outstanding pick: the receipt's facts at the moment of Rest (ADR-0024). */
|
|
81
|
+
export type HeldAnswer = {
|
|
82
|
+
/** The Machine left the Agent's state: the Turn is over. */
|
|
83
|
+
turnComplete: boolean;
|
|
84
|
+
/** The Allowed picks read AFTER the route (ADR-0029); empty once the Turn is over. */
|
|
85
|
+
allowed: string[];
|
|
86
|
+
/** The last reply since the pick — kept when the work was stopped after an earlier link replied. */
|
|
87
|
+
answer?: PickAnswer;
|
|
88
|
+
/** With `turnComplete`: something other than the pick's route left the Agent's state while the
|
|
89
|
+
* pick's work ran, and that exit stopped the work before it finished (`Hold.cutShort`). */
|
|
90
|
+
stopped?: true;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The reply one event is, when the invoking Machine receives it during a hold — or undefined when it
|
|
95
|
+
* is no reply. Rest, the answer and the ledger are computed over the SET of active states below the
|
|
96
|
+
* Agent's state (`seat`), so a reply comes from below it:
|
|
97
|
+
*
|
|
98
|
+
* - from a child invoked on an active state below the seat (`source` is that child: its end, or an
|
|
99
|
+
* event it sends its parent), or
|
|
100
|
+
* - from a party seated below it: a Gate whose invoke id, or an Agent whose slot, is invoked on an
|
|
101
|
+
* active state below the seat (the event's `sender`, ADR-0015).
|
|
102
|
+
*
|
|
103
|
+
* Everything else is skipped: the Machine's own events (`source` is the Machine itself: a `raise`, a
|
|
104
|
+
* `sendTo(self)`, a timer), an xstate event other than a child's output — a child's error
|
|
105
|
+
* (`xstate.error.actor.*`) is a defect, never a reply, so its message never reaches the model — and
|
|
106
|
+
* an event from outside the set: a Gate in a sibling region, a root Gate, an outside `jr2 send`.
|
|
107
|
+
* Read it as the event is relayed, before the Machine processes it: the states the reply came from are still active. A
|
|
108
|
+
* child is named by its slot (`src`) where it has one, else by its invoke id.
|
|
109
|
+
*/
|
|
110
|
+
export function replyOf(
|
|
111
|
+
event: AnyEventObject,
|
|
112
|
+
source: AnyActorRef | undefined,
|
|
113
|
+
machine: AnyActorRef,
|
|
114
|
+
seat: Node,
|
|
115
|
+
): PickAnswer | undefined {
|
|
116
|
+
if (source === machine) return undefined;
|
|
117
|
+
const below = activeBelow(machine.getSnapshot() as AnyMachineSnapshot, seat).flatMap((node) => node.invoke);
|
|
118
|
+
const sender = (event as { [SENDER_KEY]?: Sender })[SENDER_KEY];
|
|
119
|
+
const fromChild = source !== undefined && below.some((inv) => inv.id === source.id);
|
|
120
|
+
const fromParty =
|
|
121
|
+
sender !== undefined &&
|
|
122
|
+
below.some((inv) => (sender.party === "gate" ? inv.id === sender.id : inv.src === sender.id));
|
|
123
|
+
if (!fromChild && !fromParty) return undefined;
|
|
124
|
+
const done = /^xstate\.done\.actor\.(.+)$/.exec(event.type);
|
|
125
|
+
if (done) {
|
|
126
|
+
const src = (source as { src?: unknown } | undefined)?.src;
|
|
127
|
+
const actor = typeof src === "string" ? src : done[1]!;
|
|
128
|
+
return { kind: "output", actor, output: (event as { output?: unknown }).output };
|
|
129
|
+
}
|
|
130
|
+
if (event.type.startsWith("xstate.")) return undefined;
|
|
131
|
+
const payload = Object.fromEntries(Object.entries(event).filter(([key]) => key !== "type" && key !== SENDER_KEY));
|
|
132
|
+
return { kind: "event", event: event.type, ...(sender ? { sender: { ...sender } } : {}), payload };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The error one event is, when it is a child's error from below the Agent's state (`seat`) — or
|
|
137
|
+
* undefined. It is no reply (`replyOf`): the Agent is not told by default, and the operator reads it
|
|
138
|
+
* on the run's feed (ADR-0024). The child is named as `replyOf` names it.
|
|
139
|
+
*/
|
|
140
|
+
export function childErrorOf(
|
|
141
|
+
event: AnyEventObject,
|
|
142
|
+
source: AnyActorRef | undefined,
|
|
143
|
+
machine: AnyActorRef,
|
|
144
|
+
seat: Node,
|
|
145
|
+
): { child: string; error: string } | undefined {
|
|
146
|
+
const failed = /^xstate\.error\.actor\.(.+)$/.exec(event.type);
|
|
147
|
+
if (!failed || source === undefined || source === machine) return undefined;
|
|
148
|
+
const below = activeBelow(machine.getSnapshot() as AnyMachineSnapshot, seat).flatMap((node) => node.invoke);
|
|
149
|
+
if (!below.some((inv) => inv.id === source.id)) return undefined;
|
|
150
|
+
const src = (source as { src?: unknown }).src;
|
|
151
|
+
const error = (event as { error?: unknown }).error;
|
|
152
|
+
return {
|
|
153
|
+
child: typeof src === "string" ? src : failed[1]!,
|
|
154
|
+
error: error instanceof Error ? error.message : String(error),
|
|
155
|
+
};
|
|
156
|
+
}
|