@jr2/orchestrator 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/agents-block.md +2 -2
- package/docs/concepts/agents-and-turns.md +56 -20
- package/docs/concepts/architecture.md +30 -17
- package/docs/concepts/charts.md +6 -3
- package/docs/concepts/converge.md +3 -0
- package/docs/concepts/effects.md +86 -31
- package/docs/concepts/machines-and-workflows.md +9 -0
- package/docs/concepts/pools-and-sources.md +6 -6
- package/docs/concepts/secrets-and-trust.md +126 -42
- package/docs/concepts/the-menu.md +3 -0
- package/docs/concepts/workspaces.md +129 -51
- package/docs/get-started/an-agent-in-a-sandbox.md +21 -19
- 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 +17 -4
- package/docs/guides/bind-open-parts.md +5 -4
- package/docs/guides/cut-a-worktree.md +560 -0
- package/docs/guides/fix-a-failed-converge.md +1 -1
- package/docs/guides/let-an-agent-write-a-chart.md +5 -1
- package/docs/guides/look-inside-a-sandbox.md +18 -15
- package/docs/guides/move-from-kind-to-a-shared-cluster.md +16 -0
- package/docs/guides/push-and-sign-a-branch.md +67 -21
- package/docs/guides/run-a-command-or-call-a-url.md +84 -41
- package/docs/guides/run-one-worker-per-item.md +19 -18
- package/docs/guides/set-an-agents-access-and-dials.md +67 -22
- package/docs/guides/size-a-workspace.md +19 -6
- package/docs/guides/use-a-hosted-provider.md +6 -0
- package/docs/guides/watch-a-run.md +47 -24
- package/docs/guides/write-a-workflow.md +1 -1
- package/docs/llms.txt +33 -12
- package/docs/reference/authoring-surface/agent.md +34 -11
- package/docs/reference/authoring-surface/answer.md +19 -3
- package/docs/reference/authoring-surface/arg.md +27 -5
- package/docs/reference/authoring-surface/branchname.md +15 -0
- package/docs/reference/authoring-surface/command.md +36 -21
- package/docs/reference/authoring-surface/fail.md +17 -4
- package/docs/reference/authoring-surface/file.md +36 -16
- package/docs/reference/authoring-surface/http.md +108 -8
- package/docs/reference/authoring-surface/isready.md +14 -0
- package/docs/reference/authoring-surface/jr2setup.md +1 -1
- package/docs/reference/authoring-surface/open.md +36 -5
- package/docs/reference/authoring-surface/paramsof.md +22 -4
- package/docs/reference/authoring-surface/pool.md +4 -4
- package/docs/reference/authoring-surface/push.md +30 -12
- package/docs/reference/authoring-surface/readyof.md +21 -0
- package/docs/reference/authoring-surface/secret.md +25 -4
- package/docs/reference/authoring-surface/source.md +3 -3
- package/docs/reference/authoring-surface/type-agentdefinition.md +2 -2
- 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 +5 -6
- 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 +22 -4
- package/docs/reference/authoring-surface/type-customize.md +4 -2
- package/docs/reference/authoring-surface/type-dispatchoutput.md +8 -1
- package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
- 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-hostinjectedinput.md +2 -4
- 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 +7 -1
- package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
- 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-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 +2 -2
- package/docs/reference/authoring-surface/type-pushinput.md +22 -3
- package/docs/reference/authoring-surface/type-pushoutput.md +32 -7
- package/docs/reference/authoring-surface/type-reposlot.md +5 -5
- package/docs/reference/authoring-surface/type-sandboxoptions.md +11 -4
- package/docs/reference/authoring-surface/type-slothandles.md +5 -13
- package/docs/reference/authoring-surface/type-sourcespec.md +10 -10
- package/docs/reference/authoring-surface/type-workspaced.md +29 -8
- 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 +65 -37
- package/docs/reference/authoring-surface/worktree.md +71 -0
- package/docs/reference/authoring-surface.md +30 -10
- package/docs/reference/cli.md +1 -1
- package/docs/reference/config.md +13 -4
- package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +10 -9
- package/docs/reference/decisions/0002-duplex-streaming-actor.md +17 -10
- package/docs/reference/decisions/0004-worktree-provisioning-model.md +27 -21
- package/docs/reference/decisions/0005-sandbox-pod-composition.md +29 -19
- package/docs/reference/decisions/0006-agent-control-surface.md +6 -3
- package/docs/reference/decisions/0012-workspace-wrapper-machine.md +30 -30
- package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +35 -17
- package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +8 -2
- package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +9 -7
- package/docs/reference/decisions/0017-pool-over-a-source-port.md +6 -4
- package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +21 -9
- package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +2 -1
- package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +8 -9
- package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +62 -65
- package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +9 -1
- package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +13 -12
- package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +4 -1
- package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +18 -19
- package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +8 -8
- package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +4 -4
- 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/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +24 -24
- package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +5 -3
- package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +110 -67
- package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +101 -74
- package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +5 -6
- package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +2 -0
- package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +181 -153
- package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +74 -63
- package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +31 -20
- 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 +295 -0
- package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +320 -0
- package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
- package/docs/reference/decisions.md +3 -0
- package/docs/reference/glossary.md +51 -12
- package/docs/reference/http-api.md +45 -7
- package/docs/reference/machines.md +0 -1
- package/docs/why.md +5 -4
- package/package.json +3 -3
- package/src/actor.ts +161 -35
- package/src/agent.ts +6 -6
- package/src/ambient.ts +3 -5
- package/src/chart.ts +186 -19
- package/src/charts.ts +9 -2
- package/src/config.ts +16 -6
- package/src/custodian.lua +17 -5
- package/src/custodian.ts +251 -77
- package/src/customize.ts +11 -3
- package/src/effect-checks.ts +50 -3
- package/src/effect.ts +343 -127
- package/src/effector-wire.ts +6 -3
- package/src/fail.ts +17 -4
- package/src/harness-client.ts +74 -31
- package/src/harness-env.ts +33 -0
- package/src/held-secrets.ts +125 -16
- package/src/holds.ts +3 -0
- package/src/http-effect.ts +88 -32
- package/src/http.ts +56 -17
- package/src/index.ts +15 -1
- package/src/internal.ts +1 -0
- package/src/kube-client.ts +6 -3
- package/src/machine-doc.ts +3 -4
- package/src/model-keys.ts +24 -0
- package/src/names.ts +19 -6
- package/src/notices.ts +443 -54
- package/src/open-body.ts +85 -19
- package/src/open.ts +36 -5
- package/src/parts.ts +40 -28
- package/src/party.ts +33 -0
- package/src/pick.ts +41 -7
- package/src/pool.ts +44 -23
- package/src/registration.ts +53 -11
- package/src/repo-identity.ts +7 -3
- package/src/repos.ts +20 -2
- package/src/run-host.ts +106 -20
- package/src/sandbox-effect.ts +217 -66
- package/src/sandbox-kube.ts +63 -16
- package/src/secret-placement.ts +9 -2
- package/src/setup.ts +86 -6
- package/src/tokens.ts +107 -32
- package/src/vocabulary.ts +23 -4
- package/src/wire.ts +10 -10
- package/src/workspace.ts +264 -253
- 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/agents-block.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
jr2 is newer than your training data. Before you write a Machine or change jr2.config.ts, read the bundled docs in node_modules/@jr2/orchestrator/docs/. They are the docs of this Instance's Kit version, 0.
|
|
1
|
+
jr2 is newer than your training data. Before you write a Machine or change jr2.config.ts, read the bundled docs in node_modules/@jr2/orchestrator/docs/. They are the docs of this Instance's Kit version, 0.6.0. Start with guides/write-a-workflow.md and with reference/authoring-surface.md, the contents page of the Authoring surface. Read one page with each call of your file tool, because a file tool cuts off a long output. Before you give a change to the user, run `npx --no jr2 check` in the Instance folder. It does the typecheck and the load of each Workflow that `jr2 up` does first, with no cluster, and it gives the same refusals.
|
|
2
2
|
|
|
3
3
|
A final state means that the work succeeded: the run ends `done`. When the work did not succeed, invoke `fail()` from a state (reference/authoring-surface/fail.md), and the run ends `failed`. Do not throw to fail: a throw is a defect, and the run ends `error`.
|
|
4
4
|
|
|
5
|
-
[jr2 docs index]|root: ./node_modules/@jr2/orchestrator/docs|reference:{authoring-surface.md,machines.md,config.md,cli.md,http-api.md,mechanism-events.md,glossary.md,decisions.md}|reference/authoring-surface:{one file per export and type; reference/authoring-surface.md lists them}|guides:{iterate-on-a-machine-on-kind.md,write-a-workflow.md,bind-open-parts.md,set-an-agents-access-and-dials.md,run-a-command-or-call-a-url.md,push-and-sign-a-branch.md,start-a-run-from-a-run.md,add-tools-to-the-sandbox.md,size-a-workspace.md,run-one-worker-per-item.md,publish-a-machine-as-an-npm-package.md,let-an-agent-write-a-chart.md,run-a-chart-by-name.md,start-a-run.md,answer-a-gate.md,watch-a-run.md,look-inside-a-sandbox.md,cancel-a-run.md,open-the-console.md,use-a-local-model.md,use-a-hosted-provider.md,give-the-cluster-git-credentials.md,move-from-kind-to-a-shared-cluster.md,mirror-the-kit-images.md,upgrade-the-kit-version.md,clean-up.md,fix-a-failed-converge.md}|concepts:{machines-and-workflows.md,agents-and-turns.md,the-menu.md,gates.md,effects.md,workspaces.md,composition.md,charts.md,pools-and-sources.md,architecture.md,secrets-and-trust.md,converge.md,durable-runs.md}|.:{index.md,why.md}|get-started:{install.md,create-an-instance.md,connect-a-model.md,a-model-decides.md,an-agent-in-a-sandbox.md,have-your-assistant-write-a-machine.md}|reference/decisions:{one file per ADR; reference/decisions.md lists them}
|
|
5
|
+
[jr2 docs index]|root: ./node_modules/@jr2/orchestrator/docs|reference:{authoring-surface.md,machines.md,config.md,cli.md,http-api.md,mechanism-events.md,glossary.md,decisions.md}|reference/authoring-surface:{one file per export and type; reference/authoring-surface.md lists them}|guides:{iterate-on-a-machine-on-kind.md,write-a-workflow.md,bind-open-parts.md,set-an-agents-access-and-dials.md,cut-a-worktree.md,run-a-command-or-call-a-url.md,push-and-sign-a-branch.md,start-a-run-from-a-run.md,add-tools-to-the-sandbox.md,size-a-workspace.md,run-one-worker-per-item.md,publish-a-machine-as-an-npm-package.md,let-an-agent-write-a-chart.md,run-a-chart-by-name.md,start-a-run.md,answer-a-gate.md,watch-a-run.md,look-inside-a-sandbox.md,cancel-a-run.md,open-the-console.md,use-a-local-model.md,use-a-hosted-provider.md,give-the-cluster-git-credentials.md,move-from-kind-to-a-shared-cluster.md,mirror-the-kit-images.md,upgrade-the-kit-version.md,clean-up.md,fix-a-failed-converge.md}|concepts:{machines-and-workflows.md,agents-and-turns.md,the-menu.md,gates.md,effects.md,workspaces.md,composition.md,charts.md,pools-and-sources.md,architecture.md,secrets-and-trust.md,converge.md,durable-runs.md}|.:{index.md,why.md}|get-started:{install.md,create-an-instance.md,connect-a-model.md,a-model-decides.md,an-agent-in-a-sandbox.md,have-your-assistant-write-a-machine.md}|reference/decisions:{one file per ADR; reference/decisions.md lists them}
|
|
@@ -49,8 +49,10 @@ for one Turn, because then the name of the Agent would not tell what it is.
|
|
|
49
49
|
## The Working tools
|
|
50
50
|
|
|
51
51
|
The [Working tools](../reference/glossary.md#working-tools) are the file and shell tools of an Agent: read, write, edit,
|
|
52
|
-
bash, grep and glob. The [Harness](../reference/glossary.md#harness) runs them in its own container.
|
|
53
|
-
|
|
52
|
+
bash, grep and glob. The [Harness](../reference/glossary.md#harness) runs them in its own container. Each program that
|
|
53
|
+
they start, and the file I/O of read, write and edit, runs confined with Landlock. It can do all that the user of the
|
|
54
|
+
container can do, but it cannot read what the Harness holds. [Secrets and trust](secrets-and-trust.md) tells why. The
|
|
55
|
+
`workspace` field selects the Working tools:
|
|
54
56
|
|
|
55
57
|
- `"write"` gives all the Working tools.
|
|
56
58
|
- `"read"` gives no write tool and no edit tool. The Agent keeps bash, so a command can still write a file. A review
|
|
@@ -69,9 +71,10 @@ The field also tells where the Turn runs. An Agent with `"write"` or `"read"` wo
|
|
|
69
71
|
The `input` of the invoke is the Turn. It has five keys, and no other:
|
|
70
72
|
|
|
71
73
|
- **The [Frame](../reference/glossary.md#frame): `prompt` and `cwd`.** The prompt tells what this Turn is about. `cwd`
|
|
72
|
-
is the directory where the Working tools work. A definition cannot hold a `cwd`, because
|
|
73
|
-
|
|
74
|
-
`cwd` is
|
|
74
|
+
is the directory where the Working tools work. A definition cannot hold a `cwd`, because a Worktree exists only after
|
|
75
|
+
a run cuts it. Under a Workspace with [Repo Slots](../reference/glossary.md#repo-slot), a Turn of an Agent with
|
|
76
|
+
Working tools states its `cwd`. A Turn that wants `/work` states it too. There is no default, because a search in
|
|
77
|
+
`/work` hits `default/`, and a relative write lands outside each Worktree.
|
|
75
78
|
- **The [Dials](../reference/glossary.md#dials): `model` and `thinkingLevel`.** They tell how hard the Agent works on
|
|
76
79
|
this Turn, and they replace the values of the definition for this Turn only. The Agent stays the same Agent.
|
|
77
80
|
- **`continue`.** It tells if the Turn continues the conversation of the Agent. See
|
|
@@ -90,7 +93,7 @@ changes, the coder continues its conversation and thinks harder on that Turn:
|
|
|
90
93
|
```ts
|
|
91
94
|
import { z } from "zod";
|
|
92
95
|
import { assign } from "xstate";
|
|
93
|
-
import { agent, defineEvent, fail, jr2Setup, workspace, type Workspaced } from "@jr2/orchestrator";
|
|
96
|
+
import { agent, defineEvent, fail, isReady, jr2Setup, workspace, worktree, type Workspaced } from "@jr2/orchestrator";
|
|
94
97
|
|
|
95
98
|
const finish = defineEvent({
|
|
96
99
|
name: "finish",
|
|
@@ -112,6 +115,9 @@ const runInput = z.object({ branch: z.string() });
|
|
|
112
115
|
type BodyInput = Workspaced<z.infer<typeof runInput>, "app">;
|
|
113
116
|
type Context = BodyInput & { summary: string; notes: string };
|
|
114
117
|
|
|
118
|
+
// The Worktree of each Turn of the coder.
|
|
119
|
+
const fixTree = (context: BodyInput) => `${context.workspace.repos.app}/fix` as const;
|
|
120
|
+
|
|
115
121
|
const body = jr2Setup({
|
|
116
122
|
types: {} as { input: BodyInput; context: Context },
|
|
117
123
|
events: [finish, approve, requestChanges],
|
|
@@ -128,21 +134,30 @@ const body = jr2Setup({
|
|
|
128
134
|
workspace: "none",
|
|
129
135
|
thinkingLevel: "low",
|
|
130
136
|
}),
|
|
137
|
+
cut: worktree(),
|
|
131
138
|
fail: fail(),
|
|
132
139
|
},
|
|
133
140
|
}).createMachine({
|
|
134
141
|
id: "fix",
|
|
135
142
|
context: ({ input }) => ({ ...input, summary: "", notes: "" }),
|
|
136
143
|
on: { "workspace.lost": ".lost", "agent.fault": ".failed" },
|
|
137
|
-
initial: "
|
|
144
|
+
initial: "cutting",
|
|
138
145
|
states: {
|
|
146
|
+
// The Workspace only clones. The Machine cuts the Worktree that the Agent works in.
|
|
147
|
+
cutting: {
|
|
148
|
+
invoke: {
|
|
149
|
+
src: "cut",
|
|
150
|
+
input: ({ context }) => ({ path: fixTree(context), branch: context.branch }),
|
|
151
|
+
onDone: [{ guard: ({ event }) => isReady(event.output), target: "coding" }, { target: "notCut" }],
|
|
152
|
+
},
|
|
153
|
+
},
|
|
139
154
|
coding: {
|
|
140
155
|
invoke: {
|
|
141
156
|
src: "coder",
|
|
142
157
|
// The Frame (prompt and cwd) and continue. This Turn starts the coder's conversation.
|
|
143
158
|
input: ({ context }) => ({
|
|
144
159
|
prompt: "Make `node --test` pass, then commit.",
|
|
145
|
-
cwd: context
|
|
160
|
+
cwd: fixTree(context),
|
|
146
161
|
continue: true,
|
|
147
162
|
}),
|
|
148
163
|
},
|
|
@@ -161,7 +176,7 @@ const body = jr2Setup({
|
|
|
161
176
|
// The same conversation as in coding, with a Dial: the coder thinks harder on this Turn.
|
|
162
177
|
input: ({ context }) => ({
|
|
163
178
|
prompt: `The fix is not complete: ${context.notes}\nFix it, then commit.`,
|
|
164
|
-
cwd: context
|
|
179
|
+
cwd: fixTree(context),
|
|
165
180
|
continue: true,
|
|
166
181
|
thinkingLevel: "high",
|
|
167
182
|
}),
|
|
@@ -169,6 +184,7 @@ const body = jr2Setup({
|
|
|
169
184
|
on: { finish: { target: "checking", actions: assign({ summary: ({ event }) => event.summary }) } },
|
|
170
185
|
},
|
|
171
186
|
done: { type: "final" },
|
|
187
|
+
notCut: { invoke: { src: "fail", input: { reason: "the Worktree was not cut" } } },
|
|
172
188
|
failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
|
|
173
189
|
lost: { invoke: { src: "fail", input: { reason: "the Sandbox is gone" } } },
|
|
174
190
|
},
|
|
@@ -177,7 +193,6 @@ const body = jr2Setup({
|
|
|
177
193
|
export const machine = workspace(body, {
|
|
178
194
|
input: runInput,
|
|
179
195
|
repos: { app: "https://github.com/you/app.git" },
|
|
180
|
-
spec: ({ input }) => ({ branch: input.branch }),
|
|
181
196
|
});
|
|
182
197
|
```
|
|
183
198
|
|
|
@@ -186,7 +201,7 @@ export const machine = workspace(body, {
|
|
|
186
201
|
```ts
|
|
187
202
|
import { z } from "zod";
|
|
188
203
|
import { assign } from "xstate";
|
|
189
|
-
import { agent, defineEvent, fail, jr2Setup, workspace, type Workspaced } from "@jr2/orchestrator";
|
|
204
|
+
import { agent, defineEvent, fail, isReady, jr2Setup, workspace, worktree, type Workspaced } from "@jr2/orchestrator";
|
|
190
205
|
|
|
191
206
|
const finish = defineEvent({
|
|
192
207
|
name: "finish",
|
|
@@ -208,6 +223,9 @@ const runInput = z.object({ branch: z.string() });
|
|
|
208
223
|
type BodyInput = Workspaced<z.infer<typeof runInput>, "app">;
|
|
209
224
|
type Context = BodyInput & { summary: string; notes: string };
|
|
210
225
|
|
|
226
|
+
// The Worktree of each Turn of the coder.
|
|
227
|
+
const fixTree = (context: BodyInput) => `${context.workspace.repos.app}/fix` as const;
|
|
228
|
+
|
|
211
229
|
const body = jr2Setup({
|
|
212
230
|
types: {} as { input: BodyInput; context: Context },
|
|
213
231
|
events: [finish, approve, requestChanges],
|
|
@@ -224,21 +242,30 @@ const body = jr2Setup({
|
|
|
224
242
|
workspace: "none",
|
|
225
243
|
thinkingLevel: "low",
|
|
226
244
|
}),
|
|
245
|
+
cut: worktree(),
|
|
227
246
|
fail: fail(),
|
|
228
247
|
},
|
|
229
248
|
}).createMachine({
|
|
230
249
|
id: "fix",
|
|
231
250
|
context: ({ input }) => ({ ...input, summary: "", notes: "" }),
|
|
232
251
|
on: { "workspace.lost": ".lost", "agent.fault": ".failed" },
|
|
233
|
-
initial: "
|
|
252
|
+
initial: "cutting",
|
|
234
253
|
states: {
|
|
254
|
+
// The Workspace only clones. The Machine cuts the Worktree that the Agent works in.
|
|
255
|
+
cutting: {
|
|
256
|
+
invoke: {
|
|
257
|
+
src: "cut",
|
|
258
|
+
input: ({ context }) => ({ path: fixTree(context), branch: context.branch }),
|
|
259
|
+
onDone: [{ guard: ({ event }) => isReady(event.output), target: "coding" }, { target: "notCut" }],
|
|
260
|
+
},
|
|
261
|
+
},
|
|
235
262
|
coding: {
|
|
236
263
|
invoke: {
|
|
237
264
|
src: "coder",
|
|
238
265
|
// The Frame (prompt and cwd) and continue. This Turn starts the coder's conversation.
|
|
239
266
|
input: ({ context }) => ({
|
|
240
267
|
prompt: "Make `node --test` pass, then commit.",
|
|
241
|
-
cwd: context
|
|
268
|
+
cwd: fixTree(context),
|
|
242
269
|
continue: true,
|
|
243
270
|
}),
|
|
244
271
|
},
|
|
@@ -257,7 +284,7 @@ const body = jr2Setup({
|
|
|
257
284
|
// The same conversation as in coding, with a Dial: the coder thinks harder on this Turn.
|
|
258
285
|
input: ({ context }) => ({
|
|
259
286
|
prompt: `The fix is not complete: ${context.notes}\nFix it, then commit.`,
|
|
260
|
-
cwd: context
|
|
287
|
+
cwd: fixTree(context),
|
|
261
288
|
continue: true,
|
|
262
289
|
thinkingLevel: "high",
|
|
263
290
|
}),
|
|
@@ -265,6 +292,7 @@ const body = jr2Setup({
|
|
|
265
292
|
on: { finish: { target: "checking", actions: assign({ summary: ({ event }) => event.summary }) } },
|
|
266
293
|
},
|
|
267
294
|
done: { type: "final" },
|
|
295
|
+
notCut: { invoke: { src: "fail", input: { reason: "the Worktree was not cut" } } },
|
|
268
296
|
failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
|
|
269
297
|
lost: { invoke: { src: "fail", input: { reason: "the Sandbox is gone" } } },
|
|
270
298
|
},
|
|
@@ -273,7 +301,6 @@ const body = jr2Setup({
|
|
|
273
301
|
export const machine = workspace(body, {
|
|
274
302
|
input: runInput,
|
|
275
303
|
repos: { app: "https://github.com/you/app.git" },
|
|
276
|
-
spec: ({ input }) => ({ branch: input.branch }),
|
|
277
304
|
});
|
|
278
305
|
```
|
|
279
306
|
|
|
@@ -355,9 +382,18 @@ itself. It tells nothing about the task: the prompt tells the task. It has two p
|
|
|
355
382
|
carries a [Chart](../reference/glossary.md#chart), the Turn part also tells how a Chart runs (see
|
|
356
383
|
[Charts](charts.md)).
|
|
357
384
|
|
|
358
|
-
A notice tells
|
|
359
|
-
|
|
360
|
-
|
|
385
|
+
A notice tells a Turn what happened before it. For example: the conversation is new and the earlier context is gone, or
|
|
386
|
+
the processes of the Sandbox stopped at its memory limit. A notice about a conversation goes to the next Turn of that
|
|
387
|
+
conversation. A notice about the Workspace goes to each conversation that was in the Workspace when the processes
|
|
388
|
+
stopped:
|
|
389
|
+
|
|
390
|
+
- A Turn that is open hears it before its next model request.
|
|
391
|
+
- A continued conversation hears it at its next Turn.
|
|
392
|
+
- The next Turn of the Agent whose Turn the stop cut hears it, also when that Turn is fresh.
|
|
393
|
+
|
|
394
|
+
A conversation hears each notice one time. A conversation whose own bash call already told it about the stop does not
|
|
395
|
+
hear the notice. A conversation that starts after the stop does not hear it, except the next Turn of the Agent whose
|
|
396
|
+
Turn the stop cut.
|
|
361
397
|
|
|
362
398
|
You cannot turn the Briefing off. It contains only facts that make the Agent work better. An `instructions` that tells
|
|
363
399
|
the same fact again does no harm.
|
|
@@ -409,8 +445,8 @@ the model in `models`. A model with no context window gets no Compaction.
|
|
|
409
445
|
different key.
|
|
410
446
|
- A Turn cannot set `instructions` or `workspace`.
|
|
411
447
|
- `cwd` is an absolute path. The Harness refuses a Turn with a relative `cwd`.
|
|
412
|
-
- Under a Workspace with
|
|
413
|
-
|
|
448
|
+
- Under a Workspace with Repo Slots, each Turn of an Agent with Working tools sets `cwd`. jr2 refuses the Turn without
|
|
449
|
+
it.
|
|
414
450
|
- An Agent with `"write"` or `"read"` runs in the body of a `workspace()`. jr2 refuses its Turn outside one.
|
|
415
451
|
- `jr2 up` sees only the models of the definitions. It cannot see a model that a Turn sets as a Dial. The Harness
|
|
416
452
|
refuses a model that it cannot find when the Turn starts.
|
|
@@ -19,8 +19,8 @@ runs and what it does.
|
|
|
19
19
|
## Why the parts are separate
|
|
20
20
|
|
|
21
21
|
An Agent that works on a Repo runs code. Its [Working tools](../reference/glossary.md#working-tools) give it a shell.
|
|
22
|
-
With the shell, it can
|
|
23
|
-
pod can reach. Thus jr2 puts each thing that the Agent must not touch in a different place:
|
|
22
|
+
With the shell, it can run each program of its image on the files in `/work`. It can also connect to each address that
|
|
23
|
+
its pod can reach. Thus jr2 puts each thing that the Agent must not touch in a different place:
|
|
24
24
|
|
|
25
25
|
- The control of a run is in the Orchestrator. An Agent cannot change its own Machine. It can only pick from its
|
|
26
26
|
[Menu](../reference/glossary.md#menu), and the Orchestrator decides what each pick does.
|
|
@@ -28,6 +28,8 @@ pod can reach. Thus jr2 puts each thing that the Agent must not touch in a diffe
|
|
|
28
28
|
processes. Agents in two Workspaces share no files and no ports. [Workspaces](workspaces.md) tells more.
|
|
29
29
|
- The credentials are in containers in which the Agent executes nothing: the Custodian, the Effector and the Repo cache.
|
|
30
30
|
[Secrets and trust](secrets-and-trust.md) tells how.
|
|
31
|
+
- The Harness holds one secret value, with which it sends the picks. Each program that it starts runs confined with
|
|
32
|
+
Landlock, so the code of an Agent cannot read that value.
|
|
31
33
|
|
|
32
34
|
## What runs where
|
|
33
35
|
|
|
@@ -66,7 +68,7 @@ The [Instance Harness](../reference/glossary.md#instance-harness), the
|
|
|
66
68
|
sections below tell when.
|
|
67
69
|
|
|
68
70
|
A Sandbox pod can also have a [User Container](../reference/glossary.md#user-container), when its `workspace()` names
|
|
69
|
-
one. It runs its own image, and it mounts
|
|
71
|
+
one. It runs its own image, and it mounts `/work`, with each [Worktree](../reference/glossary.md#worktree), read-write.
|
|
70
72
|
[Workspaces](workspaces.md) tells more.
|
|
71
73
|
|
|
72
74
|
## On your computer
|
|
@@ -115,14 +117,15 @@ To isolate two sets of Workflows, use two Instances. Each Instance has its own n
|
|
|
115
117
|
|
|
116
118
|
The Harness is jr2's own server for the Turns of Agents. It speaks to the model provider. It gives the model the Agent's
|
|
117
119
|
Menu and its Working tools. It executes each Working tool that the model calls, and sends each pick through the
|
|
118
|
-
Custodian.
|
|
119
|
-
[Agents and Turns](agents-and-turns.md) tells what happens in a Turn.
|
|
120
|
+
Custodian. Each program that a Working tool starts runs confined with Landlock. The Harness holds no Agents of its own.
|
|
121
|
+
Each Turn brings the Agent's definition with it. [Agents and Turns](agents-and-turns.md) tells what happens in a Turn.
|
|
120
122
|
|
|
121
123
|
The Harness has two placements:
|
|
122
124
|
|
|
123
125
|
- **In each Sandbox.** The Harness is the primary container of the Sandbox pod, named `harness`. It runs in the
|
|
124
126
|
[Sandbox Image](../reference/glossary.md#sandbox-image), and jr2 mounts the Harness into the pod at `/opt/jr2`. Thus
|
|
125
|
-
the Working tools of the Agent reach the tools of that image.
|
|
127
|
+
the Working tools of the Agent reach the tools of that image. Each Agent that works on a Repo has its Turns here: one
|
|
128
|
+
Harness serves all the Agents of the Sandbox.
|
|
126
129
|
- **As the Instance Harness.** The Instance Harness is one StatefulSet for each Instance, `jr2-instance-harness`. Each
|
|
127
130
|
Turn of a Menu-only Agent (`workspace: "none"`) runs here. Its pod has a Harness and a Custodian, and no Worktree. It
|
|
128
131
|
keeps each conversation on its volume while a run can continue it. `jr2 up` converges it when a Machine of the
|
|
@@ -130,16 +133,20 @@ The Harness has two placements:
|
|
|
130
133
|
|
|
131
134
|
A Harness answers only the Orchestrator. The Orchestrator sends a bearer token on each call, and the Harness accepts no
|
|
132
135
|
call without it, except its health check. `jr2 up` also converges NetworkPolicies that let only the Orchestrator's pods
|
|
133
|
-
connect to a Harness pod
|
|
136
|
+
connect to a Harness pod, and that stop each Harness pod at the cloud metadata addresses
|
|
137
|
+
([Secrets and trust](secrets-and-trust.md#what-a-held-secret-does-not-stop)).
|
|
134
138
|
|
|
135
139
|
## The Custodian
|
|
136
140
|
|
|
137
141
|
Each Harness pod has a Custodian: each Sandbox, and the Instance Harness. The Custodian is a container beside the
|
|
138
142
|
Harness. The Agent executes no code in it. It does two things:
|
|
139
143
|
|
|
140
|
-
- It carries
|
|
141
|
-
a pick,
|
|
142
|
-
|
|
144
|
+
- It carries five kinds of call from the pod to the Orchestrator: the Menu reads, the picks, the polls for the answer to
|
|
145
|
+
a pick, the reports that a step boundary gave its notices to the model, and the fetch asks. It sends the first four
|
|
146
|
+
with the control token of the Sandbox, and the fetch asks with its fetch token. These five routes are all that it
|
|
147
|
+
forwards. It refuses all other routes. The Harness sends the first four on a Unix socket in `/run/jr2`. A confined
|
|
148
|
+
program cannot make a socket there, and the Custodian refuses a request on it that does not have the control Stand-in.
|
|
149
|
+
The fetch asks go to port `8081` on loopback.
|
|
143
150
|
- It holds the [Held secrets](../reference/glossary.md#held-secret) of its pod, such as a model API key. It puts the
|
|
144
151
|
value into the requests that go to the hosts of each Held secret.
|
|
145
152
|
|
|
@@ -154,15 +161,16 @@ names a target. Thus a credential that an Effect uses stays out of the Harness c
|
|
|
154
161
|
program, and it runs in a container in which the Agent executes nothing. [Effects](effects.md) tells which Effect runs
|
|
155
162
|
where. It has two placements:
|
|
156
163
|
|
|
157
|
-
- **The Sandbox Effector** is a container in a Sandbox pod, named `effector`. It mounts
|
|
158
|
-
|
|
159
|
-
|
|
164
|
+
- **The Sandbox Effector** is a container in a Sandbox pod, named `effector`. It mounts `/work` read-only. It runs a
|
|
165
|
+
`push`, an `http` Effect whose body is a Worktree file, and a `command` that names an Effect secret. Its image is the
|
|
166
|
+
Sandbox Image, unless the `workspace()` names a different image.
|
|
160
167
|
- **The Instance Effector** is one pod for each Instance, `jr2-instance-effector`. It runs each `http` Effect that reads
|
|
161
168
|
no Worktree file, and each `dispatch` that starts a run on a different Orchestrator. It holds the Instance token of
|
|
162
169
|
that Orchestrator as an Effect secret. It runs in the `jr2-harness` Kit image.
|
|
163
170
|
|
|
164
171
|
Each placement exists only when a Machine of the Instance puts an Effect there. A `command` that names no Effect secret
|
|
165
|
-
does not use an Effector. It runs in the Harness container of its Sandbox, with the same tools and access as the Agent
|
|
172
|
+
does not use an Effector. It runs in the Harness container of its Sandbox, with the same tools and access as the Agent,
|
|
173
|
+
and confined in the same way.
|
|
166
174
|
|
|
167
175
|
The Orchestrator signs each request that it sends to an Effector, and the Effector signs each answer. The Orchestrator
|
|
168
176
|
only asks, and the Effector holds the credentials.
|
|
@@ -188,7 +196,8 @@ the Orchestrator stopped for longer than the Lease allows. [Workspaces](workspac
|
|
|
188
196
|
|
|
189
197
|
The Repo cache keeps one bare clone of each Repo on each node that needs it. It is a DaemonSet, `jr2-repo-cache`, in the
|
|
190
198
|
namespace of the Instance. It has one pod on each [Sandbox node](../reference/glossary.md#sandbox-node). `jr2 up`
|
|
191
|
-
converges it when a Machine of the Instance uses a Sandbox.
|
|
199
|
+
converges it when a Machine of the Instance uses a Sandbox. Each pod also checks that the kernel of its node has
|
|
200
|
+
Landlock. On a node without Landlock, the pod is not ready, and `jr2 up` names that node and refuses to converge.
|
|
192
201
|
|
|
193
202
|
The operator mounts the cache of each Repo into a Sandbox, read-only, at `/repos`. The clone in the Sandbox gets its
|
|
194
203
|
objects from that cache. A `git fetch origin` in the Sandbox asks the Repo cache for a fetch. Then the Repo cache
|
|
@@ -204,6 +213,8 @@ credential to fetch.
|
|
|
204
213
|
- A Harness accepts a call only from the Orchestrator, with the bearer of its own pod. Only its health check needs no
|
|
205
214
|
bearer.
|
|
206
215
|
- The Agent executes code only in the Harness container. It executes nothing in the Custodian or in the Effector.
|
|
216
|
+
- In the Harness container, each program that the Harness starts runs confined. It cannot read what the Harness holds.
|
|
217
|
+
- Each Sandbox node needs Landlock in its kernel. `jr2 up` refuses to converge while a Sandbox node has none.
|
|
207
218
|
- The Orchestrator watches the Sandbox resources and never reads a pod. The operator puts the facts of the pod on the
|
|
208
219
|
status of the Sandbox resource.
|
|
209
220
|
- A Sandbox has one pod for its life. A lost pod is never made again.
|
|
@@ -226,5 +237,7 @@ credential to fetch.
|
|
|
226
237
|
[ADR-0053](../reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md),
|
|
227
238
|
[ADR-0058](../reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md),
|
|
228
239
|
[ADR-0059](../reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md),
|
|
229
|
-
[ADR-0063](../reference/decisions/0063-the-orchestrator-watches-the-cluster.md)
|
|
230
|
-
[ADR-0068](../reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md)
|
|
240
|
+
[ADR-0063](../reference/decisions/0063-the-orchestrator-watches-the-cluster.md),
|
|
241
|
+
[ADR-0068](../reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md),
|
|
242
|
+
[ADR-0076](../reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md) and
|
|
243
|
+
[ADR-0077](../reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md)
|
package/docs/concepts/charts.md
CHANGED
|
@@ -39,8 +39,10 @@ it bad text.
|
|
|
39
39
|
and the size of the Chart in bytes.
|
|
40
40
|
|
|
41
41
|
A Chart gets nothing else. A lent Effect keeps its url, its argv and its secrets, so a Chart fills only its input. A
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
lent [`worktree()`](../reference/authoring-surface/worktree.md) must state `expect`, because a Chart cannot state it. A
|
|
43
|
+
Chart reads the cut with the kit guard `jr2.ready`, then gives the same literal path as `cwd` and `worktree`. A Chart
|
|
44
|
+
has no url, no secret, no image and no Repo of its own. The Open body also decides where each Turn of the Chart runs: in
|
|
45
|
+
the Sandbox of its `workspace()`, or on the [Instance Harness](../reference/glossary.md#instance-harness).
|
|
44
46
|
|
|
45
47
|
## What a Chart holds
|
|
46
48
|
|
|
@@ -48,7 +50,8 @@ A Chart is [xstate](https://xstate.js.org)'s own JSON config, with these limits:
|
|
|
48
50
|
|
|
49
51
|
- States, transitions, `after` delays, and nested and parallel states. A Chart has 200 states at most, 8 levels deep.
|
|
50
52
|
- Each `invoke.src` is a lent slot key or an Agent of the Chart. Each guard, action and delay is a lent name. Each Chart
|
|
51
|
-
also has the guard `jr2.failure`, which is true on an `onError` when the error is a Failure
|
|
53
|
+
also has the guard `jr2.failure`, which is true on an `onError` when the error is a Failure, and the guard
|
|
54
|
+
`jr2.ready`, which is true on an `onDone` when the cut is `ready`.
|
|
52
55
|
- A state that invokes an Agent can route [`agent.fault`](../reference/mechanism-events.md#agent-fault) in its `on`, but
|
|
53
56
|
it does not have to. When no transition of the Chart takes `agent.fault`, the Open body sends it, unchanged, to the
|
|
54
57
|
Chart wrapper. The wrapper's own `agent.fault` route takes it, as for an Agent of the wrapper, so one route on the
|
|
@@ -83,6 +83,9 @@ the cluster, see [Architecture](architecture.md).
|
|
|
83
83
|
12. It applies the operator. For a Repo with an ssh url, it asks where the deploy key comes from.
|
|
84
84
|
13. It applies the Secrets and the Orchestrator, and waits for the rollout. When the Machines need them, it also waits
|
|
85
85
|
for the Repo cache, the Instance Harness and the [Instance Effector](../reference/glossary.md#instance-effector).
|
|
86
|
+
The Repo cache agent on each Sandbox node checks that the kernel of its node has Landlock, which jr2 needs. While a
|
|
87
|
+
Sandbox node has no Landlock, the converge fails. The error names each such node, the reason, and the fix: keep the
|
|
88
|
+
node out with `sandbox.nodeSelector` or a taint, or enable Landlock in its kernel. Then run `jr2 up` again.
|
|
86
89
|
14. It publishes the Assembly to the Orchestrator. When the Orchestrator already has this Assembly, it publishes
|
|
87
90
|
nothing.
|
|
88
91
|
15. It removes the images that nothing uses now.
|