@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
|
@@ -30,20 +30,35 @@ whose processes are gone, with no way to know why.
|
|
|
30
30
|
workflow prompts you again when something is." The last sentence is safe because the wake asks a model that stopped
|
|
31
31
|
with nothing allowed when a pick opens ([ADR-0016](0016-agent-turn-mechanics-are-internal.md)). A line that says a
|
|
32
32
|
Turn ends with exactly one pick would be false for a pick that keeps the Turn open.
|
|
33
|
+
- **An Agent with MCP tools is told that they are not picks.** Its standing part has one more statement: "Your tools
|
|
34
|
+
named `mcp__<server>__<tool>`, where `<server>` is not `jr2`, act on outside services, not on the workflow. A call to
|
|
35
|
+
one is not a pick, and it does not end your Turn." Both tool families share the `mcp__` prefix: the Menu's own tools
|
|
36
|
+
are `mcp__jr2__<event>`, so the statement names `jr2` as the exception. A model that read a call of a service as its
|
|
37
|
+
pick would stop with the workflow's work not done. The statement names no server and no tool, so it is byte-stable; an
|
|
38
|
+
Agent with no MCP tools does not get it. A `"none"` Agent with MCP tools is not told that it acts only by picking from
|
|
39
|
+
its Menu, because its MCP tools act too. The `@model` claim `mcp-then-pick` measures it
|
|
40
|
+
([ADR-0066](0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md),
|
|
41
|
+
[ADR-0079](0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md)).
|
|
33
42
|
- **The Turn part is written once per Submission, and restated when its Allowed picks change.** When something other
|
|
34
43
|
than the Agent's own pick changes them inside the Turn, the Harness appends the Allowed picks again at the next step
|
|
35
44
|
boundary, in a `<jr2-turn>` block of their own: "The workflow's state changed since you last read the Allowed picks.
|
|
36
45
|
Allowed now: …" ([ADR-0029](0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md)). It is
|
|
37
|
-
appended, never a rewrite of the first, so the cache argument above holds for it too. It carries
|
|
38
|
-
|
|
46
|
+
appended, never a rewrite of the first, so the cache argument above holds for it too. It carries a pick's briefing
|
|
47
|
+
when the restatement opens that pick. The same read at a step boundary returns the conversation's pending notices, and
|
|
48
|
+
the Harness appends them in the same way (below).
|
|
39
49
|
- **The Turn part comes last, because a model obeys what it read last.** Measured on the home-lab vLLM model: with a
|
|
40
50
|
prompt that argued for a pick the Turn did not allow, the Allowed picks placed ahead of the prompt were obeyed 0/10
|
|
41
51
|
(thinking off) and 5/10 (thinking on); placed after it, 10/10 both. Wording made no difference. The author's task
|
|
42
52
|
still reads first, as it would with no jr2.
|
|
43
53
|
- **Always on.** No author can turn it off: every fact in it passes the rule above, so removing one can only make the
|
|
44
54
|
Agent act worse. An `instructions` that repeats a fact is harmless.
|
|
45
|
-
- **A notice is
|
|
46
|
-
|
|
55
|
+
- **A notice is heard once by each member of its audience.** A member takes it at its next admission, or at a step
|
|
56
|
+
boundary of an open Turn: the surface read of the open Turn returns the conversation's pending notices, and the
|
|
57
|
+
Harness appends them, as it appends a changed Allowed-picks list
|
|
58
|
+
([ADR-0029](0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md), ADR-0002). For a guard
|
|
59
|
+
kill that the Agent of an open Turn was not told, a member also takes it at that Turn's nudge or wake (below). A
|
|
60
|
+
conversation is an Instance ID ([ADR-0057](0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md)): a fresh
|
|
61
|
+
Turn, a runaway reroll and a burned conversation's successor each start a new one.
|
|
47
62
|
- **Conversation scope** ("this conversation is new; earlier context is gone") goes to that Agent's next Turn. So does
|
|
48
63
|
a pick's answer that never reached its call
|
|
49
64
|
([ADR-0024](0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md)): the route left the Agent's state, and
|
|
@@ -53,29 +68,43 @@ whose processes are gone, with no way to know why.
|
|
|
53
68
|
and that stopped it" (`stopped`), with an earlier link's reply when one came. No pick-answer notice says the work
|
|
54
69
|
ran to its end. A fresh picking Turn has no such conversation, and its answer is no notice.
|
|
55
70
|
- **Workspace scope** ("the Sandbox's processes were killed at its memory limit during `coder`'s Turn; `/dev/shm` was
|
|
56
|
-
cleared. Anything that was running in the Sandbox at that time is gone.") goes to
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
cleared. Anything that was running in the Sandbox at that time is gone.") goes to an **audience** that the ledger
|
|
72
|
+
fixes when it raises the notice. The rule is: an Agent hears a kill if it was there for it.
|
|
73
|
+
1. Each conversation admitted in that Workspace before the kill that can still take a Turn: the conversation of an
|
|
74
|
+
open Turn, which hears it at its next step boundary, or a continued conversation, which hears it at its next
|
|
75
|
+
Turn. A fresh Turn that already ended is not in the audience.
|
|
76
|
+
2. When the kill cut a specific Turn — a kill read on that Turn's stream names it — that Agent's next Turn, fresh or
|
|
77
|
+
continued: the retry of the broken work.
|
|
78
|
+
3. Minus each conversation whose own `bash` answer returned the kill's verdict (`told`, below). That answer told it
|
|
79
|
+
already.
|
|
80
|
+
|
|
81
|
+
Each member hears the notice once. The notice retires when every member has heard it. A conversation that starts
|
|
82
|
+
after the kill does not hear it, except the retry of (2). So a Pool of coders in one Workspace
|
|
83
|
+
([ADR-0075](0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md)) all hear a kill that cut their work.
|
|
84
|
+
Only a kill read on a stream while its wait is live names the Turn whose Submission ran. Both forms end with the
|
|
85
|
+
whole Sandbox, because every process in it died, a shared Workspace's too, and in the past tense, because the Agent
|
|
86
|
+
may hear it after processes started again. A kill read from the pod's list names no Turn and says nothing of when or
|
|
87
|
+
whose: "the Sandbox's processes were killed at its memory limit (peak X of Y); /dev/shm was cleared. Anything that
|
|
88
|
+
was running in the Sandbox at that time is gone." The list cannot say whose Turn a kill ended: the kill may land
|
|
89
|
+
after a state exits, or end an attach, a sync or a command. A kernel kill read from the Sandbox's last end (below)
|
|
90
|
+
says the same, with the limit and no peak: "the Sandbox's processes were killed at its memory limit of Y; …". A
|
|
91
|
+
Menu-only Agent outside a Workspace receives conversation notices only.
|
|
92
|
+
|
|
66
93
|
- A notice never replaces a fault. The Machine still receives `agent.fault` and decides (ADR-0016); the notice makes
|
|
67
94
|
sure the next Agent is not working blind.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
95
|
+
|
|
96
|
+
- **The Orchestrator is the only keeper of notices and Steering messages**: pending ones are plain data in the run's
|
|
97
|
+
persisted state (ADR-0007). It sees kernel kills and fresh conversations itself. A kernel kill (ADR-0061, layer 5) has
|
|
98
|
+
two paths, under one source, the Workspace and the time the Harness container ended. A Turn that the kill ended faults
|
|
99
|
+
on it, and the fault raises the notice with the Turn's Agent. A kill while no Turn runs faults nothing, so before each
|
|
100
|
+
admission into a Workspace the Agent actor also reads the container's last end from the Sandbox watch, which the
|
|
101
|
+
operator publishes: a last end `OOMKilled` raises the notice, which names no Turn. The ledger's `seen` makes the two
|
|
102
|
+
paths one notice. An actor whose own watch saw the restart reads the last end only when it re-admits the Turn after
|
|
103
|
+
the cut. When the Turn faults, the fault's notice names the Turn's Agent. A watch that has not listed yet says
|
|
104
|
+
nothing, and the next admission reads again: the last end stays until the container ends again. A nudge and a wake
|
|
105
|
+
take no kernel kill; it stays for the next Turn. The re-admission after a cut takes it, as it takes every notice in
|
|
106
|
+
scope that is not a guard kill. The Harness reports a guard kill (ADR-0061) on two paths, under one id that the
|
|
107
|
+
Harness gives the kill:
|
|
79
108
|
- **On a stream.** While a Submission runs, the kill is an event on that conversation's updates stream. The Agent
|
|
80
109
|
actor reads a stream while it waits on the Submission. It stops when the Agent's state exits, before its abort
|
|
81
110
|
reaches the Harness. So a stream does not always reach a reader: a kill in that window is on a stream that no one
|
|
@@ -89,13 +118,15 @@ whose processes are gone, with no way to know why.
|
|
|
89
118
|
the only limit. A read that never left the Orchestrator, or a draining Harness's 503, is sent again in that time. A
|
|
90
119
|
read that fails or times out is none, and the next admission reads again: the list keeps the kill. The Orchestrator
|
|
91
120
|
logs one line when the reads from a Harness start to fail. It logs no line when a read works again, and it logs the
|
|
92
|
-
failure again only when a read fails after a read that worked. The
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
121
|
+
failure again only when a read fails after a read that worked. The file is beside the Harness's conversations, which
|
|
122
|
+
no confined child can reach
|
|
123
|
+
([ADR-0076](0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md)). The bounds stay as a
|
|
124
|
+
second layer: a `peak` or a `limit` that is not a size in the guard's form, or is long, is left out and logged, on
|
|
125
|
+
the Harness's read of the file and on the Orchestrator's read of the list and of a stream. The kill is kept, so a
|
|
126
|
+
real kill is never lost, and its notice says no sizes: "the Sandbox's processes were killed at its memory limit;
|
|
127
|
+
/dev/shm was cleared. …". Only a kill with no id or no time, or one outside the bounds below, is dropped. A stream
|
|
128
|
+
event from an older Harness has no id; its source is then its place on the stream, exactly as the Orchestrator
|
|
129
|
+
raised it before.
|
|
99
130
|
|
|
100
131
|
Both paths raise the notice with the same source, the Workspace and the kill's id. The ledger keeps every source it
|
|
101
132
|
raised (`seen`) in the run's persisted state. So a kill read on both paths, or on two streams, is one notice. Each
|
|
@@ -107,71 +138,107 @@ whose processes are gone, with no way to know why.
|
|
|
107
138
|
restart of the Orchestrator raises no kill twice. A restart of the Harness container keeps the file, which is on the
|
|
108
139
|
pod's emptyDir with its conversations (ADR-0031). A lost pod takes the file with it, but then the Workspace is lost
|
|
109
140
|
(ADR-0021). The list keeps the newest 16 kills, each with at most 32 conversations in `told`, and the Orchestrator
|
|
110
|
-
reads at most 256 KiB of it. The
|
|
111
|
-
`
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
141
|
+
reads at most 256 KiB of it. The two sides hold the same bounds: an `id` and an `at` of at most 64, an `agent` and an
|
|
142
|
+
`instanceId` of at most 128, and an `at` that parses as a time. A kill outside them is dropped, and a `told` entry
|
|
143
|
+
outside them is left out. The Harness counts the bytes of the JSON text, and the Orchestrator counts characters, which
|
|
144
|
+
are never more, so the Harness keeps no kill that the Orchestrator drops. The Harness reads at most the last 256 KiB
|
|
145
|
+
of the file. With these bounds the largest list is 149,259 bytes. When more come between two admissions, the oldest is
|
|
146
|
+
lost, and a newer kill tells the same thing. When more than 16 come in one Turn, the Turn's mark leaves the list too,
|
|
147
|
+
and every kill in the list then counts as after the mark; a nudge or a wake of that Turn may then carry a kill from
|
|
148
|
+
before it. The Instance Harness keeps no kill, because it holds no Workspace.
|
|
118
149
|
|
|
119
150
|
A notice counts as delivered when its admission is ledgered, so a restart before that point delivers it again and a
|
|
120
|
-
restart after it does not.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
so
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
the
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
alone
|
|
159
|
-
|
|
160
|
-
|
|
151
|
+
restart after it does not. A notice at a step boundary has no admission. The surface read of the open Turn reserves it
|
|
152
|
+
for that member, and it counts as delivered, and heard, when the Harness acknowledges the append. When the Turn ends
|
|
153
|
+
with no acknowledgment, the reservation is released, and the notice is pending again for that member. A reservation
|
|
154
|
+
lives in the Orchestrator's memory only, so a restart forgets it: the notice is pending again, and the next read or
|
|
155
|
+
admission carries it. So a conversation can hear a notice twice after a crash, as for an admission. Delivered is not
|
|
156
|
+
yet heard: if the Orchestrator ends the Turn before the Harness reads its surface, the Harness never prompts it
|
|
157
|
+
(ADR-0026), so the Orchestrator raises that admission's notices again. This holds for a nudge and a wake too: each one
|
|
158
|
+
must have its surface read again before its notices count as heard. An admission that fails leaves the earlier
|
|
159
|
+
admission's notices heard. A nudge and a wake re-prompt the Turn within itself. Of the notices, they take only the
|
|
160
|
+
guard kills of the list that come after the newest kill whose `told` names their conversation. That `bash` answer told
|
|
161
|
+
the Agent that all its processes died, so it covers every kill up to it. They also take no kill from before the Turn
|
|
162
|
+
started. Such a kill would tell the Agent that processes it started in this Turn died. The Turn's first read of the
|
|
163
|
+
list sets its mark: the id of the newest kill it saw, or "none". The mark is ledgered with every admission of the
|
|
164
|
+
Turn, and a restore reads it back, so a nudge or a wake takes only kills after it. Only when the first read failed is
|
|
165
|
+
the Turn's start the mark, ledgered the same way, with one second allowed for a pod clock that runs behind. A read
|
|
166
|
+
that failed is "could not tell", never an empty list, so it sets no mark. The kills they do not take stay pending for
|
|
167
|
+
the next Turn. The re-admission after a Harness restart cut the Turn continues the same Turn, so it takes every notice
|
|
168
|
+
in scope except the guard kills that this rule leaves out. When its read could not tell, it takes no guard kill at
|
|
169
|
+
all, as a nudge then takes none: it cannot know which kills its own `bash` reported. The notices that the Turn's
|
|
170
|
+
admissions carried since the Harness last read the Turn's surface are pending again first, each with the source it was
|
|
171
|
+
raised with, and this re-admission carries them whatever else its scope leaves out. A notice with no source merges
|
|
172
|
+
only into a pending one with no source, so an equal notice of another kill never takes its place. When such an unread
|
|
173
|
+
admission carried the Turn's task, the task follows the re-admit text. When the rebuilt Harness lost the conversation,
|
|
174
|
+
nothing of the Turn is left: every notice that the Turn's admissions carried is pending again whatever the surface
|
|
175
|
+
reads said, the re-admission takes every notice in scope, as a Turn's admission does, and the Turn's task follows the
|
|
176
|
+
re-admit text. It says once that the conversation is new. A restore drops the prompt from a Turn's input, so the task
|
|
177
|
+
rides the ledgered admission too; a lost conversation with no task to send faults the Turn instead. Whether the
|
|
178
|
+
Harness still holds the conversation is asked again for up to 30 seconds while the new container does not listen yet
|
|
179
|
+
or drains, and only then is the answer taken as "held". The continue path asks the same way, so a continued Turn after
|
|
180
|
+
a restart can wait up to 30 seconds before its admission. A Harness in CrashLoopBackOff can take longer than that: the
|
|
181
|
+
answer is then "held", and it can be wrong. So every admission to a conversation that the Harness was taken to keep
|
|
182
|
+
says so with `kept: true`: the re-admission after a cut, a continued Turn, and a nudge or a wake. A Harness that keeps
|
|
183
|
+
no such conversation refuses it with 404 and `kept: false` in the body, and seats none. The Orchestrator takes only
|
|
184
|
+
that 404 as the refusal; another 404 is a failed admission. Without the refusal the Harness would seat a new
|
|
185
|
+
conversation, and a model on an empty context would read "continue your task" with no task. On the refusal the Turn
|
|
186
|
+
takes the path of a lost conversation. The re-admission is admitted as a Turn, with the task after the re-admit text
|
|
187
|
+
and the notice that the conversation is new, or faults when it has no task to send. A continued Turn is told that its
|
|
188
|
+
conversation is new, and is admitted again. A nudge or a wake is admitted again as a Turn: its prompt is the task
|
|
189
|
+
alone, because nothing was cut, with the notice that the conversation is new and every notice in scope. Every notice
|
|
190
|
+
that the Turn's admissions carried is pending again first, as for a lost conversation after a cut. With no task to
|
|
191
|
+
send, the Turn faults. The Instance Harness's restart is on no watch, so this refusal is the only way a nudge or a
|
|
192
|
+
wake learns of it. What the Turn's admissions carried, and whether the Harness read their surface, is kept in the
|
|
193
|
+
Orchestrator's memory alone: after a restore of the Orchestrator before a surface read, nothing is raised again. A
|
|
194
|
+
fresh Turn's "this conversation is new", which rides an admission alone, is carried again by the next re-admission
|
|
195
|
+
when the Harness has not read the one before.
|
|
161
196
|
|
|
162
|
-
A
|
|
163
|
-
|
|
164
|
-
admission of a continued Turn too; it does not now, because a fresh Turn's conversation holds no earlier answer, and
|
|
165
|
-
the rule would then need the conversation in the ledger's scope.
|
|
197
|
+
A conversation whose own `bash` call answered a kill is not in that kill's audience, so it does not hear the kill
|
|
198
|
+
again as a notice, at its next Turn or at a step boundary.
|
|
166
199
|
|
|
200
|
+
- **One ledger carries notices and Steering messages.** A Steering message ([ADR-0002](0002-duplex-streaming-actor.md))
|
|
201
|
+
is not a notice: a notice is a fact that jr2 knows and that has no author, and a Steering message is an utterance with
|
|
202
|
+
an author, which the model weighs by who wrote it. So the two are siblings on one transport. Each entry of the ledger
|
|
203
|
+
is an **envelope** and a **body**. The envelope holds what the ledger delivers by: the address, the source, the
|
|
204
|
+
audience, and what was reserved and heard. The body is a Notice, or a Steering message with its text and its author:
|
|
205
|
+
`{ kind: "caller", via, name, onBehalfOf? }` or `{ kind: "machine", from? }`, where `from` is the Sender whose words
|
|
206
|
+
the Machine relays. The rules above, heard once, reserved at a step boundary and delivered when the append is
|
|
207
|
+
acknowledged, hold for both.
|
|
208
|
+
- **The address is a tagged value.** `{ conversation }` and `{ workspace }` are the two kinds that notices use, and
|
|
209
|
+
`{ slot }` (an Agent slot of one Machine instance of the run) is the kind that a Steering message uses. A new kind
|
|
210
|
+
is an addition, and no kind changes the meaning of another's fields.
|
|
211
|
+
- **A slot's audience is a future member.** A Workspace notice fixes its audience when the ledger raises it. A message
|
|
212
|
+
to a slot goes to the slot's open Turn, or, when there is none, to the slot's next Turn in the same Machine
|
|
213
|
+
instance, whose conversation does not exist yet at the raise. It retires, recorded "not delivered", when that
|
|
214
|
+
Machine instance ends.
|
|
215
|
+
- **The Harness frames a Steering message by its author.** A Caller's message is an instruction from outside the run,
|
|
216
|
+
and the frame names the Caller. A message that the Machine relays `from` another Agent is information from a peer.
|
|
217
|
+
The frame says that the workflow passes it on but did not write it, and a closing line after the text says that it
|
|
218
|
+
is not an instruction and does not change the Agent's task: the Agent does what the workflow asked. The closing line
|
|
219
|
+
is read last, after the peer's text (ADR-0029). In `peer-not-order`, a frame that said only "not an order" read 5 of
|
|
220
|
+
10, because a model that obeyed read "the workflow relays" as the workflow's own word. A frame that said, ahead of
|
|
221
|
+
the text only, that it was "that Agent's word, not the workflow's", read 17 of 20, and the closing line read 20 of
|
|
222
|
+
20 (ADR-0066). A message that the Machine relays `from` a Gate is what the Gate received, and the frame says that it
|
|
223
|
+
is not an order either. A Machine's message with no `from` is the workflow's own text. The text is quoted line by
|
|
224
|
+
line, so the model sees where it starts and ends, and it follows the notices in the Turn part, ahead of the Allowed
|
|
225
|
+
picks. A name in the frame (a Caller's name, its `onBehalfOf`, a peer's or a Gate's id) is a code span that its own
|
|
226
|
+
text cannot close, on one line: a name comes from outside jr2 and can hold a backtick or a line break. A message
|
|
227
|
+
whose author the Harness does not know is dropped, never framed as another author's.
|
|
228
|
+
- **The two bodies ride the wire apart.** The admit body carries `notices` and `steering`, and the surface read
|
|
229
|
+
`notices: [{ id, notice }]` and `steering: [{ id, message }]`; one acknowledgment,
|
|
230
|
+
`POST /agents/:iid/notices { heard }`, hears both by their ledger ids. A nudge and a wake take notices by their
|
|
231
|
+
sources, so they carry no message: the surface read that starts their Submission does.
|
|
167
232
|
- **The Harness writes every word.** The admit body carries typed notices
|
|
168
233
|
(`notices: [{ kind: "memory-limit", scope, agent?, limit?, peak? } | { kind: "conversation-new", reason } | { kind: "pick-answer", pick, cut, answer?, stopped? } …]`,
|
|
169
|
-
where `peak` comes only with `limit`), and the Harness renders both parts
|
|
170
|
-
|
|
171
|
-
answer
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
234
|
+
where `peak` comes only with `limit`) and Steering messages with their authors, and the Harness renders both parts and
|
|
235
|
+
frames each message. The text of a Steering message is the author's; the frame around it is the Harness's. An
|
|
236
|
+
outstanding pick's answer is data too — the last reply since the pick, a party's event with its payload or a child's
|
|
237
|
+
output (ADR-0024) — and its words ("Its answer: `approve` from Gate `review`, with {…}", "No reply came back for it.")
|
|
238
|
+
are the Harness's, in the receipt and in the notice alike; "it moved the workflow, and nothing ran" is a guard
|
|
239
|
+
straight back's receipt alone. The standing part needs no wire: the Harness already holds its limits (cgroup and
|
|
240
|
+
Downward API env), the definition's `workspace` access, and the Menu. The wording lives in one place, and Harness
|
|
241
|
+
conformance checks it (ADR-0027).
|
|
175
242
|
|
|
176
243
|
## Considered options
|
|
177
244
|
|
|
@@ -197,6 +264,9 @@ whose processes are gone, with no way to know why.
|
|
|
197
264
|
does not.
|
|
198
265
|
- **Read the kill on the Sandbox watch.** Rejected: the operator publishes what the kubelet says about the container,
|
|
199
266
|
and a guard kill does not end the container. The Harness has no Kubernetes credentials to publish it.
|
|
267
|
+
- **A Steering message as a kind of notice.** Rejected: a notice is a fact with no author, and the Harness words it. A
|
|
268
|
+
Steering message is an author's text, and the model must weigh a Caller's instruction and a peer's word differently.
|
|
269
|
+
Folded into a notice, the author and that difference have no place.
|
|
200
270
|
- **The Harness words the kill into the next Turn itself.** Rejected: a second keeper of notices, outside the run's
|
|
201
271
|
persisted state, which cannot know when an admission is ledgered or heard.
|
|
202
272
|
|
|
@@ -209,4 +279,8 @@ whose processes are gone, with no way to know why.
|
|
|
209
279
|
- The Briefing is how a Turn learns its Allowed picks: the tools block is the whole Menu, fixed for a conversation so it
|
|
210
280
|
does not break the cache
|
|
211
281
|
([ADR-0029](0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md)).
|
|
282
|
+
- A Workspace notice reaches each conversation that was there for the kill, and the retry of the Turn that it cut; an
|
|
283
|
+
open Turn hears it at its next step boundary, not only at its next admission (ADR-0002,
|
|
284
|
+
[ADR-0029](0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md)). So a memory kill reaches
|
|
285
|
+
every coder of a Pool that shares one Workspace (ADR-0075).
|
|
212
286
|
- GLOSSARY.md gains **Briefing**.
|
|
@@ -52,10 +52,9 @@ Decisions since then need pod facts at once: the scheduler's reason for a Pendin
|
|
|
52
52
|
merge patch per Workspace every 5 minutes, ±20% jitter so Leases never renew in lockstep after a restart. It no longer
|
|
53
53
|
carries detection.
|
|
54
54
|
- **The attach is a Harness call, not an exec.** The Harness runs the attach — ACL stamp, `git clone --shared` from the
|
|
55
|
-
node cache, remotes
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
Substrate need.
|
|
55
|
+
node cache, remotes — on `POST /attach { slots }`, authenticated like every Harness call (ADR-0058). No stream per
|
|
56
|
+
Workspace crosses the API server, and **`pods/exec` leaves the Orchestrator's RBAC**. It also binds work late: a pod
|
|
57
|
+
already running receives its Repos, the shape standby Sandboxes and Agent Substrate need.
|
|
59
58
|
|
|
60
59
|
## Considered options
|
|
61
60
|
|
|
@@ -70,8 +69,8 @@ Decisions since then need pod facts at once: the scheduler's reason for a Pendin
|
|
|
70
69
|
- **A jr2 WebSocket API.** Nothing to add: a Kubernetes watch is already a pushed stream of events.
|
|
71
70
|
- **Keep the attach as exec through the new client.** The exec protocol is the hardest part of any Kubernetes client, it
|
|
72
71
|
keeps one stream per Workspace through the API server, and it keeps `pods/exec`.
|
|
73
|
-
- **Attach from an init container.** Declarative, but it fixes the Repos
|
|
74
|
-
|
|
72
|
+
- **Attach from an init container.** Declarative, but it fixes the Repos at pod creation, which rules out late binding,
|
|
73
|
+
and it must wait on a cache that exists only after scheduling.
|
|
75
74
|
|
|
76
75
|
## Consequences
|
|
77
76
|
|
|
@@ -79,5 +78,6 @@ Decisions since then need pod facts at once: the scheduler's reason for a Pendin
|
|
|
79
78
|
the watch and the Harness attach. ADR-0001: the operator publishes pod facts on the Sandbox's status, and writes
|
|
80
79
|
status only on change — every write is now a watch event at the Orchestrator.
|
|
81
80
|
- The Harness gains a route that is not about a conversation. It is jr2's own work in the container ADR-0005 gives it.
|
|
82
|
-
- The CLI keeps `kubectl` on the user's machine (`jr2 up`, `jr2
|
|
81
|
+
- The CLI keeps `kubectl` on the user's machine (`jr2 up`, the `jr2 workspace` verbs, ADR-0009); only the Orchestrator
|
|
82
|
+
stops using it.
|
|
83
83
|
- Tests fake the client with a small HTTP server instead of a `kubectl` stub.
|
|
@@ -37,8 +37,8 @@ Workspaces for the Instance (R4), and concurrent Turns per model provider (R5)?
|
|
|
37
37
|
delete does not touch a name that a live Workspace of the run holds then: a parent that re-enters the state, or
|
|
38
38
|
invokes the same Machine again, gives its new Workspace the same invoke id and so the same name, and that Workspace
|
|
39
39
|
has placed it again ([ADR-0073](0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md)). A
|
|
40
|
-
Sandbox that was placed and then faulted keeps ADR-0021's rule: it stays inspectable (
|
|
41
|
-
the idle timeout.
|
|
40
|
+
Sandbox that was placed and then faulted keeps ADR-0021's rule: it stays inspectable (`kubectl logs`,
|
|
41
|
+
`jr2 workspace shell`) and ages out at the idle timeout.
|
|
42
42
|
- **jr2 caps nothing of its own.** Not live Workspaces (R4), not concurrent Turns per provider (R5). See the options.
|
|
43
43
|
- **A provider's refusal is named.** A Turn that ends because the Harness's provider retries ran out has a fault reason
|
|
44
44
|
with the fixed prefix `provider limit`, as a memory kill has `memory limit` (ADR-0061), so `jr2 status` and the
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# A persist survives a crash, and history is kept for a set time
|
|
2
|
+
|
|
3
|
+
Every transition of a run persists its whole snapshot (ADR-0007). The scaling review (R6) read the store and listed its
|
|
4
|
+
costs: `JSON.stringify` of the whole run, a synchronous upsert with an fsync on the event loop, a Pool's run as one blob
|
|
5
|
+
holding every worker, terminal rows never deleted, and `restore()` parsing every row before the server listens. It also
|
|
6
|
+
found the words "Postgres opt-in" in GLOSSARY.md, ADR-0008 and ADR-0009, with no such store in the code. Measured on the
|
|
7
|
+
home cluster's chaos Instance (2026-09-30, `local-path` volume): a persist costs **4.5 ms, whatever the blob's size** (1
|
|
8
|
+
KB or 100 KB), because the cost is the fsync, not the JSON; with the write-ahead log and `synchronous=NORMAL` the same
|
|
9
|
+
upserts cost 0.04 ms (1 KB) and 0.18 ms (100 KB). A run's blob is about 1 KB; a Pool of 40 workers is about 100 KB.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
- **A persist survives a process crash, not a power loss.** The store runs sqlite in WAL mode with `synchronous=NORMAL`:
|
|
14
|
+
a write is durable once the OS holds it, and sqlite fsyncs only at a checkpoint. A node's power loss can lose the
|
|
15
|
+
transitions since the last checkpoint; the file stays consistent, and `restore()` treats the older snapshot as it
|
|
16
|
+
treats any restart — reconcile against the live world, re-attach, or `lost`. The window in which a crash re-POSTs a
|
|
17
|
+
prompt (ADR-0007's at-most-once sliver) widens from one microtask to one checkpoint, and only on a power loss, which
|
|
18
|
+
also ends every Sandbox on that node.
|
|
19
|
+
- **The write stays synchronous and on the event loop.** At 0.2 ms for a Pool's blob there is nothing to move off the
|
|
20
|
+
loop, and the ordering matters: the row is on disk before the Machine's next side effect runs, which is what lets an
|
|
21
|
+
epoch bump (ADR-0057), an admission (ADR-0016) or an Effect's intent row (ADR-0067) be saved "at the write" — the
|
|
22
|
+
intent row before its call leaves. A worker thread, an asynchronous driver or a network store would land the row after
|
|
23
|
+
the next `invoke` fires, on every persist.
|
|
24
|
+
- **One blob per run stays.** The snapshot is a tree that xstate restores from its root, and one row keeps it atomic —
|
|
25
|
+
with the ledgers beside it (admissions, epochs, notices, timers, outstanding picks, Effects); a Pool's cost at 40
|
|
26
|
+
workers is 0.2 ms, so its workers do not become rows of their own. The Effect ledger keeps a row for every Effect the
|
|
27
|
+
run started (ADR-0067), so a Machine that loops through an Effect grows its blob by one row per pass: id, kind and
|
|
28
|
+
outcome. The output's tail is dropped once the save that carries the Effect's transition is on disk; a push row keeps
|
|
29
|
+
its published commit, its source, its `squashed` and its sync's answer whole — every later push of its Repo Slot sends
|
|
30
|
+
them as one of its pairs — and its bundle only while it is its Repo Slot's newest push and its sync did not finish —
|
|
31
|
+
one bundle per Repo Slot at most. An outstanding pick's hold is the one place tails accumulate: its `chain` keeps
|
|
32
|
+
every reply since the pick whole until the pick is answered, then it is dropped (ADR-0067).
|
|
33
|
+
- **`restore()` reads live rows only.** The store answers `WHERE status = 'live'`; terminal rows are not parsed at boot.
|
|
34
|
+
- **A finished run's row lives for `runs.retain`, 30 days by default.** `runs: { retain: "30d", volume: "5Gi" }` in
|
|
35
|
+
`jr2.config.ts` sets it. `retain` is a duration of at least `1h`, with no upper limit, and the config load refuses a
|
|
36
|
+
shorter one. It covers what jr2 keeps of an ended run: its row, its kept conversations
|
|
37
|
+
([ADR-0023](0023-the-harness-prints-the-conversation.md)) and the hook log (below). Every non-live status — `done`,
|
|
38
|
+
`failed`, `error`, `cancelled`, `lost`, `drifted` — ages from its terminal write, and a sweep at boot and every hour
|
|
39
|
+
deletes what is older, with the run's conversations. With no current Assembly (the Kit bump window,
|
|
40
|
+
[ADR-0069](0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md)), no `runs.retain` is
|
|
41
|
+
bound, so this sweep waits for the first publish; the collection of Assemblies still runs. A `live` row is never
|
|
42
|
+
swept: a run parked on a Gate for a month stays and is restored. Nor is the run that the head of a standing Workflow
|
|
43
|
+
names ([ADR-0017](0017-pool-over-a-source-port.md)): a paused Pool keeps its held items in that run's blob for as long
|
|
44
|
+
as it is paused. The setting exists because a history surface uses it: `GET /runs` lists ended runs,
|
|
45
|
+
`jr2 runs --status` and the Console show them ([ADR-0009](0009-cli-and-instance-interface.md)), and `jr2 logs` reads
|
|
46
|
+
an ended run's conversations. jr2 is still not the record of a run's results (a Machine sends its output where it
|
|
47
|
+
belongs — a PR, a Source); the history is for a person who reads what a run did and why. A swept id frees its
|
|
48
|
+
abbreviations (ADR-0009).
|
|
49
|
+
- **The state volume has a size, and a guard keeps it from filling.** `runs.volume` is the size of the Orchestrator's
|
|
50
|
+
state volume claim, `5Gi` by default. It holds `state.db`, `conversations.db`, the Assemblies and the Charts. A claim
|
|
51
|
+
grows only where its StorageClass allows expansion, and Kubernetes never makes one smaller. So `jr2 up` refuses a
|
|
52
|
+
`runs.volume` that the config sets and that is smaller than the claim, or larger on a StorageClass that does not allow
|
|
53
|
+
expansion (kind's local-path class does not), and names the class; the claim keeps its size. When the config does not
|
|
54
|
+
set `runs.volume` and a claim exists, `jr2 up` keeps the claim's size, and prints one line that says so when that size
|
|
55
|
+
is not the default, so an Instance whose claim is older than the default is not refused for a value that no one wrote.
|
|
56
|
+
The guard measures the bytes of the files on the volume (both databases with their `-wal` files, the Assemblies and
|
|
57
|
+
the Charts) against the smaller of the claim's size and the size that `statfs` gives, because a hostPath class such as
|
|
58
|
+
kind's reports the node's disk and not the claim. The Orchestrator does not read the claim: `jr2 up` gives it the size
|
|
59
|
+
that it applied, the kept size of a claim that no config names too, in the Deployment's env. It looks at each sweep
|
|
60
|
+
and every minute. When the volume is more than 80% full by that measure, the sweep deletes the kept conversations of
|
|
61
|
+
ended runs, the oldest run first, until the volume is below 80% again. The run rows stay, and each run whose
|
|
62
|
+
conversations went is marked `conversation swept: no space`, which `jr2 logs` and the Console show in place of the
|
|
63
|
+
conversation. A conversation of a live run is never swept for space. If the volume is still more than 95% full when no
|
|
64
|
+
conversation of an ended run is left, the Orchestrator keeps no more entries and marks each open conversation cut, as
|
|
65
|
+
at its cap ([ADR-0023](0023-the-harness-prints-the-conversation.md)), so the conversations never fill the volume.
|
|
66
|
+
- **The hook log keeps each Hook's delivery for `runs.retain`**, as a row of its own: the Hook, the delivery id, the
|
|
67
|
+
body's digest and the outcome ([ADR-0011](0011-workflow-defined-events.md)). The repeat check reads it, so a repeat is
|
|
68
|
+
found while the row is kept; a body caught and sent again with a new id after that passes the check, and a handler
|
|
69
|
+
that compares the commit limits what it can do. A `slack` or `discord` delivery carries a signed timestamp, and jr2
|
|
70
|
+
refuses one more than 300 seconds from its clock, so that replay fails for them
|
|
71
|
+
([ADR-0078](0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)). A run's blob keeps its
|
|
72
|
+
own `(delivery, gate)` pairs. A standing Pool's run is handed over before its Effect ledger makes its blob large:
|
|
73
|
+
after 500 dispatches or one day ([ADR-0017](0017-pool-over-a-source-port.md)).
|
|
74
|
+
- **The store is sqlite on the Instance's volume. No Postgres is owed.** The `SnapshotStore` interface stays as the
|
|
75
|
+
seam, with one implementation. `DATABASE_URL` is not a thing jr2 reads.
|
|
76
|
+
|
|
77
|
+
## Considered options
|
|
78
|
+
|
|
79
|
+
- **Keep `synchronous=FULL`, move the write to a worker thread.** Rejected: the thread hides 4.5 ms from the loop but
|
|
80
|
+
does not make the disk faster, so writes queue under load and the loop no longer knows how far behind the store is —
|
|
81
|
+
and it opens the ordering gap on every write, which NORMAL opens only on a power loss.
|
|
82
|
+
- **Batch persists in a window.** Rejected: one persist per macrostep already coalesces a transition's events, and with
|
|
83
|
+
the fsync gone there is nothing left to amortize.
|
|
84
|
+
- **A row per worker.** Rejected: it splits an atomic tree across rows (a worker's row saved, the Pool's not), makes
|
|
85
|
+
restore reassemble what xstate restores whole, and buys nothing measurable at 0.2 ms per blob.
|
|
86
|
+
- **A fixed retention, like the Sandbox idle timeout.** Rejected: a list of ended runs and their kept conversations are
|
|
87
|
+
a history surface, and how long a team reads its history is the team's decision, not jr2's.
|
|
88
|
+
- **A count cap per workflow.** Rejected: a count cap does not bound a busy workflow's disk and punishes a quiet one.
|
|
89
|
+
The space guard bounds the disk, and it deletes only the conversations, which are the largest part; run rows stay.
|
|
90
|
+
- **An upper limit on `retain`.** Rejected: how long a team reads its history is the team's decision, and the space
|
|
91
|
+
guard bounds the largest part, the conversations. What `retain` leaves to grow is the run rows and the hook log, which
|
|
92
|
+
grow with the count of runs and deliveries; a team that keeps a long history of a busy Instance sets a larger
|
|
93
|
+
`runs.volume`. A limit would refuse that team and bound nothing that the volume's size does not.
|
|
94
|
+
- **Build the Postgres store.** Rejected: the only thing it buys is an Orchestrator pod that reschedules without its
|
|
95
|
+
volume, which is a storage-class question (a network RWO volume follows the pod) and belongs to the Orchestrator pod's
|
|
96
|
+
own decision (R10), not to the store. Its cost is a dependency the owner operates, a second store in every test tier,
|
|
97
|
+
and the asynchronous write above.
|
|
98
|
+
|
|
99
|
+
## Consequences
|
|
100
|
+
|
|
101
|
+
- ADR-0008 and ADR-0009 lose "Postgres opt-in" and `DATABASE_URL`; ADR-0008's Orchestrator persists its snapshots "to
|
|
102
|
+
sqlite on its volume". ADR-0007 points here for the store's durability.
|
|
103
|
+
- The Instance's volume gains `state.db-wal` and `state.db-shm` beside `state.db`, and `conversations.db` with its own
|
|
104
|
+
`-wal` and `-shm` (ADR-0023).
|
|
105
|
+
- A `drifted` run (ADR-0030) is swept `runs.retain` after the deploy that changed its Machine, not after it ended; that
|
|
106
|
+
is the time a human has to read it.
|
|
107
|
+
- An idempotency key of a Pool item or a `dispatch` lives as long as its run's row
|
|
108
|
+
([ADR-0017](0017-pool-over-a-source-port.md), [ADR-0071](0071-a-run-starts-a-run-through-a-dispatch-effect.md)), so
|
|
109
|
+
`runs.retain` is also how long a repeated key finds its run.
|