@jr2/orchestrator 0.5.1 → 0.7.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/console/components/app.ts +55 -15
- package/console/components/bottom-drawer.ts +259 -0
- package/console/components/drawer.ts +14 -6
- package/console/components/fleet.ts +36 -13
- package/console/components/machine-pane.ts +61 -5
- package/console/components/nav.ts +54 -8
- package/console/conversation.ts +161 -0
- package/console/login.ts +123 -0
- package/console/main.ts +600 -60
- package/console/store.ts +611 -64
- package/console/style.css +231 -0
- package/docs/agents-block.md +2 -2
- package/docs/concepts/agents-and-turns.md +142 -29
- package/docs/concepts/architecture.md +60 -34
- package/docs/concepts/callers-and-hooks.md +205 -0
- package/docs/concepts/charts.md +13 -8
- package/docs/concepts/composition.md +10 -1
- package/docs/concepts/converge.md +7 -2
- package/docs/concepts/durable-runs.md +43 -16
- package/docs/concepts/effects.md +133 -50
- package/docs/concepts/gates.md +95 -25
- package/docs/concepts/machines-and-workflows.md +14 -4
- package/docs/concepts/pools-and-sources.md +193 -17
- package/docs/concepts/secrets-and-trust.md +186 -62
- package/docs/concepts/the-menu.md +14 -0
- package/docs/concepts/workspaces.md +135 -56
- package/docs/get-started/a-model-decides.md +14 -13
- package/docs/get-started/an-agent-in-a-sandbox.md +46 -35
- package/docs/get-started/connect-a-model.md +1 -1
- package/docs/get-started/create-an-instance.md +17 -14
- package/docs/get-started/have-your-assistant-write-a-machine.md +111 -40
- package/docs/get-started/install.md +4 -0
- package/docs/guides/add-tools-to-the-sandbox.md +72 -15
- package/docs/guides/answer-a-gate-from-a-forge.md +304 -0
- package/docs/guides/answer-a-gate.md +74 -37
- package/docs/guides/bind-open-parts.md +29 -12
- package/docs/guides/call-an-mcp-tool-from-a-machine.md +572 -0
- package/docs/guides/cancel-a-run.md +26 -16
- package/docs/guides/cut-a-worktree.md +560 -0
- package/docs/guides/fix-a-failed-converge.md +19 -7
- package/docs/guides/give-a-caller-a-scope.md +203 -0
- package/docs/guides/give-an-agent-mcp-tools.md +422 -0
- package/docs/guides/iterate-on-a-machine-on-kind.md +10 -9
- package/docs/guides/let-an-agent-write-a-chart.md +14 -7
- package/docs/guides/look-inside-a-sandbox.md +115 -42
- package/docs/guides/move-from-kind-to-a-shared-cluster.md +16 -0
- package/docs/guides/open-the-console.md +63 -14
- package/docs/guides/push-and-sign-a-branch.md +67 -21
- package/docs/guides/read-a-conversation.md +225 -0
- package/docs/guides/run-a-chart-by-name.md +17 -16
- package/docs/guides/run-a-command-or-call-a-url.md +84 -41
- package/docs/guides/run-one-worker-per-item.md +34 -27
- package/docs/guides/set-an-agents-access-and-dials.md +69 -22
- package/docs/guides/size-a-workspace.md +19 -6
- package/docs/guides/start-a-run-for-each-alert.md +290 -0
- package/docs/guides/start-a-run-from-a-run.md +18 -16
- package/docs/guides/start-a-run.md +24 -18
- package/docs/guides/steer-an-agent.md +332 -0
- package/docs/guides/use-a-hosted-provider.md +7 -1
- package/docs/guides/watch-a-run.md +146 -117
- package/docs/guides/write-a-hook-kind.md +238 -0
- package/docs/guides/write-a-tool-as-an-mcp-server.md +351 -0
- package/docs/guides/write-a-workflow.md +18 -8
- package/docs/llms.txt +110 -23
- package/docs/reference/authoring-surface/agent.md +35 -12
- package/docs/reference/authoring-surface/answer.md +20 -4
- package/docs/reference/authoring-surface/arg.md +28 -6
- package/docs/reference/authoring-surface/basic.md +1 -1
- package/docs/reference/authoring-surface/bearer.md +1 -1
- package/docs/reference/authoring-surface/branchname.md +15 -0
- package/docs/reference/authoring-surface/carry.md +23 -0
- package/docs/reference/authoring-surface/chartschema.md +1 -1
- package/docs/reference/authoring-surface/chartschemafor.md +1 -1
- package/docs/reference/authoring-surface/command.md +37 -22
- package/docs/reference/authoring-surface/customize.md +4 -1
- package/docs/reference/authoring-surface/defineconfig.md +1 -1
- package/docs/reference/authoring-surface/defineevent.md +1 -1
- package/docs/reference/authoring-surface/dispatch.md +5 -4
- package/docs/reference/authoring-surface/fail.md +18 -5
- package/docs/reference/authoring-surface/file.md +37 -17
- package/docs/reference/authoring-surface/frompick.md +1 -1
- package/docs/reference/authoring-surface/gate.md +118 -0
- package/docs/reference/authoring-surface/group-agents.md +15 -0
- package/docs/reference/authoring-surface/group-charts.md +16 -0
- package/docs/reference/authoring-surface/group-composition.md +25 -0
- package/docs/reference/authoring-surface/group-config.md +17 -0
- package/docs/reference/authoring-surface/group-effects.md +48 -0
- package/docs/reference/authoring-surface/group-failure.md +13 -0
- package/docs/reference/authoring-surface/group-gates.md +16 -0
- package/docs/reference/authoring-surface/group-machine-and-events.md +15 -0
- package/docs/reference/authoring-surface/group-steering.md +12 -0
- package/docs/reference/authoring-surface/group-types-these-signatures-name.md +137 -0
- package/docs/reference/authoring-surface/group-workspaces-and-repos.md +15 -0
- package/docs/reference/authoring-surface/http.md +109 -9
- package/docs/reference/authoring-surface/isfailure.md +1 -1
- package/docs/reference/authoring-surface/isready.md +14 -0
- package/docs/reference/authoring-surface/jr2setup.md +14 -4
- package/docs/reference/authoring-surface/mcp.md +107 -0
- package/docs/reference/authoring-surface/open.md +41 -8
- package/docs/reference/authoring-surface/openbody.md +1 -1
- package/docs/reference/authoring-surface/paramsof.md +23 -5
- package/docs/reference/authoring-surface/pool.md +79 -11
- package/docs/reference/authoring-surface/push.md +31 -13
- package/docs/reference/authoring-surface/readyof.md +21 -0
- package/docs/reference/authoring-surface/reread.md +19 -0
- package/docs/reference/authoring-surface/secret.md +26 -5
- package/docs/reference/authoring-surface/sentby.md +1 -1
- package/docs/reference/authoring-surface/skip.md +22 -0
- package/docs/reference/authoring-surface/source.md +59 -4
- package/docs/reference/authoring-surface/steer.md +93 -0
- package/docs/reference/authoring-surface/text.md +1 -1
- package/docs/reference/authoring-surface/type-agentdefinition.md +16 -3
- package/docs/reference/authoring-surface/type-agentmcpserver.md +54 -0
- package/docs/reference/authoring-surface/type-agentslots.md +4 -1
- 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-bindactors.md +14 -0
- package/docs/reference/authoring-surface/type-binding.md +6 -7
- package/docs/reference/authoring-surface/type-caller.md +40 -0
- package/docs/reference/authoring-surface/type-callerrecord.md +32 -0
- package/docs/reference/authoring-surface/type-carry.md +19 -0
- package/docs/reference/authoring-surface/type-chainlink.md +3 -3
- package/docs/reference/authoring-surface/type-commandinput.md +4 -3
- package/docs/reference/authoring-surface/type-commandoptions.md +0 -22
- package/docs/reference/authoring-surface/type-commandoutput.md +23 -5
- package/docs/reference/authoring-surface/type-customize.md +20 -3
- package/docs/reference/authoring-surface/type-delivered.md +17 -0
- package/docs/reference/authoring-surface/type-deliveredevent.md +18 -0
- package/docs/reference/authoring-surface/type-dispatchdefinition.md +4 -3
- package/docs/reference/authoring-surface/type-dispatchoutput.md +9 -2
- package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
- package/docs/reference/authoring-surface/type-effectbinding.md +12 -2
- package/docs/reference/authoring-surface/type-filebody.md +4 -8
- package/docs/reference/authoring-surface/type-framepath.md +14 -0
- package/docs/reference/authoring-surface/type-gatebinding.md +15 -0
- package/docs/reference/authoring-surface/type-gatehooks.md +12 -0
- package/docs/reference/authoring-surface/type-gatelook.md +37 -0
- package/docs/reference/authoring-surface/type-gateoutcome.md +32 -0
- package/docs/reference/authoring-surface/type-gatereply.md +19 -0
- package/docs/reference/authoring-surface/type-gateslot.md +21 -0
- package/docs/reference/authoring-surface/type-gateslotinput.md +13 -0
- package/docs/reference/authoring-surface/type-gateslotparts.md +18 -0
- package/docs/reference/authoring-surface/type-gateslots.md +12 -0
- package/docs/reference/authoring-surface/type-gatetypesat.md +13 -0
- package/docs/reference/authoring-surface/type-hostinjectedinput.md +3 -5
- package/docs/reference/authoring-surface/type-httpdefinition.md +6 -3
- package/docs/reference/authoring-surface/type-httpfileinput.md +20 -0
- package/docs/reference/authoring-surface/type-httpoutput.md +8 -2
- package/docs/reference/authoring-surface/type-jr2config.md +37 -2
- package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
- package/docs/reference/authoring-surface/type-lends.md +5 -1
- package/docs/reference/authoring-surface/type-lookoutput.md +14 -0
- package/docs/reference/authoring-surface/type-mcpcalloutput.md +78 -0
- package/docs/reference/authoring-surface/type-mcpnotreturned.md +42 -0
- package/docs/reference/authoring-surface/type-mcpoptions.md +47 -0
- package/docs/reference/authoring-surface/type-mcpoutput.md +50 -0
- package/docs/reference/authoring-surface/type-mcpreadoutput.md +53 -0
- package/docs/reference/authoring-surface/type-mcpremoteserver.md +50 -0
- package/docs/reference/authoring-surface/type-mcpserver.md +41 -0
- package/docs/reference/authoring-surface/type-mcpstdioserver.md +42 -0
- package/docs/reference/authoring-surface/type-namedsecret.md +4 -4
- package/docs/reference/authoring-surface/type-namestart.md +77 -0
- package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +1 -6
- package/docs/reference/authoring-surface/type-notworktreekeys.md +12 -0
- package/docs/reference/authoring-surface/type-openbodydeclaration.md +1 -1
- package/docs/reference/authoring-surface/type-openbodyoutput.md +1 -1
- package/docs/reference/authoring-surface/type-opencommandinput.md +7 -2
- package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +1 -6
- package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +5 -10
- package/docs/reference/authoring-surface/type-poolnextinput.md +22 -0
- package/docs/reference/authoring-surface/type-poolspec.md +11 -6
- package/docs/reference/authoring-surface/type-pushinput.md +22 -3
- package/docs/reference/authoring-surface/type-pushoutput.md +33 -8
- package/docs/reference/authoring-surface/type-pushsourcespec.md +38 -0
- package/docs/reference/authoring-surface/type-readsourcespec.md +47 -0
- package/docs/reference/authoring-surface/type-reposlot.md +6 -6
- package/docs/reference/authoring-surface/type-reread.md +18 -0
- package/docs/reference/authoring-surface/type-runsconfig.md +41 -0
- package/docs/reference/authoring-surface/type-sandboxoptions.md +11 -4
- package/docs/reference/authoring-surface/type-sender.md +26 -1
- package/docs/reference/authoring-surface/type-sendercallerfield.md +14 -0
- package/docs/reference/authoring-surface/type-size.md +1 -1
- package/docs/reference/authoring-surface/type-skip.md +19 -0
- package/docs/reference/authoring-surface/type-slothandles.md +5 -13
- package/docs/reference/authoring-surface/type-sourcehooks.md +23 -0
- package/docs/reference/authoring-surface/type-sourcein.md +25 -0
- package/docs/reference/authoring-surface/type-sourcereply.md +18 -0
- package/docs/reference/authoring-surface/type-sourcespec.md +26 -17
- package/docs/reference/authoring-surface/type-sourceverdict.md +49 -0
- package/docs/reference/authoring-surface/type-standingpoolmachine.md +12 -0
- package/docs/reference/authoring-surface/type-standingpoolspec.md +51 -0
- package/docs/reference/authoring-surface/type-steerargs.md +20 -0
- package/docs/reference/authoring-surface/type-thinkinglevel.md +1 -1
- package/docs/reference/authoring-surface/type-usercontainer.md +1 -1
- package/docs/reference/authoring-surface/type-workspaced.md +30 -9
- package/docs/reference/authoring-surface/type-workspacehandles.md +5 -6
- package/docs/reference/authoring-surface/type-workspaceoptions.md +1 -13
- 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 +66 -38
- package/docs/reference/authoring-surface/worktree.md +71 -0
- package/docs/reference/authoring-surface.md +88 -147
- package/docs/reference/cli.md +413 -74
- package/docs/reference/config.md +248 -6
- package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +10 -9
- package/docs/reference/decisions/0002-duplex-streaming-actor.md +81 -14
- package/docs/reference/decisions/0004-worktree-provisioning-model.md +27 -21
- package/docs/reference/decisions/0005-sandbox-pod-composition.md +39 -25
- package/docs/reference/decisions/0006-agent-control-surface.md +6 -3
- package/docs/reference/decisions/0007-durable-machine-state.md +1 -1
- package/docs/reference/decisions/0009-cli-and-instance-interface.md +150 -49
- package/docs/reference/decisions/0010-bdd-acceptance-tests.md +5 -3
- package/docs/reference/decisions/0011-workflow-defined-events.md +256 -12
- package/docs/reference/decisions/0012-workspace-wrapper-machine.md +39 -32
- package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +54 -30
- package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +53 -27
- package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +26 -16
- package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +9 -7
- package/docs/reference/decisions/0017-pool-over-a-source-port.md +226 -10
- package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +15 -7
- package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +53 -27
- package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +16 -2
- package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +4 -4
- package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +124 -43
- package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +36 -38
- package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +10 -5
- package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +16 -7
- package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +36 -19
- package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +18 -10
- package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +3 -2
- package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +23 -15
- package/docs/reference/decisions/0032-the-console-unlocks-with-a-callers-credential.md +101 -0
- package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +22 -22
- package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +6 -6
- package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +13 -12
- package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +23 -23
- package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +6 -1
- package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +57 -46
- package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +5 -0
- package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +20 -8
- package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +55 -40
- package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +14 -13
- package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +31 -24
- package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +18 -10
- package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +137 -86
- package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +165 -91
- package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +7 -7
- package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +2 -2
- package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md +109 -0
- package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +53 -23
- package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +221 -153
- package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +161 -96
- package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +32 -9
- package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +48 -33
- package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +56 -30
- package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +35 -25
- package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +1 -1
- package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +296 -0
- package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +347 -0
- package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
- package/docs/reference/decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md +285 -0
- package/docs/reference/decisions/0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md +198 -0
- package/docs/reference/decisions.md +9 -4
- package/docs/reference/glossary.md +138 -45
- package/docs/reference/hooks.md +1077 -0
- package/docs/reference/http-api.md +988 -123
- package/docs/reference/machines.md +0 -1
- package/docs/why.md +7 -5
- package/package.json +10 -3
- package/src/actor.ts +194 -42
- package/src/agent.ts +56 -6
- package/src/ambient.ts +3 -5
- package/src/assembly-checks.ts +530 -4
- package/src/callers.ts +245 -0
- package/src/chart.ts +238 -26
- package/src/charts.ts +9 -2
- package/src/config.ts +566 -7
- package/src/conversations.ts +453 -0
- package/src/custodian.lua +17 -5
- package/src/custodian.ts +257 -77
- package/src/customize.ts +154 -7
- package/src/dispatch-effect.ts +142 -26
- package/src/durability.ts +2 -0
- package/src/duration.ts +20 -0
- package/src/effect-checks.ts +52 -4
- package/src/effect.ts +401 -132
- package/src/effector-wire.ts +278 -30
- package/src/fail.ts +17 -4
- package/src/gate-brand.ts +16 -0
- package/src/gate.ts +634 -48
- package/src/harness-client.ts +84 -35
- package/src/harness-env.ts +33 -0
- package/src/held-secrets.ts +197 -39
- package/src/holds.ts +5 -2
- package/src/hook-kinds/discord.ts +92 -0
- package/src/hook-kinds/door.ts +107 -0
- package/src/hook-kinds/github.ts +89 -0
- package/src/hook-kinds/gitlab.ts +72 -0
- package/src/hook-kinds/hook.ts +7 -0
- package/src/hook-kinds/index.ts +465 -0
- package/src/hook-kinds/slack.ts +151 -0
- package/src/hook-kinds/webhook.ts +113 -0
- package/src/hooks.ts +506 -0
- package/src/http-effect.ts +164 -70
- package/src/http.ts +933 -99
- package/src/index.ts +30 -5
- package/src/instance.ts +196 -26
- package/src/internal.ts +23 -3
- package/src/kube-client.ts +6 -3
- package/src/machine-doc.ts +29 -5
- package/src/mcp-effect.ts +424 -0
- package/src/model-keys.ts +24 -0
- package/src/mount-digest.ts +41 -7
- package/src/names.ts +82 -7
- package/src/notices.ts +576 -59
- package/src/open-body.ts +110 -30
- package/src/open-effect.ts +17 -4
- package/src/open.ts +40 -7
- package/src/parts.ts +125 -35
- package/src/party.ts +35 -1
- package/src/pick.ts +43 -9
- package/src/pool.ts +1172 -172
- package/src/registration.ts +249 -17
- package/src/repo-identity.ts +7 -3
- package/src/repos.ts +20 -2
- package/src/run-host.ts +1614 -190
- package/src/run-list.ts +191 -0
- package/src/sandbox-effect.ts +217 -66
- package/src/sandbox-kube.ts +116 -21
- package/src/secret-placement.ts +248 -35
- package/src/server.ts +53 -4
- package/src/setup.ts +158 -38
- package/src/snapshot-store.ts +565 -48
- package/src/steer.ts +111 -0
- package/src/stub-harness.ts +6 -2
- package/src/tokens.ts +362 -46
- package/src/vocabulary.ts +23 -4
- package/src/wire.ts +27 -10
- package/src/workspace.ts +314 -259
- package/src/worktree.ts +249 -0
- package/docs/reference/authoring-surface/type-noslotspec.md +0 -26
- package/docs/reference/authoring-surface/type-workspacespec.md +0 -37
- package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +0 -61
- package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +0 -65
|
@@ -10,8 +10,9 @@ Run a program in the [Workspace](../reference/glossary.md#workspace), or call a
|
|
|
10
10
|
|
|
11
11
|
Put the Effect under `actors` in [`jr2Setup`](../reference/authoring-surface/jr2setup.md), beside the
|
|
12
12
|
[Agents](../reference/glossary.md#agent). Then invoke it by its key from a state, and route on its output in `onDone`.
|
|
13
|
-
In this [Workflow](../reference/glossary.md#workflow),
|
|
14
|
-
|
|
13
|
+
In this [Workflow](../reference/glossary.md#workflow), the Machine cuts a [Worktree](../reference/glossary.md#worktree),
|
|
14
|
+
and a coder fixes the tests there. It picks `run_test` to run one test file with a
|
|
15
|
+
[`command`](../reference/authoring-surface/command.md) Effect. When it picks `finished`, an
|
|
15
16
|
[`http`](../reference/authoring-surface/http.md) Effect posts its summary to a URL. Save the file as
|
|
16
17
|
`workflows/fix-and-report.ts` in your [Instance](../reference/glossary.md#instance):
|
|
17
18
|
|
|
@@ -29,9 +30,11 @@ import {
|
|
|
29
30
|
fail,
|
|
30
31
|
fromPick,
|
|
31
32
|
http,
|
|
33
|
+
isReady,
|
|
32
34
|
jr2Setup,
|
|
33
35
|
secret,
|
|
34
36
|
workspace,
|
|
37
|
+
worktree,
|
|
35
38
|
type CommandOutput,
|
|
36
39
|
type Workspaced,
|
|
37
40
|
} from "@jr2/orchestrator";
|
|
@@ -50,6 +53,9 @@ const finished = defineEvent({
|
|
|
50
53
|
const runInput = z.object({ prompt: z.string(), branch: z.string() });
|
|
51
54
|
type BodyInput = Workspaced<z.infer<typeof runInput>, "app">;
|
|
52
55
|
|
|
56
|
+
// The Worktree that the coder and the command name.
|
|
57
|
+
const fixTree = (context: BodyInput) => `${context.workspace.repos.app}/fix` as const;
|
|
58
|
+
|
|
53
59
|
const passed = (output: CommandOutput): boolean => output.outcome === "exited" && output.code === 0;
|
|
54
60
|
|
|
55
61
|
const body = jr2Setup({
|
|
@@ -60,6 +66,8 @@ const body = jr2Setup({
|
|
|
60
66
|
model: "anthropic/claude-haiku-5-5",
|
|
61
67
|
instructions: "You fix failing tests. You change no test. You commit the fix.",
|
|
62
68
|
}),
|
|
69
|
+
// Cut the Worktree: an Effect too.
|
|
70
|
+
cut: worktree(),
|
|
63
71
|
// One program, with no shell. arg("file") is one argument, from the input of the Effect.
|
|
64
72
|
test: command(["node", "--test", arg("file")], { timeout: "5m" }),
|
|
65
73
|
// A call to a fixed URL. The Effector puts the value of the secret in the header.
|
|
@@ -77,12 +85,20 @@ const body = jr2Setup({
|
|
|
77
85
|
id: "fix-and-report",
|
|
78
86
|
context: ({ input }) => input,
|
|
79
87
|
on: { "workspace.lost": ".lost" },
|
|
80
|
-
initial: "
|
|
88
|
+
initial: "cutting",
|
|
81
89
|
states: {
|
|
90
|
+
// The Workspace only clones. The Machine cuts the Worktree that the Agent works in.
|
|
91
|
+
cutting: {
|
|
92
|
+
invoke: {
|
|
93
|
+
src: "cut",
|
|
94
|
+
input: ({ context }) => ({ path: fixTree(context), branch: context.branch }),
|
|
95
|
+
onDone: [{ guard: ({ event }) => isReady(event.output), target: "coding" }, { target: "notCut" }],
|
|
96
|
+
},
|
|
97
|
+
},
|
|
82
98
|
coding: {
|
|
83
99
|
invoke: {
|
|
84
100
|
src: "coder",
|
|
85
|
-
input: ({ context }) => ({ prompt: context.prompt, cwd: context
|
|
101
|
+
input: ({ context }) => ({ prompt: context.prompt, cwd: fixTree(context) }),
|
|
86
102
|
},
|
|
87
103
|
on: {
|
|
88
104
|
finished: "reporting",
|
|
@@ -95,8 +111,8 @@ const body = jr2Setup({
|
|
|
95
111
|
testing: {
|
|
96
112
|
invoke: {
|
|
97
113
|
src: "test",
|
|
98
|
-
// The `file` of the pick goes to the arg("file") of the command.
|
|
99
|
-
input: fromPick(runTest),
|
|
114
|
+
// The `file` of the pick goes to the arg("file") of the command. The Machine gives `worktree`.
|
|
115
|
+
input: fromPick(runTest, ({ context }) => ({ worktree: fixTree(context) })),
|
|
100
116
|
onDone: {
|
|
101
117
|
target: "ready",
|
|
102
118
|
// The Agent reads this text as the answer to its run_test pick.
|
|
@@ -114,7 +130,7 @@ const body = jr2Setup({
|
|
|
114
130
|
invoke: {
|
|
115
131
|
src: "report",
|
|
116
132
|
// The Agent gives `summary`. The Machine gives `branch`.
|
|
117
|
-
input: fromPick(finished, ({ context }) => ({ branch: context.
|
|
133
|
+
input: fromPick(finished, ({ context }) => ({ branch: context.branch })),
|
|
118
134
|
onDone: [
|
|
119
135
|
{ guard: ({ event }) => event.output.outcome === "response" && event.output.status < 300, target: "done" },
|
|
120
136
|
{ target: "notReported" },
|
|
@@ -123,6 +139,7 @@ const body = jr2Setup({
|
|
|
123
139
|
},
|
|
124
140
|
done: { type: "final" },
|
|
125
141
|
// Not final states: a final state means `done`. Each one fails the run, and the run ends `failed`.
|
|
142
|
+
notCut: { invoke: { src: "fail", input: { reason: "the Worktree was not cut" } } },
|
|
126
143
|
notReported: { invoke: { src: "fail", input: { reason: "the report did not go out" } } },
|
|
127
144
|
failed: { invoke: { src: "fail", input: { reason: "the coder could not go on" } } },
|
|
128
145
|
lost: { invoke: { src: "fail", input: { reason: "the Sandbox was lost" } } },
|
|
@@ -132,7 +149,6 @@ const body = jr2Setup({
|
|
|
132
149
|
export const machine = workspace(body, {
|
|
133
150
|
input: runInput,
|
|
134
151
|
repos: { app: "https://github.com/<you>/app.git" },
|
|
135
|
-
spec: ({ input }) => ({ branch: input.branch }),
|
|
136
152
|
});
|
|
137
153
|
```
|
|
138
154
|
|
|
@@ -150,9 +166,11 @@ import {
|
|
|
150
166
|
fail,
|
|
151
167
|
fromPick,
|
|
152
168
|
http,
|
|
169
|
+
isReady,
|
|
153
170
|
jr2Setup,
|
|
154
171
|
secret,
|
|
155
172
|
workspace,
|
|
173
|
+
worktree,
|
|
156
174
|
type CommandOutput,
|
|
157
175
|
type Workspaced,
|
|
158
176
|
} from "@jr2/orchestrator";
|
|
@@ -171,6 +189,9 @@ const finished = defineEvent({
|
|
|
171
189
|
const runInput = z.object({ prompt: z.string(), branch: z.string() });
|
|
172
190
|
type BodyInput = Workspaced<z.infer<typeof runInput>, "app">;
|
|
173
191
|
|
|
192
|
+
// The Worktree that the coder and the command name.
|
|
193
|
+
const fixTree = (context: BodyInput) => `${context.workspace.repos.app}/fix` as const;
|
|
194
|
+
|
|
174
195
|
const passed = (output: CommandOutput): boolean => output.outcome === "exited" && output.code === 0;
|
|
175
196
|
|
|
176
197
|
const body = jr2Setup({
|
|
@@ -181,6 +202,8 @@ const body = jr2Setup({
|
|
|
181
202
|
model: "local/qwen3.6-35b-a3b",
|
|
182
203
|
instructions: "You fix failing tests. You change no test. You commit the fix.",
|
|
183
204
|
}),
|
|
205
|
+
// Cut the Worktree: an Effect too.
|
|
206
|
+
cut: worktree(),
|
|
184
207
|
// One program, with no shell. arg("file") is one argument, from the input of the Effect.
|
|
185
208
|
test: command(["node", "--test", arg("file")], { timeout: "5m" }),
|
|
186
209
|
// A call to a fixed URL. The Effector puts the value of the secret in the header.
|
|
@@ -198,12 +221,20 @@ const body = jr2Setup({
|
|
|
198
221
|
id: "fix-and-report",
|
|
199
222
|
context: ({ input }) => input,
|
|
200
223
|
on: { "workspace.lost": ".lost" },
|
|
201
|
-
initial: "
|
|
224
|
+
initial: "cutting",
|
|
202
225
|
states: {
|
|
226
|
+
// The Workspace only clones. The Machine cuts the Worktree that the Agent works in.
|
|
227
|
+
cutting: {
|
|
228
|
+
invoke: {
|
|
229
|
+
src: "cut",
|
|
230
|
+
input: ({ context }) => ({ path: fixTree(context), branch: context.branch }),
|
|
231
|
+
onDone: [{ guard: ({ event }) => isReady(event.output), target: "coding" }, { target: "notCut" }],
|
|
232
|
+
},
|
|
233
|
+
},
|
|
203
234
|
coding: {
|
|
204
235
|
invoke: {
|
|
205
236
|
src: "coder",
|
|
206
|
-
input: ({ context }) => ({ prompt: context.prompt, cwd: context
|
|
237
|
+
input: ({ context }) => ({ prompt: context.prompt, cwd: fixTree(context) }),
|
|
207
238
|
},
|
|
208
239
|
on: {
|
|
209
240
|
finished: "reporting",
|
|
@@ -216,8 +247,8 @@ const body = jr2Setup({
|
|
|
216
247
|
testing: {
|
|
217
248
|
invoke: {
|
|
218
249
|
src: "test",
|
|
219
|
-
// The `file` of the pick goes to the arg("file") of the command.
|
|
220
|
-
input: fromPick(runTest),
|
|
250
|
+
// The `file` of the pick goes to the arg("file") of the command. The Machine gives `worktree`.
|
|
251
|
+
input: fromPick(runTest, ({ context }) => ({ worktree: fixTree(context) })),
|
|
221
252
|
onDone: {
|
|
222
253
|
target: "ready",
|
|
223
254
|
// The Agent reads this text as the answer to its run_test pick.
|
|
@@ -235,7 +266,7 @@ const body = jr2Setup({
|
|
|
235
266
|
invoke: {
|
|
236
267
|
src: "report",
|
|
237
268
|
// The Agent gives `summary`. The Machine gives `branch`.
|
|
238
|
-
input: fromPick(finished, ({ context }) => ({ branch: context.
|
|
269
|
+
input: fromPick(finished, ({ context }) => ({ branch: context.branch })),
|
|
239
270
|
onDone: [
|
|
240
271
|
{ guard: ({ event }) => event.output.outcome === "response" && event.output.status < 300, target: "done" },
|
|
241
272
|
{ target: "notReported" },
|
|
@@ -244,6 +275,7 @@ const body = jr2Setup({
|
|
|
244
275
|
},
|
|
245
276
|
done: { type: "final" },
|
|
246
277
|
// Not final states: a final state means `done`. Each one fails the run, and the run ends `failed`.
|
|
278
|
+
notCut: { invoke: { src: "fail", input: { reason: "the Worktree was not cut" } } },
|
|
247
279
|
notReported: { invoke: { src: "fail", input: { reason: "the report did not go out" } } },
|
|
248
280
|
failed: { invoke: { src: "fail", input: { reason: "the coder could not go on" } } },
|
|
249
281
|
lost: { invoke: { src: "fail", input: { reason: "the Sandbox was lost" } } },
|
|
@@ -253,7 +285,6 @@ const body = jr2Setup({
|
|
|
253
285
|
export const machine = workspace(body, {
|
|
254
286
|
input: runInput,
|
|
255
287
|
repos: { app: "https://github.com/<you>/app.git" },
|
|
256
|
-
spec: ({ input }) => ({ branch: input.branch }),
|
|
257
288
|
});
|
|
258
289
|
```
|
|
259
290
|
|
|
@@ -261,7 +292,7 @@ The `coding` state routes [`agent.fault`](../reference/mechanism-events.md#agent
|
|
|
261
292
|
Turn of the coder fails with no pick. Each state that invokes an Agent has a route for it, on the state or on a state
|
|
262
293
|
above it: `jr2 check` refuses a Machine without one. When no transition takes the event, the run ends `error`.
|
|
263
294
|
|
|
264
|
-
`failed`, `notReported` and `lost` are not final states. Each one invokes
|
|
295
|
+
`notCut`, `failed`, `notReported` and `lost` are not final states. Each one invokes
|
|
265
296
|
[`fail`](../reference/authoring-surface/fail.md), and the run ends `failed` with its reason: a final state means `done`.
|
|
266
297
|
Do not throw from a promise to fail, because a throw is a defect, and the run ends `error`.
|
|
267
298
|
|
|
@@ -301,25 +332,24 @@ The options:
|
|
|
301
332
|
- `timeout`: how long the program can run, such as `"90s"`, `"10m"` or `"1h"`. The default is `"10m"`. After it, jr2
|
|
302
333
|
kills the process group, and the outcome is `timeout`.
|
|
303
334
|
- `tail`: how many characters of the end of the output the Machine gets. The default is 4096.
|
|
304
|
-
- `slot`: the [Repo Slot](../reference/glossary.md#repo-slot) whose [Worktree](../reference/glossary.md#worktree) the
|
|
305
|
-
program runs in. Write it only when the Workspace has more than one Repo Slot.
|
|
306
335
|
|
|
307
|
-
The
|
|
308
|
-
the
|
|
309
|
-
|
|
310
|
-
|
|
336
|
+
The input of the Effect says where the program runs. Under [Repo Slots](../reference/glossary.md#repo-slot), `worktree`
|
|
337
|
+
is required: the path of a Worktree that a cut made, or `/work/<slot>/default`. The program starts in the root of that
|
|
338
|
+
folder. In a Workspace with no Repo Slot, it starts in `/work`, the folder that the steps of the Workspace share (see
|
|
339
|
+
[Workspaces](../concepts/workspaces.md#a-workspace-with-no-repo)). To run it in a subfolder, give the path as an
|
|
340
|
+
argument, such as `["node", "--test", "<dir>/test/*.test.js"]`, or write `["sh", "-c", "cd <dir> && <program>"]`.
|
|
311
341
|
|
|
312
342
|
A `command` with no Effect secret runs in the [Harness](../reference/glossary.md#harness) container of the
|
|
313
|
-
[Sandbox](../reference/glossary.md#sandbox),
|
|
314
|
-
[
|
|
315
|
-
[Sandbox Image](../reference/glossary.md#sandbox-image). Thus invoke it in the body of a
|
|
343
|
+
[Sandbox](../reference/glossary.md#sandbox), beside the [Working tools](../reference/glossary.md#working-tools) of the
|
|
344
|
+
Agent. It uses the tools of the [Sandbox Image](../reference/glossary.md#sandbox-image). Thus invoke it in the body of a
|
|
316
345
|
[`workspace()`](../reference/authoring-surface/workspace.md).
|
|
317
346
|
|
|
318
347
|
Each outcome is output for `onDone`, also a failure. Route on `outcome`
|
|
319
348
|
([`CommandOutput`](../reference/authoring-surface/type-commandoutput.md)):
|
|
320
349
|
|
|
321
350
|
- `exited`, with `code` and `tail`. A test that fails is `exited` with a `code` that is not 0. A program that the
|
|
322
|
-
container does not have is `exited` with code 127.
|
|
351
|
+
container does not have is `exited` with code 127. A bad `worktree` is `exited` with code 127 too, and `tail` gives
|
|
352
|
+
the reason. A bad path does not add an outcome.
|
|
323
353
|
- `timeout`, with `tail`.
|
|
324
354
|
- `killed`, with `signal` and `tail`.
|
|
325
355
|
- `unknown`, with `reason`: jr2 cannot tell what the program did. jr2 does not run it again.
|
|
@@ -332,13 +362,14 @@ the value. `secret("<name>").path` gives the path of a file that holds the value
|
|
|
332
362
|
list of `jr2.config.ts`, as for `hook-token`.
|
|
333
363
|
|
|
334
364
|
A `command` with an Effect secret in `env` does not run beside the Agent. It runs in the
|
|
335
|
-
[Sandbox Effector](../reference/glossary.md#sandbox-effector), a container that the Agent cannot reach.
|
|
336
|
-
|
|
365
|
+
[Sandbox Effector](../reference/glossary.md#sandbox-effector), a container that the Agent cannot reach. Its `worktree`
|
|
366
|
+
must be a branch Worktree that a cut made in this Sandbox, because a path alone does not name the target of a
|
|
367
|
+
credential. It runs against one commit of that branch, not against the live Worktree:
|
|
337
368
|
|
|
338
369
|
- By default, the commit is the tip of the branch when the Effect starts. When the Worktree holds changes that the
|
|
339
370
|
program did not see, its output has `dirty: true`.
|
|
340
|
-
- With `pin: true`, the commit is the `sha` of the input of the Effect: `{ sha: "<full commit id>" }`. Use it
|
|
341
|
-
commit that a [Gate](../reference/glossary.md#gate) approved.
|
|
371
|
+
- With `pin: true`, the commit is the `sha` of the input of the Effect: `{ worktree, sha: "<full commit id>" }`. Use it
|
|
372
|
+
for a commit that a [Gate](../reference/glossary.md#gate) approved.
|
|
342
373
|
|
|
343
374
|
The Sandbox Effector runs in the Sandbox Image, or in the image of the `effector` option of `workspace()`. Thus that
|
|
344
375
|
image must hold the program.
|
|
@@ -362,8 +393,10 @@ image must hold the program.
|
|
|
362
393
|
- `maxBody`: how many bytes of a text body the output keeps. The default is 65536.
|
|
363
394
|
- `idempotent`: `true` when a second call is safe. The default is `false`. See [Effects](../concepts/effects.md).
|
|
364
395
|
|
|
365
|
-
To send a file of
|
|
366
|
-
|
|
396
|
+
To send a file of a Worktree as the body, use [`file(path)`](../reference/authoring-surface/file.md) as `body`. The
|
|
397
|
+
input of the Effect names the Worktree, `{ worktree }`, and a cut in this Sandbox must have made it. The path is
|
|
398
|
+
relative to that Worktree. In a Workspace with no Repo Slot, the input has no `worktree`, and the path is relative to
|
|
399
|
+
`/work`. jr2 sends a file body only with `PUT`, `POST` or `PATCH`.
|
|
367
400
|
|
|
368
401
|
An `http` Effect runs in the [Instance Effector](../reference/glossary.md#instance-effector). The
|
|
369
402
|
[Effector](../reference/glossary.md#effector) puts the value of each Effect secret into the call. The Agent never reads
|
|
@@ -414,9 +447,12 @@ jr2 up
|
|
|
414
447
|
```text
|
|
415
448
|
…
|
|
416
449
|
effects: workflow "fix-and-report" — what it runs outside itself (ADR-0067)
|
|
450
|
+
cut: worktree — 600s, in the Harness
|
|
417
451
|
test: command ["node","--test","<file>"] — 300s, in the Harness
|
|
418
452
|
report: http POST https://hooks.example.com — 30s, in the Instance Effector, secrets "hook-token"
|
|
453
|
+
…
|
|
419
454
|
failures: workflow "fix-and-report" — the states that fail the run (ADR-0073)
|
|
455
|
+
fix-and-report.notCut: invokes fail
|
|
420
456
|
fix-and-report.notReported: invokes fail
|
|
421
457
|
fix-and-report.failed: invokes fail
|
|
422
458
|
fix-and-report.lost: invokes fail
|
|
@@ -440,20 +476,19 @@ When you call `command()`, it refuses:
|
|
|
440
476
|
- `pin: true` with no Effect secret in `env`:
|
|
441
477
|
``command(argv, { pin: true }) names no secret in its env: `pin` chooses the commit a credentialed command runs against in the Sandbox Effector, and a command that names no secret runs in the live Worktree (ADR-0067) — name its secret, command(argv, { env: { NAME: secret("x") }, pin: true }), or drop `pin` ``
|
|
442
478
|
|
|
479
|
+
`workspace()` refuses a body with an invoke that does not say where it works, when it can read the input:
|
|
480
|
+
``workspace(): state "<state>": "<slot>" names no `<key>`, and a Workspace with Repo Slots says where each Turn and Effect works: a Worktree, or `cwd: "/work"` for a Turn (ADR-0057)``
|
|
481
|
+
|
|
443
482
|
When a `command` starts, it refuses:
|
|
444
483
|
|
|
445
484
|
- a state outside a `workspace()`. With no Effect secret in `env`:
|
|
446
485
|
`command "<slot>" runs in the Harness container of a Workspace, and its state is not inside a workspace() — invoke it from a Workspace's body (ADR-0067)`.
|
|
447
486
|
With one:
|
|
448
487
|
`command "<slot>" runs in the Sandbox Effector of a Workspace, and its state is not inside a workspace() — invoke it from a Workspace's body (ADR-0067)`
|
|
449
|
-
- no `
|
|
450
|
-
|
|
451
|
-
With an Effect secret in `env`, the text is
|
|
452
|
-
`command "<slot>" runs under a Workspace carrying more than one Repo Slot (<slots>) — say which: { slot: "<slot>" } (ADR-0051)`
|
|
488
|
+
- no `worktree` under Repo Slots, with no Effect secret in `env`:
|
|
489
|
+
``command "<slot>" runs under a Workspace with Repo Slots (<slots>) and its input names no `worktree`: say which, a Worktree that a cut gave or `<slot>/default` ``
|
|
453
490
|
- with an Effect secret in `env`, a Workspace with no Repo Slot:
|
|
454
491
|
`command "<slot>" runs under a Workspace with no Repo Slot: a command with an Effect secret runs against one commit of a Repo Slot's branch, and there is none — declare a Repo Slot, or name no secret in its env, and it runs in /work beside the Agent (ADR-0051)`
|
|
455
|
-
- a `slot` that the Workspace does not have:
|
|
456
|
-
`command "<slot>" names Repo Slot "<name>", which its Workspace does not carry (<slots>)`
|
|
457
492
|
- an `arg()` with no string in the input. The angle brackets around the name are in the text, and `<type>` is `nothing`
|
|
458
493
|
when the input has no such key:
|
|
459
494
|
`command "<slot>": argv slot <<name>> has no string value in its input (got <type>) — map it in the invoke's input`
|
|
@@ -477,7 +512,7 @@ When you call `http()`, it refuses:
|
|
|
477
512
|
- a file body with a method other than `PUT`, `POST` or `PATCH`:
|
|
478
513
|
`http({ method: "<method>", body: file(...) }): a file body is sent with PUT, POST, PATCH (ADR-0068)`
|
|
479
514
|
- a `body` that is not `file()`:
|
|
480
|
-
``http({ body }): a body is file(path, {
|
|
515
|
+
``http({ body }): a body is file(path, { segments?, contentType? }) — json and text come from `request` ``
|
|
481
516
|
- a `content-type` header beside the `contentType` of `file()`:
|
|
482
517
|
`http({ headers, body: file(path, { contentType }) }): "content-type" is set by file()'s contentType — write one of them (ADR-0067)`
|
|
483
518
|
- a `request` with a file body:
|
|
@@ -489,9 +524,16 @@ When an `http` Effect starts, it refuses an input that its `input` schema does n
|
|
|
489
524
|
`http "<slot>": its input does not match the definition's input schema — <path>: <message>`. With a file body, the text
|
|
490
525
|
ends after `input schema`.
|
|
491
526
|
|
|
492
|
-
An `http` Effect with a file body runs in the Sandbox Effector. When it starts, it refuses
|
|
493
|
-
|
|
494
|
-
|
|
527
|
+
An `http` Effect with a file body runs in the Sandbox Effector. When it starts, it refuses:
|
|
528
|
+
|
|
529
|
+
- a state outside a `workspace()`:
|
|
530
|
+
`http "<slot>" runs in the Sandbox Effector of a Workspace, and its state is not inside a workspace() — invoke it from a Workspace's body (ADR-0067)`
|
|
531
|
+
- no `worktree` under Repo Slots:
|
|
532
|
+
``http "<slot>" reads a Worktree file, under a Workspace with Repo Slots, and its input names no `worktree` ``
|
|
533
|
+
|
|
534
|
+
A bad `worktree` is output, because a word of a run can cause it. An `http` Effect with a file body settles `refused`. A
|
|
535
|
+
`command` settles `exited` with code 127, with the reason in `tail`. A credentialed `command` and a file body need a
|
|
536
|
+
cut: `no cut made <path> in this Sandbox: cut it with worktree()`
|
|
495
537
|
|
|
496
538
|
When you call `secret()`, `bearer()` or `text`, they refuse:
|
|
497
539
|
|
|
@@ -534,6 +576,7 @@ When you call `secret()`, `bearer()` or `text`, they refuse:
|
|
|
534
576
|
- [Secrets and trust](../concepts/secrets-and-trust.md): who holds an Effect secret.
|
|
535
577
|
- [The Menu](../concepts/the-menu.md): how a pick waits for its answer.
|
|
536
578
|
- [Push and sign a branch](push-and-sign-a-branch.md): the `push` Effect.
|
|
579
|
+
- [Cut a Worktree](cut-a-worktree.md): the Worktree that a `command` runs in, and the removal of a Worktree.
|
|
537
580
|
- Reference: [`command`](../reference/authoring-surface/command.md),
|
|
538
581
|
[`CommandOptions`](../reference/authoring-surface/type-commandoptions.md),
|
|
539
582
|
[`http`](../reference/authoring-surface/http.md), [`secret`](../reference/authoring-surface/secret.md),
|
|
@@ -31,7 +31,7 @@ The imports:
|
|
|
31
31
|
```ts
|
|
32
32
|
import { fromPromise } from "xstate";
|
|
33
33
|
import { z } from "zod";
|
|
34
|
-
import { customize, defineEvent, pool, source } from "@jr2/orchestrator";
|
|
34
|
+
import { customize, defineEvent, pool, source, type PoolNextInput } from "@jr2/orchestrator";
|
|
35
35
|
import { task } from "@jr2/machines";
|
|
36
36
|
```
|
|
37
37
|
|
|
@@ -59,13 +59,9 @@ type Issue = { number: number; title: string; body: string };
|
|
|
59
59
|
// because the request has no token.
|
|
60
60
|
const ISSUES = "https://api.github.com/repos/you/app/issues?labels=jr2&state=open&per_page=100";
|
|
61
61
|
|
|
62
|
-
// The issues that this Source gave to a worker. A worker has each one now, or it finished it.
|
|
63
|
-
// The set is in the memory of the Orchestrator, and a restart empties it.
|
|
64
|
-
const claimed = new Set<number>();
|
|
65
|
-
|
|
66
62
|
// The Source: give the next open issue that no worker has had.
|
|
67
63
|
const issues = source<Issue>({
|
|
68
|
-
next: fromPromise<Issue | null,
|
|
64
|
+
next: fromPromise<Issue | null, PoolNextInput>(async ({ input }) => {
|
|
69
65
|
const res = await fetch(ISSUES, { headers: { accept: "application/vnd.github+json" } }).catch(() => undefined);
|
|
70
66
|
if (!res?.ok) {
|
|
71
67
|
// While workers run, look again later. With no worker, stop the run with the error.
|
|
@@ -78,12 +74,11 @@ const issues = source<Issue>({
|
|
|
78
74
|
body: string | null;
|
|
79
75
|
pull_request?: unknown;
|
|
80
76
|
}>;
|
|
81
|
-
// The issues API also lists pull requests. Skip them, and skip each issue that a worker has now
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
);
|
|
77
|
+
// The issues API also lists pull requests. Skip them, and skip each issue that a worker has now
|
|
78
|
+
// or that a worker finished.
|
|
79
|
+
const taken = [...input.active, ...input.settled];
|
|
80
|
+
const issue = open.find((i) => i.pull_request === undefined && !taken.includes(`issue-${i.number}`));
|
|
85
81
|
if (!issue) return null;
|
|
86
|
-
claimed.add(issue.number);
|
|
87
82
|
return { number: issue.number, title: issue.title, body: issue.body ?? "" };
|
|
88
83
|
}),
|
|
89
84
|
// Send this event to the `source` Gate when you add the label to an issue.
|
|
@@ -129,10 +124,10 @@ The file has five parts:
|
|
|
129
124
|
When no issue is ready, it gives `null`. The request has no token, so the Repo must be public. Without a token, GitHub
|
|
130
125
|
allows 60 requests in each hour. The example reads only the first page of issues: at most 100. When the request fails
|
|
131
126
|
while workers run, `next` gives `null` in place of the error, so that the failure does not stop the run.
|
|
132
|
-
- **The record of the Source.** `next`
|
|
133
|
-
`
|
|
134
|
-
|
|
135
|
-
|
|
127
|
+
- **The record of the Source.** The Source keeps no record of its own. `next` skips each issue in `active` and in
|
|
128
|
+
`settled`. The Pool holds both, so they stay across a restart of the
|
|
129
|
+
[Orchestrator](../reference/glossary.md#orchestrator). Each run of the Workflow has its own lists, so a new run can
|
|
130
|
+
give an issue that is still open.
|
|
136
131
|
- **The worker.** `fixer` is `task` with its two [Open](../reference/glossary.md#open) parts bound: the Repo and the
|
|
137
132
|
model of the coder. [Bind a shipped Machine's Open parts](bind-open-parts.md) tells more.
|
|
138
133
|
- **The run input.** `door` is the run input of the Pool. `cap` reads `workers` from it. A worker never gets it.
|
|
@@ -149,15 +144,21 @@ jr2 up
|
|
|
149
144
|
…
|
|
150
145
|
jr2 up — instance "my-instance" → context kind-jr2 / namespace my-instance
|
|
151
146
|
typecheck: tsc --noEmit (the instance's own compiler)
|
|
147
|
+
…
|
|
148
|
+
effects: workflow "issues" — what it runs outside itself (ADR-0067)
|
|
149
|
+
cut: worktree — 600s, in the Harness
|
|
150
|
+
…
|
|
152
151
|
failures: workflow "issues" — the states that fail the run (ADR-0073)
|
|
153
152
|
issues.failing: invokes fail
|
|
153
|
+
task.noBranch: invokes fail
|
|
154
|
+
task.notCut: invokes fail
|
|
154
155
|
task.lost: invokes fail
|
|
155
156
|
…
|
|
156
157
|
seats: workflow "issues" — who may send what, per state (ADR-0015)
|
|
157
158
|
task.working agent:coder: [finish]
|
|
158
159
|
task.review gate: [approve, request_changes]
|
|
159
160
|
…
|
|
160
|
-
agents: coder, triager (carried by
|
|
161
|
+
agents: coder, triager (carried by 5 workflow machine(s))
|
|
161
162
|
…
|
|
162
163
|
sandbox nodes: jr2-control-plane
|
|
163
164
|
…
|
|
@@ -180,7 +181,7 @@ jr2 run issues --input '{"workers":2}'
|
|
|
180
181
|
```text
|
|
181
182
|
…
|
|
182
183
|
→ context kind-jr2 / namespace my-instance
|
|
183
|
-
run
|
|
184
|
+
run 7c248a11-445a-4123-8ce2-834cb57706d7 (issues) — attached
|
|
184
185
|
→ active "discovering"
|
|
185
186
|
→ active "discovering"
|
|
186
187
|
→ active "discovering"
|
|
@@ -205,7 +206,8 @@ jr2 status <run id>
|
|
|
205
206
|
```text
|
|
206
207
|
…
|
|
207
208
|
→ context kind-jr2 / namespace my-instance
|
|
208
|
-
{"runId":"
|
|
209
|
+
{"runId":"7c248a11-445a-4123-8ce2-834cb57706d7","workflow":"issues",…,"gates":[{"gate":"source",…},{"gate":"issue-3.body.review",…},{"gate":"issue-2.body.review",…}]}
|
|
210
|
+
…
|
|
209
211
|
```
|
|
210
212
|
|
|
211
213
|
In the JSON that it prints, `gates` holds each open Gate of the run:
|
|
@@ -233,7 +235,7 @@ jr2 send <run id> --gate issue-2.body.review --event approve
|
|
|
233
235
|
```text
|
|
234
236
|
…
|
|
235
237
|
→ context kind-jr2 / namespace my-instance
|
|
236
|
-
delivered approve to gate "issue-2.body.review" on
|
|
238
|
+
delivered approve to gate "issue-2.body.review" on 7c248a11-445a-4123-8ce2-834cb57706d7
|
|
237
239
|
```
|
|
238
240
|
|
|
239
241
|
The Pool then calls `next` again, and starts a worker for the next issue.
|
|
@@ -254,12 +256,18 @@ jr2 send <run id> --gate source --event issues_changed
|
|
|
254
256
|
```text
|
|
255
257
|
…
|
|
256
258
|
→ context kind-jr2 / namespace my-instance
|
|
257
|
-
delivered issues_changed to gate "source" on
|
|
259
|
+
delivered issues_changed to gate "source" on 7c248a11-445a-4123-8ce2-834cb57706d7
|
|
258
260
|
```
|
|
259
261
|
|
|
260
|
-
Without the event, the Pool calls `next` again when a worker finishes, or after `pollEvery`. A
|
|
262
|
+
Without the event, the Pool calls `next` again when a worker finishes, or after `pollEvery`. A script can send the same
|
|
261
263
|
event with [`POST /runs/:runId/gates/:gate/events`](../reference/http-api.md#post-runs-runid-gates-gate-events).
|
|
262
264
|
|
|
265
|
+
A [Hook](../reference/glossary.md#hook) can tell the Pool too. Give the Source a `hooks` handler for the forge's event,
|
|
266
|
+
for example `github` and `issues.labeled`, that gives [`reread()`](../reference/authoring-surface/reread.md), or
|
|
267
|
+
[`skip(reason)`](../reference/authoring-surface/skip.md) for a label that is not `jr2`. Then each delivery of the Hook
|
|
268
|
+
makes the Pool call `next` at once. [Pools and Sources](../concepts/pools-and-sources.md#a-hook-reaches-a-source) tells
|
|
269
|
+
how.
|
|
270
|
+
|
|
263
271
|
## The end of the run
|
|
264
272
|
|
|
265
273
|
When the last worker finishes and no issue is ready, the run ends. In the first terminal, `jr2 run` prints the last
|
|
@@ -269,7 +277,7 @@ state of the Pool, then the status of the run as JSON:
|
|
|
269
277
|
…
|
|
270
278
|
→ active "discovering"
|
|
271
279
|
→ done "drained"
|
|
272
|
-
{"runId":"
|
|
280
|
+
{"runId":"7c248a11-445a-4123-8ce2-834cb57706d7","workflow":"issues","instanceId":"1d3090c8-645b-4e22-b343-68d548aab5f4","startedAt":"2026-11-06T19:20:02.886Z","startedBy":{"kind":"caller","via":"token","name":"instance","onBehalfOf":"kubernetes-admin"},"status":"done","value":"drained","context":{"runInput":{"workers":2,"instanceId":"1d3090c8-645b-4e22-b343-68d548aab5f4"},"cap":2,"active":[],"items":{"issue-2":{"branch":"jr2/issue-2"},"issue-3":{"branch":"jr2/issue-3"},"issue-1":{"branch":"jr2/issue-1"}},"failures":{},"held":[],"ready":[],"claimed":{},"again":[],"latch":false,"taken":3,"carried":[],"refused":[],"status":"drained"},"children":[],"waiting":[],"held":[],"answers":[{"gate":"issue-2.body.review","type":"approve","at":"2026-11-06T19:21:49.638Z","answeredBy":{"via":"token","name":"instance","onBehalfOf":"kubernetes-admin"}},{"gate":"source","type":"issues_changed","at":"2026-11-06T19:21:57.842Z","answeredBy":{"via":"token","name":"instance","onBehalfOf":"kubernetes-admin"}},{"gate":"issue-3.body.review","type":"approve","at":"2026-11-06T19:22:03.493Z","answeredBy":{"via":"token","name":"instance","onBehalfOf":"kubernetes-admin"}},{"gate":"issue-1.body.review","type":"approve","at":"2026-11-06T19:22:33.752Z","answeredBy":{"via":"token","name":"instance","onBehalfOf":"kubernetes-admin"}}],"assembly":"f5bb024883b4","output":{"status":"drained","items":{"issue-2":{"branch":"jr2/issue-2"},"issue-3":{"branch":"jr2/issue-3"},"issue-1":{"branch":"jr2/issue-1"}},"failures":{}},"worktrees":[{"slot":"app","path":"/work/app/task","branch":"jr2/issue-3"},{"slot":"app","path":"/work/app/task","branch":"jr2/issue-2"},{"slot":"app","path":"/work/app/task","branch":"jr2/issue-1"}]}
|
|
273
281
|
```
|
|
274
282
|
|
|
275
283
|
In the JSON, `context.items` holds the output of each worker that finished, by item id. `context.failures` holds the
|
|
@@ -286,8 +294,8 @@ In the JSON, `context.items` holds the output of each worker that finished, by i
|
|
|
286
294
|
workers register the same Gate or [Agent](../reference/glossary.md#agent), the run stops with the status `error`, and
|
|
287
295
|
its `fault` is `registration address "<address>" is already live (two concurrent registrations share an id)`. For
|
|
288
296
|
`task`, `<address>` is `gate/<run id>/<item id>.body.review` or `agent/<run id>/<item id>.body/coder`.
|
|
289
|
-
- `next` gets
|
|
290
|
-
|
|
297
|
+
- `next` gets `active`, the ids of the items that workers have now, and `settled`, the ids of the finished and failed
|
|
298
|
+
items. It also gets `input`, the input of the Pool. To not give a finished item again, skip each id in `settled`.
|
|
291
299
|
- When a worker fails, for example when the Sandbox of a `task` is lost, the Pool keeps the Failure in `failures` and
|
|
292
300
|
goes on. Only the Sandbox of that worker is deleted. The other workers, and the work in their Sandboxes, stay. A
|
|
293
301
|
defect in a worker is different: the run stops with the status `error`.
|
|
@@ -305,9 +313,8 @@ In the JSON, `context.items` holds the output of each worker that finished, by i
|
|
|
305
313
|
starts with ``task: no `branch` on the door and no Instance ID to derive one from``.
|
|
306
314
|
- Without `cap`, one worker runs at a time. Each `task` worker has its own
|
|
307
315
|
[Workspace](../reference/glossary.md#workspace). Thus the run has `cap` Sandboxes or fewer at one time.
|
|
308
|
-
- The Pool takes the `wake` event
|
|
309
|
-
|
|
310
|
-
`error: gate "source" is open, and the run does not take "<event>" now: no writing of it would move the run with this payload — not delivered (takes now: nothing)`.
|
|
316
|
+
- The Pool takes the `wake` event at any time while the run is open. While it waits, it calls `next` at once. While
|
|
317
|
+
`next` runs, it calls `next` once more after that call, so that the change is not lost.
|
|
311
318
|
- `onDrained` takes only `"final"`: the run ends.
|
|
312
319
|
|
|
313
320
|
## Read next
|