@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/customize.ts
CHANGED
|
@@ -9,24 +9,31 @@
|
|
|
9
9
|
// `.provide()`, nothing to learn (ADR-0015's rule, restated by ADR-0049 — a `.with()` method and
|
|
10
10
|
// a callable-machine hybrid were both rejected there).
|
|
11
11
|
//
|
|
12
|
-
//
|
|
12
|
+
// Four moves, and each is the smallest one that works:
|
|
13
13
|
//
|
|
14
14
|
// - An AGENT override is xstate's own `provide`: `machine.provide({ actors: { coder:
|
|
15
15
|
// agent({ ...stock, ...override }) } })`. The slot already IS the Agent (ADR-0049), so
|
|
16
16
|
// retuning one is substituting a slot's logic, which is what `provide` is for.
|
|
17
|
+
// - An EFFECT binding is the same substitution (ADR-0054): an Effect whose url or argv the
|
|
18
|
+
// package left `open`, or whose auth, header or env names `secret(open)`, carries a binder,
|
|
19
|
+
// which builds the Effect again with the composer's parts, so the bound slot is checked as a
|
|
20
|
+
// written one is. Once — a bound part is fixed from then on.
|
|
17
21
|
// - A CHILD override is the same call one level down, recursing. Each level is exactly the
|
|
18
22
|
// one-level reach ADR-0015 found `provide` has — held by the composer who owns the child
|
|
19
23
|
// object, never host-side injection into somebody else's Machine.
|
|
20
|
-
// - The IMAGE and the
|
|
24
|
+
// - The IMAGE, the REPOS and the SIZE are the parts `provide` cannot carry: xstate copies implementations
|
|
21
25
|
// and passes the CONFIG through by reference, and every part a Machine carries is keyed on
|
|
22
26
|
// that config (parts.ts, vocabulary.ts). A new image or a bound slot therefore needs a new key
|
|
23
27
|
// — so those fields clone the wrapper's config and rebuild it with the same implementations,
|
|
24
|
-
// re-attaching what the original carried.
|
|
28
|
+
// re-attaching what the original carried. The load warnings of a bound argv are the same
|
|
29
|
+
// kind of part: the Machine the author wrote is rebuilt the same way, carrying the rechecked
|
|
30
|
+
// warnings in place of the Open Machine's.
|
|
25
31
|
//
|
|
26
32
|
// jr2's wrappers are TRANSPARENT: `agents`/`actors` route through `workspace()`'s `body` and
|
|
27
33
|
// `pool()`'s `worker`, so a consumer customizing a Workspace-rooted workflow never writes `body`
|
|
28
|
-
// and never has to know that jr2 wrapped anything. `image`/`user`/`repos` travel the same
|
|
29
|
-
// the other direction — down to the `workspace()` that owns the seats and the
|
|
34
|
+
// and never has to know that jr2 wrapped anything. `image`/`user`/`repos`/`resources` travel the same
|
|
35
|
+
// chain in the other direction — down to the `workspace()` that owns the seats, the slots and the
|
|
36
|
+
// Size.
|
|
30
37
|
//
|
|
31
38
|
// Both halves route on the wrapper's own RECORD of being one, never on a slot's spelling: the
|
|
32
39
|
// runtime reads `wrapperBodyOf` and the types read `JR2Wrapper` (parts.ts), which the wrappers
|
|
@@ -38,7 +45,7 @@
|
|
|
38
45
|
// its slot MAP Open (`repos: open`, ADR-0051) declared exactly this: every slot is the composer's
|
|
39
46
|
// to name, so the map they write is the declaration, not an addition to one.
|
|
40
47
|
|
|
41
|
-
import { StateMachine, type AnyStateMachine, type InputFrom, type ProvidedActor } from "xstate";
|
|
48
|
+
import { StateMachine, type AnyStateMachine, type InputFrom, type ProvidedActor, type UnknownActorLogic } from "xstate";
|
|
42
49
|
import type { AgentLogic } from "./actor.ts";
|
|
43
50
|
import { isAgent, type AgentDefinition } from "./agent.ts";
|
|
44
51
|
import { agent } from "./harness-client.ts";
|
|
@@ -56,7 +63,30 @@ import {
|
|
|
56
63
|
type RepoSlot,
|
|
57
64
|
type SandboxParts,
|
|
58
65
|
} from "./parts.ts";
|
|
59
|
-
import {
|
|
66
|
+
import { assertSize, assertUserSeat, layerSize, type Size, type UserContainer } from "./size.ts";
|
|
67
|
+
import {
|
|
68
|
+
attachInputSchema,
|
|
69
|
+
attachLoadWarnings,
|
|
70
|
+
attachSeats,
|
|
71
|
+
attachVocabulary,
|
|
72
|
+
inputSchemaOf,
|
|
73
|
+
loadWarningsOf,
|
|
74
|
+
seatsOf,
|
|
75
|
+
vocabularyOf,
|
|
76
|
+
} from "./vocabulary.ts";
|
|
77
|
+
import { isEffect, type Arg } from "./effect.ts";
|
|
78
|
+
import { attachLent, copyOpenBody, lentIn, openBodiesOf, openBodyDeclarationOf } from "./open-body.ts";
|
|
79
|
+
import { effectChecks } from "./effect-checks.ts";
|
|
80
|
+
import type { NamedSecret, Text } from "./http-effect.ts";
|
|
81
|
+
import {
|
|
82
|
+
binderOf,
|
|
83
|
+
effectBindingShape,
|
|
84
|
+
openPartsOf,
|
|
85
|
+
openPartsText,
|
|
86
|
+
type EffectBinding,
|
|
87
|
+
type OpenEffect,
|
|
88
|
+
type OpenEffectPart,
|
|
89
|
+
} from "./open-effect.ts";
|
|
60
90
|
|
|
61
91
|
// --- What a Machine declares, read at the TYPE level ---------------------------------------------
|
|
62
92
|
// Everything below reads xstate's own `TActor` parameter — the `{ src, logic, id }` union
|
|
@@ -86,17 +116,61 @@ type SlotsOf<M extends AnyStateMachine> =
|
|
|
86
116
|
? TActor
|
|
87
117
|
: never;
|
|
88
118
|
|
|
89
|
-
/**
|
|
119
|
+
/**
|
|
120
|
+
* The logic a Machine declares under one slot key.
|
|
121
|
+
*
|
|
122
|
+
* @internal
|
|
123
|
+
*/
|
|
90
124
|
type LogicAt<M extends AnyStateMachine, K extends string> = Extract<SlotsOf<M>, { src: K }>["logic"];
|
|
91
125
|
|
|
92
|
-
/**
|
|
126
|
+
/**
|
|
127
|
+
* The Agent slots — told from every other slot by their logic, which no other actor kind has.
|
|
128
|
+
*
|
|
129
|
+
* @internal
|
|
130
|
+
*/
|
|
93
131
|
type AgentSlots<M extends AnyStateMachine> = Extract<SlotsOf<M>, { logic: AgentLogic }>["src"];
|
|
94
132
|
|
|
133
|
+
/** The Effect slots with an Open part — told by the phantom `OpenEffect` their constructor's
|
|
134
|
+
* overload states for an `open` url or argv, or a `secret(open)` (open-effect.ts).
|
|
135
|
+
*
|
|
136
|
+
* @internal
|
|
137
|
+
*/
|
|
138
|
+
type OpenEffectSlots<M extends AnyStateMachine> = Extract<SlotsOf<M>, { logic: OpenEffect<OpenEffectPart> }>["src"];
|
|
139
|
+
|
|
140
|
+
/** What one Open part binds to: a url, an argv, a secret's name, a dispatch's target — another
|
|
141
|
+
* Orchestrator's origin and the secret that holds its Instance token, together (ADR-0071). */
|
|
142
|
+
type PartBinding<P> = P extends "url"
|
|
143
|
+
? { url: string | Text }
|
|
144
|
+
: P extends "argv"
|
|
145
|
+
? { argv: readonly (string | Arg)[] }
|
|
146
|
+
: P extends "secret"
|
|
147
|
+
? { secret: string }
|
|
148
|
+
: P extends "target"
|
|
149
|
+
? { target: { url: string; token: NamedSecret } }
|
|
150
|
+
: never;
|
|
151
|
+
|
|
152
|
+
/** A union of object types as their intersection: one binding object with every key. */
|
|
153
|
+
type Intersected<U> = (U extends unknown ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
|
|
154
|
+
|
|
155
|
+
/** What one Open Effect binds to: its url, its argv or its secret — for an Effect with two Open
|
|
156
|
+
* parts, one object with both keys, the intersection of their bindings (ADR-0054).
|
|
157
|
+
*
|
|
158
|
+
* @internal
|
|
159
|
+
*/
|
|
160
|
+
type BindingOf<L> = L extends OpenEffect<infer P> ? Intersected<PartBinding<P>> : never;
|
|
161
|
+
|
|
95
162
|
/** The child-Machine slots: the slots composition reaches (ADR-0049 — a nested Machine is an
|
|
96
|
-
* ordinary `invoke` of an ordinary slot).
|
|
163
|
+
* ordinary `invoke` of an ordinary slot).
|
|
164
|
+
*
|
|
165
|
+
* @internal
|
|
166
|
+
*/
|
|
97
167
|
type ChildSlots<M extends AnyStateMachine> = Extract<SlotsOf<M>, { logic: AnyStateMachine }>["src"];
|
|
98
168
|
|
|
99
|
-
/**
|
|
169
|
+
/**
|
|
170
|
+
* The child Machine under one slot key, constrained so `Customize` may recurse into it.
|
|
171
|
+
*
|
|
172
|
+
* @internal
|
|
173
|
+
*/
|
|
100
174
|
type ChildAt<M extends AnyStateMachine, K extends string> = Extract<LogicAt<M, K>, AnyStateMachine>;
|
|
101
175
|
|
|
102
176
|
/**
|
|
@@ -110,6 +184,7 @@ type ChildAt<M extends AnyStateMachine, K extends string> = Extract<LogicAt<M, K
|
|
|
110
184
|
// Each step is taken on the wrapper's MARKER (`JR2Wrapper`, parts.ts), never on a slot's spelling —
|
|
111
185
|
// an author is free to name a slot `body` or `worker`, and routing through it because of the name
|
|
112
186
|
// would offer the Agents of a Machine the composer never named.
|
|
187
|
+
/** @internal */
|
|
113
188
|
type Reached<M extends AnyStateMachine> = M extends JR2Wrapper<infer TBody> ? Reached<TBody> : M;
|
|
114
189
|
|
|
115
190
|
/**
|
|
@@ -117,89 +192,216 @@ type Reached<M extends AnyStateMachine> = M extends JR2Wrapper<infer TBody> ? Re
|
|
|
117
192
|
* {@link Reached}: down through the wrappers to the first Machine that composes a Sandbox, which
|
|
118
193
|
* is the one whose `JR2Repos` marker says which slots exist (ADR-0051). `never` when no wrapper on
|
|
119
194
|
* the chain composes one, which is what makes `repos` unavailable there rather than `{}`.
|
|
195
|
+
*
|
|
196
|
+
* @internal
|
|
120
197
|
*/
|
|
121
198
|
type WorkspaceOf<M extends AnyStateMachine> =
|
|
122
199
|
M extends JR2Repos<any> ? M : M extends JR2Wrapper<infer TBody> ? WorkspaceOf<TBody> : never;
|
|
123
200
|
|
|
124
201
|
/** The Repo Slot keys the reached `workspace()` declared. The `never` case is tested by itself,
|
|
125
202
|
* because `never extends JR2Repos<infer TSlots>` is true with nothing to infer from — and an
|
|
126
|
-
* uninferred `TSlots` widens to `string`, which would offer every key exactly where there are none.
|
|
203
|
+
* uninferred `TSlots` widens to `string`, which would offer every key exactly where there are none.
|
|
204
|
+
*
|
|
205
|
+
* @internal
|
|
206
|
+
*/
|
|
127
207
|
type RepoSlotsOf<M extends AnyStateMachine> = [WorkspaceOf<M>] extends [never]
|
|
128
208
|
? never
|
|
129
209
|
: WorkspaceOf<M> extends JR2Repos<infer TSlots>
|
|
130
210
|
? TSlots
|
|
131
211
|
: never;
|
|
132
212
|
|
|
213
|
+
// What may be retuned on one Machine, mirroring the DECLARATION's own shape and recursively
|
|
214
|
+
// partial (ADR-0049): every key optional, every Agent override a partial definition, every child a
|
|
215
|
+
// `Customize` of that child. `image`/`user` are the `workspace()` options (ADR-0037, ADR-0005) and
|
|
216
|
+
// apply to the wrapper the chain reaches; a Machine that composes no Sandbox refuses them at build
|
|
217
|
+
// time, because there is nothing for them to name.
|
|
133
218
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
219
|
+
* The parts of one Machine that `customize()` can retune: its Agents, its Open Effects, the
|
|
220
|
+
* Machines it composes, and the Sandbox of its `workspace()`. The type comes from the Machine
|
|
221
|
+
* itself, so it offers only the parts that the Machine declares. A key for a part the Machine does
|
|
222
|
+
* not declare does not compile. The `agents`, `effects`, `actors` and `repos` keys take no value
|
|
223
|
+
* when the Machine has no such part. `image`, `user` and `resources` compile on every Machine, and
|
|
224
|
+
* `customize()` refuses them when the Machine composes no `workspace()`.
|
|
137
225
|
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
226
|
+
* Every key is optional. Write only what you change.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* ```ts
|
|
230
|
+
* import { customize, type Customize } from "@jr2/orchestrator";
|
|
231
|
+
* import { task } from "@jr2/machines";
|
|
232
|
+
*
|
|
233
|
+
* const parts: Customize<typeof task> = {
|
|
234
|
+
* repos: { app: { url: "https://github.com/you/app.git", ref: "main" } },
|
|
235
|
+
* agents: { coder: { model: "anthropic/claude-haiku-5-5", thinkingLevel: "high" } },
|
|
236
|
+
* };
|
|
237
|
+
*
|
|
238
|
+
* export const machine = customize(task, parts);
|
|
239
|
+
* ```
|
|
240
|
+
*
|
|
241
|
+
* @see docs/guides/bind-open-parts.md
|
|
242
|
+
* @see docs/concepts/composition.md
|
|
243
|
+
* @see docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md
|
|
141
244
|
*/
|
|
142
245
|
export type Customize<M extends AnyStateMachine> = {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
246
|
+
// `{ model }` alone BINDS the model when the author left it Open (ADR-0054), which is the common
|
|
247
|
+
// consumer line for a packaged Machine. The override is a `Partial<AgentDefinition>`, never a
|
|
248
|
+
// declaration: a composer binds, and binding to `open` again is not a thing anyone means
|
|
249
|
+
// (ADR-0054). A Machine carrying no Agent takes `never` rather than `{}`: an empty object type
|
|
250
|
+
// accepts any literal, which would make the one case with nothing to name the one case with
|
|
251
|
+
// nothing checked.
|
|
252
|
+
/** Retune the Agents that this Machine carries, by slot name: `{ coder: { model } }`. Each
|
|
253
|
+
* override goes over the definition that the author wrote, field by field, so `{ model }` alone
|
|
254
|
+
* keeps the author's instructions. When the author left the model Open, this is where you bind
|
|
255
|
+
* it. Allowed fields: the fields of an `agent()` definition. At run time, `customize()` refuses a
|
|
256
|
+
* slot that holds no Agent: `customize(): machine "<id>" carries no Agent slot "<slot>" — its
|
|
257
|
+
* Agents are: <slots>`. */
|
|
150
258
|
agents?: [AgentSlots<Reached<M>>] extends [never]
|
|
151
259
|
? never
|
|
152
260
|
: { [K in AgentSlots<Reached<M>>]?: Partial<AgentDefinition> };
|
|
153
|
-
|
|
261
|
+
// `{ url }` is for an http Effect whatever its body (ADR-0054); `{ target }` binds a dispatch's
|
|
262
|
+
// url and token as one (ADR-0071). Bound once, here — a value, never a mapper over the door, so
|
|
263
|
+
// the host is fixed before any run (ADR-0054). Only an Effect with an Open part is offered: a
|
|
264
|
+
// part its author fixed stays fixed. A Machine with none takes `never`, as `agents` does.
|
|
265
|
+
/** Bind the Open parts of this Machine's Effects, by slot name: `{ url }` for an `http` Effect
|
|
266
|
+
* whose url is Open, `{ argv }` for a `command` whose argv is Open, `{ secret }` for an Effect
|
|
267
|
+
* that names `secret(open)`, `{ target: { url, token: secret("<name>") } }` for a `dispatch`
|
|
268
|
+
* whose target is Open. For an Effect with two Open parts, one object holds both keys, and
|
|
269
|
+
* a binding that leaves one out does not compile. Only Effects with an Open part are offered.
|
|
270
|
+
* See {@link EffectBinding} for the values and the refusals. */
|
|
271
|
+
effects?: [OpenEffectSlots<Reached<M>>] extends [never]
|
|
272
|
+
? never
|
|
273
|
+
: { [K in OpenEffectSlots<Reached<M>>]?: BindingOf<LogicAt<Reached<M>, K>> };
|
|
274
|
+
/** Retune a Machine that this Machine composes, by slot name. The value has the same shape as
|
|
275
|
+
* these parts, one level down. At run time, `customize()` refuses a slot that holds no Machine:
|
|
276
|
+
* `customize(): machine "<id>" composes no Machine under "<slot>"`. */
|
|
154
277
|
actors?: [ChildSlots<Reached<M>>] extends [never]
|
|
155
278
|
? never
|
|
156
279
|
: { [K in ChildSlots<Reached<M>>]?: Customize<ChildAt<Reached<M>, K>> };
|
|
157
|
-
|
|
158
|
-
|
|
280
|
+
// The Sandbox Image (ADR-0037).
|
|
281
|
+
/** The Sandbox Image: a `file:` URL to a docker context that your module ships
|
|
282
|
+
* (`import.meta.resolve("./image")`), which `jr2 up` builds, or a registry ref. It replaces the
|
|
283
|
+
* image of the `workspace()`. When the Machine composes no `workspace()`, `customize()` refuses it
|
|
284
|
+
* at run time. */
|
|
159
285
|
image?: string;
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
|
|
167
|
-
|
|
286
|
+
// The User Container (ADR-0005), in the same two shapes, or `{ image, resources }` with its split
|
|
287
|
+
// of the Size (ADR-0060). Replaces the seat whole — split included.
|
|
288
|
+
/** The User Container: its image, as a `file:` URL or a registry ref, or a
|
|
289
|
+
* {@link UserContainer} that also gives its part of the Size. It replaces the whole User
|
|
290
|
+
* Container, its part of the Size included. When the Machine composes no `workspace()`,
|
|
291
|
+
* `customize()` refuses it at run time. */
|
|
292
|
+
user?: string | UserContainer;
|
|
293
|
+
// The Size (ADR-0060), layered over the `workspace()`'s own: the composer's Repo may be bigger
|
|
294
|
+
// than the author's.
|
|
295
|
+
/** The Size, layered field by field over the Size of the `workspace()`: `{ limits: { memory } }`
|
|
296
|
+
* keeps the author's cpu. Only `limits.memory` and `limits.cpu`. See {@link Size}. Only for a
|
|
297
|
+
* Machine that composes a `workspace()`. */
|
|
298
|
+
resources?: Size;
|
|
299
|
+
// Any of the three slot forms, so a consumer can also bind a mapper over the wrapper's door
|
|
300
|
+
// (ADR-0051). A Machine that composes no Sandbox takes `never`, as `agents` does. A
|
|
301
|
+
// `workspace()` that declared its map Open takes any keys — they are the composer's words, in the
|
|
302
|
+
// order the Machine documents — and at least one, checked at the call.
|
|
303
|
+
/** Bind the Repo Slots of the `workspace()`, by slot name. Each value is one of these:
|
|
304
|
+
*
|
|
305
|
+
* - a url
|
|
306
|
+
* - a Binding `{ url, ref? }`
|
|
307
|
+
* - a function `({ input }) => url | { url, ref? }` over the run input
|
|
308
|
+
* - `open`, to leave the slot for the next composer to bind
|
|
309
|
+
*
|
|
310
|
+
* You can bind an Open slot, or give a bound or per-run slot a different Binding. A slot that
|
|
311
|
+
* the Machine does not declare does not compile. Some Machines, such as `task`, leave their
|
|
312
|
+
* whole slot map Open. Then you name every slot, and at least one. The keys are your names for
|
|
313
|
+
* the Repos, and the Machine says what their order means. */
|
|
168
314
|
repos?: [RepoSlotsOf<M>] extends [never] ? never : { [K in RepoSlotsOf<M>]?: RepoSlot<InputFrom<WorkspaceOf<M>>> };
|
|
169
315
|
};
|
|
170
316
|
|
|
171
317
|
/** The same shape with the types erased — what the implementation walks. */
|
|
172
318
|
type LooseParts = {
|
|
173
319
|
agents?: Record<string, Partial<AgentDefinition> | undefined>;
|
|
320
|
+
effects?: Record<string, EffectBinding | undefined>;
|
|
174
321
|
actors?: Record<string, LooseParts | undefined>;
|
|
175
322
|
image?: string;
|
|
176
|
-
user?: string;
|
|
323
|
+
user?: string | UserContainer;
|
|
177
324
|
repos?: Record<string, RepoSlot | undefined>;
|
|
325
|
+
resources?: Size;
|
|
178
326
|
};
|
|
179
327
|
|
|
328
|
+
// Returns a NEW Machine of the same type — the original is untouched, so two customizations of one
|
|
329
|
+
// import are two independent Machines (ADR-0049's `deep`/`quick`), and a run of either carries what
|
|
330
|
+
// it was given.
|
|
180
331
|
/**
|
|
181
|
-
* Retune the parts a Machine carries,
|
|
182
|
-
*
|
|
183
|
-
*
|
|
332
|
+
* Retune the parts that a Machine carries, without a change to the module that exports it. Use it
|
|
333
|
+
* where you register a Machine as a Workflow. It binds the parts that a packaged Machine leaves
|
|
334
|
+
* Open: its Repo Slots, an Agent's model, or an Effect's url, argv or secret. It also changes the
|
|
335
|
+
* parts that its author fixed: an Agent's instructions, the Sandbox Image, or the Size.
|
|
336
|
+
*
|
|
337
|
+
* It gives back a new Machine of the same type. The original does not change, so two
|
|
338
|
+
* `customize()` calls on one import give two Machines. Only the parts the Machine declares can be
|
|
339
|
+
* retuned. You cannot add a part: a Machine that needs another Agent is a new Machine. The types
|
|
340
|
+
* refuse most mistakes when you compile. At run time, `customize()` also throws:
|
|
341
|
+
*
|
|
342
|
+
* - `customize(): machine "<id>" carries no Agent slot "<slot>" — its Agents are: <slots>`
|
|
343
|
+
* - `customize(): machine "<id>" composes no Machine under "<slot>" — the Machines it composes are: <slots>`
|
|
344
|
+
* - `customize(): machine "<id>" carries no Effect slot "<slot>" — its Effects are: <slots>`
|
|
345
|
+
* - `customize(): machine "<id>" declares no Repo Slot "<slot>" — its slots are: <slots>`
|
|
346
|
+
* - `customize(): machine "<id>" declares its Repo Slots open as a map — name at least one`
|
|
347
|
+
* - `` customize(): `repos` say what a SANDBOX is made of, how big it is and which Repos it
|
|
348
|
+
* attaches, and machine "<id>" composes none `` (for `resources`, the text starts
|
|
349
|
+
* `` customize(): `resources` ``; for `image` or `user`, it starts `` customize(): `image`/`user` ``)
|
|
350
|
+
* - `customize(): machine "<id>": <errors>`, when an Effect argv that you bind fails the checks
|
|
351
|
+
* that the Machine passed when it was built
|
|
352
|
+
* - the refusals of {@link EffectBinding}, {@link RepoSlot}, {@link Size} and
|
|
353
|
+
* {@link UserContainer}, which start with `customize()`
|
|
354
|
+
*
|
|
355
|
+
* @param machine - The Machine to retune: one you import from a package, or one you wrote. When
|
|
356
|
+
* its root is a `workspace()` or a `pool()`, `customize()` reaches through to the Machine inside,
|
|
357
|
+
* so you never name the wrapper's own slot.
|
|
358
|
+
* @param parts - The parts to change. See {@link Customize}.
|
|
359
|
+
* @returns A new Machine of the same type, with the parts changed.
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```ts
|
|
363
|
+
* import { customize } from "@jr2/orchestrator";
|
|
364
|
+
* import { task } from "@jr2/machines";
|
|
365
|
+
*
|
|
366
|
+
* export const machine = customize(task, {
|
|
367
|
+
* repos: { app: { url: "https://github.com/you/app.git" } },
|
|
368
|
+
* agents: { coder: { model: "anthropic/claude-haiku-5-5" } },
|
|
369
|
+
* resources: { limits: { memory: "4Gi" } },
|
|
370
|
+
* });
|
|
371
|
+
* ```
|
|
372
|
+
*
|
|
373
|
+
* @see docs/guides/bind-open-parts.md
|
|
374
|
+
* @see docs/concepts/composition.md
|
|
375
|
+
* @see docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md
|
|
376
|
+
* @see docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md
|
|
184
377
|
*/
|
|
185
378
|
export function customize<M extends AnyStateMachine>(machine: M, parts: Customize<M>): M {
|
|
186
|
-
const { agents, actors, image, user, repos } = parts as LooseParts;
|
|
379
|
+
const { agents, effects, actors, image, user, repos, resources } = parts as LooseParts;
|
|
187
380
|
let out: AnyStateMachine = machine;
|
|
188
381
|
// Order matters in one direction only: the Sandbox seats and slots REBUILD the wrapper, so they
|
|
189
382
|
// go last and carry the retuned implementations with them.
|
|
190
|
-
if (agents || actors) out = retune(out, { agents, actors });
|
|
383
|
+
if (agents || effects || actors) out = retune(out, { agents, effects, actors });
|
|
191
384
|
const seats: Seats = {
|
|
192
385
|
...(image !== undefined ? { image } : {}),
|
|
193
386
|
...(user !== undefined ? { user } : {}),
|
|
194
387
|
...(repos !== undefined ? { repos } : {}),
|
|
388
|
+
...(resources !== undefined ? { resources } : {}),
|
|
195
389
|
};
|
|
196
|
-
|
|
390
|
+
// Refused where the composer wrote them, by field (ADR-0060), before any wrapper is rebuilt.
|
|
391
|
+
if (seats.user !== undefined) assertUserSeat("customize()", seats.user);
|
|
392
|
+
if (seats.resources !== undefined) assertSize("customize(): resources", seats.resources);
|
|
393
|
+
if (Object.keys(seats).length > 0) out = reseat(out, seats);
|
|
197
394
|
return out as M;
|
|
198
395
|
}
|
|
199
396
|
|
|
200
|
-
/** What `reseat` carries down the chain: the two image seats
|
|
201
|
-
* only when the composer named it. */
|
|
202
|
-
type Seats = {
|
|
397
|
+
/** What `reseat` carries down the chain: the two image seats, the slot overrides and the Size, each
|
|
398
|
+
* present only when the composer named it. */
|
|
399
|
+
type Seats = {
|
|
400
|
+
image?: string;
|
|
401
|
+
user?: string | UserContainer;
|
|
402
|
+
repos?: Record<string, RepoSlot | undefined>;
|
|
403
|
+
resources?: Size;
|
|
404
|
+
};
|
|
203
405
|
|
|
204
406
|
/** The slot keys whose logic answers `pred` — what an error names, so the message is the
|
|
205
407
|
* Machine's own declaration rather than advice. */
|
|
@@ -246,6 +448,8 @@ function retune(machine: AnyStateMachine, parts: LooseParts): AnyStateMachine {
|
|
|
246
448
|
// walk reports it again (ADR-0054).
|
|
247
449
|
actors[name] = agent({ ...logic.definition, ...override });
|
|
248
450
|
}
|
|
451
|
+
const bound = Object.entries(parts.effects ?? {}).filter(([, b]) => b !== undefined);
|
|
452
|
+
for (const [name, binding] of bound) actors[name] = bindEffect(machine, name, binding!);
|
|
249
453
|
for (const [name, childParts] of Object.entries(parts.actors ?? {})) {
|
|
250
454
|
if (!childParts) continue;
|
|
251
455
|
const child = asMachine((machine.implementations.actors as Record<string, unknown>)[name]);
|
|
@@ -257,15 +461,128 @@ function retune(machine: AnyStateMachine, parts: LooseParts): AnyStateMachine {
|
|
|
257
461
|
}
|
|
258
462
|
actors[name] = customize(child, childParts as Customize<AnyStateMachine>);
|
|
259
463
|
}
|
|
260
|
-
|
|
464
|
+
// An Open body lends by slot name (ADR-0070), and its stamped lent set must be the retuned slots,
|
|
465
|
+
// not the import's: each body is copied (the import keeps its own stamp) and stamped again over
|
|
466
|
+
// the retuned actors.
|
|
467
|
+
if (Object.keys(actors).length > 0) {
|
|
468
|
+
const impl = machine.implementations as Parameters<typeof lentIn>[0];
|
|
469
|
+
const merged = { ...impl, actors: { ...(impl.actors ?? {}), ...actors } };
|
|
470
|
+
for (const { slot, logic } of openBodiesOf(machine)) {
|
|
471
|
+
const copy = copyOpenBody(logic);
|
|
472
|
+
attachLent(copy, lentIn(merged, openBodyDeclarationOf(logic)));
|
|
473
|
+
actors[slot] = copy;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
const out = substitute(machine, actors);
|
|
477
|
+
if (bound.length === 0) return out;
|
|
478
|
+
// The bound Machine's warnings differ from the Open one's, and `provide` kept the Open one's
|
|
479
|
+
// config, which is what every attachment keys on (ADR-0049) — so it gets a config of its own,
|
|
480
|
+
// carrying what the original carried and the rechecked warnings, and any later `provide()` of
|
|
481
|
+
// it (a run's, ADR-0015) keeps them.
|
|
482
|
+
const warnings = recheck(machine, out);
|
|
483
|
+
const rebuilt = cloneConfig(out);
|
|
484
|
+
attachLoadWarnings(rebuilt, warnings);
|
|
485
|
+
return rebuilt;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Bind one Effect's Open parts (ADR-0054): the slot must be an Effect, each key of the binding one
|
|
490
|
+
* of its Open parts, and each value the value that part takes — a url (a string, or a `text` with a
|
|
491
|
+
* secret in its path), an argv, a secret's name, or a dispatch's target (ADR-0071). A key left out leaves that part Open. The binder
|
|
492
|
+
* builds the Effect again, whole, so the bound one meets every check its constructor runs: the url's
|
|
493
|
+
* literal host, the argv's entries, the secret's name. The compiler's check of the
|
|
494
|
+
* invoke's input against the argv's slots does not reach it (an Open argv's input is one string per
|
|
495
|
+
* key): a slot the invoke does not supply faults when the Effect starts (ADR-0054).
|
|
496
|
+
*/
|
|
497
|
+
function bindEffect(machine: AnyStateMachine, name: string, binding: EffectBinding): unknown {
|
|
498
|
+
const logic = (machine.implementations.actors as Record<string, unknown>)[name];
|
|
499
|
+
if (!isEffect(logic)) {
|
|
500
|
+
throw new Error(
|
|
501
|
+
`customize(): machine "${machine.id}" carries no Effect slot "${name}" — its Effects are: ` +
|
|
502
|
+
`${listed(slotsWhere(machine, isEffect))} (ADR-0067). Only declared parts can be bound.`,
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
const parts = openPartsOf(logic.effect);
|
|
506
|
+
const binder = binderOf(logic);
|
|
507
|
+
if (parts.length === 0 || binder === undefined) {
|
|
508
|
+
throw new Error(
|
|
509
|
+
`customize(): the ${logic.effect.kind} Effect "${name}" of machine "${machine.id}" leaves nothing Open — its ` +
|
|
510
|
+
"parts are fixed in its definition, and only a part a package left `open` is bound, once (ADR-0054).",
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
// One object binds them, each key an Open part; a key it leaves out leaves that part Open, and
|
|
514
|
+
// the walk reports it again (ADR-0054).
|
|
515
|
+
const given = typeof binding === "object" && binding !== null ? (binding as Record<string, unknown>) : {};
|
|
516
|
+
const keys = Object.keys(given).filter((k) => given[k] !== undefined);
|
|
517
|
+
if (keys.length === 0 || keys.some((k) => !parts.includes(k as OpenEffectPart))) {
|
|
518
|
+
throw new Error(
|
|
519
|
+
`customize(): the ${logic.effect.kind} Effect "${name}" leaves its ${openPartsText(parts)} Open — bind it ` +
|
|
520
|
+
`with ${effectBindingShape(parts)} (ADR-0054)`,
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
for (const part of keys) {
|
|
524
|
+
const value = given[part];
|
|
525
|
+
if (value === open || typeof value === "function") {
|
|
526
|
+
throw new Error(
|
|
527
|
+
`customize(): the ${logic.effect.kind} Effect "${name}"'s ${part} is bound once, to a value — not ` +
|
|
528
|
+
`${value === open ? "`open` again" : "a function of the run"}: no run chooses an Effect's ${part} (ADR-0054)`,
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
if (part === "target") {
|
|
532
|
+
// The whole target is the Open part (ADR-0071): an origin and a named secret, bound together.
|
|
533
|
+
// The dispatch's own constructor checks the url and the name when the binder rebuilds it.
|
|
534
|
+
const target = value as { url?: unknown; token?: unknown } | null;
|
|
535
|
+
if (typeof target !== "object" || target === null || typeof target.url !== "string" || !isSecret(target.token)) {
|
|
536
|
+
throw new Error(
|
|
537
|
+
`customize(): the dispatch Effect "${name}"'s target is bound to another Orchestrator's origin and the ` +
|
|
538
|
+
`secret that holds its Instance token: ${effectBindingShape("target")} (ADR-0071)`,
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
if (part === "secret" && typeof value !== "string") {
|
|
543
|
+
throw new Error(
|
|
544
|
+
`customize(): the ${logic.effect.kind} Effect "${name}"'s secret is bound to a secret's name, a string — ` +
|
|
545
|
+
`the file the Effector reads it from: { secret: "<name>" } (ADR-0054, ADR-0068)`,
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
return binder(binding);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const isSecret = (value: unknown): boolean =>
|
|
553
|
+
typeof value === "object" && value !== null && typeof (value as { secret?: unknown }).secret === "string";
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* The load walk's Effect checks (ADR-0067), again, on a Machine whose Open argv this `customize()`
|
|
557
|
+
* bound: at build its argv slots were unknown and every pick param counted as read; now the bound
|
|
558
|
+
* argv names them. A param it does not read is refused as at build, and a warning the walk gives
|
|
559
|
+
* for the bound argv (a param only an action the probe does not run may read) replaces the
|
|
560
|
+
* findings it gave for the Open one: the result is the warnings the same Machine written with that
|
|
561
|
+
* argv carries, which the caller attaches to the bound Machine's own config.
|
|
562
|
+
*/
|
|
563
|
+
function recheck(before: AnyStateMachine, machine: AnyStateMachine): string[] {
|
|
564
|
+
const defs = vocabularyOf(machine);
|
|
565
|
+
if (!defs) return loadWarningsOf(before);
|
|
566
|
+
const checks = (m: AnyStateMachine) =>
|
|
567
|
+
effectChecks(m, m.implementations.actors as Record<string, UnknownActorLogic>, defs);
|
|
568
|
+
const { errors, warnings } = checks(machine);
|
|
569
|
+
if (errors.length) throw new Error(`customize(): machine "${machine.id}": ${errors.join("; ")}`);
|
|
570
|
+
const unbound = new Set(checks(before).warnings);
|
|
571
|
+
const kept = loadWarningsOf(before).filter((warning) => !unbound.has(warning));
|
|
572
|
+
return [...new Set([...kept, ...warnings])];
|
|
261
573
|
}
|
|
262
574
|
|
|
263
575
|
/** The Sandbox seats and slots, applied to the `workspace()` the chain reaches. */
|
|
264
576
|
function reseat(machine: AnyStateMachine, seats: Seats): AnyStateMachine {
|
|
265
577
|
if (composesSandbox(machine)) {
|
|
266
|
-
const { repos: override, ...images } = seats;
|
|
578
|
+
const { repos: override, resources, ...images } = seats;
|
|
267
579
|
const parts = sandboxPartsOf(machine);
|
|
268
|
-
|
|
580
|
+
// The Size layers over the author's, field by field — a composer who raises memory keeps the
|
|
581
|
+
// author's cpu (ADR-0060).
|
|
582
|
+
const size = layerSize(parts.resources, resources);
|
|
583
|
+
const sized = size !== undefined ? { resources: size } : {};
|
|
584
|
+
if (parts.repos === open)
|
|
585
|
+
return rebuild(machine, { ...parts, ...images, ...sized, repos: nameSlots(machine, override) });
|
|
269
586
|
const repos = { ...parts.repos };
|
|
270
587
|
// Only DECLARED slots can be bound (ADR-0051): the Machine's own word for each Repo is the
|
|
271
588
|
// key, and a key it never declared would attach a repository the body has no handle for.
|
|
@@ -285,15 +602,16 @@ function reseat(machine: AnyStateMachine, seats: Seats): AnyStateMachine {
|
|
|
285
602
|
assertRepoSlot("customize()", slot, value);
|
|
286
603
|
repos[slot] = value;
|
|
287
604
|
}
|
|
288
|
-
return rebuild(machine, { ...parts, ...images, repos });
|
|
605
|
+
return rebuild(machine, { ...parts, ...images, ...sized, repos });
|
|
289
606
|
}
|
|
290
607
|
const inner = bodyOf(machine);
|
|
291
608
|
if (!inner) {
|
|
292
|
-
const named =
|
|
609
|
+
const named =
|
|
610
|
+
seats.repos !== undefined ? "`repos`" : seats.resources !== undefined ? "`resources`" : "`image`/`user`";
|
|
293
611
|
throw new Error(
|
|
294
|
-
`customize(): ${named} say what a SANDBOX is made of and which Repos it attaches, and
|
|
295
|
-
`"${machine.id}" composes none — only a workspace() wrapper carries those seats
|
|
296
|
-
"(ADR-0049, ADR-0037, ADR-0051).",
|
|
612
|
+
`customize(): ${named} say what a SANDBOX is made of, how big it is and which Repos it attaches, and ` +
|
|
613
|
+
`machine "${machine.id}" composes none — only a workspace() wrapper carries those seats, that Size ` +
|
|
614
|
+
"and those slots (ADR-0049, ADR-0037, ADR-0051, ADR-0060).",
|
|
297
615
|
);
|
|
298
616
|
}
|
|
299
617
|
return substitute(machine, { [inner.slot]: reseat(inner.body, seats) });
|
|
@@ -336,13 +654,28 @@ function nameSlots(machine: AnyStateMachine, override: Seats["repos"]): SandboxP
|
|
|
336
654
|
* fingerprint a restore compares (ADR-0030) — is bit-identical to the original's.
|
|
337
655
|
*/
|
|
338
656
|
function rebuild(machine: AnyStateMachine, seats: SandboxParts): AnyStateMachine {
|
|
657
|
+
const clone = cloneConfig(machine);
|
|
658
|
+
attachSandboxParts(clone, seats);
|
|
659
|
+
return clone;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* The Machine around a NEW config object — a shallow clone, so the shape and the fingerprint are
|
|
664
|
+
* the original's — with the same implementations, and every attachment the original carried
|
|
665
|
+
* re-attached under the new key (ADR-0049): the vocabulary, the door, the seats, the load
|
|
666
|
+
* warnings and a wrapper's body. What the caller attaches after is the clone's alone.
|
|
667
|
+
*/
|
|
668
|
+
function cloneConfig(machine: AnyStateMachine): AnyStateMachine {
|
|
339
669
|
const clone = new StateMachine({ ...machine.config }, machine.implementations) as AnyStateMachine;
|
|
340
670
|
const vocabulary = vocabularyOf(machine);
|
|
341
671
|
if (vocabulary) attachVocabulary(clone, vocabulary);
|
|
342
672
|
const door = inputSchemaOf(machine);
|
|
343
673
|
if (door) attachInputSchema(clone, door);
|
|
674
|
+
const seats = seatsOf(machine);
|
|
675
|
+
if (seats.length > 0) attachSeats(clone, seats);
|
|
676
|
+
const warnings = loadWarningsOf(machine);
|
|
677
|
+
if (warnings.length > 0) attachLoadWarnings(clone, warnings);
|
|
344
678
|
const slot = wrapperBodyOf(machine);
|
|
345
679
|
if (slot) attachWrapperBody(clone, slot);
|
|
346
|
-
attachSandboxParts(clone, seats);
|
|
347
680
|
return clone;
|
|
348
681
|
}
|