@jr2/orchestrator 0.4.1 → 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/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 +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 +5 -3
- package/src/actor.ts +541 -79
- package/src/agent.ts +114 -38
- package/src/ambient.ts +2 -1
- package/src/assembly-checks.ts +189 -7
- package/src/cause.ts +234 -0
- package/src/chart.ts +169 -37
- package/src/charts.ts +34 -6
- package/src/config.ts +430 -178
- package/src/custodian.ts +74 -22
- package/src/customize.ts +188 -46
- package/src/dispatch-effect.ts +945 -0
- package/src/door-compat.ts +217 -0
- package/src/durability.ts +11 -4
- package/src/effect.ts +341 -44
- package/src/effector-wire.ts +160 -11
- package/src/fail.ts +209 -0
- package/src/harness-client.ts +541 -47
- package/src/http-effect.ts +495 -53
- package/src/http.ts +427 -16
- package/src/index.ts +60 -82
- package/src/instance.ts +68 -8
- package/src/internal.ts +84 -0
- package/src/machine-doc.ts +21 -3
- package/src/mechanism-routes.ts +168 -0
- package/src/model-keys.generated.ts +53 -0
- package/src/model-keys.ts +18 -58
- package/src/notices.ts +66 -13
- package/src/open-body.ts +159 -23
- package/src/open-effect.ts +81 -8
- package/src/open.ts +59 -6
- package/src/parts.ts +184 -13
- package/src/party.ts +68 -2
- package/src/pick.ts +463 -11
- package/src/pool.ts +247 -36
- package/src/registration.ts +83 -4
- package/src/repos.ts +2 -1
- package/src/run-host.ts +565 -31
- package/src/run-output.ts +38 -0
- package/src/sandbox-effect.ts +273 -38
- package/src/sandbox-kube.ts +3 -1
- package/src/secret-placement.ts +52 -17
- package/src/server.ts +3 -0
- package/src/setup.ts +239 -56
- 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/vocabulary.ts +69 -14
- package/src/wire.ts +90 -16
- package/src/workspace.ts +697 -175
package/src/custodian.ts
CHANGED
|
@@ -217,30 +217,72 @@ function meta(fields: Record<string, string>): Json {
|
|
|
217
217
|
return { filter_metadata: { [LUA_FILTER]: fields } };
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
/**
|
|
221
|
-
*
|
|
222
|
-
|
|
220
|
+
/** Envoy's flags for a request it did not send to the target (ADR-0059): the connection failed, no
|
|
221
|
+
* host was healthy, the DNS lookup failed. */
|
|
222
|
+
export const NOT_SENT_FLAGS = ["UF", "UH", "DF"] as const;
|
|
223
|
+
|
|
224
|
+
/** Envoy's flags for a request that may have reached the target before the stream ended (ADR-0059):
|
|
225
|
+
* the target closed the connection or reset the stream, Envoy reset it, a protocol error on either
|
|
226
|
+
* side (a body framing error can come after the headers went). `UMSDR` (the maximum stream
|
|
227
|
+
* duration) ends a stream that was sent. `URX` (the retry limit) comes with the last attempt's own
|
|
228
|
+
* flag (`UF,URX`, `UC,URX`), so alone it says nothing of the send; the mapper for
|
|
229
|
+
* {@link NOT_SENT_FLAGS} comes first, so `UF,URX` still reads "could not be reached". These routes
|
|
230
|
+
* set no retry policy and no `max_stream_duration`, so Envoy sets neither flag on them; they keep
|
|
231
|
+
* the union equal to ORCHESTRATOR_UNREACHABLE's. */
|
|
232
|
+
export const MAY_HAVE_SENT_FLAGS = ["UC", "DPE", "UMSDR", "URX", "UPE", "UR", "LR"] as const;
|
|
233
|
+
|
|
234
|
+
/** One of the Custodian's own 502s for a target that gave no answer (§5.4): the target, what
|
|
235
|
+
* jr2 knows about the request, and Envoy's reason — never a retry without verification. */
|
|
236
|
+
function noAnswer(target: string, flags: readonly string[], words: string): Json {
|
|
223
237
|
return {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
text_format_source: {
|
|
230
|
-
inline_string: `jr2 custodian: ${target} could not be reached: %RESPONSE_CODE_DETAILS% %UPSTREAM_TRANSPORT_FAILURE_REASON%\n`,
|
|
231
|
-
},
|
|
232
|
-
},
|
|
238
|
+
filter: { response_flag_filter: { flags: [...flags] } },
|
|
239
|
+
status_code: 502,
|
|
240
|
+
body_format_override: {
|
|
241
|
+
text_format_source: {
|
|
242
|
+
inline_string: `jr2 custodian: ${target} ${words}: %RESPONSE_CODE_DETAILS% %UPSTREAM_TRANSPORT_FAILURE_REASON%\n`,
|
|
233
243
|
},
|
|
234
|
-
|
|
244
|
+
},
|
|
235
245
|
};
|
|
236
246
|
}
|
|
237
247
|
|
|
248
|
+
const NOT_REACHED = "could not be reached";
|
|
249
|
+
const MAY_HAVE_REACHED = "did not answer; the request may have reached it";
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* An intercepted target that gave no answer (ADR-0059). Two mappers, because the words must be
|
|
253
|
+
* true: after a `NOT_SENT_FLAGS` failure the target did not get the request, and after a
|
|
254
|
+
* `MAY_HAVE_SENT_FLAGS` one it may have it — so an Agent that sends a POST again can write twice,
|
|
255
|
+
* and the text must not tell it the first one went nowhere. One reply can carry several flags,
|
|
256
|
+
* and Envoy uses the first mapper that matches, in order: the `NOT_SENT_FLAGS` mapper is first, so
|
|
257
|
+
* a reply with `UF`, `UH` or `DF` reads "could not be reached" whatever else it carries. Without
|
|
258
|
+
* the mappers, Envoy answers each of these flags itself (503 for most, 502 for `UPE`) with a body
|
|
259
|
+
* that names no target, which a caller can read as the target's answer. No caller reads the flags;
|
|
260
|
+
* the Orchestrator route's go to the Harness, which tells "never reached" from "not known"
|
|
261
|
+
* (ADR-0002).
|
|
262
|
+
*/
|
|
263
|
+
function unreachable(target: string): Json {
|
|
264
|
+
return {
|
|
265
|
+
mappers: [noAnswer(target, NOT_SENT_FLAGS, NOT_REACHED), noAnswer(target, MAY_HAVE_SENT_FLAGS, MAY_HAVE_REACHED)],
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** The tunnel's 502 (ADR-0059): every flag, one text. Envoy answers a `CONNECT` with 200 only after
|
|
270
|
+
* its TCP connection to the target is up, and no byte of the Agent's request goes before that 200.
|
|
271
|
+
* So a 502 on the tunnel always comes before the request reached the target, whatever its flag. */
|
|
272
|
+
function tunnelUnreachable(target: string): Json {
|
|
273
|
+
return { mappers: [noAnswer(target, [...NOT_SENT_FLAGS, ...MAY_HAVE_SENT_FLAGS], NOT_REACHED)] };
|
|
274
|
+
}
|
|
275
|
+
|
|
238
276
|
/** The Orchestrator unreachable, said so the Harness can tell it from an answer and retry the
|
|
239
277
|
* surface read the way ADR-0042 asks — the one route on this pod that retries. */
|
|
240
278
|
const ORCHESTRATOR_UNREACHABLE: Json = {
|
|
241
279
|
mappers: [
|
|
242
280
|
{
|
|
243
|
-
|
|
281
|
+
// Envoy's own answers for a stream it reset (`UPE`, `UR`, `LR`: 503, or 502 for `UPE`) are said
|
|
282
|
+
// here too, so the Harness reads them as "not known" and not as a stopping Orchestrator's 503
|
|
283
|
+
// (ADR-0002). One text for
|
|
284
|
+
// both lists: the Harness reads the flags, not the words.
|
|
285
|
+
filter: { response_flag_filter: { flags: [...NOT_SENT_FLAGS, ...MAY_HAVE_SENT_FLAGS] } },
|
|
244
286
|
status_code: 502,
|
|
245
287
|
headers_to_add: [
|
|
246
288
|
{ header: { key: "x-jr2-custodian", value: "unreachable" }, append_action: "OVERWRITE_IF_EXISTS_OR_ADD" },
|
|
@@ -333,7 +375,7 @@ function egressReplies(): Json {
|
|
|
333
375
|
},
|
|
334
376
|
},
|
|
335
377
|
},
|
|
336
|
-
...(
|
|
378
|
+
...(tunnelUnreachable("%REQ(:AUTHORITY)%").mappers as Json[]),
|
|
337
379
|
],
|
|
338
380
|
};
|
|
339
381
|
}
|
|
@@ -794,6 +836,12 @@ export type CustodianCompositionOptions = {
|
|
|
794
836
|
/** This pod's Sandbox — the name the ask is addressed by (ADR-0053). Absent on the Instance
|
|
795
837
|
* Harness, which mounts no Repo and answers an ask by saying so. */
|
|
796
838
|
sandbox?: string;
|
|
839
|
+
/** The names of the Instance objects that this Custodian reads in place of the defaults. The
|
|
840
|
+
* defaults are the Instance's own: `pod`'s literal values (`heldSecretsSecret(pod)`),
|
|
841
|
+
* `jr2-held-tls`, `jr2-ca`, and the program in `jr2-held`.
|
|
842
|
+
* The check pod (ADR-0019) reads its own copies, so a check pod writes nothing that a running pod
|
|
843
|
+
* of the Instance reads. */
|
|
844
|
+
reads?: { literals: string; tls: string; trust: string; program: string };
|
|
797
845
|
/** The Instance ships a `harness.caBundle` (ADR-0020). */
|
|
798
846
|
caBundle: boolean;
|
|
799
847
|
/** The Custodian container's resources (ADR-0060) — the placement's to choose: a Sandbox's fixed
|
|
@@ -837,6 +885,12 @@ const CUSTODIAN_SECURITY = {
|
|
|
837
885
|
export function custodianComposition(manifest: HeldManifest, opts: CustodianCompositionOptions): CustodianComposition {
|
|
838
886
|
const held = manifest.secrets.length > 0;
|
|
839
887
|
const literal = manifest.secrets.filter((s) => s.source.kind === "literal").map((s) => s.name);
|
|
888
|
+
const {
|
|
889
|
+
literals = heldSecretsSecret(opts.pod),
|
|
890
|
+
tls = HELD_TLS_SECRET,
|
|
891
|
+
trust: trustMap = CA_CONFIGMAP,
|
|
892
|
+
program = HELD_CONFIGMAP,
|
|
893
|
+
} = opts.reads ?? {};
|
|
840
894
|
|
|
841
895
|
const harnessEnv: HarnessEnvVar[] = [
|
|
842
896
|
{ name: "JR2_CUSTODIAN_URL", value: CUSTODIAN_URL },
|
|
@@ -870,9 +924,7 @@ export function custodianComposition(manifest: HeldManifest, opts: CustodianComp
|
|
|
870
924
|
defaultMode: 0o440,
|
|
871
925
|
sources: [
|
|
872
926
|
{ secret: { name: opts.token.secret, items: [{ key: opts.token.key, path: SANDBOX_TOKEN_NAME }] } },
|
|
873
|
-
...(literal.length
|
|
874
|
-
? [{ secret: { name: heldSecretsSecret(opts.pod), items: literal.map((n) => ({ key: n, path: n })) } }]
|
|
875
|
-
: []),
|
|
927
|
+
...(literal.length ? [{ secret: { name: literals, items: literal.map((n) => ({ key: n, path: n })) } }] : []),
|
|
876
928
|
...manifest.secrets.flatMap((s) =>
|
|
877
929
|
s.source.kind === "secret"
|
|
878
930
|
? [{ secret: { name: s.source.secret, items: [{ key: s.source.key, path: s.name }] } }]
|
|
@@ -896,20 +948,20 @@ export function custodianComposition(manifest: HeldManifest, opts: CustodianComp
|
|
|
896
948
|
],
|
|
897
949
|
},
|
|
898
950
|
},
|
|
899
|
-
{ configMap: { name:
|
|
951
|
+
{ configMap: { name: program, items: [{ key: CUSTODIAN_PROGRAM_KEY, path: CUSTODIAN_PROGRAM_KEY }] } },
|
|
900
952
|
],
|
|
901
953
|
},
|
|
902
954
|
},
|
|
903
955
|
...(held
|
|
904
956
|
? [
|
|
905
|
-
{ name: "custodian-tls", secret: { secretName:
|
|
957
|
+
{ name: "custodian-tls", secret: { secretName: tls, defaultMode: 0o440 } },
|
|
906
958
|
{
|
|
907
959
|
name: "custodian-trust",
|
|
908
|
-
configMap: { name:
|
|
960
|
+
configMap: { name: trustMap, items: [{ key: "upstream.crt", path: "upstream.crt" }] },
|
|
909
961
|
},
|
|
910
962
|
]
|
|
911
963
|
: []),
|
|
912
|
-
...(trust ? [{ name: "ca", configMap: { name:
|
|
964
|
+
...(trust ? [{ name: "ca", configMap: { name: trustMap } }] : []),
|
|
913
965
|
];
|
|
914
966
|
|
|
915
967
|
const custodianContainer: Container = {
|
package/src/customize.ts
CHANGED
|
@@ -77,7 +77,7 @@ import {
|
|
|
77
77
|
import { isEffect, type Arg } from "./effect.ts";
|
|
78
78
|
import { attachLent, copyOpenBody, lentIn, openBodiesOf, openBodyDeclarationOf } from "./open-body.ts";
|
|
79
79
|
import { effectChecks } from "./effect-checks.ts";
|
|
80
|
-
import type { Text } from "./http-effect.ts";
|
|
80
|
+
import type { NamedSecret, Text } from "./http-effect.ts";
|
|
81
81
|
import {
|
|
82
82
|
binderOf,
|
|
83
83
|
effectBindingShape,
|
|
@@ -116,37 +116,61 @@ type SlotsOf<M extends AnyStateMachine> =
|
|
|
116
116
|
? TActor
|
|
117
117
|
: never;
|
|
118
118
|
|
|
119
|
-
/**
|
|
119
|
+
/**
|
|
120
|
+
* The logic a Machine declares under one slot key.
|
|
121
|
+
*
|
|
122
|
+
* @internal
|
|
123
|
+
*/
|
|
120
124
|
type LogicAt<M extends AnyStateMachine, K extends string> = Extract<SlotsOf<M>, { src: K }>["logic"];
|
|
121
125
|
|
|
122
|
-
/**
|
|
126
|
+
/**
|
|
127
|
+
* The Agent slots — told from every other slot by their logic, which no other actor kind has.
|
|
128
|
+
*
|
|
129
|
+
* @internal
|
|
130
|
+
*/
|
|
123
131
|
type AgentSlots<M extends AnyStateMachine> = Extract<SlotsOf<M>, { logic: AgentLogic }>["src"];
|
|
124
132
|
|
|
125
133
|
/** The Effect slots with an Open part — told by the phantom `OpenEffect` their constructor's
|
|
126
|
-
* overload states for an `open` url or argv, or a `secret(open)` (open-effect.ts).
|
|
134
|
+
* overload states for an `open` url or argv, or a `secret(open)` (open-effect.ts).
|
|
135
|
+
*
|
|
136
|
+
* @internal
|
|
137
|
+
*/
|
|
127
138
|
type OpenEffectSlots<M extends AnyStateMachine> = Extract<SlotsOf<M>, { logic: OpenEffect<OpenEffectPart> }>["src"];
|
|
128
139
|
|
|
129
|
-
/** What one Open part binds to: a url, an argv, a secret's name
|
|
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). */
|
|
130
142
|
type PartBinding<P> = P extends "url"
|
|
131
143
|
? { url: string | Text }
|
|
132
144
|
: P extends "argv"
|
|
133
145
|
? { argv: readonly (string | Arg)[] }
|
|
134
146
|
: P extends "secret"
|
|
135
147
|
? { secret: string }
|
|
136
|
-
:
|
|
148
|
+
: P extends "target"
|
|
149
|
+
? { target: { url: string; token: NamedSecret } }
|
|
150
|
+
: never;
|
|
137
151
|
|
|
138
152
|
/** A union of object types as their intersection: one binding object with every key. */
|
|
139
153
|
type Intersected<U> = (U extends unknown ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
|
|
140
154
|
|
|
141
155
|
/** What one Open Effect binds to: its url, its argv or its secret — for an Effect with two Open
|
|
142
|
-
* parts, one object with both keys, the intersection of their bindings (ADR-0054).
|
|
156
|
+
* parts, one object with both keys, the intersection of their bindings (ADR-0054).
|
|
157
|
+
*
|
|
158
|
+
* @internal
|
|
159
|
+
*/
|
|
143
160
|
type BindingOf<L> = L extends OpenEffect<infer P> ? Intersected<PartBinding<P>> : never;
|
|
144
161
|
|
|
145
162
|
/** The child-Machine slots: the slots composition reaches (ADR-0049 — a nested Machine is an
|
|
146
|
-
* ordinary `invoke` of an ordinary slot).
|
|
163
|
+
* ordinary `invoke` of an ordinary slot).
|
|
164
|
+
*
|
|
165
|
+
* @internal
|
|
166
|
+
*/
|
|
147
167
|
type ChildSlots<M extends AnyStateMachine> = Extract<SlotsOf<M>, { logic: AnyStateMachine }>["src"];
|
|
148
168
|
|
|
149
|
-
/**
|
|
169
|
+
/**
|
|
170
|
+
* The child Machine under one slot key, constrained so `Customize` may recurse into it.
|
|
171
|
+
*
|
|
172
|
+
* @internal
|
|
173
|
+
*/
|
|
150
174
|
type ChildAt<M extends AnyStateMachine, K extends string> = Extract<LogicAt<M, K>, AnyStateMachine>;
|
|
151
175
|
|
|
152
176
|
/**
|
|
@@ -160,6 +184,7 @@ type ChildAt<M extends AnyStateMachine, K extends string> = Extract<LogicAt<M, K
|
|
|
160
184
|
// Each step is taken on the wrapper's MARKER (`JR2Wrapper`, parts.ts), never on a slot's spelling —
|
|
161
185
|
// an author is free to name a slot `body` or `worker`, and routing through it because of the name
|
|
162
186
|
// would offer the Agents of a Machine the composer never named.
|
|
187
|
+
/** @internal */
|
|
163
188
|
type Reached<M extends AnyStateMachine> = M extends JR2Wrapper<infer TBody> ? Reached<TBody> : M;
|
|
164
189
|
|
|
165
190
|
/**
|
|
@@ -167,67 +192,125 @@ type Reached<M extends AnyStateMachine> = M extends JR2Wrapper<infer TBody> ? Re
|
|
|
167
192
|
* {@link Reached}: down through the wrappers to the first Machine that composes a Sandbox, which
|
|
168
193
|
* is the one whose `JR2Repos` marker says which slots exist (ADR-0051). `never` when no wrapper on
|
|
169
194
|
* the chain composes one, which is what makes `repos` unavailable there rather than `{}`.
|
|
195
|
+
*
|
|
196
|
+
* @internal
|
|
170
197
|
*/
|
|
171
198
|
type WorkspaceOf<M extends AnyStateMachine> =
|
|
172
199
|
M extends JR2Repos<any> ? M : M extends JR2Wrapper<infer TBody> ? WorkspaceOf<TBody> : never;
|
|
173
200
|
|
|
174
201
|
/** The Repo Slot keys the reached `workspace()` declared. The `never` case is tested by itself,
|
|
175
202
|
* because `never extends JR2Repos<infer TSlots>` is true with nothing to infer from — and an
|
|
176
|
-
* 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
|
+
*/
|
|
177
207
|
type RepoSlotsOf<M extends AnyStateMachine> = [WorkspaceOf<M>] extends [never]
|
|
178
208
|
? never
|
|
179
209
|
: WorkspaceOf<M> extends JR2Repos<infer TSlots>
|
|
180
210
|
? TSlots
|
|
181
211
|
: never;
|
|
182
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.
|
|
183
218
|
/**
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
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()`.
|
|
225
|
+
*
|
|
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
|
+
* };
|
|
187
237
|
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
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
|
|
191
244
|
*/
|
|
192
245
|
export type Customize<M extends AnyStateMachine> = {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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>`. */
|
|
200
258
|
agents?: [AgentSlots<Reached<M>>] extends [never]
|
|
201
259
|
? never
|
|
202
260
|
: { [K in AgentSlots<Reached<M>>]?: Partial<AgentDefinition> };
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
*
|
|
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. */
|
|
209
271
|
effects?: [OpenEffectSlots<Reached<M>>] extends [never]
|
|
210
272
|
? never
|
|
211
273
|
: { [K in OpenEffectSlots<Reached<M>>]?: BindingOf<LogicAt<Reached<M>, K>> };
|
|
212
|
-
/** Retune a Machine this
|
|
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>"`. */
|
|
213
277
|
actors?: [ChildSlots<Reached<M>>] extends [never]
|
|
214
278
|
? never
|
|
215
279
|
: { [K in ChildSlots<Reached<M>>]?: Customize<ChildAt<Reached<M>, K>> };
|
|
216
|
-
|
|
217
|
-
|
|
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. */
|
|
218
285
|
image?: string;
|
|
219
|
-
|
|
220
|
-
|
|
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. */
|
|
221
292
|
user?: string | UserContainer;
|
|
222
|
-
|
|
223
|
-
|
|
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()`. */
|
|
224
298
|
resources?: Size;
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
*
|
|
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. */
|
|
231
314
|
repos?: [RepoSlotsOf<M>] extends [never] ? never : { [K in RepoSlotsOf<M>]?: RepoSlot<InputFrom<WorkspaceOf<M>>> };
|
|
232
315
|
};
|
|
233
316
|
|
|
@@ -242,10 +325,55 @@ type LooseParts = {
|
|
|
242
325
|
resources?: Size;
|
|
243
326
|
};
|
|
244
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.
|
|
245
331
|
/**
|
|
246
|
-
* Retune the parts a Machine carries,
|
|
247
|
-
*
|
|
248
|
-
*
|
|
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
|
|
249
377
|
*/
|
|
250
378
|
export function customize<M extends AnyStateMachine>(machine: M, parts: Customize<M>): M {
|
|
251
379
|
const { agents, effects, actors, image, user, repos, resources } = parts as LooseParts;
|
|
@@ -360,7 +488,7 @@ function retune(machine: AnyStateMachine, parts: LooseParts): AnyStateMachine {
|
|
|
360
488
|
/**
|
|
361
489
|
* Bind one Effect's Open parts (ADR-0054): the slot must be an Effect, each key of the binding one
|
|
362
490
|
* of its Open parts, and each value the value that part takes — a url (a string, or a `text` with a
|
|
363
|
-
* secret in its path), an argv, or 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
|
|
364
492
|
* builds the Effect again, whole, so the bound one meets every check its constructor runs: the url's
|
|
365
493
|
* literal host, the argv's entries, the secret's name. The compiler's check of the
|
|
366
494
|
* invoke's input against the argv's slots does not reach it (an Open argv's input is one string per
|
|
@@ -400,6 +528,17 @@ function bindEffect(machine: AnyStateMachine, name: string, binding: EffectBindi
|
|
|
400
528
|
`${value === open ? "`open` again" : "a function of the run"}: no run chooses an Effect's ${part} (ADR-0054)`,
|
|
401
529
|
);
|
|
402
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
|
+
}
|
|
403
542
|
if (part === "secret" && typeof value !== "string") {
|
|
404
543
|
throw new Error(
|
|
405
544
|
`customize(): the ${logic.effect.kind} Effect "${name}"'s secret is bound to a secret's name, a string — ` +
|
|
@@ -410,6 +549,9 @@ function bindEffect(machine: AnyStateMachine, name: string, binding: EffectBindi
|
|
|
410
549
|
return binder(binding);
|
|
411
550
|
}
|
|
412
551
|
|
|
552
|
+
const isSecret = (value: unknown): boolean =>
|
|
553
|
+
typeof value === "object" && value !== null && typeof (value as { secret?: unknown }).secret === "string";
|
|
554
|
+
|
|
413
555
|
/**
|
|
414
556
|
* The load walk's Effect checks (ADR-0067), again, on a Machine whose Open argv this `customize()`
|
|
415
557
|
* bound: at build its argv slots were unknown and every pick param counted as read; now the bound
|