@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
|
@@ -6,14 +6,21 @@ A Sandbox pod composes up to four containers around one shared worktree volume (
|
|
|
6
6
|
[ADR-0037](0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md)) with jr2's runtime mounted at
|
|
7
7
|
`/opt/jr2` and the command overridden to start the Harness. The Agent's toolchain lives here because the Working tools
|
|
8
8
|
(read/write/edit/bash) execute here — the tools the Agent can reach are this image's. It is also where a human
|
|
9
|
-
|
|
9
|
+
inspects a run with the agent's exact tools, worktrees, and filesystem: `jr2 workspace shell` starts its shell through
|
|
10
|
+
the spawner, as an Agent's `bash` starts ([ADR-0009](0009-cli-and-instance-interface.md)), so the shell does not reach
|
|
11
|
+
the control Stand-in. One Harness container serves every workspace-ful Agent of the Sandbox. Each process that the
|
|
12
|
+
Harness starts in `/work` — a Working tool's shell, a test, git, a `command` Effect — runs as a confined child of the
|
|
13
|
+
Harness process, so no code from `/work` reaches the control credential that the Harness process holds
|
|
14
|
+
([ADR-0076](0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md)).
|
|
10
15
|
- **Custodian container** — jr2-composed sidecar, upstream Envoy configured at provision by the Orchestrator
|
|
11
16
|
([ADR-0013](0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md),
|
|
12
17
|
[ADR-0059](0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md)): the pod's credential holder. It holds
|
|
13
18
|
the Sandbox token and the held secrets its pod names — model API keys above all — as files, and carries the Harness's
|
|
14
19
|
traffic out: its Menu and the ask to the Orchestrator, and its outbound HTTPS. It exists as a separate container
|
|
15
20
|
precisely _because_ the Working tools give the Agent code execution in the Harness container — the credentials live
|
|
16
|
-
where the Agent cannot read them, and the Harness container
|
|
21
|
+
where the Agent cannot read them, and the Harness container holds only their Stand-ins, which are secret: only the
|
|
22
|
+
Harness process holds them, as files, and a process it confines gets only those that its `workspace()` grants
|
|
23
|
+
(ADR-0059, ADR-0076).
|
|
17
24
|
- **User Container** — opt-in third seat, composed only when the `workspace()` names its image (a `file:` docker context
|
|
18
25
|
the Machine ships or a registry ref, the same resolution as the Sandbox Image, no default —
|
|
19
26
|
[ADR-0049](0049-a-machine-carries-its-parts-and-composes-by-invoke.md)). The authoring surface is that string —
|
|
@@ -22,10 +29,11 @@ A Sandbox pod composes up to four containers around one shared worktree volume (
|
|
|
22
29
|
Size ([ADR-0060](0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md)): a budget is the pod's business, not
|
|
23
30
|
the process's, so it puts nothing in the container. Env and ports are not forwarded, because every key jr2 forwarded
|
|
24
31
|
into the process would be a crack in "jr2 puts nothing in it"; a routable port (the follow-up below) is the known
|
|
25
|
-
candidate for the next key. In the pod it is the container named `user
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
candidate for the next key. In the pod it is the container named `user`: `jr2 workspace shell <ws> --user` is
|
|
33
|
+
`kubectl exec -c user`, and `jr2 workspace forward <ws> <port> [--stdio]` reaches its sshd or any of its listeners by
|
|
34
|
+
port-forward ([ADR-0009](0009-cli-and-instance-interface.md)). It runs its **own entrypoint, untouched**: jr2 injects
|
|
35
|
+
nothing into its process — no env, no command, no probe — and overrides nothing; it mounts only what the checkouts
|
|
36
|
+
need to be checkouts (below). The zero-contract seat, which is exactly why it exists. Two jobs no other seat can do:
|
|
29
37
|
- **Unattended services.** A container's one command belongs to the Harness in the primary seat (ADR-0037), so an
|
|
30
38
|
image's own services — an sshd for managed access, an IDE server, a metrics agent — need a container whose command
|
|
31
39
|
jr2 deliberately does not own. A system built _on_ jr2 that hands people access to their Sandboxes automates through
|
|
@@ -63,8 +71,8 @@ What each container mounts of the credentials, stated once:
|
|
|
63
71
|
|
|
64
72
|
| Container | Mounts |
|
|
65
73
|
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
66
|
-
| `harness` | `jr2-ca` at `/etc/jr2/ca` (the trust bundles). None of the Custodian's volumes.
|
|
67
|
-
| `custodian` | its values (the Sandbox token, its pod's `jr2-held-secrets-<pod>`, each user `secretKeyRef` its pod names) at `/etc/jr2/custodian/values`; its leaves (`jr2-held-tls`); its pod's config (its own `<sandbox>-custodian`, and `program.lua` from `jr2-held`); `upstream.crt`.
|
|
74
|
+
| `harness` | `jr2-ca` at `/etc/jr2/ca` (the trust bundles); the Stand-ins — the control one and each held secret's — as files under `/run/jr2/` that only the Harness process reads and no process it confines can (ADR-0059, ADR-0076); no Stand-in is in its env. None of the Custodian's volumes. |
|
|
75
|
+
| `custodian` | its values (the Sandbox's control token and fetch token, its pod's `jr2-held-secrets-<pod>`, each user `secretKeyRef` its pod names) at `/etc/jr2/custodian/values`; its leaves (`jr2-held-tls`); its pod's config (its own `<sandbox>-custodian`, and `program.lua` from `jr2-held`); `upstream.crt`. |
|
|
68
76
|
| `user` | the checkouts' three volumes, and nothing else. |
|
|
69
77
|
| `effector` | `/work`, `/repos/<key>` and `/opt/jr2` read-only; at `/run/jr2/secrets` (0400) the secrets its Effects name — its pod's `jr2-effector-<pod>` literals, each user `secretKeyRef` by reference — and at `@git/<name>/` each `git.credentials` entry's push Secret the Sandbox CR names (`gitCredentials`), which the operator projects from that entry's Secret by reference into this container alone (`@` is no character of a secret's name, so no secret's file takes that directory); at `/run/jr2/effector` its key (its own `<sandbox>-effector`) and `effector.json`; an emptyDir at `/run/jr2/scratch` as `$HOME`; at `/etc/jr2/effector-ca`, read-only, the instance's caBundle ConfigMap when the instance ships one (ADR-0020). |
|
|
70
78
|
|
|
@@ -98,12 +106,13 @@ jr2 sets `runAsUser` nowhere — each image's own `USER` decides its seat's uid
|
|
|
98
106
|
and plain POSIX modes would then make the other seat's files read-only. jr2 closes that with two mechanisms it happens
|
|
99
107
|
to own, unconditionally (both are inert when the uids already match):
|
|
100
108
|
|
|
101
|
-
- **The pod carries `fsGroup` — the work group, default `2000`.** The default is convention; the one override
|
|
102
|
-
|
|
103
|
-
ADR-0037), for the image whose session users already hold a gid of their own. Never a config key.
|
|
104
|
-
for the jr2 seats: Kubernetes grants the fsGroup as a supplemental group to every container process,
|
|
105
|
-
writes `/work` whatever the number, and no image's `/etc/group` needs to know it. fsGroup puts a setgid
|
|
106
|
-
`/work`'s root, and setgid propagates down, so everything either seat creates is group-_owned_ by the work
|
|
109
|
+
- **The pod carries `fsGroup` — the work group, default `2000`.** The default is convention; the one override is an
|
|
110
|
+
option of `workspace()` (`workspace(body, { workGroup: 2000 })`, beside `image` and `user` — pod composition is the
|
|
111
|
+
wrapper's business, ADR-0037), for the image whose session users already hold a gid of their own. Never a config key.
|
|
112
|
+
Any value is safe for the jr2 seats: Kubernetes grants the fsGroup as a supplemental group to every container process,
|
|
113
|
+
so the Harness writes `/work` whatever the number, and no image's `/etc/group` needs to know it. fsGroup puts a setgid
|
|
114
|
+
group on `/work`'s root, and setgid propagates down, so everything either seat creates is group-_owned_ by the work
|
|
115
|
+
group.
|
|
107
116
|
- **The attach stamps a default ACL on every repo root it creates** — `u::rwx,g::rwx,o::r-x`, set by `work-acl`, a
|
|
108
117
|
static helper on the runtime volume (part of ADR-0037's published `/opt/jr2` surface, static for the same reason `rg`
|
|
109
118
|
is: it executes on a libc jr2 does not control). A default ACL is inherited by everything created beneath it, and
|
|
@@ -116,16 +125,16 @@ to own, unconditionally (both are inert when the uids already match):
|
|
|
116
125
|
Group _membership_ is the one thing an image can still owe. `kubectl exec` sessions and a non-root sshd's logins hold
|
|
117
126
|
the work group automatically — both inherit the container's supplemental groups, where Kubernetes put the fsGroup. A
|
|
118
127
|
**root** sshd is the exception: the logins it setuids rebuild their groups from `/etc/group`, so that image puts its
|
|
119
|
-
session user in the work group itself (`groupadd -g 2000 work && usermod -aG work <user>`) — or the
|
|
128
|
+
session user in the work group itself (`groupadd -g 2000 work && usermod -aG work <user>`) — or the `workspace()` points
|
|
120
129
|
`workGroup` at a gid its users already hold.
|
|
121
130
|
|
|
122
131
|
**The promise is scoped to repo trees.** An ad-hoc path at `/work`'s top level — either seat's `mkdir /work/scratch` —
|
|
123
132
|
carries no default ACL, so files there fall back to the writer's umask. The Harness runs at `umask 002` (itself and
|
|
124
133
|
every tool child) and the attach script opens with the same line, so jr2's own writes are group-writable even there;
|
|
125
134
|
what a User Container session writes outside a repo tree is its own business. A Workspace with no Repo Slot (ADR-0051)
|
|
126
|
-
has no repo tree: its steps share `/work` itself, every Agent and `command` of it
|
|
127
|
-
|
|
128
|
-
more.
|
|
135
|
+
has no repo tree: its steps share `/work` itself, every Agent and `command` of it at the one uid in the one Harness
|
|
136
|
+
container, so they need no ACL; a User Container's writes there have this paragraph's limit, until a user of that seat
|
|
137
|
+
needs more.
|
|
129
138
|
|
|
130
139
|
## Constraints on the seat
|
|
131
140
|
|
|
@@ -134,8 +143,8 @@ The seat is zero-_contract_, not zero-_physics_. What an image brought here must
|
|
|
134
143
|
- **The entrypoint must block.** The pod's restart policy restarts an exited container, so a `CMD` that finishes
|
|
135
144
|
crash-loops. Run a long-lived process — an sshd, an IDE server, `sleep infinity` at minimum.
|
|
136
145
|
- **The ports of jr2's seats are taken** — one network namespace: `:8080` (the Harness), the Custodian's
|
|
137
|
-
`127.0.0.1:8081` (
|
|
138
|
-
pod that has one). Everything else is the image's. A process that binds `:8082` while the Sandbox Effector is down
|
|
146
|
+
`127.0.0.1:8081` (the fetch), `127.0.0.1:15001` (egress) and `:15021` (health), and `:8082` (the Sandbox Effector, in
|
|
147
|
+
a pod that has one). Everything else is the image's. A process that binds `:8082` while the Sandbox Effector is down
|
|
139
148
|
gets signed requests it can neither forge nor answer (ADR-0068): the Effect fails closed.
|
|
140
149
|
- **Root is allowed.** The hardened security context (non-root, all capabilities dropped, no escalation) applies to the
|
|
141
150
|
seats jr2 owns — Harness, Custodian and Sandbox Effector, per container — and deliberately not here: the seat's
|
|
@@ -162,7 +171,10 @@ namespace, node, and the worktree volume, and are one trust domain in every isol
|
|
|
162
171
|
container edge does provide is **credential visibility** — the asymmetry ADR-0013 builds on: the Agent executes code in
|
|
163
172
|
the Harness container and none in the others, so a secret that exists only in another container's mount namespace is out
|
|
164
173
|
of its reach. That is why the Custodian holds the Orchestrator credential and the model keys, and it is what the User
|
|
165
|
-
Container offers a human session
|
|
174
|
+
Container offers a human session. Inside the Harness container, a second edge holds the one credential the Harness keeps
|
|
175
|
+
itself, the control Stand-in: the Harness process is jr2's code, and each process it starts in `/work` is confined from
|
|
176
|
+
that Stand-in by Landlock (ADR-0076, [ADR-0077](0077-jr2-requires-landlock-on-every-sandbox-node.md)). The edges, stated
|
|
177
|
+
plainly:
|
|
166
178
|
|
|
167
179
|
- It holds only for what stays in the private filesystem or session. Anything on `/work` or listening on `localhost` is
|
|
168
180
|
shared with the Agent — the pod has one network namespace. The Custodian's listeners are on `localhost` too; what they
|
|
@@ -206,8 +218,8 @@ Container offers a human session — with its edges stated plainly:
|
|
|
206
218
|
|
|
207
219
|
- The generic `Sandbox` CRD composes all of this as plain container specs
|
|
208
220
|
([ADR-0001](0001-operator-owned-generic-sandbox.md) holds — the operator stays agent-agnostic); the Orchestrator's
|
|
209
|
-
`kubectlSandbox` supplies the images. The pod runs two containers, plus `user` when the
|
|
210
|
-
when an Effect is placed there.
|
|
221
|
+
`kubectlSandbox` supplies the images. The pod runs two containers, plus `user` when the `workspace()` names one and
|
|
222
|
+
`effector` when an Effect is placed there.
|
|
211
223
|
- Every container but the Sandbox Effector carries a limit from the Workspace's Size (ADR-0060): the Custodian a fixed
|
|
212
224
|
kit slice, the User Container its split if stated, the Harness the rest. The Sandbox Effector carries no limit of its
|
|
213
225
|
own: like a User Container with no split, it shares the pod's Size ceiling — the whole Size, as pod-level resources —
|
|
@@ -225,6 +237,8 @@ Container offers a human session — with its edges stated plainly:
|
|
|
225
237
|
- The Harness container's `/work` writes being group-writable widens nothing: every process that could abuse group
|
|
226
238
|
access already runs in the same trust domain, and the work group exists only inside this pod.
|
|
227
239
|
- **Routable access is a follow-up, deliberately untaken**: the operator's Service serves the Harness at `:8080`, and a
|
|
228
|
-
managed system wanting cluster-routed ssh to the User Container needs a declared port there.
|
|
229
|
-
|
|
240
|
+
managed system wanting cluster-routed ssh to the User Container needs a declared port there. A person reaches the User
|
|
241
|
+
Container through the kube API: `jr2 workspace shell --user` by exec, and `jr2 workspace forward` by port-forward,
|
|
242
|
+
which reaches any listener in the pod with no Service. `forward --stdio` is an ssh `ProxyCommand`, so
|
|
243
|
+
`ssh <sandbox>.jr2` with agent forwarding reaches the User Container's sshd (ADR-0009).
|
|
230
244
|
- **GLOSSARY.md**: **User Container** is a glossary term.
|
|
@@ -42,6 +42,9 @@ is the only structured channel. Consequences:
|
|
|
42
42
|
|
|
43
43
|
## Residual
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
jr2's own typed notices now reach a running Agent through the step-boundary read (ADR-0002,
|
|
46
|
+
[ADR-0062](0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md)); the Menu still opens only when the Agent
|
|
47
|
+
reaches a checkpoint or a turn boundary. Free text from a Caller, or from the Machine, is a Steering message on the same
|
|
48
|
+
channel (ADR-0002, [ADR-0078](0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)): the Agent
|
|
49
|
+
hears it once, and it is never a pick or a workflow event. Evidence for the findings above:
|
|
50
|
+
`poc/actor/POC-5B-FINDINGS.md`, `poc/actor/POC-5C-FINDINGS.md` (`poc/` is gitignored; conclusions live here).
|
|
@@ -15,7 +15,7 @@ it from the start (the top Machine keeps only counts + ids, children report comp
|
|
|
15
15
|
snapshot/restore possible.
|
|
16
16
|
|
|
17
17
|
The store's own durability — what a persist survives, and how long a finished run's row lives — is
|
|
18
|
-
[ADR-0065](0065-a-persist-survives-a-crash-and-history-is-a-
|
|
18
|
+
[ADR-0065](0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md).
|
|
19
19
|
|
|
20
20
|
Durable **agent handles** do not ride Machine context at all: the host persists an `iid → admission` ledger beside the
|
|
21
21
|
snapshot in the same save (ADR-0016). Iids are globally unique, so restore needs no context-tree walking to find them.
|
|
@@ -63,12 +63,23 @@ export default defineConfig({
|
|
|
63
63
|
([ADR-0068](0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md)): the credentials the Agents use — an
|
|
64
64
|
Anthropic key, read from `process.env`/`.env`, held by the Custodian and never by the Harness (ADR-0059) — and the
|
|
65
65
|
ones an Effect names, which only its Effector holds. Where each goes is read, never declared.
|
|
66
|
+
- **`mcp` is the Instance's MCP servers**
|
|
67
|
+
([ADR-0079](0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md)): each
|
|
68
|
+
by a name that the author chooses, remote (`url`, an optional `secret`) or stdio (`command`, `args`), with a
|
|
69
|
+
`timeout`. An Agent and an `mcp` Effect name a server by that name.
|
|
70
|
+
- **`callers`, `hooks` and `cors`** are the Instance's reach for Callers
|
|
71
|
+
([ADR-0078](0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)): the named Caller tokens
|
|
72
|
+
and the JWT issuers with their scopes, the Hooks with their credentials and the Hook kinds that the config imports,
|
|
73
|
+
and the origins a browser app may call from.
|
|
66
74
|
- **`registry`** (deployment-varying, resolve from env): absent → images are `kind load`-ed; present → pushed
|
|
67
75
|
(ADR-0019). **`kitRegistry`** (also env) re-homes the published Kit image refs for self-hosted, air-gapped, or
|
|
68
76
|
mirror-only clusters ([ADR-0044](0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md)).
|
|
69
77
|
- **The snapshot store is sqlite** on a PVC in the instance's namespace (zero setup); no other store is owed
|
|
70
|
-
([ADR-0065](0065-a-persist-survives-a-crash-and-history-is-a-
|
|
71
|
-
sqlite, and `replicas: 1` keeps it single-writer.
|
|
78
|
+
([ADR-0065](0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md)) — the single-table, single-writer
|
|
79
|
+
snapshot fits sqlite, and `replicas: 1` keeps it single-writer. The kept conversations are a second sqlite file on the
|
|
80
|
+
same volume ([ADR-0023](0023-the-harness-prints-the-conversation.md)).
|
|
81
|
+
- **`runs`** sets how long jr2 keeps an ended run and the size of the state volume, `{ retain: "30d", volume: "5Gi" }`
|
|
82
|
+
by default (ADR-0065).
|
|
72
83
|
- Git credentials for private repos: an HTTPS token from `.env`, or a `jr2-git-ssh` Secret holding the key `jr2 up`
|
|
73
84
|
asked the user to choose — a generated in-cluster deploy keypair (the recommended default), a local key, or one pasted
|
|
74
85
|
on stdin (ADR-0019, [ADR-0047](0047-the-git-ssh-key-source-is-the-users-choice.md)). Kube target: the current
|
|
@@ -79,28 +90,42 @@ export default defineConfig({
|
|
|
79
90
|
The kit publishes to npm under `@jr2/*`; stock Agent definitions ship in **`@jr2/agents`** (coder, reviewer, …), and a
|
|
80
91
|
package may ship whole Machines with their Agents and image inside (ADR-0049). Because a definition is plain data
|
|
81
92
|
(ADR-0018), composition needs no API: a Machine carries `agent(coder)`, a workflows file exports a packaged Machine
|
|
82
|
-
as-is or through `customize(machine, { agents: { coder: { model: "…" } } })`.
|
|
83
|
-
|
|
93
|
+
as-is or through `customize(machine, { agents: { coder: { model: "…" } } })`. An Agent's tools beyond its own come from
|
|
94
|
+
the MCP servers that it names, and the same `customize()` rebinds them
|
|
95
|
+
([ADR-0079](0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md)). Adding
|
|
96
|
+
skills waits on the definition contract growing that seat (ADR-0018).
|
|
84
97
|
|
|
85
98
|
## Orchestrator HTTP API (hono, run-addressed-by-id)
|
|
86
99
|
|
|
87
100
|
Push + control + observe only — the **pull** path needs no HTTP (the Orchestrator's Source pulls, ADR-0017). Auth bands
|
|
88
101
|
per ADR-0014: structure + observation open; the Agent surface takes the Sandbox token; run state, control, and gates
|
|
89
|
-
take the Instance token.
|
|
102
|
+
take a Caller with the scope in brackets (ADR-0078); `[instance]` is the Instance token alone.
|
|
90
103
|
|
|
91
104
|
```
|
|
92
105
|
GET /workflows # list registered workflows [open]
|
|
93
106
|
GET /workflows/:name/machine /viz/* # machine structure + visualizer [open]
|
|
94
107
|
GET /workflows/:name/runs[, /:runId/events] # observation projection (ADR-0014) [open]
|
|
108
|
+
GET /workflows/:name/runs/:runId/machine # the run's own Machine, Charts compiled (ADR-0069) [open]
|
|
95
109
|
GET /workflows/:name/events # SSE: the whole workflow, level-triggered (ADR-0022) [open]
|
|
96
|
-
POST /workflows/:name/runs # start a run (push work); body = input → { runId } [
|
|
97
|
-
|
|
98
|
-
GET /runs
|
|
99
|
-
GET /runs/:runId
|
|
100
|
-
|
|
101
|
-
|
|
110
|
+
POST /workflows/:name/runs # start a run (push work); body = input → { runId } [start]
|
|
111
|
+
# a standing Workflow: resume a paused one [?drop-held=true] (ADR-0017)
|
|
112
|
+
GET /runs # run summaries; ?status ?workflow ?since ?cursor [read]
|
|
113
|
+
GET /runs/:runId # durable run status (read-through) [read]
|
|
114
|
+
GET /runs/resolve?prefix=<p> # run ids sharing a prefix, live + settled [any scope]
|
|
115
|
+
GET /runs/:runId/events # SSE: status replay + live deltas [read]
|
|
116
|
+
GET /runs/:runId/conversations # the run's kept conversations (ADR-0023) [read]
|
|
117
|
+
GET /runs/:runId/conversations/:iid[?after=<seq>] /events # entries; SSE follow [read]
|
|
118
|
+
GET /gates # open Gates the Caller may answer; ?workflow [answer]
|
|
119
|
+
POST /runs/:runId/events # run-level infra interrupt: CANCEL [cancel]
|
|
120
|
+
POST /runs/:runId/gates/:gate/events # deliver a workflow event to an open gate [answer]
|
|
121
|
+
POST /runs/:runId/steering # a Steering message to an Agent slot (ADR-0002) [steer]
|
|
122
|
+
GET /runs/:runId/agents # the slot list, one row per Agent slot [steer]
|
|
123
|
+
GET /whoami # the request's Caller record (ADR-0078) [any Caller]
|
|
124
|
+
POST /hooks/:name # a Hook's delivery (ADR-0011) [the Hook's verifier]
|
|
125
|
+
GET /hooks/:name # the Hook's log, newest first (ADR-0011) [instance]
|
|
102
126
|
GET /repos # per-Repo node-cache sync state (ADR-0048) [instance]
|
|
103
127
|
GET /agents/:iid/surface POST /agents/:iid/events # the Menu, via the Custodian [sandbox]
|
|
128
|
+
POST /agents/:iid/conversation # a batch of the record's entries (ADR-0023) [sandbox]
|
|
104
129
|
POST /sandboxes/:name/fetch # the ask (ADR-0053) [sandbox]
|
|
105
130
|
GET /healthz GET /readyz
|
|
106
131
|
```
|
|
@@ -112,11 +137,11 @@ ADR-0047's "register the key, the cache agent retries" points at. Instance band,
|
|
|
112
137
|
carries git's error text, exactly the class of author/deployment detail the open projections strip (ADR-0014).
|
|
113
138
|
|
|
114
139
|
Gates are the human/webhook/CI seam (ADR-0011): a gated state registers `{ gate, accepts, meta }`; `GET /runs/:runId`
|
|
115
|
-
lists the open gates (with schemas + `meta`),
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
**ends** the run — its Agents' turns end with
|
|
119
|
-
([ADR-0025](0025-cancel-ends-a-run-stop-parks-it.md)).
|
|
140
|
+
lists the open gates (with schemas + `meta`), `GET /gates` lists the open Gates of every live run that the Caller may
|
|
141
|
+
answer, and the gate POST validates the body against the named event schema and delivers it into that state. Per-gate
|
|
142
|
+
addressing exists because concurrent children park concurrently — a run-level events POST is ambiguous. `CANCEL` is the
|
|
143
|
+
one reserved run-level event; everything else is workflow vocabulary. It **ends** the run — its Agents' turns end with
|
|
144
|
+
it and it does not restore ([ADR-0025](0025-cancel-ends-a-run-stop-parks-it.md)).
|
|
120
145
|
|
|
121
146
|
## The `jr2` CLI
|
|
122
147
|
|
|
@@ -137,25 +162,78 @@ jr2 version [--local] [--json] # this jr2, the global that handed off, the I
|
|
|
137
162
|
|
|
138
163
|
# runs / workflows (wrap the HTTP API)
|
|
139
164
|
jr2 run <workflow> [--input <json>] [--detach]
|
|
140
|
-
jr2
|
|
165
|
+
jr2 run <workflow> [--drop-held] # resume a paused standing Pool (ADR-0017)
|
|
166
|
+
jr2 runs [--status <s>] [--workflow <w>] [--since <d>] # live runs by default; `--status ended|all|<list>`
|
|
167
|
+
jr2 workflows [--json] # the Workflows a run can start, with their origins and Charts (ADR-0070)
|
|
168
|
+
jr2 status [runId|abbrev] # bare `status` = the instance's repos (ADR-0048)
|
|
169
|
+
jr2 watch <runId|abbrev> [--json] # the run's status feed, followed to its end
|
|
170
|
+
jr2 logs <runId|abbrev> [agent] [-f] # the run's conversations, printed as ADR-0023 prints them
|
|
171
|
+
jr2 gates [--workflow <w>] # the open Gates this Caller may answer, oldest first
|
|
141
172
|
jr2 send <runId|abbrev> --event CANCEL
|
|
142
173
|
jr2 send <runId|abbrev> --gate <gate> --event <name> [--input <json>]
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
jr2
|
|
147
|
-
jr2
|
|
174
|
+
jr2 steer <runId|abbrev> [<agent> "<text>"] # a Steering message; the slots alone (ADR-0002)
|
|
175
|
+
|
|
176
|
+
# Callers and Hooks (ADR-0078, ADR-0011)
|
|
177
|
+
jr2 whoami # the Caller this credential is
|
|
178
|
+
jr2 caller token <name> # print a named Caller token's value
|
|
179
|
+
jr2 caller rotate <name> # make a new value; the old one stops working
|
|
180
|
+
jr2 hooks <name> # the Hook's log of deliveries
|
|
181
|
+
|
|
182
|
+
# workspaces (kubectl-style, over the operator's Sandbox CRs; kube credentials only)
|
|
183
|
+
jr2 workspace ls [run] # the Workspaces, with run, path, status and age
|
|
184
|
+
jr2 workspace shell <ws> [--user] [-- <cmd>] # a shell in the Workspace, as its Agents work (--user: the User Container)
|
|
185
|
+
jr2 workspace forward <ws> <port> [--stdio] # a local port to a port in the pod (--stdio: an ssh ProxyCommand)
|
|
186
|
+
jr2 workspace rm <ws> # remove a Sandbox that no live run holds
|
|
187
|
+
|
|
188
|
+
# shell completion
|
|
189
|
+
jr2 completion bash|zsh|fish # the completion script, generated from the command table
|
|
148
190
|
```
|
|
149
191
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
192
|
+
**The workspace verbs read the cluster, not the Orchestrator.** They use the kube credentials of the current context and
|
|
193
|
+
no Caller scope, so they work when the Orchestrator is down, and kube RBAC is the gate. A Workspace's handle is
|
|
194
|
+
`<run-prefix>/<workspace path>`: a prefix of its run's id and its path in the run's Machine, read off the CR's label
|
|
195
|
+
`jr2.dev/run` and its annotation `jr2.dev/workspace-path` ([ADR-0012](0012-workspace-wrapper-machine.md)). The CLI lists
|
|
196
|
+
the instance's CRs and compares on the client, because a label selector cannot match a prefix. `workspace ls` prints the
|
|
197
|
+
exact path. The CR's name is also a handle, and a handle that ends in `.jr2` is the CR's name with that suffix, for the
|
|
198
|
+
ssh config below. A handle that matches more than one Workspace prints the candidates and fails, as a run prefix does.
|
|
199
|
+
|
|
200
|
+
- **`workspace shell`** asks the Harness to start a shell through its spawner, so the shell is a process of the Agents'
|
|
201
|
+
Landlock domain, with its env, the Workspace's grants and its cwd
|
|
202
|
+
([ADR-0076](0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md)). It can signal and trace
|
|
203
|
+
the Agents' processes, as a later `bash` call can. So the shell has the agent's own tools, worktrees and filesystem
|
|
204
|
+
([ADR-0037](0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md)), and it never reaches the control
|
|
205
|
+
Stand-in or any Stand-in that the Workspace does not grant. `-- <cmd>` runs one command in place of a shell. `--user`
|
|
206
|
+
is `kubectl exec -c user` into the optional User Container ([ADR-0005](0005-sandbox-pod-composition.md)), where jr2
|
|
207
|
+
starts nothing of its own.
|
|
208
|
+
- **`workspace forward`** is a port-forward to the Sandbox pod: a local port to `<port>` in the pod's network namespace,
|
|
209
|
+
which reaches a server the Agent started or the User Container's sshd. It prints `127.0.0.1:<local port>` and holds
|
|
210
|
+
the forward until Ctrl-C. With `--stdio` it carries the connection on its stdin and stdout, so it is an ssh
|
|
211
|
+
`ProxyCommand`: `Host *.jr2` with `ProxyCommand jr2 workspace forward %h 22 --stdio -n <namespace>`, where the host is
|
|
212
|
+
the CR's name with `.jr2` after it. ssh runs the command in any folder, and with `-n` the verb needs no instance
|
|
213
|
+
folder. There is no routable Service for the User Container; the forward reaches it through the kube API.
|
|
214
|
+
- **`workspace rm`** deletes a Sandbox that no live run holds: an ended run's Sandbox that waits for its idle timeout,
|
|
215
|
+
or one that a fault left as evidence (ADR-0021). It refuses the Sandbox of a live run and says "cancel the run first".
|
|
216
|
+
It tells from the cluster alone, with no Orchestrator, by the hold mark that the Lease writes on the CR (ADR-0021):
|
|
217
|
+
`jr2.dev/hold` is `held` from the Lease's first write, and the Lease sets it to `released` when it stops for the
|
|
218
|
+
body's final state, a fault, a cancel or a parent that leaves the Workspace's state, but not while a live Workspace of
|
|
219
|
+
the run holds the same name (a parent that invokes the same Machine again after a defect). A restore that ends a run
|
|
220
|
+
`drifted` or `lost` without hosting it marks each of its CRs `released`. A park and a process shutdown write nothing,
|
|
221
|
+
so the CR of a run that an Orchestrator crash left stays `held`. `rm` removes a CR that is `released`, and a CR whose
|
|
222
|
+
`jr2.dev/keepalive` is older than the CR's idle timeout, which the operator reaps then in any case. It refuses every
|
|
223
|
+
other CR, a CR with no mark included. It measures the keepalive's age on the API server's clock (the `Date` header of
|
|
224
|
+
its answer), not on the local clock, so a fast laptop clock cannot make a live Sandbox look free. There is no
|
|
225
|
+
`--force`: the run owns its Workspace's life (ADR-0012).
|
|
226
|
+
|
|
227
|
+
Pod logs stay `kubectl logs`. The conversations of a Workspace's Agents are `jr2 logs <run> [agent]`, which reads the
|
|
228
|
+
Orchestrator's record and works after the Sandbox is gone (ADR-0023).
|
|
229
|
+
|
|
230
|
+
**`jr2 completion`** prints the script for bash, zsh or fish, made from the command table, so a verb or a flag cannot
|
|
231
|
+
complete that the CLI does not take. It completes dynamic values too: Workspace handles (from the CR labels and the
|
|
232
|
+
`jr2.dev/workspace-path` annotation), live run ids, Workflow names, and the Agent slots of a run. A lookup that takes
|
|
233
|
+
more than 2 s offers nothing, so a slow cluster never holds the shell.
|
|
155
234
|
|
|
156
235
|
The CLI is the everyday surface; HTTP is the machine-to-machine one. The workspace verbs make the orchestrator feel like
|
|
157
|
-
`kubectl` for agents
|
|
158
|
-
can group without the orchestrator being reachable. CLI and hono app sit on one shared API client.
|
|
236
|
+
`kubectl` for agents. CLI and hono app sit on one shared API client.
|
|
159
237
|
|
|
160
238
|
A planned `jr2 build` (build + push + render manifests, no apply — the pure-GitOps CI verb) is deferred; `jr2 up` in CI
|
|
161
239
|
covers the interim (ADR-0019).
|
|
@@ -165,9 +243,10 @@ covers the interim (ADR-0019).
|
|
|
165
243
|
**Instance addressing.** The CLI finds its instance by walking up from cwd to the directory containing `jr2.config.ts` —
|
|
166
244
|
the root marker. The _deployment_ is addressed by the current kube context + the instance's namespace: run-verbs
|
|
167
245
|
port-forward the Orchestrator Service for the duration of the command and read the Instance token from its in-cluster
|
|
168
|
-
Secret (kube RBAC is the gate)
|
|
169
|
-
|
|
170
|
-
drift is visible
|
|
246
|
+
Secret (kube RBAC is the gate); its Caller is `token/instance`, on behalf of the kube user (ADR-0078). `--url` /
|
|
247
|
+
`JR2_URL` (+ `JR2_TOKEN`, any Caller's credential) overrides both — the ingress-exposed/remote-caller case — and skips
|
|
248
|
+
the folder walk entirely. Every run-verb prints the context it targets on stderr, so ambient-context drift is visible
|
|
249
|
+
(ADR-0019).
|
|
171
250
|
|
|
172
251
|
**The command table.** Each verb is an entry in `packages/cli/src/command-table.ts`: its arguments, its flags, the env
|
|
173
252
|
vars it reads, what it prints, and its ADRs, as data. The verb's `parseArgs` options come from its entry, and so does
|
|
@@ -178,12 +257,18 @@ usage line. The CLI Reference of the docs is generated from the same table
|
|
|
178
257
|
|
|
179
258
|
**Two output classes (amended 2026-09-20).** The stream rule below — JSON result on stdout, activity on stderr — was
|
|
180
259
|
written for `jr2 run`, whose reason is the pipe: `jr2 run … | jq` must yield the result alone. It generalises to every
|
|
181
|
-
**result verb** — `run`, `runs`, `status <runId>`, `
|
|
182
|
-
lines beside it are advisory. It does not generalise to a **report verb** — `version`,
|
|
183
|
-
|
|
184
|
-
readable form exists nowhere. So a report verb prints its human table on **stdout**, and `--json` swaps
|
|
185
|
-
the
|
|
186
|
-
`check`, `up`, `down`, `gc
|
|
260
|
+
**result verb** — `run`, `runs`, `status <runId>`, `gates`, `kit push` — because their result is small and the stderr
|
|
261
|
+
lines beside it are advisory. It does not generalise to a **report verb** — `version`, `status` with no run id, `watch`,
|
|
262
|
+
`logs` and `workspace ls` — whose output a human reads whole: dual-printing there shows a JSON blob and a partial table,
|
|
263
|
+
and the readable form exists nowhere. So a report verb prints its human table or lines on **stdout**, and `--json` swaps
|
|
264
|
+
them for the JSON (for `watch`, the feed's JSON frames; `logs` has no `--json`). No `isatty` sniffing: output that
|
|
265
|
+
changes with how it is invoked is a debugging trap of its own. `init`, `check`, `up`, `down`, `gc`, `send`,
|
|
266
|
+
`caller rotate` and `workspace rm` are neither class — activity only, no result. `workspace shell` and
|
|
267
|
+
`workspace forward --stdio` give the terminal to the session or the connection. `whoami` is a result verb. A **value
|
|
268
|
+
verb** — `caller token`, `completion`, `workspace forward` (its local address) — prints one value alone on stdout, not
|
|
269
|
+
JSON, and activity on stderr, because its one use is `JR2_TOKEN=$(jr2 caller token ci)`, and a JSON string there needs
|
|
270
|
+
`jq -r` for nothing. `caller rotate` prints no value: the new one is a `caller token` away, and a value is printed only
|
|
271
|
+
when it is asked for.
|
|
187
272
|
|
|
188
273
|
**`jr2 version` (amended 2026-09-20).** The report you paste into a bug: what is _here_ and what is _deployed_, side by
|
|
189
274
|
side, so the gap between the halves is the answer ("you edited the pin and never ran `jr2 up`"). The local half: `cli`
|
|
@@ -225,13 +310,28 @@ orchestrator**, not a temporary per-invocation runtime — a jr2 run is durable
|
|
|
225
310
|
outliving the CLI call. `--detach` starts the run, prints its `runId`, returns.
|
|
226
311
|
|
|
227
312
|
**Attach / detach / re-attach.** A run lives server-side, so attaching = opening `GET /runs/:runId/events` and detaching
|
|
228
|
-
= closing it (Ctrl-C); neither affects the run. `jr2
|
|
229
|
-
|
|
230
|
-
|
|
313
|
+
= closing it (Ctrl-C); neither affects the run. `jr2 watch <runId>` re-attaches to any run: it prints the lines that
|
|
314
|
+
`jr2 run` prints while it is attached, follows the run to its end, and for an ended run prints its last status once. It
|
|
315
|
+
exits 0 when it followed the feed to its end, for a failed or a parked run too: its exit code is the watch's, and
|
|
316
|
+
`jr2 run`'s is the run's result. On attach, the SSE **replays the run's current status immediately** (so a parked run
|
|
317
|
+
shows where it is) before streaming live deltas; it carries `status` events (auto, per transition) and `emit` events
|
|
318
|
+
(the workflow author's `emit({…})`).
|
|
231
319
|
|
|
232
320
|
**Terminal runs stay readable.** The final snapshot persists to the store before the run drops from the live registry,
|
|
233
321
|
so `status` / `GET /runs/:runId` **read through to the store** — a completed run reports its terminal status/context
|
|
234
|
-
instead of `404`.
|
|
322
|
+
instead of `404`.
|
|
323
|
+
|
|
324
|
+
**`GET /runs` lists history.** Its filters are `status` (`live` by default; `ended`, `all`, or a list of statuses),
|
|
325
|
+
`workflow` and `since` (a duration back from now, such as `7d`, or an ISO-8601 time). It lists the newest start first, a
|
|
326
|
+
page at a time: `limit` (50 by default, at most 500) and `cursor`, and the answer is `{ runs, next? }`. An item is a
|
|
327
|
+
summary, not the whole `RunStatus`:
|
|
328
|
+
`{ runId, workflow, status, reason?, startedBy?, startedAt, endedAt?, assembly?, openGates, placing?, dispatchedBy? }`,
|
|
329
|
+
where `placing` names the child paths of the Workspaces that wait for capacity
|
|
330
|
+
([ADR-0064](0064-a-workspace-waits-for-capacity.md)) and `dispatchedBy` names the run that started this one
|
|
331
|
+
([ADR-0071](0071-a-run-starts-a-run-through-a-dispatch-effect.md)), because `jr2 runs` prints both. A `drifted` run is
|
|
332
|
+
listed, with its reason (ADR-0030). A `live` row whose restore a boot deferred or that failed is listed as `active`,
|
|
333
|
+
with the reason `waiting for restore`. A run is listed while its row is kept (`runs.retain`, ADR-0065). `jr2 runs` takes
|
|
334
|
+
the same filters as `--status`, `--workflow` and `--since`.
|
|
235
335
|
|
|
236
336
|
**Run ids abbreviate to a unique prefix.** A run id is a bare uuid, so every id-taking verb takes a git-style short
|
|
237
337
|
form: `jr2 status 1a2b3c4d`. **Prefix only** — not fuzzy, not suffix — which keeps it an indexed range scan and keeps
|
|
@@ -250,10 +350,10 @@ returns silently for an unknown run, so an unresolved CANCEL used to report succ
|
|
|
250
350
|
The candidate set is the ids that **exist**, not the ones that read: `lost` runs are included, because dropping them
|
|
251
351
|
would let a prefix they share with a live run resolve silently to the live one. The set unions the live registry with
|
|
252
352
|
the store — neither alone is complete, since `persist()` is microtask-scheduled (a just-started run is live before it is
|
|
253
|
-
stored) and a settled run is stored but not live.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
353
|
+
stored) and a settled run is stored but not live. It answers ids only — no status, no context, no gates — to a Caller
|
|
354
|
+
with any scope for their Workflow, where `GET /runs` needs `read`, and it is guarded rather than open, since prefix
|
|
355
|
+
probing on an open route would be a run-id enumeration oracle. Any scope opens it, for the Workflows of that scope,
|
|
356
|
+
because a Caller that may only cancel or steer also addresses a run by a prefix.
|
|
257
357
|
|
|
258
358
|
**`jr2 init` (v1).** Scaffolds the minimum runnable instance: `jr2.config.ts` (root marker), `package.json` (deps on
|
|
259
359
|
`@jr2/*` + xstate), one starter `workflows/<name>.ts`, `images/default/Dockerfile` (the Sandbox Image every Workspace
|
|
@@ -266,10 +366,11 @@ lockfile is what picks one ([ADR-0043](0043-the-kit-is-tested-as-installed-a-loc
|
|
|
266
366
|
|
|
267
367
|
## Consequences
|
|
268
368
|
|
|
269
|
-
- The instance-facing packages (`@jr2/cli`, `@jr2/orchestrator`, `@jr2/agent-protocol`) publish to npm;
|
|
270
|
-
ships inside a Kit image, never via npm
|
|
369
|
+
- The instance-facing packages (`@jr2/cli`, `@jr2/orchestrator`, `@jr2/agent-protocol`, `@jr2/machines`) publish to npm;
|
|
370
|
+
`@jr2/harness` ships inside a Kit image, never via npm
|
|
271
371
|
([ADR-0043](0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md)). The CLI ships as the `jr2`
|
|
272
372
|
bin (`npx jr2`).
|
|
273
|
-
- Workspaces are a first-class CLI resource backed by the operator's `Sandbox` CRs, label-linked to their runs
|
|
373
|
+
- Workspaces are a first-class CLI resource backed by the operator's `Sandbox` CRs, label-linked to their runs and their
|
|
374
|
+
paths, and reached with kube credentials alone.
|
|
274
375
|
- Code is never loaded from a third party at run time (ADR-0008, ADR-0069): discovery is over the instance's own
|
|
275
376
|
Assembly, which only `jr2 up` publishes. A run may bind a Machine's Open body with a Chart, which is data (ADR-0070).
|
|
@@ -81,14 +81,16 @@ Cucumber.js**, living in a top-level `./features/` workspace package (`@jr2/e2e`
|
|
|
81
81
|
one is the tier watching the fix rather than merely enjoying it: the retry is absorbed by design, so without it a
|
|
82
82
|
cluster drifting toward the 90s window would keep passing — slightly slower, silently — until the day it did not.
|
|
83
83
|
- **An opt-in `@console` tier for the Console's UX** (added with
|
|
84
|
-
[ADR-0032](0032-the-console-unlocks-with-
|
|
84
|
+
[ADR-0032](0032-the-console-unlocks-with-a-callers-credential.md)). The Console's risky behavior is interaction —
|
|
85
85
|
token-mode switching, the frame-triggered gate inbox, selection vs. folding — which no reducer test sees and no CLI
|
|
86
86
|
step drives. These scenarios hold a **Playwright page inside ordinary Cucumber steps** (Playwright the _library_, not
|
|
87
87
|
the `@playwright/test` runner): the browser is a second driver of the same black box, reusing the per-scenario
|
|
88
88
|
orchestrator fixture unchanged, and a scenario may drive both (the CLI starts a run; the browser sees it park). Tagged
|
|
89
89
|
`@console` and **excluded from the default profile** exactly as `@kind` is — the everyday suite must not require a
|
|
90
|
-
Chromium install — but they need no docker: a host-booted orchestrator serves the Console fine.
|
|
91
|
-
|
|
90
|
+
Chromium install — but they need no docker: a host-booted orchestrator serves the Console fine. The one exception is
|
|
91
|
+
the login, which needs an identity provider: its scenario runs Dex in a container and is also tagged `@dex`. The
|
|
92
|
+
`console` profile runs it; a host with no docker runs `--tags "@console and not @dex"`. Scope discipline: they assert
|
|
93
|
+
what a user does and sees, never pixels or layout; what the page _believes_ stays in `console-store.test.ts`.
|
|
92
94
|
- **Harness conformance, in `@jr2/harness`, in the default gate**
|
|
93
95
|
([ADR-0027](0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md)). The three tiers above all reach the
|
|
94
96
|
Harness through the **stub Harness**, which is a hand-written model of the _wire_ — it holds no conversation, so it
|