@dynamicagents/plugins 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -77
- package/dist/browser/index.d.ts +0 -2
- package/dist/browser/index.js +21 -27
- package/dist/claude-code/cards.d.ts +55 -0
- package/dist/claude-code/cards.js +283 -0
- package/dist/claude-code/config.d.ts +28 -63
- package/dist/claude-code/config.js +0 -11
- package/dist/claude-code/egress.d.ts +8 -1
- package/dist/claude-code/egress.js +42 -4
- package/dist/claude-code/events.d.ts +106 -9
- package/dist/claude-code/events.js +201 -56
- package/dist/claude-code/github.d.ts +39 -0
- package/dist/claude-code/github.js +82 -0
- package/dist/claude-code/index.d.ts +16 -90
- package/dist/claude-code/index.js +14 -276
- package/dist/claude-code/model.d.ts +152 -0
- package/dist/claude-code/model.js +366 -0
- package/dist/claude-code/run.d.ts +230 -116
- package/dist/claude-code/run.js +380 -167
- package/dist/claude-code/session.d.ts +131 -0
- package/dist/claude-code/session.js +288 -0
- package/dist/claude-code/spec.d.ts +46 -0
- package/dist/claude-code/spec.js +83 -0
- package/dist/claude-code/verified.d.ts +15 -0
- package/dist/claude-code/verified.js +15 -0
- package/dist/computer/context.d.ts +1 -5
- package/dist/computer/context.js +7 -10
- package/dist/computer/file-lock.js +5 -4
- package/dist/computer/gate.d.ts +4 -4
- package/dist/computer/gate.js +5 -5
- package/dist/computer/index.d.ts +40 -258
- package/dist/computer/index.js +71 -324
- package/dist/computer/paths.d.ts +36 -16
- package/dist/computer/paths.js +42 -29
- package/dist/computer/proxy.d.ts +45 -0
- package/dist/computer/proxy.js +234 -0
- package/dist/computer/render.d.ts +1 -40
- package/dist/computer/render.js +2 -68
- package/dist/computer/tools-exec.js +12 -13
- package/dist/computer/tools-file.d.ts +9 -1
- package/dist/computer/tools-file.js +22 -82
- package/dist/computer/{tools-find.d.ts → tools-grep.d.ts} +1 -1
- package/dist/computer/tools-grep.js +129 -0
- package/dist/repo/checkout.d.ts +4 -5
- package/dist/repo/checkout.js +31 -8
- package/dist/repo/context.d.ts +35 -9
- package/dist/repo/context.js +101 -49
- package/dist/repo/index.d.ts +73 -28
- package/dist/repo/index.js +112 -96
- package/dist/repo/tools-checks.d.ts +3 -0
- package/dist/repo/tools-checks.js +112 -0
- package/dist/repo/tools-clone.js +1 -1
- package/dist/repo/tools-pr.d.ts +14 -0
- package/dist/repo/{tools-forge.js → tools-pr.js} +53 -36
- package/dist/repo/tools-review.d.ts +9 -0
- package/dist/repo/tools-review.js +67 -47
- package/dist/repo/tools-worktree.js +144 -60
- package/dist/repo/tools-worktrees.d.ts +12 -0
- package/dist/repo/tools-worktrees.js +48 -0
- package/dist/repo/url.d.ts +2 -2
- package/dist/repo/url.js +7 -7
- package/dist/scratch/index.d.ts +4 -4
- package/dist/scratch/index.js +12 -11
- package/dist/{computer → workspace}/advisory.d.ts +4 -4
- package/dist/{computer → workspace}/advisory.js +2 -2
- package/dist/{computer/host → workspace}/ca-trust.d.ts +8 -1
- package/dist/{computer/host → workspace}/ca-trust.js +12 -3
- package/dist/{computer/host → workspace}/container-deps.d.ts +4 -0
- package/dist/{computer/host → workspace}/container-deps.js +26 -1
- package/dist/{computer/host → workspace}/container-fault.d.ts +3 -3
- package/dist/{computer/host → workspace}/container-fault.js +23 -8
- package/dist/workspace/deps-snapshot.d.ts +110 -0
- package/dist/workspace/deps-snapshot.js +424 -0
- package/dist/workspace/exec.d.ts +94 -0
- package/dist/workspace/exec.js +153 -0
- package/dist/workspace/format.d.ts +36 -0
- package/dist/workspace/format.js +57 -0
- package/dist/{computer/host → workspace}/git-host.d.ts +21 -4
- package/dist/{computer/host → workspace}/git-host.js +72 -2
- package/dist/{computer/host → workspace}/git-identity.d.ts +1 -1
- package/dist/{computer/host → workspace}/git-identity.js +1 -1
- package/dist/workspace/index.d.ts +44 -47
- package/dist/workspace/index.js +52 -112
- package/dist/{computer/host → workspace}/install-job.d.ts +53 -4
- package/dist/{computer/host → workspace}/install-job.js +346 -178
- package/dist/{computer → workspace}/install.d.ts +17 -8
- package/dist/{computer → workspace}/install.js +24 -6
- package/dist/{computer/host/workspace.d.ts → workspace/object.d.ts} +125 -27
- package/dist/{computer/host/workspace.js → workspace/object.js} +341 -58
- package/dist/workspace/open.d.ts +95 -0
- package/dist/workspace/open.js +63 -0
- package/dist/workspace/read.d.ts +16 -0
- package/dist/workspace/read.js +22 -0
- package/dist/{computer → workspace}/shell.d.ts +5 -5
- package/dist/{computer → workspace}/shell.js +6 -6
- package/dist/{computer/host → workspace}/wake.d.ts +3 -1
- package/package.json +17 -37
- package/dist/arc-agi/analysis.d.ts +0 -322
- package/dist/arc-agi/analysis.js +0 -802
- package/dist/arc-agi/client.d.ts +0 -82
- package/dist/arc-agi/client.js +0 -113
- package/dist/arc-agi/game-tools.d.ts +0 -24
- package/dist/arc-agi/game-tools.js +0 -26
- package/dist/arc-agi/index.d.ts +0 -40
- package/dist/arc-agi/index.js +0 -175
- package/dist/arc-agi/main-agent.d.ts +0 -24
- package/dist/arc-agi/main-agent.js +0 -62
- package/dist/arc-agi/recipe.d.ts +0 -48
- package/dist/arc-agi/recipe.js +0 -97
- package/dist/arc-agi/scorecard.d.ts +0 -118
- package/dist/arc-agi/scorecard.js +0 -163
- package/dist/arc-agi/soul.d.ts +0 -29
- package/dist/arc-agi/soul.js +0 -79
- package/dist/arc-agi/store.d.ts +0 -153
- package/dist/arc-agi/store.js +0 -215
- package/dist/arc-agi/tools.d.ts +0 -36
- package/dist/arc-agi/tools.js +0 -574
- package/dist/arc-agi/types.d.ts +0 -211
- package/dist/arc-agi/types.js +0 -19
- package/dist/claude-code/recipe.d.ts +0 -53
- package/dist/claude-code/recipe.js +0 -100
- package/dist/computer/read.d.ts +0 -133
- package/dist/computer/read.js +0 -200
- package/dist/computer/tools-find.js +0 -216
- package/dist/recall/index.d.ts +0 -105
- package/dist/recall/index.js +0 -181
- package/dist/repo/tools-forge.d.ts +0 -4
- package/dist/triage/index.d.ts +0 -360
- package/dist/triage/index.js +0 -311
- /package/dist/{computer/host → workspace}/sync.d.ts +0 -0
- /package/dist/{computer/host → workspace}/sync.js +0 -0
- /package/dist/{computer/host → workspace}/wake.js +0 -0
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* ## Why this exists at all
|
|
12
12
|
*
|
|
13
|
-
* The alternative is a sentence in the
|
|
13
|
+
* The alternative is a sentence in the sub-agent's soul — "install dependencies
|
|
14
14
|
* before you do anything else" — and a standard with no command attached is one a
|
|
15
15
|
* model satisfies with whichever check is cheapest: edit a README, run
|
|
16
16
|
* `prettier --check` on that one file, report the change verified, and never run
|
|
@@ -36,16 +36,16 @@ export interface InstallProbe {
|
|
|
36
36
|
* Where the install has got to, as the host reports it and the tools read it.
|
|
37
37
|
*
|
|
38
38
|
* Declared here rather than in the host because both sides depend on it and
|
|
39
|
-
* neither owns the other: the Durable Object runs the job, and `
|
|
39
|
+
* neither owns the other: the Durable Object runs the job, and `bash` refuses
|
|
40
40
|
* to run against a tree that is still being built. A shape they agreed on
|
|
41
41
|
* informally would drift, and the drift would show up as a shell command running
|
|
42
42
|
* halfway through an `npm ci`.
|
|
43
43
|
*
|
|
44
|
-
* ## Why it is
|
|
44
|
+
* ## Why it is the host's `JobState` with one field intersected in
|
|
45
45
|
*
|
|
46
|
-
* Core owns the *choreography* around a job a Durable Object drives
|
|
47
|
-
* alarm — arming, the single-flight guard, the staleness bound, the
|
|
48
|
-
*
|
|
46
|
+
* Core's `/job` owns the *choreography* around a job a Durable Object drives
|
|
47
|
+
* through its alarm — arming, the single-flight guard, the staleness bound, the
|
|
48
|
+
* generation a drain writes under. This package owns what an install
|
|
49
49
|
* specifically **is**: which package manager, which lockfile, what counts as
|
|
50
50
|
* skippable. Splitting them along that line is what lets a second job on the
|
|
51
51
|
* same object — a coding-agent run, say — reuse every rule without inheriting
|
|
@@ -146,7 +146,7 @@ export declare function resolveInstallCommand(fs: InstallProbe, dir: string, pla
|
|
|
146
146
|
* from `pnpm@9` to `pnpm@10`, or add a dependency without regenerating the lock,
|
|
147
147
|
* and every one of those changes what an install produces while leaving the
|
|
148
148
|
* lockfile byte-identical. Hashing the lockfile alone calls that a match, skips
|
|
149
|
-
* the install, and hands the
|
|
149
|
+
* the install, and hands the agent a `node_modules` that is quietly wrong —
|
|
150
150
|
* which surfaces as a missing module in a build, three tool calls later, with
|
|
151
151
|
* nothing pointing back at the install that never ran.
|
|
152
152
|
*
|
|
@@ -184,6 +184,15 @@ export declare function resolveInstallCommand(fs: InstallProbe, dir: string, pla
|
|
|
184
184
|
* A matching fingerprint means "the same install would produce the same tree".
|
|
185
185
|
* It does **not** mean the tree is there: it is on the container's disk, and
|
|
186
186
|
* goes with the container. The caller also needs a record that this container
|
|
187
|
-
* finished it — see `./
|
|
187
|
+
* finished it — see `./install-job.ts`.
|
|
188
188
|
*/
|
|
189
189
|
export declare function installFingerprint(fs: InstallProbe, dir: string, resolution: InstallResolution): Promise<string | null>;
|
|
190
|
+
/**
|
|
191
|
+
* The same digest for a package root an install reached without being pointed
|
|
192
|
+
* at it — a submodule a bootstrap installs, say — where there is no command to
|
|
193
|
+
* fold in, and every lockfile the plan knows stands in for the one it would pick.
|
|
194
|
+
*
|
|
195
|
+
* For `./deps-snapshot.ts`, which restores every root's tree at once and so has
|
|
196
|
+
* to know none of them moved.
|
|
197
|
+
*/
|
|
198
|
+
export declare function rootFingerprint(fs: InstallProbe, dir: string, plan: InstallPlan): Promise<string | null>;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* ## Why this exists at all
|
|
12
12
|
*
|
|
13
|
-
* The alternative is a sentence in the
|
|
13
|
+
* The alternative is a sentence in the sub-agent's soul — "install dependencies
|
|
14
14
|
* before you do anything else" — and a standard with no command attached is one a
|
|
15
15
|
* model satisfies with whichever check is cheapest: edit a README, run
|
|
16
16
|
* `prettier --check` on that one file, report the change verified, and never run
|
|
@@ -96,6 +96,10 @@ function pinnedManager(packageJson) {
|
|
|
96
96
|
return undefined;
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
+
/** Every lockfile the plan names, each once. */
|
|
100
|
+
function planLockfiles(plan) {
|
|
101
|
+
return [...new Set(plan.rules.flatMap((r) => [...r.lockfiles]))];
|
|
102
|
+
}
|
|
99
103
|
/**
|
|
100
104
|
* Every lockfile the plan knows of that is present in `dir`.
|
|
101
105
|
*
|
|
@@ -103,9 +107,8 @@ function pinnedManager(packageJson) {
|
|
|
103
107
|
* about which manager it drives.
|
|
104
108
|
*/
|
|
105
109
|
async function knownLockfiles(fs, dir, plan) {
|
|
106
|
-
const names = [...new Set(plan.rules.flatMap((r) => [...r.lockfiles]))];
|
|
107
110
|
const present = [];
|
|
108
|
-
for (const name of
|
|
111
|
+
for (const name of planLockfiles(plan)) {
|
|
109
112
|
if (await fs.exists(`${dir}/${name}`))
|
|
110
113
|
present.push(name);
|
|
111
114
|
}
|
|
@@ -221,7 +224,7 @@ async function firstPresent(fs, dir, names) {
|
|
|
221
224
|
* from `pnpm@9` to `pnpm@10`, or add a dependency without regenerating the lock,
|
|
222
225
|
* and every one of those changes what an install produces while leaving the
|
|
223
226
|
* lockfile byte-identical. Hashing the lockfile alone calls that a match, skips
|
|
224
|
-
* the install, and hands the
|
|
227
|
+
* the install, and hands the agent a `node_modules` that is quietly wrong —
|
|
225
228
|
* which surfaces as a missing module in a build, three tool calls later, with
|
|
226
229
|
* nothing pointing back at the install that never ran.
|
|
227
230
|
*
|
|
@@ -259,7 +262,7 @@ async function firstPresent(fs, dir, names) {
|
|
|
259
262
|
* A matching fingerprint means "the same install would produce the same tree".
|
|
260
263
|
* It does **not** mean the tree is there: it is on the container's disk, and
|
|
261
264
|
* goes with the container. The caller also needs a record that this container
|
|
262
|
-
* finished it — see `./
|
|
265
|
+
* finished it — see `./install-job.ts`.
|
|
263
266
|
*/
|
|
264
267
|
export async function installFingerprint(fs, dir, resolution) {
|
|
265
268
|
if (resolution.kind !== "run")
|
|
@@ -273,10 +276,25 @@ export async function installFingerprint(fs, dir, resolution) {
|
|
|
273
276
|
...resolution.lockfiles,
|
|
274
277
|
...INSTALL_CONFIG_FILES
|
|
275
278
|
];
|
|
279
|
+
return await digestFiles(fs, dir, names, resolution.command);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* The same digest for a package root an install reached without being pointed
|
|
283
|
+
* at it — a submodule a bootstrap installs, say — where there is no command to
|
|
284
|
+
* fold in, and every lockfile the plan knows stands in for the one it would pick.
|
|
285
|
+
*
|
|
286
|
+
* For `./deps-snapshot.ts`, which restores every root's tree at once and so has
|
|
287
|
+
* to know none of them moved.
|
|
288
|
+
*/
|
|
289
|
+
export async function rootFingerprint(fs, dir, plan) {
|
|
290
|
+
return await digestFiles(fs, dir, ["package.json", ...planLockfiles(plan), ...INSTALL_CONFIG_FILES], "");
|
|
291
|
+
}
|
|
292
|
+
/** `prefix`, then every file of `names` present in `dir`; null when none is. */
|
|
293
|
+
async function digestFiles(fs, dir, names, prefix) {
|
|
276
294
|
// Names as well as contents. Two lockfiles will not collide on content in
|
|
277
295
|
// practice, but a digest that cannot say *which* file it read is one that
|
|
278
296
|
// silently depends on the caller resolving them in the same order forever.
|
|
279
|
-
let input =
|
|
297
|
+
let input = prefix;
|
|
280
298
|
let found = false;
|
|
281
299
|
for (const name of names) {
|
|
282
300
|
if (!(await fs.exists(`${dir}/${name}`)))
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { DurableObject } from "cloudflare:workers";
|
|
2
2
|
import { type WorkspaceEgressPolicy, type WorkspaceStub } from "@cloudflare/computer";
|
|
3
|
-
import {
|
|
4
|
-
import { type
|
|
5
|
-
import
|
|
6
|
-
import type {
|
|
3
|
+
import { ContainerBackend, type ContainerInstanceSize } from "@cloudflare/computer/backends/container";
|
|
4
|
+
import { type DepsSnapshotRecord } from "./deps-snapshot.js";
|
|
5
|
+
import { type WorkspaceAdvisory } from "./advisory.js";
|
|
6
|
+
import type { InstallPlan, InstallState } from "./install.js";
|
|
7
|
+
import type { RepoGitResult } from "../repo/index.js";
|
|
7
8
|
/**
|
|
8
9
|
* A workspace: one Durable Object, one container, one repository.
|
|
9
10
|
*
|
|
@@ -16,7 +17,7 @@ import type { RepoGitResult } from "../../repo/index.js";
|
|
|
16
17
|
* A directory inside the plugin rather than a subpath beside it, so that one
|
|
17
18
|
* capability stays one import path and a consumer cannot take the tools without
|
|
18
19
|
* the object they address. The bundle cost of that is nothing: `sideEffects` is
|
|
19
|
-
* false, so an agent that only calls `
|
|
20
|
+
* false, so an agent that only calls `bash` carries no container backend and
|
|
20
21
|
* no isomorphic-git.
|
|
21
22
|
*
|
|
22
23
|
* What this file must not do is import the plugin barrel. The barrel re-exports
|
|
@@ -29,7 +30,7 @@ import type { RepoGitResult } from "../../repo/index.js";
|
|
|
29
30
|
* FUSE at `/workspace`. Commands run against the same tree the Worker reads over
|
|
30
31
|
* RPC, and the tree outlives the container.
|
|
31
32
|
*
|
|
32
|
-
* **One repository per object**, because
|
|
33
|
+
* **One repository per object**, because `@cloudflare/computer` is strictly 1 DO ↔ 1
|
|
33
34
|
* container: the id derives from caller *and* repository (see `workspaceName`),
|
|
34
35
|
* so two repositories for one caller are two objects and two containers.
|
|
35
36
|
*
|
|
@@ -49,10 +50,11 @@ import type { RepoGitResult } from "../../repo/index.js";
|
|
|
49
50
|
* and each carries the reasoning for its own decisions:
|
|
50
51
|
*
|
|
51
52
|
* - `./install-job.ts` — when a dependency install runs, and every guard on it.
|
|
53
|
+
* - `./deps-snapshot.ts` — starting a container from one that already installed.
|
|
52
54
|
* - `./sync.ts` — the host's half of syncing, which nothing else will do.
|
|
53
55
|
* - `./ca-trust.ts` — making a container able to speak TLS.
|
|
54
56
|
* - `./git-identity.ts` — who a container's commits belong to.
|
|
55
|
-
* - `./git-host.ts` — the operations that hold the
|
|
57
|
+
* - `./git-host.ts` — the operations that hold the GitHub credential.
|
|
56
58
|
*
|
|
57
59
|
* Each takes a small explicit seam, and that is the test: a module that needed
|
|
58
60
|
* the whole object back would be a module that did not want extracting.
|
|
@@ -63,7 +65,7 @@ export declare const WORKSPACE_DIR = "/workspace";
|
|
|
63
65
|
* The Durable Object name for one caller's checkout of one repository.
|
|
64
66
|
*
|
|
65
67
|
* Exported because several places must agree on it and none can see the others:
|
|
66
|
-
* the plugin that resolves the stub, the agent that hands it down to
|
|
68
|
+
* the plugin that resolves the stub, the agent that hands it down to sub-agents,
|
|
67
69
|
* and the cancellation path. A pipe rather than a slash, so the caller half
|
|
68
70
|
* cannot forge a repository boundary by containing one.
|
|
69
71
|
*
|
|
@@ -76,10 +78,23 @@ export declare const WORKSPACE_DIR = "/workspace";
|
|
|
76
78
|
*
|
|
77
79
|
* `repo` is not always a repository. A host may pass a sentinel through here as
|
|
78
80
|
* one — a scratchpad is modelled that way, which is what keys it to its own
|
|
79
|
-
* object and its own container. Anything that is not a
|
|
81
|
+
* object and its own container. Anything that is not a GitHub name works, as long
|
|
80
82
|
* as no caller could clone something that collides with it.
|
|
81
83
|
*/
|
|
82
84
|
export declare function workspaceName(callerKey: string, repo?: string): string;
|
|
85
|
+
/**
|
|
86
|
+
* How long a workspace survives without being used.
|
|
87
|
+
*
|
|
88
|
+
* A Durable Object is **never** reclaimed by the platform, and a namespace
|
|
89
|
+
* cannot be enumerated from a Worker, so nothing else is coming to clean up.
|
|
90
|
+
* Source-only workspaces are small (6.3 MB for slack-gatekeeper), which makes
|
|
91
|
+
* this hygiene rather than cost control — but unbounded hygiene is still
|
|
92
|
+
* unbounded.
|
|
93
|
+
*
|
|
94
|
+
* Exported because a host keeping work in a workspace has to say how long that
|
|
95
|
+
* work survives untouched.
|
|
96
|
+
*/
|
|
97
|
+
export declare const IDLE_RECLAIM_MS: number;
|
|
83
98
|
/**
|
|
84
99
|
* `work`, refused once `ms` pass without it settling. The work itself goes on;
|
|
85
100
|
* only the wait for it ends — see {@link READY_DEADLINE_MS}.
|
|
@@ -97,16 +112,15 @@ declare class WorkspaceContainerHost extends DurableObject<Cloudflare.Env> {
|
|
|
97
112
|
}
|
|
98
113
|
/**
|
|
99
114
|
* `withWorkspaceContainer` adds one method, `getWorkspaceContainer()`, over
|
|
100
|
-
* `this.ctx.container` — the runtime's own container handle
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* everything else.
|
|
115
|
+
* `this.ctx.container` — the runtime's own container handle, started with the
|
|
116
|
+
* image and instance size this object asks for. Stopping it is this object's
|
|
117
|
+
* job — see {@link CONTAINER_IDLE_MS}.
|
|
104
118
|
*/
|
|
105
119
|
declare const WorkspaceContainerBase: typeof WorkspaceContainerHost & (new (...args: any[]) => WorkspaceContainerHost & {
|
|
106
120
|
getWorkspaceContainer(): import("@cloudflare/computer/backends/container").WorkspaceContainerAPI;
|
|
107
121
|
});
|
|
108
122
|
/**
|
|
109
|
-
* The
|
|
123
|
+
* The GitHub credential, and who a commit made on this side is attributed to.
|
|
110
124
|
*
|
|
111
125
|
* Config rather than an env read, because this class cannot name a consumer's
|
|
112
126
|
* ambient `Env` — it is an interface `wrangler types` generates into *their*
|
|
@@ -120,7 +134,7 @@ declare const WorkspaceContainerBase: typeof WorkspaceContainerHost & (new (...a
|
|
|
120
134
|
*/
|
|
121
135
|
export interface WorkspaceGitConfig {
|
|
122
136
|
/**
|
|
123
|
-
* The **name** of the binding holding the
|
|
137
|
+
* The **name** of the binding holding the GitHub credential — never the
|
|
124
138
|
* credential itself.
|
|
125
139
|
*
|
|
126
140
|
* `workspaceConfig()` is an ordinary method on the prototype, and on a Durable
|
|
@@ -196,7 +210,7 @@ export interface WorkspaceObjectConfig {
|
|
|
196
210
|
egress: WorkspaceEgressPolicy;
|
|
197
211
|
/** How this deployment installs dependencies for this agent's checkouts. */
|
|
198
212
|
installPlan: InstallPlan;
|
|
199
|
-
/** Log prefix — `
|
|
213
|
+
/** Log prefix — `coding-workspace`, `claude-coordinator-workspace`. */
|
|
200
214
|
label: string;
|
|
201
215
|
/**
|
|
202
216
|
* How long this agent's container stays up after the last command **started**.
|
|
@@ -204,14 +218,23 @@ export interface WorkspaceObjectConfig {
|
|
|
204
218
|
* A per-agent value because the invariant on {@link CONTAINER_IDLE_MS} — it
|
|
205
219
|
* must exceed the longest command the shell allows — is an invariant about the
|
|
206
220
|
* *agent*, and the two differ by a factor of four. The coder's longest command
|
|
207
|
-
* is a tool call; `claude-
|
|
221
|
+
* is a tool call; `claude-coordinator`'s is a whole `claude -p` session that runs
|
|
208
222
|
* detached for its entire timeout.
|
|
209
223
|
*
|
|
210
224
|
* Omit it for the default. Raise it, never lower it, and raise it whenever the
|
|
211
225
|
* agent's longest command grows.
|
|
212
226
|
*/
|
|
213
227
|
containerIdleMs?: number;
|
|
214
|
-
/**
|
|
228
|
+
/**
|
|
229
|
+
* The size this agent's container starts at — a tier such as `standard-2`, or
|
|
230
|
+
* `{ vcpu, memoryMib, diskMb }`.
|
|
231
|
+
*
|
|
232
|
+
* Requested on every start, and part of what a running container is matched
|
|
233
|
+
* against: change it and the next connect replaces a container started at the
|
|
234
|
+
* old size. Which size, and why there is no default: {@link file://./README.md}.
|
|
235
|
+
*/
|
|
236
|
+
instance: ContainerInstanceSize;
|
|
237
|
+
/** The GitHub credential and commit identity — see {@link WorkspaceGitConfig}. */
|
|
215
238
|
git: WorkspaceGitConfig;
|
|
216
239
|
}
|
|
217
240
|
/**
|
|
@@ -219,14 +242,13 @@ export interface WorkspaceObjectConfig {
|
|
|
219
242
|
*
|
|
220
243
|
* Abstract because a Durable Object class takes no constructor arguments, so
|
|
221
244
|
* per-agent configuration cannot arrive that way. {@link workspaceConfig} is the
|
|
222
|
-
* seam,
|
|
223
|
-
* uses for exactly the same reason.
|
|
245
|
+
* seam, for the same reason Think's own hooks are methods rather than options.
|
|
224
246
|
*
|
|
225
247
|
* ## Why `backend` and `#workspace` are lazy
|
|
226
248
|
*
|
|
227
249
|
* **Base class fields run before subclass fields**, so as plain fields they
|
|
228
250
|
* would read `undefined` from any `workspaceConfig()` that touches a subclass
|
|
229
|
-
* field — which `
|
|
251
|
+
* field — which `ClaudeCoordinatorWorkspace`'s does, for its credential store.
|
|
230
252
|
* Memoised getters remove the hazard rather than documenting it, which is what
|
|
231
253
|
* lets a subclass implement the seam however it likes.
|
|
232
254
|
*/
|
|
@@ -236,7 +258,7 @@ export declare abstract class WorkspaceObjectBase<TEnv extends Cloudflare.Env =
|
|
|
236
258
|
* This Worker's bindings, as the host's own generated `Env` spells them.
|
|
237
259
|
*
|
|
238
260
|
* Re-declared rather than inherited so a subclass reading a binding of its own
|
|
239
|
-
* — `claude-
|
|
261
|
+
* — `claude-coordinator`'s reaches for its credential pool — gets its real type
|
|
240
262
|
* instead of the base's. `declare` because the field is the runtime's; this
|
|
241
263
|
* only narrows what TypeScript believes about it.
|
|
242
264
|
*/
|
|
@@ -245,11 +267,11 @@ export declare abstract class WorkspaceObjectBase<TEnv extends Cloudflare.Env =
|
|
|
245
267
|
* Everything this agent's workspace does differently. Called once, lazily.
|
|
246
268
|
*
|
|
247
269
|
* Read through {@link #cfg}, never directly: an implementation may build
|
|
248
|
-
* something real — `claude-
|
|
270
|
+
* something real — `claude-coordinator`'s constructs its egress gateway — and this
|
|
249
271
|
* is consulted on the busiest path in the object.
|
|
250
272
|
*/
|
|
251
273
|
protected abstract workspaceConfig(): WorkspaceObjectConfig;
|
|
252
|
-
get backend():
|
|
274
|
+
get backend(): ContainerBackend;
|
|
253
275
|
/**
|
|
254
276
|
* What `getWorkspace(stub)` calls from outside this object.
|
|
255
277
|
*
|
|
@@ -270,11 +292,11 @@ export declare abstract class WorkspaceObjectBase<TEnv extends Cloudflare.Env =
|
|
|
270
292
|
*
|
|
271
293
|
* A stub cannot enforce what the caller does with it: `runtime.exec` on this
|
|
272
294
|
* one would run against a container with no CA. The exec paths take the other
|
|
273
|
-
* method — see `openWorkspaceFs` in
|
|
295
|
+
* method — see `openWorkspaceFs` in `./open.ts`.
|
|
274
296
|
*/
|
|
275
297
|
__getWorkspaceFsStub(): Promise<WorkspaceStub>;
|
|
276
298
|
/**
|
|
277
|
-
* Clone, fetch and push — the operations that need the
|
|
299
|
+
* Clone, fetch and push — the operations that need the GitHub token.
|
|
278
300
|
*
|
|
279
301
|
* The work is `./git-host.ts`, which owns why the credential stays on this
|
|
280
302
|
* side of the boundary. What stays here is what only this object can do: the
|
|
@@ -300,6 +322,12 @@ export declare abstract class WorkspaceObjectBase<TEnv extends Cloudflare.Env =
|
|
|
300
322
|
branch: string;
|
|
301
323
|
allowedHosts: string[];
|
|
302
324
|
}): Promise<RepoGitResult>;
|
|
325
|
+
/**
|
|
326
|
+
* A checkout's `origin`, from this object's storage. Starts no container and
|
|
327
|
+
* moves no deadline, like {@link checkoutDir}: a pull request polled for its
|
|
328
|
+
* review is not the workspace in use.
|
|
329
|
+
*/
|
|
330
|
+
gitOrigin(dir: string): Promise<string | undefined>;
|
|
303
331
|
/**
|
|
304
332
|
* Start this checkout's dependency install, and return without waiting.
|
|
305
333
|
*
|
|
@@ -311,6 +339,76 @@ export declare abstract class WorkspaceObjectBase<TEnv extends Cloudflare.Env =
|
|
|
311
339
|
dir: string;
|
|
312
340
|
repo?: string;
|
|
313
341
|
}): Promise<InstallState>;
|
|
342
|
+
/**
|
|
343
|
+
* This workspace's dependency snapshot, for another of the same class to start
|
|
344
|
+
* from — see {@link seedDepsSnapshot}. Starts no container and moves no
|
|
345
|
+
* deadline.
|
|
346
|
+
*/
|
|
347
|
+
depsSnapshot(): Promise<DepsSnapshotRecord | undefined>;
|
|
348
|
+
/**
|
|
349
|
+
* Start this workspace's next container from `record`, another workspace's
|
|
350
|
+
* dependency snapshot, when it fits the checkout here — a worktree at the same
|
|
351
|
+
* path as the checkout it was cut from. Call it after the checkout and before
|
|
352
|
+
* the first command.
|
|
353
|
+
*
|
|
354
|
+
* A snapshot is that workspace's whole disk, so offer one only between
|
|
355
|
+
* workspaces of the same caller.
|
|
356
|
+
*/
|
|
357
|
+
seedDepsSnapshot(record: DepsSnapshotRecord): Promise<{
|
|
358
|
+
seeded: boolean;
|
|
359
|
+
reason?: string;
|
|
360
|
+
}>;
|
|
361
|
+
/**
|
|
362
|
+
* Stop the container, and **keep everything on disk**.
|
|
363
|
+
*
|
|
364
|
+
* The counterpart to {@link reclaimIfIdle}, and the difference is the whole
|
|
365
|
+
* reason both exist. A reclaim is for a workspace nobody wants again: it empties
|
|
366
|
+
* storage, so the checkout and the dependency snapshot go with the container.
|
|
367
|
+
* This is for a workspace that will be worked in again — the next task on the
|
|
368
|
+
* same repository skips the clone, and the install by restoring the snapshot.
|
|
369
|
+
*
|
|
370
|
+
* **Why a caller needs this at all.** The idle deadline would eventually do it,
|
|
371
|
+
* but it cannot be tuned down to meet a cost target: `containerIdleMs` must
|
|
372
|
+
* exceed the longest command the agent allows, and a container that outlives its
|
|
373
|
+
* work by that much bills for the difference. So a host that knows the work is
|
|
374
|
+
* finished says so, and the deadline goes back to being a backstop for the case
|
|
375
|
+
* where nothing got to say anything.
|
|
376
|
+
*
|
|
377
|
+
* Deliberately does **not** touch: `#touch()` would push the very deadline this
|
|
378
|
+
* is standing in for.
|
|
379
|
+
*
|
|
380
|
+
* `released: false` means the container is still up and the standing deadline
|
|
381
|
+
* still owns it — an install is running, or a pull is still moving blocks. Both
|
|
382
|
+
* resolve themselves: the drain re-arms the deadline to come back and finish,
|
|
383
|
+
* which is the same path `#onContainerIdle` takes. Under a {@link hold} the
|
|
384
|
+
* release is deferred instead, to the last `unhold`.
|
|
385
|
+
*/
|
|
386
|
+
releaseContainer(): Promise<{
|
|
387
|
+
released: boolean;
|
|
388
|
+
}>;
|
|
389
|
+
/**
|
|
390
|
+
* Keep the container up for work this object cannot see, until `untilMs` or
|
|
391
|
+
* {@link unhold}.
|
|
392
|
+
*
|
|
393
|
+
* A Claude Code session a host starts here runs detached: nothing calls in
|
|
394
|
+
* while it works, and the runtime cannot list
|
|
395
|
+
* what is running. So a host done with the workspace cannot tell that the
|
|
396
|
+
* container is not, and {@link releaseContainer} would stop the session with
|
|
397
|
+
* it. A hold is the host saying so. It defers a release; it is not use, so it
|
|
398
|
+
* does not touch, and the idle deadline already outlasts a session.
|
|
399
|
+
*
|
|
400
|
+
* `untilMs` bounds a hold whose `unhold` never comes — a run cut between its
|
|
401
|
+
* start and its end. Past it the hold is ignored.
|
|
402
|
+
*/
|
|
403
|
+
hold(key: string, untilMs: number): Promise<void>;
|
|
404
|
+
/**
|
|
405
|
+
* The work a {@link hold} stood for is over. When it was the last hold, a
|
|
406
|
+
* release asked for while it stood happens now — unless the workspace was
|
|
407
|
+
* used since, in which case the next release is asked for by whoever used it.
|
|
408
|
+
*/
|
|
409
|
+
unhold(key: string): Promise<{
|
|
410
|
+
released: boolean;
|
|
411
|
+
}>;
|
|
314
412
|
/**
|
|
315
413
|
* Throw this workspace away if nothing has touched it for `maxIdleMs`.
|
|
316
414
|
*
|
|
@@ -375,7 +473,7 @@ export declare abstract class WorkspaceObjectBase<TEnv extends Cloudflare.Env =
|
|
|
375
473
|
checkoutDir(): Promise<string | undefined>;
|
|
376
474
|
/**
|
|
377
475
|
* Everything currently true about this workspace that a caller must not assume
|
|
378
|
-
* away — the array `
|
|
476
|
+
* away — the array `bash`, `edit` and the workspace's writes all consult.
|
|
379
477
|
*
|
|
380
478
|
* The policy is not here. `deriveAdvisories` decides which facts matter and
|
|
381
479
|
* how they are worded; this method gathers what only the object can see and
|