@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
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Composition
|
|
3
|
+
description: How a Machine composes Machines that you import, and how you bind the parts that they leave Open.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Composition
|
|
7
|
+
|
|
8
|
+
Composition is how you make a [Workflow](../reference/glossary.md#workflow) from
|
|
9
|
+
[Machines](../reference/glossary.md#machine) that exist already. It has three parts. A Machine invokes a different
|
|
10
|
+
Machine as its child. [`customize()`](../reference/authoring-surface/customize.md) changes the parts that a Machine
|
|
11
|
+
carries. And you bind the parts that a Machine leaves [Open](../reference/glossary.md#open). The kit ships Machines for
|
|
12
|
+
you to compose in this way, in the package [`@jr2/machines`](../reference/machines.md).
|
|
13
|
+
|
|
14
|
+
## Why it exists
|
|
15
|
+
|
|
16
|
+
A Machine that you import must work in your [Instance](../reference/glossary.md#instance) with no change to its module.
|
|
17
|
+
Thus a Machine carries all that it needs: its [Vocabulary](../reference/glossary.md#vocabulary), its
|
|
18
|
+
[Agents](../reference/glossary.md#agent), its [Effects](../reference/glossary.md#effect), its
|
|
19
|
+
[Sandbox Image](../reference/glossary.md#sandbox-image) and its [Repo Slots](../reference/glossary.md#repo-slot). The
|
|
20
|
+
Machine does not need an Agent list or event names from the Instance. The Instance gives only access and defaults.
|
|
21
|
+
Access is the model provider and its key, the secrets, and the git credentials. A default is a Sandbox Image or a
|
|
22
|
+
[Size](../reference/glossary.md#size) for a Machine that states none.
|
|
23
|
+
|
|
24
|
+
A package cannot know some parts. It does not know your [Repo](../reference/glossary.md#repo), and it cannot pay for
|
|
25
|
+
your model. The package leaves these parts Open. You bind them where you register the Machine as a Workflow, or where
|
|
26
|
+
you compose it. If you do not bind an Open part, [`jr2 up`](../reference/cli.md#up) stops before it converges. Thus a
|
|
27
|
+
missing part does not become a run that spends money on a model that nobody chose.
|
|
28
|
+
|
|
29
|
+
## A Machine inside a Machine
|
|
30
|
+
|
|
31
|
+
A Machine composes a different Machine as an [xstate](https://xstate.js.org) child. It imports the Machine, puts it
|
|
32
|
+
under `actors`, and a state invokes it by its key. The key is a slot, the same as the slot of an Agent.
|
|
33
|
+
|
|
34
|
+
This Machine composes the shipped Machine [`task`](../reference/machines.md#task) two times. A
|
|
35
|
+
[Menu](../reference/glossary.md#menu)-only Agent, `triager`, reads the prompt first. Then the run goes to one of the two
|
|
36
|
+
copies of `task`. The copies differ only in the thinking level of their coder.
|
|
37
|
+
|
|
38
|
+
The Workflow, with the model of the tab:
|
|
39
|
+
|
|
40
|
+
**Anthropic**
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { z } from "zod";
|
|
44
|
+
import { agent, customize, defineEvent, fail, jr2Setup, type HostInjectedInput } from "@jr2/orchestrator";
|
|
45
|
+
import { task } from "@jr2/machines";
|
|
46
|
+
|
|
47
|
+
const model = "anthropic/claude-haiku-5-5";
|
|
48
|
+
|
|
49
|
+
// Bind the two Open parts of `task`: the Repo that the coder works in, and the coder's model.
|
|
50
|
+
const coding = customize(task, {
|
|
51
|
+
repos: { target: { url: "https://github.com/you/app.git" } },
|
|
52
|
+
agents: { coder: { model } },
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Two changed copies of one Machine. `coding` does not change.
|
|
56
|
+
const deep = customize(coding, { agents: { coder: { thinkingLevel: "high" } } });
|
|
57
|
+
const quick = customize(coding, { agents: { coder: { thinkingLevel: "low" } } });
|
|
58
|
+
|
|
59
|
+
const door = z.object({ prompt: z.string().describe("What to do, in prose.") });
|
|
60
|
+
|
|
61
|
+
const route = defineEvent({
|
|
62
|
+
name: "route",
|
|
63
|
+
description: "Say how much care the task needs.",
|
|
64
|
+
audience: "agent",
|
|
65
|
+
input: z.object({
|
|
66
|
+
size: z.enum(["small", "large"]).describe("small: a change to one file. large: any other change."),
|
|
67
|
+
}),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
type Input = HostInjectedInput & z.infer<typeof door>;
|
|
71
|
+
|
|
72
|
+
export const machine = jr2Setup({
|
|
73
|
+
types: {} as { input: Input; context: Input },
|
|
74
|
+
events: [route],
|
|
75
|
+
actors: {
|
|
76
|
+
triager: agent({
|
|
77
|
+
model,
|
|
78
|
+
workspace: "none",
|
|
79
|
+
thinkingLevel: "off",
|
|
80
|
+
instructions: "Read the task. Call route once: small for a change to one file, large for any other change.",
|
|
81
|
+
}),
|
|
82
|
+
// Each composed Machine is a slot, like an Agent.
|
|
83
|
+
deep,
|
|
84
|
+
quick,
|
|
85
|
+
fail: fail(),
|
|
86
|
+
},
|
|
87
|
+
}).createMachine({
|
|
88
|
+
id: "route",
|
|
89
|
+
input: door,
|
|
90
|
+
context: ({ input }) => input,
|
|
91
|
+
on: { "agent.fault": ".failed" },
|
|
92
|
+
initial: "triaging",
|
|
93
|
+
states: {
|
|
94
|
+
triaging: {
|
|
95
|
+
invoke: { src: "triager", input: ({ context }) => ({ prompt: context.prompt }) },
|
|
96
|
+
on: {
|
|
97
|
+
route: [{ guard: ({ event }) => event.size === "large", target: "deep" }, { target: "quick" }],
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
// A composed `task` gets its input from this Machine, so it needs a branch.
|
|
101
|
+
deep: {
|
|
102
|
+
invoke: {
|
|
103
|
+
src: "deep",
|
|
104
|
+
input: ({ context }) => ({ prompt: context.prompt, branch: `jr2/route-${context.instanceId}` }),
|
|
105
|
+
onDone: "done",
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
quick: {
|
|
109
|
+
invoke: {
|
|
110
|
+
src: "quick",
|
|
111
|
+
input: ({ context }) => ({ prompt: context.prompt, branch: `jr2/route-${context.instanceId}` }),
|
|
112
|
+
onDone: "done",
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
done: { type: "final" },
|
|
116
|
+
failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Local**
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
import { z } from "zod";
|
|
125
|
+
import { agent, customize, defineEvent, fail, jr2Setup, type HostInjectedInput } from "@jr2/orchestrator";
|
|
126
|
+
import { task } from "@jr2/machines";
|
|
127
|
+
|
|
128
|
+
const model = "local/qwen3.6-35b-a3b";
|
|
129
|
+
|
|
130
|
+
// Bind the two Open parts of `task`: the Repo that the coder works in, and the coder's model.
|
|
131
|
+
const coding = customize(task, {
|
|
132
|
+
repos: { target: { url: "https://github.com/you/app.git" } },
|
|
133
|
+
agents: { coder: { model } },
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Two changed copies of one Machine. `coding` does not change.
|
|
137
|
+
const deep = customize(coding, { agents: { coder: { thinkingLevel: "high" } } });
|
|
138
|
+
const quick = customize(coding, { agents: { coder: { thinkingLevel: "low" } } });
|
|
139
|
+
|
|
140
|
+
const door = z.object({ prompt: z.string().describe("What to do, in prose.") });
|
|
141
|
+
|
|
142
|
+
const route = defineEvent({
|
|
143
|
+
name: "route",
|
|
144
|
+
description: "Say how much care the task needs.",
|
|
145
|
+
audience: "agent",
|
|
146
|
+
input: z.object({
|
|
147
|
+
size: z.enum(["small", "large"]).describe("small: a change to one file. large: any other change."),
|
|
148
|
+
}),
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
type Input = HostInjectedInput & z.infer<typeof door>;
|
|
152
|
+
|
|
153
|
+
export const machine = jr2Setup({
|
|
154
|
+
types: {} as { input: Input; context: Input },
|
|
155
|
+
events: [route],
|
|
156
|
+
actors: {
|
|
157
|
+
triager: agent({
|
|
158
|
+
model,
|
|
159
|
+
workspace: "none",
|
|
160
|
+
thinkingLevel: "off",
|
|
161
|
+
instructions: "Read the task. Call route once: small for a change to one file, large for any other change.",
|
|
162
|
+
}),
|
|
163
|
+
// Each composed Machine is a slot, like an Agent.
|
|
164
|
+
deep,
|
|
165
|
+
quick,
|
|
166
|
+
fail: fail(),
|
|
167
|
+
},
|
|
168
|
+
}).createMachine({
|
|
169
|
+
id: "route",
|
|
170
|
+
input: door,
|
|
171
|
+
context: ({ input }) => input,
|
|
172
|
+
on: { "agent.fault": ".failed" },
|
|
173
|
+
initial: "triaging",
|
|
174
|
+
states: {
|
|
175
|
+
triaging: {
|
|
176
|
+
invoke: { src: "triager", input: ({ context }) => ({ prompt: context.prompt }) },
|
|
177
|
+
on: {
|
|
178
|
+
route: [{ guard: ({ event }) => event.size === "large", target: "deep" }, { target: "quick" }],
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
// A composed `task` gets its input from this Machine, so it needs a branch.
|
|
182
|
+
deep: {
|
|
183
|
+
invoke: {
|
|
184
|
+
src: "deep",
|
|
185
|
+
input: ({ context }) => ({ prompt: context.prompt, branch: `jr2/route-${context.instanceId}` }),
|
|
186
|
+
onDone: "done",
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
quick: {
|
|
190
|
+
invoke: {
|
|
191
|
+
src: "quick",
|
|
192
|
+
input: ({ context }) => ({ prompt: context.prompt, branch: `jr2/route-${context.instanceId}` }),
|
|
193
|
+
onDone: "done",
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
done: { type: "final" },
|
|
197
|
+
failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
To use `@jr2/machines`, add it to the Instance at the [Kit version](../reference/glossary.md#kit-version):
|
|
203
|
+
`npm install --save-exact @jr2/machines@<version>`. `<version>` is the version of `@jr2/orchestrator` in your
|
|
204
|
+
`package.json`.
|
|
205
|
+
|
|
206
|
+
A composed Machine is a part of the run of its parent:
|
|
207
|
+
|
|
208
|
+
- The run has one run id and one snapshot. A child Machine does not start a second run.
|
|
209
|
+
- The [Gates](../reference/glossary.md#gate) of the child show in the run of the parent. Its
|
|
210
|
+
[Emits](../reference/glossary.md#emit) go to the same feed.
|
|
211
|
+
- Each Machine keeps its own Vocabulary. The `approve` of a child is not an event of its parent, and the parent does not
|
|
212
|
+
declare it again.
|
|
213
|
+
- The parent gets to the child by `import`. It does not use the Workflow name that [`jr2 run`](../reference/cli.md#run)
|
|
214
|
+
uses.
|
|
215
|
+
- Only the root Machine of a run gets `instanceId`
|
|
216
|
+
([`HostInjectedInput`](../reference/authoring-surface/type-hostinjectedinput.md)) beside the run input. A child gets
|
|
217
|
+
only the input that its parent gives it. Thus the example gives each `task` a `branch`.
|
|
218
|
+
|
|
219
|
+
### Composing a Workflow
|
|
220
|
+
|
|
221
|
+
**The Workflow route, and the Machines that it composes**
|
|
222
|
+
|
|
223
|
+
- **route**: the Machine that workflows/route.ts registers as the Workflow route
|
|
224
|
+
- **triager** (Agent): picks deep or quick
|
|
225
|
+
- **deep** (Machine): thinking high
|
|
226
|
+
- **quick** (Machine): thinking low
|
|
227
|
+
- ↓ deep → coding: customize() · quick → coding: customize()
|
|
228
|
+
- **coding**: binds the Open parts of task
|
|
229
|
+
- **Repo Slot target**: your app
|
|
230
|
+
- **coder model**: the model of the tab
|
|
231
|
+
- ↓ coding is a customize() of task
|
|
232
|
+
- **task**: from @jr2/machines
|
|
233
|
+
- **coder** (Agent): edits and commits
|
|
234
|
+
- **Repo Slots**: Open
|
|
235
|
+
- **coder model**: Open
|
|
236
|
+
|
|
237
|
+
The file `workflows/route.ts` registers the Machine `route` as the Workflow `route`. The Machine declares three slots of
|
|
238
|
+
its own: the Agent `triager` and the two Machines `deep` and `quick`. Each of the two is a `customize()` of `coding`,
|
|
239
|
+
and `coding` is a `customize()` of `task`. `task` leaves its Repo Slots and the model of its coder Open. `coding` binds
|
|
240
|
+
them, so `deep` and `quick` have them too.
|
|
241
|
+
|
|
242
|
+
## customize()
|
|
243
|
+
|
|
244
|
+
[`customize(machine, parts)`](../reference/authoring-surface/customize.md) gives a new Machine with the parts changed.
|
|
245
|
+
The Machine that you give it does not change. Thus two calls on one import give two Machines, as `deep` and `quick`
|
|
246
|
+
show.
|
|
247
|
+
|
|
248
|
+
[`Customize`](../reference/authoring-surface/type-customize.md) gives the parts that you can change:
|
|
249
|
+
|
|
250
|
+
| Key | What it changes |
|
|
251
|
+
| ----------- | ------------------------------------------------------------------------------------- |
|
|
252
|
+
| `agents` | The definition of an Agent, by slot, field by field. `{ model }` keeps the rest. |
|
|
253
|
+
| `effects` | The Open url, argv or secret of an Effect, by slot. |
|
|
254
|
+
| `actors` | A Machine that this Machine composes, by slot. The value has the same keys, one down. |
|
|
255
|
+
| `repos` | The Repo Slots of the [`workspace()`](../reference/authoring-surface/workspace.md). |
|
|
256
|
+
| `image` | The Sandbox Image of the `workspace()`. |
|
|
257
|
+
| `user` | The [User Container](../reference/glossary.md#user-container) of the `workspace()`. |
|
|
258
|
+
| `resources` | The Size of the `workspace()`, field by field. |
|
|
259
|
+
|
|
260
|
+
The type comes from the Machine. It offers only the Agents, Effects, Machines and Repo Slots that the Machine declares.
|
|
261
|
+
A slot that the Machine does not have does not compile.
|
|
262
|
+
|
|
263
|
+
`workspace()` and [`pool()`](../reference/authoring-surface/pool.md) wrap a Machine. `customize()` goes through each
|
|
264
|
+
wrapper to the Machine inside it. Thus `customize(task, { agents })` changes the Agents of the Machine in the
|
|
265
|
+
`workspace()` of `task`, and you do not name the wrapper's own slot. `repos`, `image`, `user` and `resources` go down to
|
|
266
|
+
the first `workspace()` on that path.
|
|
267
|
+
|
|
268
|
+
## Open parts
|
|
269
|
+
|
|
270
|
+
A Machine marks a part Open with [`open`](../reference/authoring-surface/open.md). These parts can be Open:
|
|
271
|
+
|
|
272
|
+
- a Repo Slot with no url, or the whole Repo Slot map (`repos: open`)
|
|
273
|
+
- the model of an Agent (`agent({ model: open, … })`)
|
|
274
|
+
- the url of an `http` Effect, the argv of a `command`, or the name of the secret of an Effect (`secret(open)`)
|
|
275
|
+
|
|
276
|
+
An Open part has no default. You bind it with `customize()`. Each Open part has two checks:
|
|
277
|
+
|
|
278
|
+
- `jr2 up` reads every registered Machine. It refuses an Open part that nobody bound, and it prints the `customize()`
|
|
279
|
+
line that binds it.
|
|
280
|
+
- At run time, the part refuses too. An Agent does not start a [Turn](../reference/glossary.md#turn) with an Open model.
|
|
281
|
+
An Effect does not run while it is Open.
|
|
282
|
+
|
|
283
|
+
[Bind a shipped Machine's Open parts](../guides/bind-open-parts.md) shows each binding.
|
|
284
|
+
|
|
285
|
+
## @jr2/machines
|
|
286
|
+
|
|
287
|
+
`@jr2/machines` is the package of Machines that the kit ships. It ships Machines and never Workflows. A Machine becomes
|
|
288
|
+
a Workflow only when a file in your `workflows/` folder exports it.
|
|
289
|
+
|
|
290
|
+
It ships `task`: a coder Agent works on one prompt in a [Workspace](../reference/glossary.md#workspace), and a person
|
|
291
|
+
approves the work at the `review` Gate. `task` leaves two parts Open: the whole Repo Slot map and the model of `coder`.
|
|
292
|
+
The first Repo Slot that you name is the Repo that the coder works in. The coder can read the other Repos. It also ships
|
|
293
|
+
[`body`](../reference/machines.md#body), the Machine inside `task`, for a `workspace()` of your own.
|
|
294
|
+
|
|
295
|
+
The version of `@jr2/machines` is the Kit version. It names `@jr2/orchestrator` at that exact version as a peer
|
|
296
|
+
dependency.
|
|
297
|
+
|
|
298
|
+
## A Machine as an npm package
|
|
299
|
+
|
|
300
|
+
A package exports a Machine and nothing more. The run input, the Vocabulary, the Agents, the Effects, the Sandbox Image
|
|
301
|
+
and the Repo Slots are all on the Machine. A Workflow uses an imported Machine in one of three ways:
|
|
302
|
+
|
|
303
|
+
- as it is, when it leaves no part Open: `export const machine = imported;`
|
|
304
|
+
- changed: `export const machine = customize(task, { … });`
|
|
305
|
+
- composed in a Machine of your own, as in the example above
|
|
306
|
+
|
|
307
|
+
`task` cannot be used as it is, because it leaves two parts Open.
|
|
308
|
+
|
|
309
|
+
A file in `workflows/` uses the same code. A package has no API of its own.
|
|
310
|
+
|
|
311
|
+
A Machine package names `@jr2/orchestrator`, `xstate` and `zod` as peer dependencies, never as dependencies. jr2 keeps
|
|
312
|
+
the parts of a Machine in the one copy of `@jr2/orchestrator` and of `xstate` that the Instance loads. A Machine made
|
|
313
|
+
with a second copy of either has empty Menus and Gates that accept nothing. `jr2 up` shows no error for it, and prints
|
|
314
|
+
no `seats:` line for it. Its run stops with the status `error` when an Agent or a Gate of the Machine starts.
|
|
315
|
+
`@jr2/machines` has this shape, and your package copies it.
|
|
316
|
+
|
|
317
|
+
A package can ship a docker context for its Sandbox Image. The Machine names the folder with
|
|
318
|
+
`import.meta.resolve("./image")`, and `jr2 up` builds it.
|
|
319
|
+
|
|
320
|
+
## Rules
|
|
321
|
+
|
|
322
|
+
- A nested Machine is a child in the same run. jr2 has no second run for a child, and no invoke by Workflow name.
|
|
323
|
+
- An [`agent()`](../reference/authoring-surface/agent.md), an Effect or a `workspace()` is a key of `actors`. jr2
|
|
324
|
+
refuses one that a state writes in its `invoke.src`.
|
|
325
|
+
[`jr2Setup()`](../reference/authoring-surface/jr2setup.md)`.createMachine` refuses it, and `jr2 up` refuses it too.
|
|
326
|
+
- `customize()` changes only the parts that a Machine declares. You cannot add a part. A Machine that needs one more
|
|
327
|
+
Agent is a new Machine.
|
|
328
|
+
- For a part that the Machine does not declare, the compiler refuses the key, and `customize()` refuses it at run time.
|
|
329
|
+
`image`, `user` and `resources` compile on every Machine. `customize()` refuses them when the Machine composes no
|
|
330
|
+
`workspace()`.
|
|
331
|
+
- `jr2 up` refuses a registered Machine that leaves a part Open, and it names the Workflow and the part.
|
|
332
|
+
- `jr2 up` also refuses an Open part on a Machine that a state writes in its `invoke.src`, because no `customize()` can
|
|
333
|
+
get to it. Put that Machine under `actors`.
|
|
334
|
+
- A Repo Slot map that is Open takes the slots that you name. Name one slot or more. Their order means what the Machine
|
|
335
|
+
says it means.
|
|
336
|
+
- An Effect's Open part is bound one time, to a value. A run does not choose it, and a second `customize()` cannot
|
|
337
|
+
change it.
|
|
338
|
+
- A file in `workflows/` that a different Workflow imports is also a Workflow. Give a Machine that is only for
|
|
339
|
+
composition a file name that starts with `_`, or keep it outside `workflows/`.
|
|
340
|
+
- `task` inside a different Machine needs `branch` in its input. Without it, `task` refuses to start.
|
|
341
|
+
|
|
342
|
+
## Read next
|
|
343
|
+
|
|
344
|
+
- [Bind a shipped Machine's Open parts](../guides/bind-open-parts.md)
|
|
345
|
+
- [Publish a Machine as an npm package](../guides/publish-a-machine-as-an-npm-package.md)
|
|
346
|
+
- [Machines and Workflows](machines-and-workflows.md)
|
|
347
|
+
- [Workspaces](workspaces.md)
|
|
348
|
+
- [Pools and Sources](pools-and-sources.md)
|
|
349
|
+
- Reference: [`customize()`](../reference/authoring-surface/customize.md),
|
|
350
|
+
[`Customize`](../reference/authoring-surface/type-customize.md), [`open`](../reference/authoring-surface/open.md),
|
|
351
|
+
[`@jr2/machines`](../reference/machines.md), [`jr2 up`](../reference/cli.md#up)
|
|
352
|
+
- [ADR-0049](../reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md): a Machine carries its
|
|
353
|
+
parts and composes by invoke
|
|
354
|
+
- [ADR-0051](../reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md): a Repo is a slot
|
|
355
|
+
on the Workspace
|
|
356
|
+
- [ADR-0054](../reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md): the kit
|
|
357
|
+
ships Machines, and a shipped Machine leaves its parts Open
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Converge
|
|
3
|
+
description: What jr2 up makes in the cluster from your Instance folder, and the Assembly that it publishes.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Converge
|
|
7
|
+
|
|
8
|
+
To converge is to make a cluster agree with your [Instance](../reference/glossary.md#instance) folder. One command does
|
|
9
|
+
it: [`jr2 up`](../reference/cli.md#up). It reads the folder, builds the images that the folder needs, and applies the
|
|
10
|
+
objects to the cluster of your current kubectl context. Then it publishes your Workflows to the
|
|
11
|
+
[Orchestrator](../reference/glossary.md#orchestrator) as an [Assembly](../reference/glossary.md#assembly). You can run
|
|
12
|
+
it again at any time. When nothing changed, it changes nothing.
|
|
13
|
+
|
|
14
|
+
## Why one command
|
|
15
|
+
|
|
16
|
+
A [Workflow](../reference/glossary.md#workflow) needs many parts in the cluster: the Orchestrator, its Secret, its
|
|
17
|
+
images and the operator. Some Workflows also need a [Sandbox Image](../reference/glossary.md#sandbox-image) or the
|
|
18
|
+
[Instance Harness](../reference/glossary.md#instance-harness). If you put each part there by hand, you must remember the
|
|
19
|
+
steps and their order. When you forget a step, the cluster is not correct, and you find the problem only when a run
|
|
20
|
+
fails. `jr2 up` does every step, in the correct order, each time. It tells you what it does at each step.
|
|
21
|
+
|
|
22
|
+
The Instance folder holds no record of a cluster. The kubectl context is the address, and the namespace is the identity.
|
|
23
|
+
Thus the same folder converges into kind on your computer and into a shared cluster. Only the kubectl context changes,
|
|
24
|
+
and, for a cluster that is not kind, the value that [`registry`](../reference/config.md#registry) reads from `.env`.
|
|
25
|
+
|
|
26
|
+
## Setup and usage
|
|
27
|
+
|
|
28
|
+
The diagram shows what `jr2 up` makes, and what [`jr2 run`](../reference/cli.md#run) and
|
|
29
|
+
[`jr2 send`](../reference/cli.md#send) use after it.
|
|
30
|
+
|
|
31
|
+
**What jr2 up makes, and what jr2 run and jr2 send use**
|
|
32
|
+
|
|
33
|
+
- **You**: the Instance folder, on your computer
|
|
34
|
+
- **jr2 up**: converges the cluster
|
|
35
|
+
- **jr2 run, jr2 send**: use what jr2 up made
|
|
36
|
+
- ↓ jr2 up → the cluster: applies, then publishes · ↓ jr2 run, jr2 send → Orchestrator: read the token, then call
|
|
37
|
+
- **the cluster**: of the current kubectl context
|
|
38
|
+
- **Instance**: in the namespace of the Instance
|
|
39
|
+
- **Orchestrator**: loads your Workflows
|
|
40
|
+
- **Assembly**: your Workflows and your config
|
|
41
|
+
- **jr2-instance Secret**: holds the Instance token
|
|
42
|
+
- **Instance Harness**: when an Agent is Menu-only
|
|
43
|
+
- **Instance Effector**: when an Effect runs there
|
|
44
|
+
- **Repo cache**: when a Machine has a Sandbox
|
|
45
|
+
- ↓ the Orchestrator asks for each Sandbox · ↑ the operator makes its pod
|
|
46
|
+
- **jr2-system**: one for each cluster
|
|
47
|
+
- **operator**: makes each Sandbox pod
|
|
48
|
+
- **images**: that the pods run
|
|
49
|
+
- **Kit images**: jr2 up pulls them
|
|
50
|
+
- **Pinned image**: the Envoy of the Custodian
|
|
51
|
+
- **Sandbox Images**: jr2 up builds them
|
|
52
|
+
|
|
53
|
+
`jr2 up` reads the Instance folder. It applies the objects, and then it publishes the Assembly to the Orchestrator,
|
|
54
|
+
which loads it. `jr2 run` and `jr2 send` do not apply objects to the cluster. They read the Instance token from the
|
|
55
|
+
`jr2-instance` Secret. Then they open a port forward to the Orchestrator for the time of the command. The Orchestrator
|
|
56
|
+
starts the run, and it makes each [Sandbox](../reference/glossary.md#sandbox) that the run needs. For all the parts of
|
|
57
|
+
the cluster, see [Architecture](architecture.md).
|
|
58
|
+
|
|
59
|
+
## What jr2 up does
|
|
60
|
+
|
|
61
|
+
`jr2 up` does its steps in this order. Steps 1 to 4 change nothing in the cluster.
|
|
62
|
+
[`jr2 check`](../reference/cli.md#check) does steps 2 and 3 alone, with the same code and no cluster:
|
|
63
|
+
|
|
64
|
+
1. It writes the docs index of the [Kit version](../reference/glossary.md#kit-version) between the two `jr2-docs`
|
|
65
|
+
markers in `AGENTS.md`. This step changes the folder, not the cluster. Thus a converge that fails still leaves the
|
|
66
|
+
index correct.
|
|
67
|
+
2. It typechecks the Instance with the Instance's own TypeScript compiler.
|
|
68
|
+
3. It loads each Workflow in `workflows/`. It reads the parts that each [Machine](../reference/glossary.md#machine)
|
|
69
|
+
carries: its [Agents](../reference/glossary.md#agent), its [Repos](../reference/glossary.md#repo), its Sandbox Images
|
|
70
|
+
and its [Effects](../reference/glossary.md#effect). It also checks each [Chart](../reference/glossary.md#chart) file.
|
|
71
|
+
4. It makes the Assembly from the folder, and calculates its hash. See [The Assembly](#the-assembly).
|
|
72
|
+
5. It checks the Kubernetes version of the cluster.
|
|
73
|
+
6. It checks that the namespace is this Instance's. Before it uses a namespace for the first time, it asks you.
|
|
74
|
+
7. It checks that the cluster keeps pod-level resources.
|
|
75
|
+
8. It finds the platforms of the nodes, and it decides the address of each image.
|
|
76
|
+
9. It builds the Sandbox Images.
|
|
77
|
+
10. It makes the values of the Secrets, and checks each Secret that the config names but that `jr2 up` does not write.
|
|
78
|
+
11. It checks each model that an Agent uses: a pod in the cluster asks the provider for one tool call for each model.
|
|
79
|
+
Until each check passes, `jr2 up` writes nothing that the Instance or the operator reads. The pod has up to 5
|
|
80
|
+
minutes to start, and each model has up to 6 minutes. `jr2 up` writes each model's result as it comes. With
|
|
81
|
+
`--skip-provider-preflight`, no pod checks the models, and `jr2 up` writes a warning that names each model that it
|
|
82
|
+
did not check.
|
|
83
|
+
12. It applies the operator. For a Repo with an ssh url, it asks where the deploy key comes from.
|
|
84
|
+
13. It applies the Secrets and the Orchestrator, and waits for the rollout. When the Machines need them, it also waits
|
|
85
|
+
for the Repo cache, the Instance Harness and the [Instance Effector](../reference/glossary.md#instance-effector).
|
|
86
|
+
The Repo cache agent on each Sandbox node checks that the kernel of its node has Landlock, which jr2 needs. While a
|
|
87
|
+
Sandbox node has no Landlock, the converge fails. The error names each such node, the reason, and the fix: keep the
|
|
88
|
+
node out with `sandbox.nodeSelector` or a taint, or enable Landlock in its kernel. Then run `jr2 up` again.
|
|
89
|
+
14. It publishes the Assembly to the Orchestrator. When the Orchestrator already has this Assembly, it publishes
|
|
90
|
+
nothing.
|
|
91
|
+
15. It removes the images that nothing uses now.
|
|
92
|
+
|
|
93
|
+
`jr2 up` says `converged` only when each pod that it rolled out runs the image that it expected. The labels and the
|
|
94
|
+
records that `jr2 up` writes help it to skip work. They do not prove that the cluster is correct. The pods do.
|
|
95
|
+
|
|
96
|
+
The Machines decide some parts. When no registered Machine makes a Sandbox, `jr2 up` builds no Sandbox Image and runs no
|
|
97
|
+
Repo cache. When no Agent has `workspace: "none"`, there is no Instance Harness. When the Machines stop using the Repo
|
|
98
|
+
cache or the Instance Harness, `jr2 up` removes it from the namespace. The image sweep removes a Sandbox Image that
|
|
99
|
+
nothing names.
|
|
100
|
+
|
|
101
|
+
## The images
|
|
102
|
+
|
|
103
|
+
Each image that a converge uses is one of these kinds:
|
|
104
|
+
|
|
105
|
+
| Image | Who owns it | What `jr2 up` does |
|
|
106
|
+
| ----------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
107
|
+
| [Kit image](../reference/glossary.md#kit-image) | jr2 | It builds nothing. The cluster pulls `jr2-orchestrator`, `jr2-harness` and `jr2-operator` at the tag of the Kit version. |
|
|
108
|
+
| [Pinned image](../reference/glossary.md#pinned-image) | Upstream | It builds nothing. The cluster pulls the Envoy image of the [Custodian](../reference/glossary.md#custodian), by its tag and its digest. |
|
|
109
|
+
| Sandbox Image, from a `file:` URL | You | It builds the folder that a [`workspace()`](../reference/authoring-surface/workspace.md) names, and the `images/default/` folder that [`jr2 init`](../reference/cli.md#init) writes. |
|
|
110
|
+
| Sandbox Image, from a registry | You | It builds nothing. The cluster pulls the image. |
|
|
111
|
+
|
|
112
|
+
The Custodian is a container in each [Harness](../reference/glossary.md#harness) pod.
|
|
113
|
+
[Workspaces](workspaces.md#the-sandbox-image) explains what a Sandbox Image holds.
|
|
114
|
+
|
|
115
|
+
The tag of each image that `jr2 up` builds is a hash of the inputs of that image and of its platforms. Thus a new tag
|
|
116
|
+
means new content, and the same tag means the same content. This rule has three results:
|
|
117
|
+
|
|
118
|
+
- When the inputs of an image do not change, `jr2 up` does not build it again. A converge with no change builds no
|
|
119
|
+
image.
|
|
120
|
+
- When an input changes, the tag changes. A Sandbox that starts after the converge uses the new image. A Sandbox that
|
|
121
|
+
runs does not, as the text below explains.
|
|
122
|
+
- `--force` builds each image again, even when its tag did not change.
|
|
123
|
+
|
|
124
|
+
The Kit images are at `ghcr.io/snapwich` by default. A cluster that cannot pull from there pulls from a mirror. A mirror
|
|
125
|
+
holds the same tags. [`kitRegistry`](../reference/config.md#kitregistry) names the mirror, and
|
|
126
|
+
[`jr2 kit push`](../reference/cli.md#kit-push) fills it. `jr2 up` never pushes to `kitRegistry`. `kitRegistry` also
|
|
127
|
+
changes where the cluster pulls the Pinned image, and `jr2 kit push` copies it to the mirror too.
|
|
128
|
+
|
|
129
|
+
`jr2 up` sends each image that it builds to the nodes in one of two ways:
|
|
130
|
+
|
|
131
|
+
- When you set `registry`, it pushes the image to that registry, and the nodes pull it.
|
|
132
|
+
- When you do not set `registry`, on a kind cluster it loads the image into the nodes with `kind load`. A context whose
|
|
133
|
+
name starts with `kind-` is a kind cluster.
|
|
134
|
+
|
|
135
|
+
A change to a Sandbox Image changes only the Sandboxes that start after the converge. A Sandbox that runs keeps its
|
|
136
|
+
image, because a new pod would lose its [Worktrees](../reference/glossary.md#worktree) and the commits that you did not
|
|
137
|
+
push. `jr2 up` tells you how many running [Workspaces](../reference/glossary.md#workspace) keep each older image.
|
|
138
|
+
|
|
139
|
+
`jr2 up` uses Docker only to build a Sandbox Image. When no registered Machine makes a Sandbox, it builds no image, and
|
|
140
|
+
your computer does not need Docker for `jr2 up`.
|
|
141
|
+
|
|
142
|
+
## The Assembly
|
|
143
|
+
|
|
144
|
+
The Assembly is what the Orchestrator loads from your Instance folder: the Workflows in `workflows/`, `jr2.config.ts`,
|
|
145
|
+
the folders of the `file:` Sandbox Images, and the packages that they import. The image of the Orchestrator is a Kit
|
|
146
|
+
image, the same for each Instance. Your code is not in it. `jr2 up` makes the Assembly and publishes it to the
|
|
147
|
+
Orchestrator that runs.
|
|
148
|
+
|
|
149
|
+
- **No restart.** The Orchestrator loads a new Assembly in its process. A change to a Machine or to `jr2.config.ts`
|
|
150
|
+
starts no pod again. Thus a port forward to the [Console](../reference/glossary.md#console) stays open.
|
|
151
|
+
- **The Kit packages come from the image.** `jr2 up` removes `@jr2/orchestrator` and each package that it needs, such as
|
|
152
|
+
`xstate` and `zod`, from the Assembly. The Orchestrator uses its own copies. A second copy of one of these packages
|
|
153
|
+
makes Machines whose Menus are empty, so `jr2 up` refuses it.
|
|
154
|
+
- **One hash for each Assembly.** The hash comes from the files and the paths of the Assembly. When the Orchestrator
|
|
155
|
+
already has the Assembly with this hash, `jr2 up` does not publish it again.
|
|
156
|
+
- **The Orchestrator checks it again.** Before it makes an Assembly current, the Orchestrator loads each Workflow and
|
|
157
|
+
does the same checks as `jr2 up`. When it refuses the Assembly, the Assembly that was current stays current.
|
|
158
|
+
- **A run keeps its Assembly.** A new run starts under the current Assembly. A live run keeps the Assembly that it
|
|
159
|
+
started under, also after a restart. Thus a publish does not stop a run that waits at a
|
|
160
|
+
[Gate](../reference/glossary.md#gate). `jr2 run`, `jr2 runs` and the Console use the current Assembly. The
|
|
161
|
+
Orchestrator removes an older Assembly when no live run holds it.
|
|
162
|
+
- **Only `jr2 up` publishes.** The publish needs the publish token, which is in the `jr2-instance` Secret. `jr2 up`
|
|
163
|
+
reads it through the kubectl context. The Instance token does not publish, and an Agent cannot publish code.
|
|
164
|
+
|
|
165
|
+
[`jr2 status`](../reference/cli.md#status), with no run id, shows the current Assembly and each Assembly that a live run
|
|
166
|
+
holds.
|
|
167
|
+
|
|
168
|
+
## Kit version and Handoff
|
|
169
|
+
|
|
170
|
+
An Instance has one Kit version: the version of the `@jr2/orchestrator` package that the Instance pins. It is the tag of
|
|
171
|
+
each Kit image, the image of the Orchestrator too. `jr2 init` pins `@jr2/orchestrator` and `@jr2/cli` to one exact
|
|
172
|
+
version in `package.json`. The Assembly records the Kit version, and the Orchestrator refuses an Assembly for a
|
|
173
|
+
different Kit version. When you change the Kit version, one `jr2 up` starts the Orchestrator on its new image first, and
|
|
174
|
+
then it publishes the Assembly for the new version.
|
|
175
|
+
|
|
176
|
+
You install the [jr2 CLI](../reference/glossary.md#jr2-cli) two times: one time with `npm i -g`, and one time in the
|
|
177
|
+
Instance, from its `package.json`. In an Instance folder, the global `jr2` runs the Instance's own `@jr2/cli`, with the
|
|
178
|
+
same arguments. This is the [Handoff](../reference/glossary.md#handoff). Thus the CLI that runs is always the one that
|
|
179
|
+
the Instance pins. In the Instance, the version of your global `jr2` does not matter.
|
|
180
|
+
[`jr2 version`](../reference/cli.md#version) shows the CLI that runs, the global CLI that handed off, the Kit version of
|
|
181
|
+
the Instance, and what the cluster runs.
|
|
182
|
+
|
|
183
|
+
All the Instances on a cluster share one operator. `jr2 up` upgrades an older operator. It does not downgrade an
|
|
184
|
+
operator that a different Instance upgraded to a later version.
|
|
185
|
+
|
|
186
|
+
## Clean up
|
|
187
|
+
|
|
188
|
+
When each tag is a content hash, each change makes a new image. Ten changes to a Dockerfile make ten images. Thus
|
|
189
|
+
`jr2 up` removes the old images at the end of each converge that succeeds.
|
|
190
|
+
|
|
191
|
+
An image is garbage when no live root in the cluster names it. The roots are the image map of each Instance on the
|
|
192
|
+
cluster, the image of each Sandbox, and the images of the pods that run. jr2 removes only an image that it built and
|
|
193
|
+
labeled. It removes the image from your computer and, on a kind cluster, from the nodes. It never removes an image that
|
|
194
|
+
it did not build, such as the Pinned image. The output gives the size that it removed:
|
|
195
|
+
`swept <count> image(s) (<size>)`.
|
|
196
|
+
|
|
197
|
+
`jr2 up` and [`jr2 down`](../reference/cli.md#down) remove the images. [`jr2 gc`](../reference/cli.md#gc) does the same
|
|
198
|
+
work at a different time, for example when your disk is full. It also removes each Repo resource that no Machine binds
|
|
199
|
+
and that no run used in the last 7 days, by default. `--repo-ttl` changes the time. `jr2 gc --dry-run` shows what it
|
|
200
|
+
would remove, and removes nothing.
|
|
201
|
+
|
|
202
|
+
`jr2 down` deletes the namespace of the Instance. The runs, the store of the runs and the Sandboxes go with it.
|
|
203
|
+
`jr2 down --all` also removes the operator.
|
|
204
|
+
|
|
205
|
+
## Rules
|
|
206
|
+
|
|
207
|
+
- `jr2 up` changes nothing in the cluster when the Instance does not typecheck.
|
|
208
|
+
- `jr2 up` changes nothing in the cluster when the Assembly holds a second copy of a Kit package, or when its Kit
|
|
209
|
+
version is not the Kit version of the CLI.
|
|
210
|
+
- `jr2 up` changes nothing in the cluster when a registered Machine has an [Open](../reference/glossary.md#open) part
|
|
211
|
+
that is not bound. Examples are a [Repo Slot](../reference/glossary.md#repo-slot) with no url and an Agent with no
|
|
212
|
+
model.
|
|
213
|
+
- `jr2 up` refuses a cluster that runs a Kubernetes version earlier than 1.35. It also refuses a cluster that does not
|
|
214
|
+
keep pod-level resources.
|
|
215
|
+
- `jr2 up` refuses a namespace that belongs to a different Instance. It asks you before it uses a namespace for the
|
|
216
|
+
first time. `--yes` gives the answer for CI.
|
|
217
|
+
- `jr2 up` refuses a cluster that is not kind when `registry` is not set.
|
|
218
|
+
- Each verb that reads the Instance, other than `jr2 version`, refuses when the Instance and the CLI do not use the same
|
|
219
|
+
copy of `@jr2/orchestrator`.
|
|
220
|
+
- `jr2 up` never downgrades the operator.
|
|
221
|
+
- `jr2 up` never replaces the pod of a Sandbox that runs.
|
|
222
|
+
- The Orchestrator refuses an Assembly that fails its checks, and the Assembly that was current stays current.
|
|
223
|
+
- Only the publish token publishes an Assembly.
|
|
224
|
+
- A converge that fails removes no image.
|
|
225
|
+
- `jr2 gc`, `jr2 up` and `jr2 down` remove only images that jr2 built and that no live root names.
|
|
226
|
+
|
|
227
|
+
[Fix a failed converge](../guides/fix-a-failed-converge.md) gives the refusal text of each rule, and the fix.
|
|
228
|
+
|
|
229
|
+
## Read next
|
|
230
|
+
|
|
231
|
+
- [Iterate on a Machine on kind](../guides/iterate-on-a-machine-on-kind.md)
|
|
232
|
+
- [Move from kind to a shared cluster](../guides/move-from-kind-to-a-shared-cluster.md)
|
|
233
|
+
- [Mirror the Kit images](../guides/mirror-the-kit-images.md)
|
|
234
|
+
- [Upgrade the Kit version](../guides/upgrade-the-kit-version.md)
|
|
235
|
+
- [Clean up](../guides/clean-up.md)
|
|
236
|
+
- [Fix a failed converge](../guides/fix-a-failed-converge.md)
|
|
237
|
+
- Reference: [`jr2 up`](../reference/cli.md#up), [`jr2 down`](../reference/cli.md#down),
|
|
238
|
+
[`jr2 gc`](../reference/cli.md#gc), [`jr2 kit push`](../reference/cli.md#kit-push),
|
|
239
|
+
[`jr2 version`](../reference/cli.md#version), [`registry`](../reference/config.md#registry),
|
|
240
|
+
[`kitRegistry`](../reference/config.md#kitregistry) and [`platforms`](../reference/config.md#platforms)
|
|
241
|
+
- [ADR-0019](../reference/decisions/0019-one-converging-command-against-the-current-context.md): one converging command
|
|
242
|
+
against the current context
|
|
243
|
+
- [ADR-0038](../reference/decisions/0038-jr2-up-builds-every-image-it-deploys.md): `jr2 up` builds each image that the
|
|
244
|
+
Instance runs
|
|
245
|
+
- [ADR-0039](../reference/decisions/0039-image-garbage-collects-by-reachability.md): `jr2 gc` removes the images that
|
|
246
|
+
nothing reaches
|
|
247
|
+
- [ADR-0041](../reference/decisions/0041-a-build-the-host-already-holds-is-not-spent-again.md): a build that the
|
|
248
|
+
computer already holds is not done again
|
|
249
|
+
- [ADR-0044](../reference/decisions/0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md): the Kit images
|
|
250
|
+
and their mirror
|
|
251
|
+
- [ADR-0045](../reference/decisions/0045-the-platform-joins-the-image-address-and-the-cluster-chooses-it.md): the
|
|
252
|
+
platform is a part of the image tag
|
|
253
|
+
- [ADR-0046](../reference/decisions/0046-a-converge-failure-carries-its-diagnosis.md): a failed converge says why
|
|
254
|
+
- [ADR-0056](../reference/decisions/0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md):
|
|
255
|
+
the Handoff, and one Kit version for each Instance
|
|
256
|
+
- [ADR-0069](../reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md): the
|
|
257
|
+
Orchestrator is a Kit image, and an Instance publishes its Assembly
|