@jr2/orchestrator 0.4.1 → 0.6.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/README.md +20 -2
- package/console/components/app.ts +2 -0
- package/console/components/fleet.ts +14 -1
- package/console/components/machine-pane.ts +41 -2
- package/console/main.ts +13 -1
- package/console/store.ts +102 -6
- package/console/style.css +21 -0
- package/docs/agents-block.md +5 -0
- package/docs/concepts/agents-and-turns.md +478 -0
- package/docs/concepts/architecture.md +243 -0
- package/docs/concepts/charts.md +146 -0
- package/docs/concepts/composition.md +357 -0
- package/docs/concepts/converge.md +257 -0
- package/docs/concepts/durable-runs.md +189 -0
- package/docs/concepts/effects.md +658 -0
- package/docs/concepts/gates.md +208 -0
- package/docs/concepts/machines-and-workflows.md +298 -0
- package/docs/concepts/pools-and-sources.md +230 -0
- package/docs/concepts/secrets-and-trust.md +306 -0
- package/docs/concepts/the-menu.md +381 -0
- package/docs/concepts/workspaces.md +756 -0
- package/docs/get-started/a-model-decides.md +563 -0
- package/docs/get-started/an-agent-in-a-sandbox.md +383 -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 +993 -0
- package/docs/get-started/install.md +130 -0
- package/docs/guides/add-tools-to-the-sandbox.md +308 -0
- package/docs/guides/answer-a-gate.md +304 -0
- package/docs/guides/bind-open-parts.md +224 -0
- package/docs/guides/cancel-a-run.md +146 -0
- package/docs/guides/clean-up.md +206 -0
- package/docs/guides/cut-a-worktree.md +560 -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 +230 -0
- package/docs/guides/look-inside-a-sandbox.md +177 -0
- package/docs/guides/mirror-the-kit-images.md +176 -0
- package/docs/guides/move-from-kind-to-a-shared-cluster.md +318 -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 +532 -0
- package/docs/guides/run-a-chart-by-name.md +270 -0
- package/docs/guides/run-a-command-or-call-a-url.md +587 -0
- package/docs/guides/run-one-worker-per-item.md +322 -0
- package/docs/guides/set-an-agents-access-and-dials.md +420 -0
- package/docs/guides/size-a-workspace.md +217 -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 +214 -0
- package/docs/guides/use-a-local-model.md +313 -0
- package/docs/guides/watch-a-run.md +349 -0
- package/docs/guides/write-a-workflow.md +318 -0
- package/docs/index.md +44 -0
- package/docs/llms.txt +237 -0
- package/docs/reference/authoring-surface/agent.md +151 -0
- package/docs/reference/authoring-surface/answer.md +156 -0
- package/docs/reference/authoring-surface/arg.md +82 -0
- package/docs/reference/authoring-surface/basic.md +62 -0
- package/docs/reference/authoring-surface/bearer.md +53 -0
- package/docs/reference/authoring-surface/branchname.md +15 -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 +206 -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 +96 -0
- package/docs/reference/authoring-surface/file.md +142 -0
- package/docs/reference/authoring-surface/frompick.md +277 -0
- package/docs/reference/authoring-surface/http.md +259 -0
- package/docs/reference/authoring-surface/isfailure.md +58 -0
- package/docs/reference/authoring-surface/isready.md +14 -0
- package/docs/reference/authoring-surface/jr2setup.md +237 -0
- package/docs/reference/authoring-surface/open.md +105 -0
- package/docs/reference/authoring-surface/openbody.md +86 -0
- package/docs/reference/authoring-surface/paramsof.md +143 -0
- package/docs/reference/authoring-surface/pool.md +186 -0
- package/docs/reference/authoring-surface/push.md +130 -0
- package/docs/reference/authoring-surface/readyof.md +21 -0
- package/docs/reference/authoring-surface/secret.md +139 -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 +15 -0
- package/docs/reference/authoring-surface/type-answeraction.md +12 -0
- 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-arg.md +19 -0
- package/docs/reference/authoring-surface/type-argnames.md +12 -0
- package/docs/reference/authoring-surface/type-bindactors.md +14 -0
- package/docs/reference/authoring-surface/type-binding.md +61 -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 +20 -0
- package/docs/reference/authoring-surface/type-commandoptions.md +56 -0
- package/docs/reference/authoring-surface/type-commandoutput.md +122 -0
- package/docs/reference/authoring-surface/type-customize.md +111 -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 +72 -0
- package/docs/reference/authoring-surface/type-doorlessinput.md +19 -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 +37 -0
- package/docs/reference/authoring-surface/type-framepath.md +14 -0
- package/docs/reference/authoring-surface/type-frominput.md +14 -0
- package/docs/reference/authoring-surface/type-hostinjectedinput.md +56 -0
- package/docs/reference/authoring-surface/type-httpdefinition.md +104 -0
- package/docs/reference/authoring-surface/type-httpfileinput.md +20 -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 +95 -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-lateroutcome.md +21 -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-namestart.md +77 -0
- package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +27 -0
- package/docs/reference/authoring-surface/type-notworktreekeys.md +12 -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 +22 -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 +26 -0
- package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +27 -0
- package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
- package/docs/reference/authoring-surface/type-poolnextinput.md +22 -0
- package/docs/reference/authoring-surface/type-poolspec.md +60 -0
- package/docs/reference/authoring-surface/type-pushinput.md +34 -0
- package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
- package/docs/reference/authoring-surface/type-pushoutput.md +149 -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 +84 -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 +24 -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 +115 -0
- package/docs/reference/authoring-surface/type-workspacehandles.md +22 -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 +27 -0
- 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 +287 -0
- package/docs/reference/authoring-surface/worktree.md +71 -0
- package/docs/reference/authoring-surface.md +217 -0
- package/docs/reference/cli.md +433 -0
- package/docs/reference/config.md +765 -0
- package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +114 -0
- package/docs/reference/decisions/0002-duplex-streaming-actor.md +117 -0
- package/docs/reference/decisions/0004-worktree-provisioning-model.md +130 -0
- package/docs/reference/decisions/0005-sandbox-pod-composition.md +240 -0
- package/docs/reference/decisions/0006-agent-control-surface.md +50 -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 +175 -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 +271 -0
- package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +126 -0
- package/docs/reference/decisions/0017-pool-over-a-source-port.md +71 -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 +287 -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 +101 -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 +175 -0
- package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +436 -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 +245 -0
- package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +90 -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 +193 -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 +126 -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 +238 -0
- package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +86 -0
- package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +123 -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 +108 -0
- package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +427 -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 +239 -0
- package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +82 -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 +552 -0
- package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +458 -0
- package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +475 -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 +334 -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/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +295 -0
- package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +320 -0
- package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
- package/docs/reference/decisions.md +86 -0
- package/docs/reference/glossary.md +687 -0
- package/docs/reference/http-api.md +1386 -0
- package/docs/reference/machines.md +189 -0
- package/docs/reference/mechanism-events.md +72 -0
- package/docs/why.md +207 -0
- package/package.json +5 -3
- package/src/actor.ts +694 -106
- package/src/agent.ts +114 -38
- package/src/ambient.ts +3 -4
- package/src/assembly-checks.ts +189 -7
- package/src/cause.ts +234 -0
- package/src/chart.ts +345 -46
- package/src/charts.ts +43 -8
- package/src/config.ts +440 -178
- package/src/custodian.lua +17 -5
- package/src/custodian.ts +325 -99
- package/src/customize.ts +199 -49
- 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 +50 -3
- package/src/effect.ts +641 -128
- package/src/effector-wire.ts +165 -13
- package/src/fail.ts +222 -0
- package/src/harness-client.ts +587 -50
- package/src/harness-env.ts +33 -0
- package/src/held-secrets.ts +125 -16
- package/src/holds.ts +3 -0
- package/src/http-effect.ts +556 -58
- package/src/http.ts +480 -30
- package/src/index.ts +74 -82
- package/src/instance.ts +68 -8
- package/src/internal.ts +85 -0
- package/src/kube-client.ts +6 -3
- package/src/machine-doc.ts +24 -7
- package/src/mechanism-routes.ts +168 -0
- package/src/model-keys.generated.ts +53 -0
- package/src/model-keys.ts +42 -58
- package/src/names.ts +19 -6
- package/src/notices.ts +483 -41
- package/src/open-body.ts +242 -40
- package/src/open-effect.ts +81 -8
- package/src/open.ts +90 -6
- package/src/parts.ts +213 -30
- package/src/party.ts +101 -2
- package/src/pick.ts +497 -11
- package/src/pool.ts +282 -50
- package/src/registration.ts +133 -12
- package/src/repo-identity.ts +7 -3
- package/src/repos.ts +22 -3
- package/src/run-host.ts +665 -45
- package/src/run-output.ts +38 -0
- package/src/sandbox-effect.ts +457 -71
- package/src/sandbox-kube.ts +66 -17
- package/src/secret-placement.ts +59 -17
- package/src/server.ts +3 -0
- package/src/setup.ts +325 -62
- package/src/size.ts +121 -9
- package/src/snapshot-store.ts +54 -10
- package/src/stub-harness.ts +44 -16
- package/src/timers.ts +14 -1
- package/src/tokens.ts +107 -32
- package/src/vocabulary.ts +88 -14
- package/src/wire.ts +100 -26
- package/src/workspace.ts +771 -238
- package/src/worktree.ts +249 -0
package/src/open-body.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
type UnknownActorLogic,
|
|
25
25
|
} from "xstate";
|
|
26
26
|
import { isAgent, type AgentDefinition, type WorkspaceAccess } from "./agent.ts";
|
|
27
|
+
import { ambientHandlesFor } from "./ambient.ts";
|
|
27
28
|
import {
|
|
28
29
|
admitChart,
|
|
29
30
|
chartDigest,
|
|
@@ -31,8 +32,10 @@ import {
|
|
|
31
32
|
ChartRefusal,
|
|
32
33
|
GENERIC_ACTIONS,
|
|
33
34
|
pathSegments,
|
|
35
|
+
READY_GUARD,
|
|
34
36
|
resolveSource,
|
|
35
37
|
resolveReferences,
|
|
38
|
+
slotFolderFault,
|
|
36
39
|
type AdmittedChart,
|
|
37
40
|
type ChartAction,
|
|
38
41
|
type ChartGuard,
|
|
@@ -43,28 +46,33 @@ import {
|
|
|
43
46
|
type ChartTransition,
|
|
44
47
|
type ChartTransitions,
|
|
45
48
|
} from "./chart.ts";
|
|
46
|
-
import {
|
|
49
|
+
import { inputShapeOf, isEffect, type Duration, type InputShape } from "./effect.ts";
|
|
50
|
+
import { fail, FAILURE_GUARD, failureGuard } from "./fail.ts";
|
|
47
51
|
import { agent } from "./harness-client.ts";
|
|
48
52
|
import { SENT_BY } from "./party.ts";
|
|
49
|
-
import { wrapperBodyOf } from "./parts.ts";
|
|
53
|
+
import { composesSandbox, sandboxPartsOf, wrapperBodyOf } from "./parts.ts";
|
|
54
|
+
import { open } from "./open.ts";
|
|
50
55
|
import { actorPath, boundRun } from "./registration.ts";
|
|
51
|
-
import { jr2Setup } from "./setup.ts";
|
|
56
|
+
import { COMPILED_CHART, jr2Setup } from "./setup.ts";
|
|
52
57
|
import { attachBoundMachine, boundMachineFor } from "./vocabulary.ts";
|
|
53
58
|
|
|
59
|
+
// ADR-0070: "counters are the Open body's".
|
|
54
60
|
/**
|
|
55
|
-
* The
|
|
56
|
-
*
|
|
61
|
+
* The limits that an Open body sets on its Chart. Each one is a positive bound. When the Chart gets
|
|
62
|
+
* to one, the Open body stops it and ends with `{ outcome: "ceiling", ceiling, detail }`.
|
|
57
63
|
*/
|
|
58
64
|
export type Ceilings = {
|
|
59
|
-
/** The Agent Turns
|
|
65
|
+
/** The Agent Turns that start under the Open body, at all levels. The Turns of a lent Machine
|
|
66
|
+
* count too. */
|
|
60
67
|
turns: number;
|
|
61
|
-
/** The
|
|
68
|
+
/** The time from the start of the Open body, such as `"30m"`. At most about 596 hours. */
|
|
62
69
|
wallClock: Duration;
|
|
63
|
-
/** The
|
|
70
|
+
/** The transitions of the Chart in the whole run. xstate has no guard against a loop, so this is
|
|
71
|
+
* the guard. */
|
|
64
72
|
transitions: number;
|
|
65
|
-
/** The children
|
|
73
|
+
/** The children that run under the Open body at one time. */
|
|
66
74
|
fanOut: number;
|
|
67
|
-
/** The Chart
|
|
75
|
+
/** The size of the Chart, as JSON, in bytes. At most 1048576 (1 MiB). */
|
|
68
76
|
size: number;
|
|
69
77
|
};
|
|
70
78
|
|
|
@@ -106,8 +114,11 @@ export function jsonOver(value: unknown, cap: number): boolean {
|
|
|
106
114
|
return false;
|
|
107
115
|
}
|
|
108
116
|
|
|
109
|
-
/** The
|
|
110
|
-
*
|
|
117
|
+
/** The parts of the Chart wrapper that a Chart can use, by their keys in `actors`, `guards`,
|
|
118
|
+
* `actions` and `delays`. `gate: true` lends the Gate, so that a Chart can wait for an answer from
|
|
119
|
+
* outside the run. `fail: true` lends the Part that fails the run, so that a Chart can end its run
|
|
120
|
+
* as a Failure (ADR-0073): the Machine decides which ends a Chart can choose. A Chart can use no
|
|
121
|
+
* other part. */
|
|
111
122
|
export type Lends = {
|
|
112
123
|
agents?: string[];
|
|
113
124
|
effects?: string[];
|
|
@@ -116,13 +127,23 @@ export type Lends = {
|
|
|
116
127
|
actions?: string[];
|
|
117
128
|
delays?: string[];
|
|
118
129
|
gate?: boolean;
|
|
130
|
+
fail?: boolean;
|
|
119
131
|
};
|
|
120
132
|
|
|
121
|
-
/**
|
|
133
|
+
/**
|
|
134
|
+
* What an Open body declares: the parts that it lends a Chart, what the Agents of a Chart can
|
|
135
|
+
* have, and the limits of a Chart. Give it to {@link openBody}, and to `chartSchemaFor` for the
|
|
136
|
+
* pick of a planner.
|
|
137
|
+
*
|
|
138
|
+
* @see docs/concepts/charts.md
|
|
139
|
+
*/
|
|
122
140
|
export type OpenBodyDeclaration = {
|
|
141
|
+
/** The parts that a Chart can use. */
|
|
123
142
|
lends: Lends;
|
|
124
|
-
/** What a Chart
|
|
143
|
+
/** What the Agents that a Chart adds can have: a model from `models`, and a Workspace access
|
|
144
|
+
* from `workspace`. With no model, a Chart cannot add an Agent. */
|
|
125
145
|
allows: { models: string[]; workspace: WorkspaceAccess[] };
|
|
146
|
+
/** The limits of one Chart. */
|
|
126
147
|
ceilings: Ceilings;
|
|
127
148
|
};
|
|
128
149
|
|
|
@@ -151,6 +172,8 @@ const reservedName = (kind: (typeof LEND_KINDS)[number], v: string): string | un
|
|
|
151
172
|
if (v.includes(".")) return `"${v}" holds a dot; a lent name is a slot key, and dotted names are jr2's and xstate's`;
|
|
152
173
|
if ((kind === "agents" || kind === "effects" || kind === "machines") && v === "gate")
|
|
153
174
|
return `"gate" is the kit's Gate; lend a slot of another name`;
|
|
175
|
+
if ((kind === "agents" || kind === "effects" || kind === "machines") && v === "fail")
|
|
176
|
+
return `"fail" is the kit's Part that fails the run, lent as \`fail: true\`; lend a slot of another name`;
|
|
154
177
|
if (kind === "actions" && (GENERIC_ACTIONS as readonly string[]).includes(v))
|
|
155
178
|
return `"${v}" is a generic action every Chart has; lend an action of another name`;
|
|
156
179
|
if (kind === "delays" && /^(0|[1-9][0-9]*)$/.test(v))
|
|
@@ -201,8 +224,12 @@ export function declarationFaults(decl: unknown): string[] {
|
|
|
201
224
|
if (typeof (lends as { gate?: unknown }).gate !== "boolean") {
|
|
202
225
|
faults.push("lends.gate: the kit's Gate is lent whole: true or false");
|
|
203
226
|
}
|
|
227
|
+
} else if (key === "fail") {
|
|
228
|
+
if (typeof (lends as { fail?: unknown }).fail !== "boolean") {
|
|
229
|
+
faults.push("lends.fail: the kit's Part that fails the run is lent whole: true or false");
|
|
230
|
+
}
|
|
204
231
|
} else if (!(LEND_KINDS as readonly string[]).includes(key)) {
|
|
205
|
-
faults.push(`lends.${key}: an Open body lends ${LEND_KINDS.join(", ")} and
|
|
232
|
+
faults.push(`lends.${key}: an Open body lends ${LEND_KINDS.join(", ")}, gate and fail only`);
|
|
206
233
|
}
|
|
207
234
|
}
|
|
208
235
|
for (const kind of LEND_KINDS) {
|
|
@@ -261,6 +288,7 @@ function frozen(decl: OpenBodyDeclaration): OpenBodyDeclaration {
|
|
|
261
288
|
if (v !== undefined) lends[kind] = Object.freeze([...v]) as string[];
|
|
262
289
|
}
|
|
263
290
|
if (decl.lends.gate !== undefined) lends.gate = decl.lends.gate;
|
|
291
|
+
if (decl.lends.fail !== undefined) lends.fail = decl.lends.fail;
|
|
264
292
|
return Object.freeze({
|
|
265
293
|
lends: Object.freeze(lends),
|
|
266
294
|
allows: Object.freeze({
|
|
@@ -271,11 +299,78 @@ function frozen(decl: OpenBodyDeclaration): OpenBodyDeclaration {
|
|
|
271
299
|
});
|
|
272
300
|
}
|
|
273
301
|
|
|
302
|
+
// It validates the declaration now, so a bad one fails where the Machine is written, and returns
|
|
303
|
+
// actor logic branded with it. `jr2Setup` resolves every lent name against its own `actors`,
|
|
304
|
+
// `guards`, `actions` and `delays` at createMachine, and stamps the result with `attachLent`
|
|
305
|
+
// (ADR-0070).
|
|
274
306
|
/**
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
307
|
+
* Make an Open body: the actor slot that a Chart binds. Put it in `actors`, and invoke it from a
|
|
308
|
+
* state with `{ chart, ...input }`. `chart` is the text of a Chart, or `{ name }` for a named
|
|
309
|
+
* Chart of this wrapper. The other keys are the input of the Chart, which it reads as
|
|
310
|
+
* `{{input.<key>}}`. A Machine with an Open body is a Chart wrapper.
|
|
311
|
+
*
|
|
312
|
+
* Each Agent Turn of the Chart runs where the Open body runs: in the Sandbox of its `workspace()`,
|
|
313
|
+
* or on the Instance Harness. The Open body ends with an {@link OpenBodyOutput}.
|
|
314
|
+
*
|
|
315
|
+
* It refuses a declaration that is not correct: `openBody: <problem>; <problem>`, for example
|
|
316
|
+
* `ceilings.turns: missing; an Open body states every ceiling`. `createMachine` refuses a lent
|
|
317
|
+
* name that the Machine does not have: `machine "<id>": Open body "<slot>": lends.agents: "<name>"
|
|
318
|
+
* is no actor slot of this Machine; its actors: <slots>`.
|
|
319
|
+
*
|
|
320
|
+
* @param decl - What a Chart can use, and its limits (see {@link OpenBodyDeclaration}).
|
|
321
|
+
* @returns The actor logic of the slot.
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* ```ts
|
|
325
|
+
* import { assign } from "xstate";
|
|
326
|
+
* import { z } from "zod";
|
|
327
|
+
* import { agent, chartSchema, fail, jr2Setup, openBody, type OpenBodyOutput } from "@jr2/orchestrator";
|
|
328
|
+
*
|
|
329
|
+
* // `chart` is a Chart, so a run input can carry one, and Charts can have names.
|
|
330
|
+
* const door = z.object({ topic: z.string(), chart: chartSchema });
|
|
331
|
+
* type Input = z.infer<typeof door>;
|
|
332
|
+
* type Context = Input & { result?: OpenBodyOutput };
|
|
333
|
+
*
|
|
334
|
+
* export const machine = jr2Setup({
|
|
335
|
+
* types: {} as { context: Context; input: Input },
|
|
336
|
+
* actors: {
|
|
337
|
+
* writer: agent({
|
|
338
|
+
* model: "anthropic/claude-haiku-5-5",
|
|
339
|
+
* workspace: "none",
|
|
340
|
+
* instructions: "You write clear, short text for engineers.",
|
|
341
|
+
* }),
|
|
342
|
+
* body: openBody({
|
|
343
|
+
* lends: { agents: ["writer"] },
|
|
344
|
+
* allows: { models: [], workspace: [] },
|
|
345
|
+
* ceilings: { turns: 6, wallClock: "15m", transitions: 200, fanOut: 1, size: 65536 },
|
|
346
|
+
* }),
|
|
347
|
+
* fail: fail(),
|
|
348
|
+
* },
|
|
349
|
+
* }).createMachine({
|
|
350
|
+
* id: "notes",
|
|
351
|
+
* input: door,
|
|
352
|
+
* context: ({ input }) => ({ ...input }),
|
|
353
|
+
* // The fault of `writer` when a Chart does not route it: the Open body sends it here.
|
|
354
|
+
* on: { "agent.fault": ".failed" },
|
|
355
|
+
* initial: "running",
|
|
356
|
+
* states: {
|
|
357
|
+
* running: {
|
|
358
|
+
* invoke: {
|
|
359
|
+
* src: "body",
|
|
360
|
+
* input: ({ context }: { context: Context }) => ({ chart: context.chart, topic: context.topic }),
|
|
361
|
+
* onDone: { target: "done", actions: assign({ result: ({ event }) => event.output as OpenBodyOutput }) },
|
|
362
|
+
* },
|
|
363
|
+
* },
|
|
364
|
+
* done: { type: "final" },
|
|
365
|
+
* failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
|
|
366
|
+
* },
|
|
367
|
+
* });
|
|
368
|
+
* ```
|
|
369
|
+
*
|
|
370
|
+
* @see docs/concepts/charts.md
|
|
371
|
+
* @see docs/guides/run-a-chart-by-name.md
|
|
372
|
+
* @see docs/guides/let-an-agent-write-a-chart.md
|
|
373
|
+
* @see docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md
|
|
279
374
|
*/
|
|
280
375
|
export function openBody(decl: OpenBodyDeclaration): OpenBodyLogic {
|
|
281
376
|
return openBodyWith(decl);
|
|
@@ -358,7 +453,16 @@ export function lentOf(logic: OpenBodyLogic): LentParts | undefined {
|
|
|
358
453
|
/** What an Open body is invoked with: the Chart, by text or by name, and its `input` (the rest). */
|
|
359
454
|
export type OpenBodyInput = { chart: ChartText | { name: string } } & Record<string, unknown>;
|
|
360
455
|
|
|
361
|
-
/**
|
|
456
|
+
/**
|
|
457
|
+
* The output of an Open body. Route on `outcome` in the `onDone` of the state that invokes it:
|
|
458
|
+
*
|
|
459
|
+
* - `done`: the Chart got to a final state. `output` is the output of the Chart.
|
|
460
|
+
* - `refused`: the door refused the Chart. `report` has one line for each problem. Give it to the
|
|
461
|
+
* Agent that wrote the Chart, so that it can send a corrected one.
|
|
462
|
+
* - `ceiling`: the Chart got to a ceiling, and the Open body stopped it. `ceiling` names it.
|
|
463
|
+
*
|
|
464
|
+
* @see docs/concepts/charts.md
|
|
465
|
+
*/
|
|
362
466
|
export type OpenBodyOutput =
|
|
363
467
|
| { outcome: "done"; output: unknown }
|
|
364
468
|
| { outcome: "refused"; report: string[] }
|
|
@@ -473,9 +577,12 @@ export function admitBodyChart(
|
|
|
473
577
|
text: unknown,
|
|
474
578
|
decl: OpenBodyDeclaration,
|
|
475
579
|
lent: LentParts,
|
|
476
|
-
options: { inputKeys?: readonly string[] } = {},
|
|
580
|
+
options: { inputKeys?: readonly string[]; repoSlots?: readonly string[] } = {},
|
|
477
581
|
): AdmittedChart {
|
|
478
|
-
const admitted = admitChart(text, decl, lent,
|
|
582
|
+
const admitted = admitChart(text, decl, lent, {
|
|
583
|
+
...(options.inputKeys ? { inputKeys: options.inputKeys } : {}),
|
|
584
|
+
...(options.repoSlots ? { repoSlots: options.repoSlots } : {}),
|
|
585
|
+
});
|
|
479
586
|
try {
|
|
480
587
|
compileChart(admitted, lent, decl);
|
|
481
588
|
} catch (err) {
|
|
@@ -527,11 +634,23 @@ export function admitOpenBodyChart(
|
|
|
527
634
|
const logic = (inner.implementations.actors as Record<string, unknown>)[slot];
|
|
528
635
|
if (!isOpenBody(logic)) throw new Error(`machine "${machine.id}" has no Open body "${slot}"`);
|
|
529
636
|
const decl = openBodyDeclarationOf(logic);
|
|
637
|
+
const repoSlots = repoSlotsAround(machine);
|
|
530
638
|
return admitBodyChart(text, decl, lentIn(inner.implementations as Implementations, decl), {
|
|
531
639
|
...(inputKeys ? { inputKeys } : {}),
|
|
640
|
+
...(repoSlots ? { repoSlots } : {}),
|
|
532
641
|
});
|
|
533
642
|
}
|
|
534
643
|
|
|
644
|
+
/** The Repo Slots of the `workspace()` around a Chart wrapper, where the door can see one: the
|
|
645
|
+
* wrapper IS the `workspace()`. Undefined: an Open map, or a Workspace that a parent state invokes,
|
|
646
|
+
* whose slots this door cannot read. Then the Open body's own door reads them off the running
|
|
647
|
+
* Workspace, and a Turn's or an Effect's start refuses what no door could see. */
|
|
648
|
+
export function repoSlotsAround(machine: AnyStateMachine): readonly string[] | undefined {
|
|
649
|
+
if (!composesSandbox(machine)) return undefined;
|
|
650
|
+
const { repos } = sandboxPartsOf(machine);
|
|
651
|
+
return repos === open ? undefined : Object.keys(repos);
|
|
652
|
+
}
|
|
653
|
+
|
|
535
654
|
/**
|
|
536
655
|
* Admit a Chart field of a Chart wrapper's door (ADR-0070), the door a run's start runs before the
|
|
537
656
|
* run exists. The door cannot tell which Open body a field feeds, so the Chart is admitted against
|
|
@@ -548,7 +667,10 @@ export function admitWrapperChart(machine: AnyStateMachine, text: unknown): Admi
|
|
|
548
667
|
for (const { slot, logic } of bodies) {
|
|
549
668
|
const decl = openBodyDeclarationOf(logic);
|
|
550
669
|
try {
|
|
551
|
-
|
|
670
|
+
const repoSlots = repoSlotsAround(machine);
|
|
671
|
+
return admitBodyChart(text, decl, lentIn(inner.implementations as Implementations, decl), {
|
|
672
|
+
...(repoSlots ? { repoSlots } : {}),
|
|
673
|
+
});
|
|
552
674
|
} catch (err) {
|
|
553
675
|
if (!(err instanceof ChartRefusal)) throw err;
|
|
554
676
|
report.push(...err.report.map((line) => (bodies.length > 1 ? `body "${slot}": ${line}` : line)));
|
|
@@ -575,7 +697,7 @@ const scopeOf = (args: Args) => ({
|
|
|
575
697
|
input: chartInputOf((args.context?.[CHART_INPUT_KEY] as Record<string, unknown> | undefined) ?? {}),
|
|
576
698
|
});
|
|
577
699
|
|
|
578
|
-
/** An invoke's error whose message jr2 wrote for a Chart to read: a
|
|
700
|
+
/** An invoke's error whose message jr2 wrote for a Chart to read: a fault in an input the door could not see. */
|
|
579
701
|
class ChartInvokeFault extends Error {}
|
|
580
702
|
|
|
581
703
|
/**
|
|
@@ -739,6 +861,10 @@ function transitionsOf(ts: ChartTransitions): unknown {
|
|
|
739
861
|
return Array.isArray(ts) ? ts.map(transitionOf) : transitionOf(ts);
|
|
740
862
|
}
|
|
741
863
|
|
|
864
|
+
/** `jr2.ready`: is this cut's output `ready`? On an `onDone`. */
|
|
865
|
+
const readyGuard = ({ event }: { event: unknown }): boolean =>
|
|
866
|
+
(event as { output?: { outcome?: unknown } } | undefined)?.output?.outcome === "ready";
|
|
867
|
+
|
|
742
868
|
const mapValues = <T, U>(o: Record<string, T>, fn: (v: T) => U): Record<string, U> =>
|
|
743
869
|
Object.fromEntries(Object.entries(o).map(([k, v]) => [k, fn(v)]));
|
|
744
870
|
|
|
@@ -747,15 +873,17 @@ const mapValues = <T, U>(o: Record<string, T>, fn: (v: T) => U): Record<string,
|
|
|
747
873
|
* resolved object again at invoke time"), or undefined when it is. The door checked the literal
|
|
748
874
|
* parts; a reference may still resolve to a value that is no string.
|
|
749
875
|
*/
|
|
750
|
-
function effectInputFault(
|
|
876
|
+
function effectInputFault(shape: InputShape, value: unknown): string | undefined {
|
|
751
877
|
const given = typeof value === "object" && value !== null && !Array.isArray(value) ? Object.keys(value) : undefined;
|
|
878
|
+
const known = [...shape.required, ...shape.optional, ...(shape.oneOf ?? [])];
|
|
752
879
|
const ok =
|
|
753
880
|
given !== undefined &&
|
|
754
|
-
|
|
755
|
-
|
|
881
|
+
shape.required.every((k) => given.includes(k)) &&
|
|
882
|
+
given.every((k) => known.includes(k) && typeof (value as Record<string, unknown>)[k] === "string") &&
|
|
883
|
+
(shape.oneOf === undefined || shape.oneOf.filter((k) => given.includes(k)).length === 1);
|
|
756
884
|
return ok
|
|
757
885
|
? undefined
|
|
758
|
-
: `its input resolved to ${JSON.stringify(value)}; it takes
|
|
886
|
+
: `its input resolved to ${JSON.stringify(value)}; it takes ${known.join(", ") || "nothing"}, each a string (ADR-0070)`;
|
|
759
887
|
}
|
|
760
888
|
|
|
761
889
|
const checkedEffects = new WeakMap<object, UnknownActorLogic>();
|
|
@@ -767,13 +895,15 @@ const checkedEffects = new WeakMap<object, UnknownActorLogic>();
|
|
|
767
895
|
*/
|
|
768
896
|
function checkedEffect(slot: string, logic: UnknownActorLogic): UnknownActorLogic {
|
|
769
897
|
if (!isEffect(logic)) return logic;
|
|
770
|
-
const
|
|
771
|
-
if (
|
|
898
|
+
const shape = inputShapeOf(logic.effect);
|
|
899
|
+
if (shape === undefined) return logic;
|
|
772
900
|
const hit = checkedEffects.get(logic);
|
|
773
901
|
if (hit) return hit;
|
|
774
902
|
const checked = Object.create(logic) as UnknownActorLogic;
|
|
775
903
|
checked.getInitialSnapshot = (scope, input) => {
|
|
776
|
-
|
|
904
|
+
// An Effect that takes nothing, invoked with no input, is a whole input: the door let it through.
|
|
905
|
+
const fault =
|
|
906
|
+
input === undefined && shape.required.length === 0 && !shape.oneOf ? undefined : effectInputFault(shape, input);
|
|
777
907
|
if (fault === undefined) return logic.getInitialSnapshot(scope, input);
|
|
778
908
|
return {
|
|
779
909
|
status: "error",
|
|
@@ -785,9 +915,38 @@ function checkedEffect(slot: string, logic: UnknownActorLogic): UnknownActorLogi
|
|
|
785
915
|
return checked;
|
|
786
916
|
}
|
|
787
917
|
|
|
788
|
-
|
|
918
|
+
const checkedAgents = new WeakMap<object, UnknownActorLogic>();
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* An Agent as a Chart invokes it: the Agent itself, whose start refuses a resolved `cwd` that is a
|
|
922
|
+
* Repo Slot's folder. A reference hides that value from the door. The check is in `start`, because
|
|
923
|
+
* the Workspace's handles are registered at a start, and a Chart's first invoke resolves before
|
|
924
|
+
* that. The refusal is THAT invoke's error, as in {@link checkedEffect}.
|
|
925
|
+
*/
|
|
926
|
+
function checkedAgent(slot: string, logic: UnknownActorLogic): UnknownActorLogic {
|
|
927
|
+
const hit = checkedAgents.get(logic);
|
|
928
|
+
if (hit) return hit;
|
|
929
|
+
const checked = Object.create(logic) as UnknownActorLogic;
|
|
930
|
+
checked.start = (snapshot, scope) => {
|
|
931
|
+
const cwd = ((snapshot as { input?: unknown }).input as { cwd?: unknown } | undefined)?.cwd;
|
|
932
|
+
const reads = (logic as { definition?: { workspace?: unknown } }).definition?.workspace !== "none";
|
|
933
|
+
const repos = reads && typeof cwd === "string" ? ambientHandlesFor(scope.self)?.repos : undefined;
|
|
934
|
+
const fault = repos ? slotFolderFault(cwd as string, Object.keys(repos)) : undefined;
|
|
935
|
+
if (fault !== undefined) throw new ChartInvokeFault(`the Agent "${slot}": ${fault}`);
|
|
936
|
+
logic.start?.(snapshot, scope);
|
|
937
|
+
};
|
|
938
|
+
checkedAgents.set(logic, checked);
|
|
939
|
+
return checked;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/** The lent actors as a Chart invokes them: each Effect and Agent checked at its start. */
|
|
789
943
|
function chartActors(actors: Record<string, UnknownActorLogic>): Record<string, UnknownActorLogic> {
|
|
790
|
-
return Object.fromEntries(
|
|
944
|
+
return Object.fromEntries(
|
|
945
|
+
Object.entries(actors).map(([slot, logic]) => [
|
|
946
|
+
slot,
|
|
947
|
+
isAgent(logic) ? checkedAgent(slot, logic) : checkedEffect(slot, logic),
|
|
948
|
+
]),
|
|
949
|
+
);
|
|
791
950
|
}
|
|
792
951
|
|
|
793
952
|
/** An Agent Turn's input with `prompt` and `cwd` as strings: a whole placeholder may resolve to
|
|
@@ -863,13 +1022,16 @@ export type CompileOptions = OpenBodyOptions & {
|
|
|
863
1022
|
|
|
864
1023
|
/**
|
|
865
1024
|
* Build the Machine a Chart describes (ADR-0070), with `jr2Setup` over: the lent actors by their
|
|
866
|
-
* slot names, `agent(def)` for each Agent the Chart mints,
|
|
867
|
-
* and `jr2.
|
|
868
|
-
* resolves at invoke time, so the Machine holds the
|
|
1025
|
+
* slot names, `agent(def)` for each Agent the Chart mints, the kit's `gate`, and `fail()` where the
|
|
1026
|
+
* body lends it; the lent guards, `jr2.sentBy`, `jr2.failure` and `jr2.ready`; the lent actions and the four
|
|
1027
|
+
* generic ones; the lent delays. Every reference resolves at invoke time, so the Machine holds the
|
|
1028
|
+
* text's shape and no run's data. The Chart's
|
|
869
1029
|
* input is the context key {@link CHART_INPUT_KEY}; a done Chart outputs `{ outcome: "done",
|
|
870
1030
|
* output }`, its `output` resolved (or, without one, its final state's). The transitions ceiling
|
|
871
1031
|
* is xstate's `maxIterations` as well, so a guarded `always` loop that never ends in one macrostep
|
|
872
|
-
* still ends. `jr2Setup`'s own refusals (a parallel state below an Agent's) throw here
|
|
1032
|
+
* still ends. `jr2Setup`'s own refusals (a parallel state below an Agent's) throw here, but not its
|
|
1033
|
+
* `agent.fault` refusal: in a Chart that route is optional, and the body sends an `agent.fault` that
|
|
1034
|
+
* no transition of the Chart takes to the Machine that invoked it (ADR-0074).
|
|
873
1035
|
*/
|
|
874
1036
|
export function compileChart(
|
|
875
1037
|
admitted: AdmittedChart,
|
|
@@ -881,7 +1043,9 @@ export function compileChart(
|
|
|
881
1043
|
const mint = options.mintAgent ?? ((_slot: string, definition: AgentDefinition) => agent(definition));
|
|
882
1044
|
const minted = mapValues(admitted.agents, (def) => def);
|
|
883
1045
|
const actors: Record<string, UnknownActorLogic> = chartActors(lent.actors);
|
|
884
|
-
for (const [slot, def] of Object.entries(minted)) actors[slot] = mint(slot, def);
|
|
1046
|
+
for (const [slot, def] of Object.entries(minted)) actors[slot] = checkedAgent(slot, mint(slot, def));
|
|
1047
|
+
// The kit's Part that fails the run, where the body lends it (ADR-0073): no slot of the wrapper's.
|
|
1048
|
+
if (decl.lends.fail === true) actors.fail = fail();
|
|
885
1049
|
// The slots an invoke runs an Agent Turn in: the lent Agents and the Chart's own.
|
|
886
1050
|
const agentSlots = new Set([
|
|
887
1051
|
...Object.keys(lent.actors).filter((slot) => isAgent(lent.actors[slot])),
|
|
@@ -905,11 +1069,19 @@ export function compileChart(
|
|
|
905
1069
|
: (event as { output?: unknown } | undefined)?.output,
|
|
906
1070
|
}) satisfies OpenBodyOutput,
|
|
907
1071
|
options: { maxIterations: decl.ceilings.transitions },
|
|
1072
|
+
// In a Chart the `agent.fault` route is optional (ADR-0070): the Open body sends an `agent.fault`
|
|
1073
|
+
// that no transition takes to its parent, so `createMachine` does not refuse a state with none.
|
|
1074
|
+
[COMPILED_CHART]: true,
|
|
908
1075
|
};
|
|
909
1076
|
return jr2Setup({
|
|
910
1077
|
events: admitted.events,
|
|
911
1078
|
actors,
|
|
912
|
-
|
|
1079
|
+
// `jr2.failure` is in every Chart, as `jr2.sentBy` is: a predicate, with no capability (ADR-0073).
|
|
1080
|
+
guards: {
|
|
1081
|
+
...(lent.guards as Record<string, unknown>),
|
|
1082
|
+
[FAILURE_GUARD]: failureGuard,
|
|
1083
|
+
[READY_GUARD]: readyGuard,
|
|
1084
|
+
} as never,
|
|
913
1085
|
actions: { ...(lent.actions as Record<string, unknown>), ...GENERIC } as never,
|
|
914
1086
|
delays: lent.delays as never,
|
|
915
1087
|
}).createMachine(config as never) as unknown as AnyStateMachine;
|
|
@@ -1097,6 +1269,20 @@ function watchTurns(self: AnyActorRef, turns: number, ceiling: number): TurnWatc
|
|
|
1097
1269
|
return watch;
|
|
1098
1270
|
}
|
|
1099
1271
|
|
|
1272
|
+
/**
|
|
1273
|
+
* Does a transition of the Chart take this event now? xstate's own selection, guards and all. A
|
|
1274
|
+
* guard that throws (a reference that resolved to nothing) counts as a take: the step then runs
|
|
1275
|
+
* the guard again, and settles the body `refused` with the reference named.
|
|
1276
|
+
*/
|
|
1277
|
+
function takes(machine: AnyStateMachine, snapshot: BodySnapshot, event: AnyEventObject): boolean {
|
|
1278
|
+
try {
|
|
1279
|
+
return machine.getTransitionData(snapshot as never, event as never).length > 0;
|
|
1280
|
+
} catch {
|
|
1281
|
+
runFault = undefined;
|
|
1282
|
+
return true;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1100
1286
|
/** The actor logic of an Open body: a custom ActorLogic that delegates to the compiled Chart. */
|
|
1101
1287
|
export function openBodyLogic(decl: OpenBodyDeclaration, options: OpenBodyOptions = {}): UnknownActorLogic {
|
|
1102
1288
|
const wallMs = durationMs(decl.ceilings.wallClock) ?? Number.POSITIVE_INFINITY;
|
|
@@ -1259,7 +1445,10 @@ export function openBodyLogic(decl: OpenBodyDeclaration, options: OpenBodyOption
|
|
|
1259
1445
|
/** Admit and compile a Chart text, and record the Machine the actor runs. Throws ChartRefusal. */
|
|
1260
1446
|
const bind = (self: AnyActorRef, text: unknown, inputKeys: readonly string[]) => {
|
|
1261
1447
|
const lent = liveLentOf(self, logic);
|
|
1262
|
-
|
|
1448
|
+
// A Chart that comes after the Workspace started finds its slots here. A Chart in the step that
|
|
1449
|
+
// starts the Workspace comes before them, and the wrapper's door or the Turn's start saw it.
|
|
1450
|
+
const repos = ambientHandlesFor(self)?.repos;
|
|
1451
|
+
const admitted = admitChart(text, decl, lent, { inputKeys, ...(repos ? { repoSlots: Object.keys(repos) } : {}) });
|
|
1263
1452
|
const src = (self as { src?: unknown }).src;
|
|
1264
1453
|
const machine = compiledFor(admitted, lent, decl, {
|
|
1265
1454
|
...options,
|
|
@@ -1392,6 +1581,19 @@ export function openBodyLogic(decl: OpenBodyDeclaration, options: OpenBodyOption
|
|
|
1392
1581
|
ceiling("transitions", transitionsDetail(fields.counters.transitions), snapshot, fields, scope),
|
|
1393
1582
|
);
|
|
1394
1583
|
}
|
|
1584
|
+
// An `agent.fault` that no transition of the Chart takes goes to the Machine that invoked the
|
|
1585
|
+
// body, unchanged (ADR-0074): in a Chart the route is optional, and the parent's `agent.fault`
|
|
1586
|
+
// route is the route for a Chart's Agents too. The body does not step the Chart: the parent's
|
|
1587
|
+
// route decides, and its exit stops the body. With no parent route that takes it, the run
|
|
1588
|
+
// host faults the run, as for any `agent.fault` that no transition took.
|
|
1589
|
+
if (event.type === "agent.fault" && !takes(machine, snapshot, event)) {
|
|
1590
|
+
// Relayed from the body, as `sendBack` relays from an Agent: the run host reads a relayed
|
|
1591
|
+
// event's source, so a pick held in the parent is answered as for an Agent of its own.
|
|
1592
|
+
const parent = scope.self._parent;
|
|
1593
|
+
const system = scope.system as unknown as { _relay(from: unknown, to: unknown, e: AnyEventObject): void };
|
|
1594
|
+
if (parent) scope.defer(() => system._relay(scope.self, parent, event));
|
|
1595
|
+
return snapshot;
|
|
1596
|
+
}
|
|
1395
1597
|
return release(
|
|
1396
1598
|
scope.self,
|
|
1397
1599
|
step(
|
package/src/open-effect.ts
CHANGED
|
@@ -25,18 +25,82 @@
|
|
|
25
25
|
import type { ActorLogic, AnyActorRef, EventObject } from "xstate";
|
|
26
26
|
import { open } from "./open.ts";
|
|
27
27
|
import type { Arg, EffectLogic, EffectParts, EffectSnapshot } from "./effect.ts";
|
|
28
|
-
import type { Text } from "./http-effect.ts";
|
|
28
|
+
import type { Secret, Text } from "./http-effect.ts";
|
|
29
29
|
|
|
30
|
-
/** The parts of an Effect a package may leave Open: an http url, a command's argv, a secret's name
|
|
31
|
-
|
|
30
|
+
/** The parts of an Effect a package may leave Open: an http url, a command's argv, a secret's name,
|
|
31
|
+
* a dispatch's target (its url and token together, ADR-0071). */
|
|
32
|
+
export type OpenEffectPart = "url" | "argv" | "secret" | "target";
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
// What a composer binds an Effect's Open parts to, each key one part (ADR-0054); a dispatch's
|
|
35
|
+
// target is its url and token, bound as one (ADR-0071). One object for an
|
|
36
|
+
// Effect with two Open parts. `customize()` reads each Effect's own binding type off its logic
|
|
37
|
+
// (`BindingOf`, customize.ts), so this loose shape is what the implementation walks.
|
|
38
|
+
/**
|
|
39
|
+
* The values that bind the Open parts of one Effect. A packaged Machine leaves a part of an Effect
|
|
40
|
+
* Open when it cannot know it. That part is the url of an `http` Effect, the argv of a `command`,
|
|
41
|
+
* the name of a secret, written `secret(open)`, or the target of a `dispatch`: the url of another
|
|
42
|
+
* Orchestrator and the secret that holds its Instance token, bound as one. You bind it once, where you register the
|
|
43
|
+
* Machine, with `customize(machine, { effects: { <slot>: binding } })`. Each key binds one Open
|
|
44
|
+
* part. For an Effect with two Open parts, one object holds both keys.
|
|
45
|
+
*
|
|
46
|
+
* The binding is a value, never a function of the run: no run chooses an Effect's url, argv or
|
|
47
|
+
* secret. `customize()` builds the Effect again with the bound parts, so a bound part gets every
|
|
48
|
+
* check that a part written in the definition gets.
|
|
49
|
+
*
|
|
50
|
+
* `customize()` refuses:
|
|
51
|
+
* - an empty binding, or a key for a part that is not Open: `customize(): the <kind> Effect
|
|
52
|
+
* "<slot>" leaves its <parts> Open — bind it with <binding> (ADR-0054)`
|
|
53
|
+
* - an Effect with no Open part: ``customize(): the <kind> Effect "<slot>" of machine "<id>"
|
|
54
|
+
* leaves nothing Open — its parts are fixed in its definition, and only a part a package left
|
|
55
|
+
* `open` is bound, once (ADR-0054).``
|
|
56
|
+
* - `open` as a value: ``customize(): the <kind> Effect "<slot>"'s <part> is bound once, to a
|
|
57
|
+
* value — not `open` again: no run chooses an Effect's <part> (ADR-0054)``. A function as a
|
|
58
|
+
* value gives the same text, with `a function of the run` for `` `open` again ``.
|
|
59
|
+
* - a secret that is not a string: `customize(): the <kind> Effect "<slot>"'s secret is bound to
|
|
60
|
+
* a secret's name, a string — the file the Effector reads it from: { secret: "<name>" }
|
|
61
|
+
* (ADR-0054, ADR-0068)`
|
|
62
|
+
*
|
|
63
|
+
* `jr2 up` refuses a registered Machine with an Open part that nobody bound, and prints the
|
|
64
|
+
* `customize` line that binds it.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* import { customize, http, jr2Setup, open, type EffectBinding } from "@jr2/orchestrator";
|
|
69
|
+
*
|
|
70
|
+
* const notifier = jr2Setup({
|
|
71
|
+
* actors: { notify: http({ method: "POST", url: open }) },
|
|
72
|
+
* }).createMachine({
|
|
73
|
+
* id: "notifier",
|
|
74
|
+
* initial: "sending",
|
|
75
|
+
* states: {
|
|
76
|
+
* sending: { invoke: { src: "notify", onDone: "done" } },
|
|
77
|
+
* done: { type: "final" },
|
|
78
|
+
* },
|
|
79
|
+
* });
|
|
80
|
+
*
|
|
81
|
+
* const notify = { url: "https://hooks.example.com/build" } satisfies EffectBinding;
|
|
82
|
+
*
|
|
83
|
+
* export const machine = customize(notifier, { effects: { notify } });
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* @see docs/guides/bind-open-parts.md
|
|
87
|
+
* @see docs/concepts/effects.md
|
|
88
|
+
* @see docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md
|
|
89
|
+
* @see docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md
|
|
90
|
+
*/
|
|
36
91
|
export type EffectBinding = {
|
|
92
|
+
/** The url of an `http` Effect whose url is Open: a string, or a `text` template with a secret
|
|
93
|
+
* in its path, such as `` text`https://hooks.example.com/services/${secret("hook")}` ``. Its
|
|
94
|
+
* scheme and host must be literal. */
|
|
37
95
|
readonly url?: string | Text;
|
|
96
|
+
/** The argv of a `command` whose argv is Open: one entry for each argument, never shell text.
|
|
97
|
+
* An entry is a string or an `arg("<param>")` that the invoke's input fills. It must not be
|
|
98
|
+
* empty. */
|
|
38
99
|
readonly argv?: readonly (string | Arg)[];
|
|
100
|
+
/** The name of the secret that `secret(open)` stands for: an entry of `secrets` in
|
|
101
|
+
* `jr2.config.ts`. The Effector reads it from a file of that name. */
|
|
39
102
|
readonly secret?: string;
|
|
103
|
+
readonly target?: { readonly url: string; readonly token: Secret };
|
|
40
104
|
};
|
|
41
105
|
|
|
42
106
|
declare const openPart: unique symbol;
|
|
@@ -46,10 +110,16 @@ declare const openPart: unique symbol;
|
|
|
46
110
|
* `effects` offers exactly the Open slots, each with the binding its parts take — the intersection
|
|
47
111
|
* of each part's, for an Effect with two. Phantom, like `JR2Wrapper`: the property exists in the
|
|
48
112
|
* type alone.
|
|
113
|
+
*
|
|
114
|
+
* @internal
|
|
49
115
|
*/
|
|
50
116
|
export type OpenEffect<P extends OpenEffectPart> = { readonly [openPart]: P };
|
|
51
117
|
|
|
52
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* The phantom for these Open parts, or nothing for none: what a constructor's type adds.
|
|
120
|
+
*
|
|
121
|
+
* @internal
|
|
122
|
+
*/
|
|
53
123
|
export type OpenOf<P extends OpenEffectPart> = [P] extends [never] ? unknown : OpenEffect<P>;
|
|
54
124
|
|
|
55
125
|
/** The binder an Open Effect carries, under a `Symbol.for` key so a second copy of this module (an
|
|
@@ -58,11 +128,13 @@ const BIND: unique symbol = Symbol.for("jr2.effect.bind");
|
|
|
58
128
|
|
|
59
129
|
type Binder = (binding: EffectBinding) => EffectLogic<unknown, unknown>;
|
|
60
130
|
|
|
61
|
-
/** Which parts of these parts are Open, in binding order: the url or the
|
|
131
|
+
/** Which parts of these parts are Open, in binding order: the url, the argv or the target, then the
|
|
132
|
+
* secret. A dispatch's target is one part: its token is never `secret(open)` (ADR-0071). */
|
|
62
133
|
export function openPartsOf(parts: EffectParts): OpenEffectPart[] {
|
|
63
134
|
const out: OpenEffectPart[] = [];
|
|
64
135
|
if (parts.kind === "http" && parts.url === open) out.push("url");
|
|
65
136
|
if (parts.kind === "command" && parts.argv === open) out.push("argv");
|
|
137
|
+
if (parts.kind === "dispatch" && parts.target === open) out.push("target");
|
|
66
138
|
if (parts.kind !== "push" && parts.openSecret) out.push("secret");
|
|
67
139
|
return out;
|
|
68
140
|
}
|
|
@@ -78,6 +150,7 @@ const BINDING_SAMPLE: Record<OpenEffectPart, string> = {
|
|
|
78
150
|
url: 'url: "https://<host>/<path>"',
|
|
79
151
|
argv: 'argv: ["<program>", "<arg>"]',
|
|
80
152
|
secret: 'secret: "<name>"',
|
|
153
|
+
target: 'target: { url: "https://<host>", token: secret("<name>") }',
|
|
81
154
|
};
|
|
82
155
|
|
|
83
156
|
/** One binding object for these Open parts — `{ url: "…", secret: "<name>" }` for two. */
|