@jr2/orchestrator 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -2
- package/console/components/app.ts +2 -0
- package/console/components/fleet.ts +14 -1
- package/console/components/machine-pane.ts +41 -2
- package/console/main.ts +13 -1
- package/console/store.ts +102 -6
- package/console/style.css +21 -0
- package/docs/agents-block.md +5 -0
- package/docs/concepts/agents-and-turns.md +478 -0
- package/docs/concepts/architecture.md +243 -0
- package/docs/concepts/charts.md +146 -0
- package/docs/concepts/composition.md +357 -0
- package/docs/concepts/converge.md +257 -0
- package/docs/concepts/durable-runs.md +189 -0
- package/docs/concepts/effects.md +658 -0
- package/docs/concepts/gates.md +208 -0
- package/docs/concepts/machines-and-workflows.md +298 -0
- package/docs/concepts/pools-and-sources.md +230 -0
- package/docs/concepts/secrets-and-trust.md +306 -0
- package/docs/concepts/the-menu.md +381 -0
- package/docs/concepts/workspaces.md +756 -0
- package/docs/get-started/a-model-decides.md +563 -0
- package/docs/get-started/an-agent-in-a-sandbox.md +383 -0
- package/docs/get-started/connect-a-model.md +248 -0
- package/docs/get-started/create-an-instance.md +194 -0
- package/docs/get-started/have-your-assistant-write-a-machine.md +993 -0
- package/docs/get-started/install.md +130 -0
- package/docs/guides/add-tools-to-the-sandbox.md +308 -0
- package/docs/guides/answer-a-gate.md +304 -0
- package/docs/guides/bind-open-parts.md +224 -0
- package/docs/guides/cancel-a-run.md +146 -0
- package/docs/guides/clean-up.md +206 -0
- package/docs/guides/cut-a-worktree.md +560 -0
- package/docs/guides/fix-a-failed-converge.md +361 -0
- package/docs/guides/give-the-cluster-git-credentials.md +222 -0
- package/docs/guides/iterate-on-a-machine-on-kind.md +190 -0
- package/docs/guides/let-an-agent-write-a-chart.md +230 -0
- package/docs/guides/look-inside-a-sandbox.md +177 -0
- package/docs/guides/mirror-the-kit-images.md +176 -0
- package/docs/guides/move-from-kind-to-a-shared-cluster.md +318 -0
- package/docs/guides/open-the-console.md +91 -0
- package/docs/guides/publish-a-machine-as-an-npm-package.md +350 -0
- package/docs/guides/push-and-sign-a-branch.md +532 -0
- package/docs/guides/run-a-chart-by-name.md +270 -0
- package/docs/guides/run-a-command-or-call-a-url.md +587 -0
- package/docs/guides/run-one-worker-per-item.md +322 -0
- package/docs/guides/set-an-agents-access-and-dials.md +420 -0
- package/docs/guides/size-a-workspace.md +217 -0
- package/docs/guides/start-a-run-from-a-run.md +237 -0
- package/docs/guides/start-a-run.md +207 -0
- package/docs/guides/upgrade-the-kit-version.md +240 -0
- package/docs/guides/use-a-hosted-provider.md +214 -0
- package/docs/guides/use-a-local-model.md +313 -0
- package/docs/guides/watch-a-run.md +349 -0
- package/docs/guides/write-a-workflow.md +318 -0
- package/docs/index.md +44 -0
- package/docs/llms.txt +237 -0
- package/docs/reference/authoring-surface/agent.md +151 -0
- package/docs/reference/authoring-surface/answer.md +156 -0
- package/docs/reference/authoring-surface/arg.md +82 -0
- package/docs/reference/authoring-surface/basic.md +62 -0
- package/docs/reference/authoring-surface/bearer.md +53 -0
- package/docs/reference/authoring-surface/branchname.md +15 -0
- package/docs/reference/authoring-surface/chartschema.md +23 -0
- package/docs/reference/authoring-surface/chartschemafor.md +48 -0
- package/docs/reference/authoring-surface/command.md +206 -0
- package/docs/reference/authoring-surface/customize.md +64 -0
- package/docs/reference/authoring-surface/defineconfig.md +41 -0
- package/docs/reference/authoring-surface/defineevent.md +114 -0
- package/docs/reference/authoring-surface/dispatch.md +126 -0
- package/docs/reference/authoring-surface/fail.md +96 -0
- package/docs/reference/authoring-surface/file.md +142 -0
- package/docs/reference/authoring-surface/frompick.md +277 -0
- package/docs/reference/authoring-surface/http.md +259 -0
- package/docs/reference/authoring-surface/isfailure.md +58 -0
- package/docs/reference/authoring-surface/isready.md +14 -0
- package/docs/reference/authoring-surface/jr2setup.md +237 -0
- package/docs/reference/authoring-surface/open.md +105 -0
- package/docs/reference/authoring-surface/openbody.md +86 -0
- package/docs/reference/authoring-surface/paramsof.md +143 -0
- package/docs/reference/authoring-surface/pool.md +186 -0
- package/docs/reference/authoring-surface/push.md +130 -0
- package/docs/reference/authoring-surface/readyof.md +21 -0
- package/docs/reference/authoring-surface/secret.md +139 -0
- package/docs/reference/authoring-surface/sentby.md +83 -0
- package/docs/reference/authoring-surface/source.md +51 -0
- package/docs/reference/authoring-surface/text.md +62 -0
- package/docs/reference/authoring-surface/type-agentdeclaration.md +27 -0
- package/docs/reference/authoring-surface/type-agentdefinition.md +80 -0
- package/docs/reference/authoring-surface/type-agentlogic.md +15 -0
- package/docs/reference/authoring-surface/type-agentslots.md +15 -0
- package/docs/reference/authoring-surface/type-answeraction.md +12 -0
- package/docs/reference/authoring-surface/type-anyframepath.md +14 -0
- package/docs/reference/authoring-surface/type-anyworktreepath.md +15 -0
- package/docs/reference/authoring-surface/type-arg.md +19 -0
- package/docs/reference/authoring-surface/type-argnames.md +12 -0
- package/docs/reference/authoring-surface/type-bindactors.md +14 -0
- package/docs/reference/authoring-surface/type-binding.md +61 -0
- package/docs/reference/authoring-surface/type-bindingof.md +12 -0
- package/docs/reference/authoring-surface/type-bodyacceptsdoor.md +15 -0
- package/docs/reference/authoring-surface/type-bodyacceptsslots.md +17 -0
- package/docs/reference/authoring-surface/type-ceilings.md +41 -0
- package/docs/reference/authoring-surface/type-chainlink.md +19 -0
- package/docs/reference/authoring-surface/type-chartaction.md +46 -0
- package/docs/reference/authoring-surface/type-chartagent.md +28 -0
- package/docs/reference/authoring-surface/type-chartevent.md +20 -0
- package/docs/reference/authoring-surface/type-chartguard.md +20 -0
- package/docs/reference/authoring-surface/type-chartinvoke.md +28 -0
- package/docs/reference/authoring-surface/type-chartjson.md +14 -0
- package/docs/reference/authoring-surface/type-chartstate.md +46 -0
- package/docs/reference/authoring-surface/type-charttext.md +40 -0
- package/docs/reference/authoring-surface/type-charttransition.md +22 -0
- package/docs/reference/authoring-surface/type-charttransitions.md +14 -0
- package/docs/reference/authoring-surface/type-chartvalue.md +15 -0
- package/docs/reference/authoring-surface/type-childat.md +12 -0
- package/docs/reference/authoring-surface/type-childslots.md +12 -0
- package/docs/reference/authoring-surface/type-commandenv.md +15 -0
- package/docs/reference/authoring-surface/type-commandinput.md +20 -0
- package/docs/reference/authoring-surface/type-commandoptions.md +56 -0
- package/docs/reference/authoring-surface/type-commandoutput.md +122 -0
- package/docs/reference/authoring-surface/type-customize.md +111 -0
- package/docs/reference/authoring-surface/type-dispatchdefinition.md +39 -0
- package/docs/reference/authoring-surface/type-dispatchinput.md +14 -0
- package/docs/reference/authoring-surface/type-dispatchopenparts.md +14 -0
- package/docs/reference/authoring-surface/type-dispatchoutput.md +72 -0
- package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
- package/docs/reference/authoring-surface/type-duration.md +15 -0
- package/docs/reference/authoring-surface/type-effectbinding.md +93 -0
- package/docs/reference/authoring-surface/type-effectlogic.md +12 -0
- package/docs/reference/authoring-surface/type-envopenparts.md +14 -0
- package/docs/reference/authoring-surface/type-eventaudience.md +27 -0
- package/docs/reference/authoring-surface/type-eventdef.md +45 -0
- package/docs/reference/authoring-surface/type-eventfrom.md +16 -0
- package/docs/reference/authoring-surface/type-eventsemantics.md +26 -0
- package/docs/reference/authoring-surface/type-execoutcome.md +34 -0
- package/docs/reference/authoring-surface/type-failslot.md +14 -0
- package/docs/reference/authoring-surface/type-failure.md +19 -0
- package/docs/reference/authoring-surface/type-failureinput.md +20 -0
- package/docs/reference/authoring-surface/type-filebody.md +37 -0
- package/docs/reference/authoring-surface/type-framepath.md +14 -0
- package/docs/reference/authoring-surface/type-frominput.md +14 -0
- package/docs/reference/authoring-surface/type-hostinjectedinput.md +56 -0
- package/docs/reference/authoring-surface/type-httpdefinition.md +104 -0
- package/docs/reference/authoring-surface/type-httpfileinput.md +20 -0
- package/docs/reference/authoring-surface/type-httpinput.md +12 -0
- package/docs/reference/authoring-surface/type-httpmethod.md +14 -0
- package/docs/reference/authoring-surface/type-httpopenparts.md +12 -0
- package/docs/reference/authoring-surface/type-httpoutput.md +95 -0
- package/docs/reference/authoring-surface/type-httprequestparts.md +33 -0
- package/docs/reference/authoring-surface/type-jr2config.md +126 -0
- package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
- package/docs/reference/authoring-surface/type-lends.md +38 -0
- package/docs/reference/authoring-surface/type-logicat.md +12 -0
- package/docs/reference/authoring-surface/type-markedif.md +12 -0
- package/docs/reference/authoring-surface/type-mergedactors.md +12 -0
- package/docs/reference/authoring-surface/type-namedsecret.md +25 -0
- package/docs/reference/authoring-surface/type-namestart.md +77 -0
- package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +27 -0
- package/docs/reference/authoring-surface/type-notworktreekeys.md +12 -0
- package/docs/reference/authoring-surface/type-openbodydeclaration.md +40 -0
- package/docs/reference/authoring-surface/type-openbodylogic.md +14 -0
- package/docs/reference/authoring-surface/type-openbodyoutput.md +42 -0
- package/docs/reference/authoring-surface/type-openbodyslot.md +18 -0
- package/docs/reference/authoring-surface/type-opencommandinput.md +22 -0
- package/docs/reference/authoring-surface/type-openeffect.md +12 -0
- package/docs/reference/authoring-surface/type-openeffectslots.md +12 -0
- package/docs/reference/authoring-surface/type-openof.md +12 -0
- package/docs/reference/authoring-surface/type-opensecret.md +23 -0
- package/docs/reference/authoring-surface/type-opensecretmark.md +12 -0
- package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +26 -0
- package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +27 -0
- package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
- package/docs/reference/authoring-surface/type-poolnextinput.md +22 -0
- package/docs/reference/authoring-surface/type-poolspec.md +60 -0
- package/docs/reference/authoring-surface/type-pushinput.md +34 -0
- package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
- package/docs/reference/authoring-surface/type-pushoutput.md +149 -0
- package/docs/reference/authoring-surface/type-pushwireoutput.md +12 -0
- package/docs/reference/authoring-surface/type-quantity.md +14 -0
- package/docs/reference/authoring-surface/type-reached.md +12 -0
- package/docs/reference/authoring-surface/type-reposlot.md +70 -0
- package/docs/reference/authoring-surface/type-reposlotsof.md +16 -0
- package/docs/reference/authoring-surface/type-sandboxoptions.md +84 -0
- package/docs/reference/authoring-surface/type-secret.md +25 -0
- package/docs/reference/authoring-surface/type-sender.md +19 -0
- package/docs/reference/authoring-surface/type-sentbyguards.md +12 -0
- package/docs/reference/authoring-surface/type-size.md +85 -0
- package/docs/reference/authoring-surface/type-slothandles.md +24 -0
- package/docs/reference/authoring-surface/type-sourcenextresult.md +17 -0
- package/docs/reference/authoring-surface/type-sourcespec.md +44 -0
- package/docs/reference/authoring-surface/type-syncstate.md +12 -0
- package/docs/reference/authoring-surface/type-text.md +19 -0
- package/docs/reference/authoring-surface/type-textpart.md +24 -0
- package/docs/reference/authoring-surface/type-thinkinglevel.md +39 -0
- package/docs/reference/authoring-surface/type-toparameterizedobject.md +14 -0
- package/docs/reference/authoring-surface/type-toprovidedactor.md +14 -0
- package/docs/reference/authoring-surface/type-unknown.md +21 -0
- package/docs/reference/authoring-surface/type-usercontainer.md +77 -0
- package/docs/reference/authoring-surface/type-withoutemptyindex.md +20 -0
- package/docs/reference/authoring-surface/type-workflowevent.md +46 -0
- package/docs/reference/authoring-surface/type-workspaceaccess.md +16 -0
- package/docs/reference/authoring-surface/type-workspaced.md +115 -0
- package/docs/reference/authoring-surface/type-workspacehandles.md +22 -0
- package/docs/reference/authoring-surface/type-workspacemachine.md +29 -0
- package/docs/reference/authoring-surface/type-workspaceof.md +13 -0
- package/docs/reference/authoring-surface/type-workspaceoptions.md +27 -0
- package/docs/reference/authoring-surface/type-worktreefound.md +16 -0
- package/docs/reference/authoring-surface/type-worktreeinput.md +56 -0
- package/docs/reference/authoring-surface/type-worktreekey.md +14 -0
- package/docs/reference/authoring-surface/type-worktreeoptions.md +29 -0
- package/docs/reference/authoring-surface/type-worktreeoutput.md +45 -0
- package/docs/reference/authoring-surface/type-worktreepath.md +16 -0
- package/docs/reference/authoring-surface/type-worktreeready.md +37 -0
- package/docs/reference/authoring-surface/type-worktreerefused.md +21 -0
- package/docs/reference/authoring-surface/workspace.md +287 -0
- package/docs/reference/authoring-surface/worktree.md +71 -0
- package/docs/reference/authoring-surface.md +217 -0
- package/docs/reference/cli.md +433 -0
- package/docs/reference/config.md +765 -0
- package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +114 -0
- package/docs/reference/decisions/0002-duplex-streaming-actor.md +117 -0
- package/docs/reference/decisions/0004-worktree-provisioning-model.md +130 -0
- package/docs/reference/decisions/0005-sandbox-pod-composition.md +240 -0
- package/docs/reference/decisions/0006-agent-control-surface.md +50 -0
- package/docs/reference/decisions/0007-durable-machine-state.md +109 -0
- package/docs/reference/decisions/0008-orchestrator-as-deployed-app.md +60 -0
- package/docs/reference/decisions/0009-cli-and-instance-interface.md +275 -0
- package/docs/reference/decisions/0010-bdd-acceptance-tests.md +160 -0
- package/docs/reference/decisions/0011-workflow-defined-events.md +141 -0
- package/docs/reference/decisions/0012-workspace-wrapper-machine.md +103 -0
- package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +175 -0
- package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +95 -0
- package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +271 -0
- package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +126 -0
- package/docs/reference/decisions/0017-pool-over-a-source-port.md +71 -0
- package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +111 -0
- package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +287 -0
- package/docs/reference/decisions/0020-private-ca-trust-is-a-named-concept.md +60 -0
- package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +101 -0
- package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +103 -0
- package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +175 -0
- package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +436 -0
- package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +65 -0
- package/docs/reference/decisions/0026-a-turn-that-is-over-is-never-prompted.md +62 -0
- package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +245 -0
- package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +90 -0
- package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +154 -0
- package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +100 -0
- package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +193 -0
- package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +61 -0
- package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +126 -0
- package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +57 -0
- package/docs/reference/decisions/0035-a-runaway-turn-is-ended-rerolled-once-then-a-fault.md +92 -0
- package/docs/reference/decisions/0036-a-turn-compacts-its-own-context-mid-flight.md +138 -0
- package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +196 -0
- package/docs/reference/decisions/0038-jr2-up-builds-every-image-it-deploys.md +170 -0
- package/docs/reference/decisions/0039-image-garbage-collects-by-reachability.md +115 -0
- package/docs/reference/decisions/0041-a-build-the-host-already-holds-is-not-spent-again.md +64 -0
- package/docs/reference/decisions/0042-ready-is-not-routable-an-admission-retries-its-connection.md +142 -0
- package/docs/reference/decisions/0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md +170 -0
- package/docs/reference/decisions/0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md +85 -0
- package/docs/reference/decisions/0045-the-platform-joins-the-image-address-and-the-cluster-chooses-it.md +82 -0
- package/docs/reference/decisions/0046-a-converge-failure-carries-its-diagnosis.md +34 -0
- package/docs/reference/decisions/0047-the-git-ssh-key-source-is-the-users-choice.md +47 -0
- package/docs/reference/decisions/0048-the-orchestrator-boots-without-its-repos.md +36 -0
- package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +126 -0
- package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +73 -0
- package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +238 -0
- package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +86 -0
- package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +123 -0
- package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +126 -0
- package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +107 -0
- package/docs/reference/decisions/0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md +62 -0
- package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +152 -0
- package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +108 -0
- package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +427 -0
- package/docs/reference/decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md +111 -0
- package/docs/reference/decisions/0061-a-memory-kill-ends-the-agents-processes-not-the-harness.md +83 -0
- package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +239 -0
- package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +82 -0
- package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +79 -0
- package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +65 -0
- package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +552 -0
- package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +458 -0
- package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +475 -0
- package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +180 -0
- package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +334 -0
- package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +183 -0
- package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +144 -0
- package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +122 -0
- package/docs/reference/decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md +76 -0
- package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +295 -0
- package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +320 -0
- package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
- package/docs/reference/decisions.md +86 -0
- package/docs/reference/glossary.md +687 -0
- package/docs/reference/http-api.md +1386 -0
- package/docs/reference/machines.md +189 -0
- package/docs/reference/mechanism-events.md +72 -0
- package/docs/why.md +207 -0
- package/package.json +5 -3
- package/src/actor.ts +694 -106
- package/src/agent.ts +114 -38
- package/src/ambient.ts +3 -4
- package/src/assembly-checks.ts +189 -7
- package/src/cause.ts +234 -0
- package/src/chart.ts +345 -46
- package/src/charts.ts +43 -8
- package/src/config.ts +440 -178
- package/src/custodian.lua +17 -5
- package/src/custodian.ts +325 -99
- package/src/customize.ts +199 -49
- package/src/dispatch-effect.ts +945 -0
- package/src/door-compat.ts +217 -0
- package/src/durability.ts +11 -4
- package/src/effect-checks.ts +50 -3
- package/src/effect.ts +641 -128
- package/src/effector-wire.ts +165 -13
- package/src/fail.ts +222 -0
- package/src/harness-client.ts +587 -50
- package/src/harness-env.ts +33 -0
- package/src/held-secrets.ts +125 -16
- package/src/holds.ts +3 -0
- package/src/http-effect.ts +556 -58
- package/src/http.ts +480 -30
- package/src/index.ts +74 -82
- package/src/instance.ts +68 -8
- package/src/internal.ts +85 -0
- package/src/kube-client.ts +6 -3
- package/src/machine-doc.ts +24 -7
- package/src/mechanism-routes.ts +168 -0
- package/src/model-keys.generated.ts +53 -0
- package/src/model-keys.ts +42 -58
- package/src/names.ts +19 -6
- package/src/notices.ts +483 -41
- package/src/open-body.ts +242 -40
- package/src/open-effect.ts +81 -8
- package/src/open.ts +90 -6
- package/src/parts.ts +213 -30
- package/src/party.ts +101 -2
- package/src/pick.ts +497 -11
- package/src/pool.ts +282 -50
- package/src/registration.ts +133 -12
- package/src/repo-identity.ts +7 -3
- package/src/repos.ts +22 -3
- package/src/run-host.ts +665 -45
- package/src/run-output.ts +38 -0
- package/src/sandbox-effect.ts +457 -71
- package/src/sandbox-kube.ts +66 -17
- package/src/secret-placement.ts +59 -17
- package/src/server.ts +3 -0
- package/src/setup.ts +325 -62
- package/src/size.ts +121 -9
- package/src/snapshot-store.ts +54 -10
- package/src/stub-harness.ts +44 -16
- package/src/timers.ts +14 -1
- package/src/tokens.ts +107 -32
- package/src/vocabulary.ts +88 -14
- package/src/wire.ts +100 -26
- package/src/workspace.ts +771 -238
- package/src/worktree.ts +249 -0
package/src/http.ts
CHANGED
|
@@ -5,17 +5,14 @@
|
|
|
5
5
|
// TWO dialects, one primitive (ADR-0013). The Orchestrator does not speak MCP — nothing does: the
|
|
6
6
|
// Harness presents the Menu to its model itself, and reaches these routes through the Custodian in
|
|
7
7
|
// its pod, which holds the Sandbox token. What is left here are two thin adapters over the same
|
|
8
|
-
// registration table:
|
|
8
|
+
// registration table: the Gate resource of ADR-0011, for a human, a webhook or CI
|
|
9
|
+
// (`GET /runs/:id`, `POST /runs/:id/gates/:gate/events`), and the Agent's surface, for the Agent's
|
|
10
|
+
// Harness through its Custodian and nothing else (`/agents/:iid/*`). `/sandboxes/:name/fetch` also
|
|
11
|
+
// serves the Harness's pod, but it is not an adapter over the registration table.
|
|
9
12
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// # the Agent's Harness, through its Custodian, and nothing else
|
|
15
|
-
// GET /agents/:iid/surface accepts + schemas + semantics [Sandbox token]
|
|
16
|
-
// POST /agents/:iid/events validate + deliver → the turn receipt [Sandbox token]
|
|
17
|
-
// GET /agents/:iid/deliveries/:id long-poll an outstanding pick's answer [Sandbox token]
|
|
18
|
-
// POST /sandboxes/:name/fetch ask the node cache to fetch one Repo [Sandbox token]
|
|
13
|
+
// Every route, with its band, is a row of `HTTP_ROUTES` below. That table is data: the HTTP API
|
|
14
|
+
// Reference is generated from it (ADR-0072), and http-routes.test.ts holds it to the routes
|
|
15
|
+
// `createApp` registers, both ways.
|
|
19
16
|
//
|
|
20
17
|
// # named Charts (ADR-0070): a Chart under a name is a Workflow `<wrapper>/<name>`
|
|
21
18
|
// GET /workflows/:wrapper/chart-schema the grammar, narrowed to the Open body [open]
|
|
@@ -53,16 +50,397 @@ import { Hono } from "hono";
|
|
|
53
50
|
import type { Context, MiddlewareHandler } from "hono";
|
|
54
51
|
import { accepts } from "hono/accepts";
|
|
55
52
|
import { streamSSE } from "hono/streaming";
|
|
56
|
-
import {
|
|
53
|
+
import { z } from "zod";
|
|
54
|
+
import {
|
|
55
|
+
DISPATCHED_BY_HEADER,
|
|
56
|
+
EventValidationError,
|
|
57
|
+
NotTakenError,
|
|
58
|
+
OutstandingError,
|
|
59
|
+
UnknownAddressError,
|
|
60
|
+
type DispatchedBy,
|
|
61
|
+
} from "./registration.ts";
|
|
57
62
|
import type { RepoStatus } from "./repos.ts";
|
|
58
63
|
import { UnmountedRepoError, type FetchAnswer } from "./repo-fetch.ts";
|
|
59
|
-
import {
|
|
60
|
-
|
|
64
|
+
import {
|
|
65
|
+
isAgentPrincipal,
|
|
66
|
+
mayAskForSandbox,
|
|
67
|
+
mayDeliverToAgent,
|
|
68
|
+
SLOT_HEADER,
|
|
69
|
+
type Authenticator,
|
|
70
|
+
type Principal,
|
|
71
|
+
} from "./tokens.ts";
|
|
72
|
+
import {
|
|
73
|
+
DispatchKeyConflictError,
|
|
74
|
+
keyAnswer,
|
|
75
|
+
keyConflict,
|
|
76
|
+
observe,
|
|
77
|
+
UnknownWorkflowError,
|
|
78
|
+
type RunFeedEvent,
|
|
79
|
+
type RunHost,
|
|
80
|
+
type RunStatus,
|
|
81
|
+
type WorkflowFeedEvent,
|
|
82
|
+
} from "./run-host.ts";
|
|
61
83
|
import { KIT_VERSION } from "./config.ts";
|
|
62
84
|
import type { AssemblyDoor } from "./assembly.ts";
|
|
63
85
|
import { ChartRefusal } from "./chart.ts";
|
|
64
86
|
import { chartName, NamedChartError, splitChartName, type NamedCharts } from "./charts.ts";
|
|
65
87
|
|
|
88
|
+
// ---- The route table (ADR-0072) -------------------------------------------------------------
|
|
89
|
+
// The routes `createApp` registers, as data. It is the source of the HTTP API Reference page, so a
|
|
90
|
+
// row says what a CALLER needs: the address, the credential, the body, the answer. Why a route is
|
|
91
|
+
// in its band stays in the comment above its handler. http-routes.test.ts compares the rows with
|
|
92
|
+
// the app's own registrations both ways and checks each row's band on the wire, so the table
|
|
93
|
+
// cannot drift from the code by forgetting.
|
|
94
|
+
|
|
95
|
+
/** The access bands of ADR-0014, named for the credential each requires, and the publish door's
|
|
96
|
+
* band (ADR-0069): the publish token alone. */
|
|
97
|
+
export type HttpBand = "open" | "authenticated" | "instanceOnly" | "publishOnly";
|
|
98
|
+
|
|
99
|
+
/** One route of the Orchestrator's HTTP API (ADR-0072). */
|
|
100
|
+
export type HttpRoute = {
|
|
101
|
+
method: "GET" | "POST" | "PUT" | "DELETE";
|
|
102
|
+
/** In Hono syntax, exactly as registered: `:param`, and `:param{regex}` for a constrained one. */
|
|
103
|
+
path: string;
|
|
104
|
+
band: HttpBand;
|
|
105
|
+
/** One line: what the route does. */
|
|
106
|
+
summary: string;
|
|
107
|
+
/** The request body, as a type name or in short; absent when the route reads none. */
|
|
108
|
+
body?: string;
|
|
109
|
+
/** The success answer, in short. Errors are `{ error }` with the status the handler says. */
|
|
110
|
+
response: string;
|
|
111
|
+
/** `true` when the answer is a Server-Sent Events stream. */
|
|
112
|
+
stream: boolean;
|
|
113
|
+
/** The decisions that shape the route, as bare ADR numbers: `"0014"`. */
|
|
114
|
+
adrs: readonly string[];
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/** Every route of the Orchestrator's HTTP API, in the order `createApp` registers them. */
|
|
118
|
+
export const HTTP_ROUTES: readonly HttpRoute[] = [
|
|
119
|
+
{
|
|
120
|
+
method: "GET",
|
|
121
|
+
path: "/healthz",
|
|
122
|
+
band: "open",
|
|
123
|
+
summary: "Liveness, and what this instance is: its Kit version and image content hash.",
|
|
124
|
+
response: "{ ok: true, version, hash? }",
|
|
125
|
+
stream: false,
|
|
126
|
+
adrs: ["0009", "0019"],
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
method: "GET",
|
|
130
|
+
path: "/readyz",
|
|
131
|
+
band: "open",
|
|
132
|
+
summary: "Readiness.",
|
|
133
|
+
response: "{ ready: true }",
|
|
134
|
+
stream: false,
|
|
135
|
+
adrs: ["0009"],
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
method: "GET",
|
|
139
|
+
path: "/workflows",
|
|
140
|
+
band: "open",
|
|
141
|
+
summary:
|
|
142
|
+
"The Workflows a run can start, each with its origin, and each Chart wrapper's named Charts in its entry. A named Chart that cannot run now says why.",
|
|
143
|
+
response: "WorkflowListing[]",
|
|
144
|
+
stream: false,
|
|
145
|
+
adrs: ["0014", "0070"],
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
method: "GET",
|
|
149
|
+
path: "/assembly",
|
|
150
|
+
band: "open",
|
|
151
|
+
summary: "The current Assembly and the Assemblies that live runs hold.",
|
|
152
|
+
response: "AssemblyView",
|
|
153
|
+
stream: false,
|
|
154
|
+
adrs: ["0069"],
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
method: "PUT",
|
|
158
|
+
path: "/assembly",
|
|
159
|
+
band: "publishOnly",
|
|
160
|
+
summary:
|
|
161
|
+
"Publish the next Assembly. `jr2 up` calls it, and nothing else. A refused Assembly leaves the current one current.",
|
|
162
|
+
body: "the Assembly as a tar stream, with the headers `jr2-kit-version` and `jr2-assembly-hash`",
|
|
163
|
+
response:
|
|
164
|
+
"200 with the Assembly, its Workflows, the held Assemblies and the ones collected; 400, 409 or 422 with the reason",
|
|
165
|
+
stream: false,
|
|
166
|
+
adrs: ["0069"],
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
method: "GET",
|
|
170
|
+
path: "/workflows/:wrapper/chart-schema",
|
|
171
|
+
band: "open",
|
|
172
|
+
summary: "The Chart grammar, narrowed to the Open body of one Chart wrapper.",
|
|
173
|
+
response: "JSON Schema",
|
|
174
|
+
stream: false,
|
|
175
|
+
adrs: ["0070"],
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
method: "PUT",
|
|
179
|
+
path: "/workflows/:wrapper/charts/:name",
|
|
180
|
+
band: "instanceOnly",
|
|
181
|
+
summary: "Admit and keep a runtime Chart under a name. Its Workflow is `<wrapper>/<name>`.",
|
|
182
|
+
body: "the Chart text alone",
|
|
183
|
+
response: "201 { name, origin, hash } when new, 200 when it replaced one; 422 { error, report } when refused",
|
|
184
|
+
stream: false,
|
|
185
|
+
adrs: ["0070"],
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
method: "DELETE",
|
|
189
|
+
path: "/workflows/:wrapper/charts/:name",
|
|
190
|
+
band: "instanceOnly",
|
|
191
|
+
summary: "Remove a runtime Chart. A live run of it keeps its own text.",
|
|
192
|
+
response: "204",
|
|
193
|
+
stream: false,
|
|
194
|
+
adrs: ["0070"],
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
method: "POST",
|
|
198
|
+
path: "/workflows/:wrapper/charts/:name/runs",
|
|
199
|
+
band: "instanceOnly",
|
|
200
|
+
summary: "Start a run of a named Chart, as `POST /workflows/<wrapper>%2F<name>/runs` does.",
|
|
201
|
+
body: "the Chart wrapper's input, without the Chart",
|
|
202
|
+
response: "201 { runId, instanceId }; 409 when the named Chart cannot run now",
|
|
203
|
+
stream: false,
|
|
204
|
+
adrs: ["0070"],
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
method: "GET",
|
|
208
|
+
path: "/workflows/:name/runs/:runId/machine",
|
|
209
|
+
band: "open",
|
|
210
|
+
summary: "The Machine that a run binds, with each bound Chart compiled in, which the Console draws.",
|
|
211
|
+
response: "MachineDoc",
|
|
212
|
+
stream: false,
|
|
213
|
+
adrs: ["0070"],
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
method: "GET",
|
|
217
|
+
path: "/",
|
|
218
|
+
band: "open",
|
|
219
|
+
summary: "The Console page to a browser; a 404 to a JSON caller.",
|
|
220
|
+
response: "text/html (the Console shell)",
|
|
221
|
+
stream: false,
|
|
222
|
+
adrs: ["0032"],
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
method: "GET",
|
|
226
|
+
path: "/workflows/:name",
|
|
227
|
+
band: "open",
|
|
228
|
+
summary: "The Console page to a browser; to a JSON caller, the workflow and its declared input schema.",
|
|
229
|
+
response: "{ name, machineId, input: JSON Schema | null }, or text/html",
|
|
230
|
+
stream: false,
|
|
231
|
+
adrs: ["0032", "0033"],
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
method: "POST",
|
|
235
|
+
path: "/workflows/:name/runs",
|
|
236
|
+
band: "instanceOnly",
|
|
237
|
+
summary:
|
|
238
|
+
"Start a run of a registered workflow. A `dispatch` from another Orchestrator sends the header `jr2-dispatched-by`, and a repeated start with its key answers the run that it started.",
|
|
239
|
+
body: "the workflow's input, checked against its declared input schema",
|
|
240
|
+
response:
|
|
241
|
+
"201 { runId, instanceId }; 200 { runId, instanceId, existing: true } for a repeated dispatch; 422 { error, report } when the door refuses a Chart in the input",
|
|
242
|
+
stream: false,
|
|
243
|
+
adrs: ["0033", "0058", "0070", "0071"],
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
method: "GET",
|
|
247
|
+
path: "/workflows/:name/machine",
|
|
248
|
+
band: "open",
|
|
249
|
+
summary: "The registered template Machine's structure, which the Console draws.",
|
|
250
|
+
response: "MachineDoc",
|
|
251
|
+
stream: false,
|
|
252
|
+
adrs: ["0014"],
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
method: "GET",
|
|
256
|
+
path: "/workflows/:name/runs",
|
|
257
|
+
band: "open",
|
|
258
|
+
summary: "The live runs of one workflow, as observations: identity and state value, no context.",
|
|
259
|
+
response: "RunObservation[]",
|
|
260
|
+
stream: false,
|
|
261
|
+
adrs: ["0014"],
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
method: "GET",
|
|
265
|
+
path: "/workflows/:name/events",
|
|
266
|
+
band: "open",
|
|
267
|
+
summary: "The activity of every run of one workflow, as observations, on one connection.",
|
|
268
|
+
response: "SSE: runs, status, emit (type only), retry, gone",
|
|
269
|
+
stream: true,
|
|
270
|
+
adrs: ["0014", "0022"],
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
method: "GET",
|
|
274
|
+
path: "/workflows/:name/runs/:runId/events",
|
|
275
|
+
band: "open",
|
|
276
|
+
summary: "One live run's activity, as observations, until it settles.",
|
|
277
|
+
response: "SSE: status, emit (type only), retry",
|
|
278
|
+
stream: true,
|
|
279
|
+
adrs: ["0014", "0022"],
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
method: "GET",
|
|
283
|
+
path: "/assets/style.css",
|
|
284
|
+
band: "open",
|
|
285
|
+
summary: "The Console's stylesheet.",
|
|
286
|
+
response: "text/css",
|
|
287
|
+
stream: false,
|
|
288
|
+
adrs: ["0032"],
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
method: "GET",
|
|
292
|
+
path: "/assets/elk.js",
|
|
293
|
+
band: "open",
|
|
294
|
+
summary: "The Console's graph layout library (the elkjs bundle).",
|
|
295
|
+
response: "text/javascript",
|
|
296
|
+
stream: false,
|
|
297
|
+
adrs: ["0032"],
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
method: "GET",
|
|
301
|
+
path: "/assets/:file{[\\w.-]+\\.ts}",
|
|
302
|
+
band: "open",
|
|
303
|
+
summary: "A Console source module, with its types erased.",
|
|
304
|
+
response: "text/javascript",
|
|
305
|
+
stream: false,
|
|
306
|
+
adrs: ["0034"],
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
method: "GET",
|
|
310
|
+
path: "/assets/components/:file{[\\w.-]+\\.ts}",
|
|
311
|
+
band: "open",
|
|
312
|
+
summary: "A Console view module, with its types erased.",
|
|
313
|
+
response: "text/javascript",
|
|
314
|
+
stream: false,
|
|
315
|
+
adrs: ["0034"],
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
method: "GET",
|
|
319
|
+
path: "/assets/vendor/:file",
|
|
320
|
+
band: "open",
|
|
321
|
+
summary: "Preact's browser ESM, which the Console's import map names.",
|
|
322
|
+
response: "text/javascript",
|
|
323
|
+
stream: false,
|
|
324
|
+
adrs: ["0034"],
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
method: "GET",
|
|
328
|
+
path: "/runs",
|
|
329
|
+
band: "instanceOnly",
|
|
330
|
+
summary: "The live runs, with their full status.",
|
|
331
|
+
response: "RunStatus[]",
|
|
332
|
+
stream: false,
|
|
333
|
+
adrs: ["0009", "0014"],
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
method: "GET",
|
|
337
|
+
path: "/repos",
|
|
338
|
+
band: "instanceOnly",
|
|
339
|
+
summary: "The Repos as the cluster reports them.",
|
|
340
|
+
response: "{ dataPlane, repos: RepoStatus[] }",
|
|
341
|
+
stream: false,
|
|
342
|
+
adrs: ["0048", "0051"],
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
method: "GET",
|
|
346
|
+
path: "/runs/resolve",
|
|
347
|
+
band: "instanceOnly",
|
|
348
|
+
summary: "The run ids that start with a prefix (`?prefix=`, at least 4 characters).",
|
|
349
|
+
response: "{ prefix, runIds: string[], truncated }",
|
|
350
|
+
stream: false,
|
|
351
|
+
adrs: ["0009"],
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
method: "GET",
|
|
355
|
+
path: "/runs/:runId",
|
|
356
|
+
band: "instanceOnly",
|
|
357
|
+
summary: "One run's status, live or settled, with its open Gates.",
|
|
358
|
+
response: "RunStatus & { gates: GateView[] }",
|
|
359
|
+
stream: false,
|
|
360
|
+
adrs: ["0009", "0011"],
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
// `authenticated` in the code, and the handler refuses a Sandbox token itself (ADR-0013): the
|
|
364
|
+
// credential the route requires is the Instance token's, so this is its band.
|
|
365
|
+
method: "POST",
|
|
366
|
+
path: "/runs/:runId/gates/:gate/events",
|
|
367
|
+
band: "instanceOnly",
|
|
368
|
+
summary: "Deliver an event to an open Gate of a run.",
|
|
369
|
+
body: "{ type, ...payload }, checked against the Gate's schema for that event",
|
|
370
|
+
response: "{ ok: true }; 409 { error, acceptsNow } when the run does not take it now",
|
|
371
|
+
stream: false,
|
|
372
|
+
adrs: ["0011", "0013"],
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
method: "GET",
|
|
376
|
+
path: "/runs/:runId/events",
|
|
377
|
+
band: "instanceOnly",
|
|
378
|
+
summary: "One run's full activity until it settles; a settled run's final status, once.",
|
|
379
|
+
response: "SSE: status, emit, retry, admission, pick, placing, placed, child-error, effect, gone",
|
|
380
|
+
stream: true,
|
|
381
|
+
adrs: ["0022", "0023", "0064", "0067"],
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
method: "POST",
|
|
385
|
+
path: "/runs/:runId/events",
|
|
386
|
+
band: "instanceOnly",
|
|
387
|
+
summary: "Cancel a run: it ends, and its Workspaces go unless `keep` is true.",
|
|
388
|
+
body: '{ type: "CANCEL", keep?: boolean }',
|
|
389
|
+
response: "{ ok: true }",
|
|
390
|
+
stream: false,
|
|
391
|
+
adrs: ["0002", "0025"],
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
method: "GET",
|
|
395
|
+
path: "/agents/:instanceId/surface",
|
|
396
|
+
band: "authenticated",
|
|
397
|
+
summary:
|
|
398
|
+
"An Agent's Menu, this Turn's Allowed picks and the conversation's pending notices. A control token reads only its own pod's, for the slot it states.",
|
|
399
|
+
response: "AgentSurfaceView & { notices: Array<{ id, notice }> }",
|
|
400
|
+
stream: false,
|
|
401
|
+
adrs: ["0013", "0029"],
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
method: "POST",
|
|
405
|
+
path: "/agents/:instanceId/events",
|
|
406
|
+
band: "authenticated",
|
|
407
|
+
summary: "Deliver an Agent's pick into the state that invoked it.",
|
|
408
|
+
body: "{ type, ...payload }, checked against the pick's input schema",
|
|
409
|
+
response: 'AgentDeliveryReceipt; 409 { refused: "outstanding", outstanding?, allowed }',
|
|
410
|
+
stream: false,
|
|
411
|
+
adrs: ["0013", "0024", "0029"],
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
method: "GET",
|
|
415
|
+
path: "/agents/:instanceId/deliveries/:deliveryId",
|
|
416
|
+
band: "authenticated",
|
|
417
|
+
summary: "Long-poll the answer to an outstanding pick; 503 means ask again.",
|
|
418
|
+
response: "AgentDeliveryReceipt",
|
|
419
|
+
stream: false,
|
|
420
|
+
adrs: ["0013", "0024"],
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
method: "POST",
|
|
424
|
+
path: "/agents/:instanceId/notices",
|
|
425
|
+
band: "authenticated",
|
|
426
|
+
summary: "Acknowledge the notices a step boundary appended to the Turn: they are heard in its conversation.",
|
|
427
|
+
body: "{ heard: string[] }",
|
|
428
|
+
response: "{ heard: number }",
|
|
429
|
+
stream: false,
|
|
430
|
+
adrs: ["0062", "0076"],
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
method: "POST",
|
|
434
|
+
path: "/sandboxes/:name/fetch",
|
|
435
|
+
band: "authenticated",
|
|
436
|
+
summary: "Ask the node cache to fetch one Repo that this Sandbox mounts. A fetch token asks only for its own.",
|
|
437
|
+
body: "{ identity }",
|
|
438
|
+
response: "FetchAnswer: { fetched } | { stale, asOf }",
|
|
439
|
+
stream: false,
|
|
440
|
+
adrs: ["0053"],
|
|
441
|
+
},
|
|
442
|
+
];
|
|
443
|
+
|
|
66
444
|
/** A `POST /runs/:id/events` body: the down-channel event. CANCEL is all that is left of it
|
|
67
445
|
* (ADR-0013): APPROVE and STEER rode the deferred/poll machinery: `deferred`
|
|
68
446
|
* is retired (ADR-0006), `poll` reserved but not built.
|
|
@@ -89,6 +467,40 @@ async function readJson(text: Promise<string>): Promise<Record<string, unknown>>
|
|
|
89
467
|
}
|
|
90
468
|
}
|
|
91
469
|
|
|
470
|
+
/**
|
|
471
|
+
* The `jr2-dispatched-by` header a foreign `dispatch` sends (ADR-0071). `instance` is required: a
|
|
472
|
+
* start that comes over the door with a key comes from another Orchestrator by construction (an
|
|
473
|
+
* in-process dispatch never reaches the door). Unknown fields are dropped, as a door drops them: a
|
|
474
|
+
* caller at a later Kit version may send more.
|
|
475
|
+
*/
|
|
476
|
+
const DispatchedByHeader = z.object({
|
|
477
|
+
run: z.string().min(1),
|
|
478
|
+
slot: z.string().min(1),
|
|
479
|
+
effect: z.string().min(1),
|
|
480
|
+
depth: z.number().int().positive(),
|
|
481
|
+
instance: z.string().min(1),
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
/** The header's {@link DispatchedBy}, absent when no header was sent, or why it is refused. */
|
|
485
|
+
function dispatchedByOf(raw: string | undefined): { by?: DispatchedBy } | { error: string } {
|
|
486
|
+
if (raw === undefined) return {};
|
|
487
|
+
let json: unknown;
|
|
488
|
+
try {
|
|
489
|
+
json = JSON.parse(raw);
|
|
490
|
+
} catch (err) {
|
|
491
|
+
return { error: `the ${DISPATCHED_BY_HEADER} header is not JSON: ${errMessage(err)} (ADR-0071)` };
|
|
492
|
+
}
|
|
493
|
+
const parsed = DispatchedByHeader.safeParse(json);
|
|
494
|
+
if (!parsed.success) {
|
|
495
|
+
return {
|
|
496
|
+
error:
|
|
497
|
+
`the ${DISPATCHED_BY_HEADER} header must be { run, slot, effect, depth, instance }: ` +
|
|
498
|
+
`${parsed.error.issues.map((i) => `${i.path.join(".") || "(header)"}: ${i.message}`).join("; ")} (ADR-0071)`,
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
return { by: parsed.data };
|
|
502
|
+
}
|
|
503
|
+
|
|
92
504
|
/** The message of a thrown error, for the `{ error }` 404 body. */
|
|
93
505
|
function errMessage(err: unknown): string {
|
|
94
506
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -434,7 +846,8 @@ export function createApp(host: RunHost, auth?: Authenticator, opts: CreateAppOp
|
|
|
434
846
|
* hand every Agent in the cluster a delivery API, which is the hole this ADR exists to close. */
|
|
435
847
|
const authenticated: MiddlewareHandler<JR2Env> = async (c, next) => {
|
|
436
848
|
if (!auth) return next(); // no authenticator configured: tests only (see the doc comment)
|
|
437
|
-
|
|
849
|
+
// The slot a control request states (ADR-0076): a control token without one resolves to nothing.
|
|
850
|
+
const principal = auth(bearerOf(c), c.req.header(SLOT_HEADER));
|
|
438
851
|
if (!principal) return c.json({ error: "unauthorized" }, 401);
|
|
439
852
|
if (principal.kind === "publisher") return c.json({ error: PUBLISHER_ONLY }, 403);
|
|
440
853
|
c.set("principal", principal);
|
|
@@ -453,7 +866,7 @@ export function createApp(host: RunHost, auth?: Authenticator, opts: CreateAppOp
|
|
|
453
866
|
*/
|
|
454
867
|
const instanceOnly: MiddlewareHandler<JR2Env> = async (c, next) => {
|
|
455
868
|
if (!auth) return next(); // no authenticator configured: tests only (see the doc comment)
|
|
456
|
-
const principal = auth(bearerOf(c));
|
|
869
|
+
const principal = auth(bearerOf(c), c.req.header(SLOT_HEADER));
|
|
457
870
|
if (!principal) return c.json({ error: "unauthorized" }, 401);
|
|
458
871
|
if (principal.kind === "publisher") return c.json({ error: PUBLISHER_ONLY }, 403);
|
|
459
872
|
if (principal.kind !== "instance") {
|
|
@@ -581,18 +994,35 @@ export function createApp(host: RunHost, auth?: Authenticator, opts: CreateAppOp
|
|
|
581
994
|
* Start a run of a Workflow (ADR-0009). A named Chart that cannot run now is 409 with why
|
|
582
995
|
* (ADR-0070); a Chart in the input that the door refuses is 422 with the report; input failing
|
|
583
996
|
* the door schema is 400 (ADR-0033); an unknown Workflow is 404.
|
|
997
|
+
*
|
|
998
|
+
* A foreign `dispatch` sends `jr2-dispatched-by` (ADR-0071): its key makes a repeated start one
|
|
999
|
+
* run, answered `200 { runId, instanceId, existing: true }` where a new one is `201`. The key is
|
|
1000
|
+
* asked first, so a re-send answers its run even when the name would be refused now; a key a
|
|
1001
|
+
* run of another Workflow carries is 409. A header that does not parse is 400. `jr2 run` and the
|
|
1002
|
+
* Console send none. Any throw that is not a refusal is a defect, 500: a foreign caller reads a
|
|
1003
|
+
* 404 as "nothing was started", and a defect may come after a start.
|
|
584
1004
|
*/
|
|
585
1005
|
const startRun = async (c: Context<JR2Env>, name: string): Promise<Response> => {
|
|
1006
|
+
const header = dispatchedByOf(c.req.header(DISPATCHED_BY_HEADER));
|
|
1007
|
+
if ("error" in header) return c.json({ error: header.error }, 400);
|
|
1008
|
+
const { by } = header;
|
|
1009
|
+
if (by) {
|
|
1010
|
+
const found = await host.dispatched(by.effect);
|
|
1011
|
+
if (found && found.workflow !== name) return c.json({ error: keyConflict(found, name, by.effect) }, 409);
|
|
1012
|
+
if (found) return c.json({ ...keyAnswer(found, name, by.effect), existing: true }, 200);
|
|
1013
|
+
}
|
|
586
1014
|
const unrunnable = opts.charts?.get(name)?.unrunnable;
|
|
587
1015
|
if (unrunnable !== undefined) return c.json({ error: `named Chart "${name}" cannot run: ${unrunnable}` }, 409);
|
|
588
1016
|
const input = await readJson(c.req.text());
|
|
589
1017
|
try {
|
|
590
|
-
const { runId, instanceId } = await host.start(name, input);
|
|
591
|
-
return c.json({ runId, instanceId }, 201);
|
|
1018
|
+
const { runId, instanceId, existing } = await host.start(name, input, by ? { dispatchedBy: by } : {});
|
|
1019
|
+
return existing ? c.json({ runId, instanceId, existing }, 200) : c.json({ runId, instanceId }, 201);
|
|
592
1020
|
} catch (err) {
|
|
593
1021
|
if (err instanceof ChartRefusal) return c.json({ error: "refused", report: err.report }, 422);
|
|
594
1022
|
if (err instanceof EventValidationError) return c.json({ error: errMessage(err) }, 400);
|
|
595
|
-
return c.json({ error: errMessage(err) },
|
|
1023
|
+
if (err instanceof DispatchKeyConflictError) return c.json({ error: errMessage(err) }, 409);
|
|
1024
|
+
if (err instanceof UnknownWorkflowError) return c.json({ error: errMessage(err) }, 404);
|
|
1025
|
+
return c.json({ error: errMessage(err) }, 500);
|
|
596
1026
|
}
|
|
597
1027
|
};
|
|
598
1028
|
|
|
@@ -660,7 +1090,7 @@ export function createApp(host: RunHost, auth?: Authenticator, opts: CreateAppOp
|
|
|
660
1090
|
});
|
|
661
1091
|
});
|
|
662
1092
|
|
|
663
|
-
// Push work: start a run of a registered workflow. Unknown workflow →
|
|
1093
|
+
// Push work: start a run of a registered workflow. Unknown workflow → UnknownWorkflowError → 404.
|
|
664
1094
|
// A body failing the machine's declared input schema (ADR-0033) → 400 naming the accepted
|
|
665
1095
|
// shape — the same error class, and the same wire mapping, as a gate delivery failing its
|
|
666
1096
|
// schema (`POST /runs/:id/gates/:gate/events` below). Instance-band (ADR-0058): starting work is
|
|
@@ -877,11 +1307,11 @@ export function createApp(host: RunHost, auth?: Authenticator, opts: CreateAppOp
|
|
|
877
1307
|
// the gated state. Unknown gate (never opened, state exited, run settled) → 404; a name the gate
|
|
878
1308
|
// doesn't accept, or a payload failing its schema → 400 naming what IS accepted.
|
|
879
1309
|
//
|
|
880
|
-
// A Gate is a HUMAN's decision (or a webhook's, or CI's).
|
|
881
|
-
// refused here unconditionally
|
|
882
|
-
// and "the Agent approves its own PR" (ADR-0013).
|
|
1310
|
+
// A Gate is a HUMAN's decision (or a webhook's, or CI's). Every Agent principal — a control
|
|
1311
|
+
// token or a fetch token — is refused here unconditionally: this is the exact line between "the
|
|
1312
|
+
// Agent reports an outcome" and "the Agent approves its own PR" (ADR-0013, ADR-0076).
|
|
883
1313
|
app.post("/runs/:runId/gates/:gate/events", authenticated, async (c) => {
|
|
884
|
-
if (principalOf(c)
|
|
1314
|
+
if (isAgentPrincipal(principalOf(c))) {
|
|
885
1315
|
return c.json({ error: "a Sandbox token cannot deliver to a gate — gates are not on the Agent's surface" }, 403);
|
|
886
1316
|
}
|
|
887
1317
|
const body = await readJson(c.req.text());
|
|
@@ -1008,7 +1438,9 @@ export function createApp(host: RunHost, auth?: Authenticator, opts: CreateAppOp
|
|
|
1008
1438
|
// and delivery are implemented once, in `registration.ts`.
|
|
1009
1439
|
|
|
1010
1440
|
/** Guard: the surface must exist, and this principal must be allowed to speak for it. */
|
|
1011
|
-
const agentRegistration = (
|
|
1441
|
+
const agentRegistration = (
|
|
1442
|
+
c: Context<JR2Env, "/agents/:instanceId/surface" | "/agents/:instanceId/events" | "/agents/:instanceId/notices">,
|
|
1443
|
+
) => {
|
|
1012
1444
|
const instanceId = c.req.param("instanceId");
|
|
1013
1445
|
// Only a Turn that is over answers 404 (ADR-0026). While the host is stopping its runs are
|
|
1014
1446
|
// parked for restore, not over, so every `/agents/*` call answers 503: the Harness re-asks a
|
|
@@ -1031,9 +1463,10 @@ export function createApp(host: RunHost, auth?: Authenticator, opts: CreateAppOp
|
|
|
1031
1463
|
// iid) → there is no surface to serve. 404 BEFORE the scope check — a caller with a valid
|
|
1032
1464
|
// token learns nothing from it that it did not already know.
|
|
1033
1465
|
if (!surface) return { error: c.json({ error: `no live agent surface for instance "${instanceId}"` }, 404) };
|
|
1034
|
-
if (!mayDeliverToAgent(principalOf(c), surface
|
|
1035
|
-
// A
|
|
1036
|
-
//
|
|
1466
|
+
if (!mayDeliverToAgent(principalOf(c), surface)) {
|
|
1467
|
+
// A control token for a DIFFERENT pod (or for a workspace-less run, which no pod owns), a
|
|
1468
|
+
// stated slot that is not this registration's (ADR-0076), or a fetch token. The first is the
|
|
1469
|
+
// check that keeps one feature's coder out of another's reviewer.
|
|
1037
1470
|
return { error: c.json({ error: `this token cannot speak for instance "${instanceId}"` }, 403) };
|
|
1038
1471
|
}
|
|
1039
1472
|
return { surface };
|
|
@@ -1043,11 +1476,28 @@ export function createApp(host: RunHost, auth?: Authenticator, opts: CreateAppOp
|
|
|
1043
1476
|
// semantics, fixed for the conversation — and this Turn's Allowed picks (ADR-0029). A transition
|
|
1044
1477
|
// swaps the registration, which swaps the Allowed picks, never the Menu; the Harness reads both
|
|
1045
1478
|
// per Submission and needs no `list_changed`.
|
|
1046
|
-
|
|
1479
|
+
// The same read at a step boundary carries the conversation's pending notices in its audience
|
|
1480
|
+
// (ADR-0062), reserved for this read: the Harness appends them and acknowledges them below.
|
|
1481
|
+
app.get("/agents/:instanceId/surface", authenticated, async (c) => {
|
|
1047
1482
|
const { surface, error } = agentRegistration(c);
|
|
1048
1483
|
if (error) return error;
|
|
1049
1484
|
host.agentSurfaceServed(c.req.param("instanceId"));
|
|
1050
|
-
|
|
1485
|
+
const notices = await host.boundaryNotices(c.req.param("instanceId"));
|
|
1486
|
+
return c.json({ ...surface, notices });
|
|
1487
|
+
});
|
|
1488
|
+
|
|
1489
|
+
// The Harness appended a step boundary's notices to the Turn (ADR-0062, ADR-0076): they are heard
|
|
1490
|
+
// in that conversation, ledgered in one save. An id this conversation did not reserve is ignored,
|
|
1491
|
+
// so the call says nothing a caller did not already know.
|
|
1492
|
+
app.post("/agents/:instanceId/notices", authenticated, async (c) => {
|
|
1493
|
+
const { error } = agentRegistration(c);
|
|
1494
|
+
if (error) return error;
|
|
1495
|
+
const body = await readJson(c.req.text());
|
|
1496
|
+
const heard = (body as { heard?: unknown }).heard;
|
|
1497
|
+
if (!Array.isArray(heard) || heard.some((id) => typeof id !== "string")) {
|
|
1498
|
+
return c.json({ error: "an acknowledgment names the notices heard: { heard: string[] }" }, 400);
|
|
1499
|
+
}
|
|
1500
|
+
return c.json({ heard: host.noticesHeard(c.req.param("instanceId"), heard as string[]) });
|
|
1051
1501
|
});
|
|
1052
1502
|
|
|
1053
1503
|
// The Agent's pick, delivered into the state that invoked it. The receipt describes itself
|
|
@@ -1103,7 +1553,7 @@ export function createApp(host: RunHost, auth?: Authenticator, opts: CreateAppOp
|
|
|
1103
1553
|
return c.json({ error: restorePendingMessage(`delivery "${deliveryId}"`) }, 503);
|
|
1104
1554
|
}
|
|
1105
1555
|
if (!scope) return c.json({ error: `no delivery "${deliveryId}" for instance "${instanceId}"` }, 404);
|
|
1106
|
-
if (!mayDeliverToAgent(principalOf(c), scope
|
|
1556
|
+
if (!mayDeliverToAgent(principalOf(c), scope)) {
|
|
1107
1557
|
return c.json({ error: `this token cannot speak for instance "${instanceId}"` }, 403);
|
|
1108
1558
|
}
|
|
1109
1559
|
const receipt = await host.awaitDelivery(instanceId, deliveryId, opts.deliveryWaitMs, c.req.raw.signal);
|