@dynamicagents/plugins 0.14.0 → 0.15.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 +14 -14
- package/dist/claude-code/cards.d.ts +2 -0
- package/dist/claude-code/cards.js +11 -0
- package/dist/claude-code/config.d.ts +10 -83
- package/dist/claude-code/config.js +0 -2
- package/dist/claude-code/credentials.d.ts +32 -50
- package/dist/claude-code/credentials.js +115 -74
- package/dist/claude-code/daemon/index.d.ts +24 -0
- package/dist/claude-code/daemon/index.js +203 -0
- package/dist/claude-code/egress.d.ts +35 -117
- package/dist/claude-code/egress.js +80 -325
- package/dist/claude-code/events.d.ts +14 -17
- package/dist/claude-code/events.js +19 -15
- package/dist/claude-code/harness-wire.d.ts +45 -0
- package/dist/claude-code/harness-wire.js +47 -0
- package/dist/claude-code/index.d.ts +19 -29
- package/dist/claude-code/index.js +17 -25
- package/dist/claude-code/language-model.d.ts +19 -0
- package/dist/claude-code/language-model.js +71 -0
- package/dist/claude-code/model.d.ts +77 -70
- package/dist/claude-code/model.js +327 -327
- package/dist/claude-code/placeholder.d.ts +12 -0
- package/dist/claude-code/placeholder.js +12 -0
- package/dist/claude-code/spec.d.ts +4 -4
- package/dist/claude-code/spec.js +4 -4
- package/dist/computer/client.d.ts +88 -0
- package/dist/computer/client.js +6 -0
- package/dist/computer/context.d.ts +7 -7
- package/dist/computer/context.js +14 -18
- package/dist/computer/gate.d.ts +4 -23
- package/dist/computer/gate.js +1 -37
- package/dist/computer/index.d.ts +44 -43
- package/dist/computer/index.js +29 -50
- package/dist/computer/paths.d.ts +5 -27
- package/dist/computer/paths.js +7 -30
- package/dist/computer/proxy.d.ts +17 -31
- package/dist/computer/proxy.js +9 -44
- package/dist/computer/render.d.ts +3 -40
- package/dist/computer/render.js +3 -36
- package/dist/computer/sandbox.d.ts +55 -0
- package/dist/computer/sandbox.js +236 -0
- package/dist/computer/shell.d.ts +36 -0
- package/dist/computer/shell.js +41 -0
- package/dist/computer/tools-exec.js +3 -9
- package/dist/computer/tools-file.d.ts +1 -1
- package/dist/computer/tools-file.js +1 -1
- package/dist/{repo → repo-github}/checkout.d.ts +3 -4
- package/dist/{repo → repo-github}/checkout.js +3 -4
- package/dist/{repo → repo-github}/context.d.ts +14 -32
- package/dist/{repo → repo-github}/context.js +46 -81
- package/dist/{repo → repo-github}/index.d.ts +30 -13
- package/dist/{repo → repo-github}/index.js +25 -6
- package/dist/repo-github/mcp.d.ts +95 -0
- package/dist/repo-github/mcp.js +107 -0
- package/dist/repo-github/sandbox-egress.d.ts +68 -0
- package/dist/repo-github/sandbox-egress.js +272 -0
- package/dist/repo-github/sandbox-git.d.ts +30 -0
- package/dist/repo-github/sandbox-git.js +113 -0
- package/dist/{repo → repo-github}/tools-checks.js +43 -4
- package/dist/{repo → repo-github}/tools-clone.js +2 -21
- package/dist/{repo → repo-github}/tools-pr.d.ts +0 -1
- package/dist/{repo → repo-github}/tools-pr.js +85 -20
- package/dist/{repo → repo-github}/tools-review.d.ts +6 -0
- package/dist/{repo → repo-github}/tools-review.js +62 -176
- package/dist/{repo → repo-github}/url.d.ts +11 -0
- package/dist/{repo → repo-github}/url.js +12 -1
- package/dist/{workspace → sandbox}/advisory.d.ts +16 -35
- package/dist/{workspace → sandbox}/advisory.js +2 -29
- package/dist/sandbox/agent-exec.d.ts +31 -0
- package/dist/sandbox/agent-exec.js +62 -0
- package/dist/sandbox/ca-trust.d.ts +9 -0
- package/dist/sandbox/ca-trust.js +21 -0
- package/dist/sandbox/constants.d.ts +22 -0
- package/dist/sandbox/constants.js +22 -0
- package/dist/sandbox/daemon/composite.d.ts +9 -0
- package/dist/sandbox/daemon/composite.js +35 -0
- package/dist/sandbox/daemon/index.d.ts +47 -0
- package/dist/sandbox/daemon/index.js +116 -0
- package/dist/sandbox/egress.d.ts +81 -0
- package/dist/sandbox/egress.js +109 -0
- package/dist/sandbox/exec.d.ts +57 -0
- package/dist/sandbox/exec.js +52 -0
- package/dist/sandbox/format.d.ts +2 -0
- package/dist/sandbox/format.js +7 -0
- package/dist/sandbox/header.d.ts +50 -0
- package/dist/sandbox/header.js +94 -0
- package/dist/sandbox/index.d.ts +17 -0
- package/dist/sandbox/index.js +17 -0
- package/dist/{workspace/install.d.ts → sandbox/install-plan.d.ts} +2 -2
- package/dist/{workspace/install.js → sandbox/install-plan.js} +2 -2
- package/dist/sandbox/name.d.ts +27 -0
- package/dist/sandbox/name.js +32 -0
- package/dist/sandbox/object.d.ts +244 -0
- package/dist/sandbox/object.js +1139 -0
- package/dist/scratch/index.d.ts +1 -9
- package/dist/scratch/index.js +2 -18
- package/package.json +22 -18
- package/dist/claude-code/github.d.ts +0 -39
- package/dist/claude-code/github.js +0 -82
- package/dist/claude-code/run.d.ts +0 -491
- package/dist/claude-code/run.js +0 -913
- package/dist/claude-code/session.d.ts +0 -131
- package/dist/claude-code/session.js +0 -288
- package/dist/workspace/ca-trust.d.ts +0 -95
- package/dist/workspace/ca-trust.js +0 -312
- package/dist/workspace/container-deps.d.ts +0 -58
- package/dist/workspace/container-deps.js +0 -249
- package/dist/workspace/container-fault.d.ts +0 -38
- package/dist/workspace/container-fault.js +0 -73
- package/dist/workspace/deps-snapshot.d.ts +0 -110
- package/dist/workspace/deps-snapshot.js +0 -424
- package/dist/workspace/exec.d.ts +0 -94
- package/dist/workspace/exec.js +0 -153
- package/dist/workspace/format.d.ts +0 -36
- package/dist/workspace/format.js +0 -57
- package/dist/workspace/git-host.d.ts +0 -79
- package/dist/workspace/git-host.js +0 -226
- package/dist/workspace/git-identity.d.ts +0 -57
- package/dist/workspace/git-identity.js +0 -147
- package/dist/workspace/index.d.ts +0 -50
- package/dist/workspace/index.js +0 -58
- package/dist/workspace/install-job.d.ts +0 -170
- package/dist/workspace/install-job.js +0 -934
- package/dist/workspace/object.d.ts +0 -522
- package/dist/workspace/object.js +0 -1541
- package/dist/workspace/open.d.ts +0 -95
- package/dist/workspace/open.js +0 -63
- package/dist/workspace/read.d.ts +0 -16
- package/dist/workspace/read.js +0 -22
- package/dist/workspace/shell.d.ts +0 -45
- package/dist/workspace/shell.js +0 -80
- package/dist/workspace/sync.d.ts +0 -111
- package/dist/workspace/sync.js +0 -124
- package/dist/workspace/wake.d.ts +0 -21
- package/dist/workspace/wake.js +0 -10
- /package/dist/{repo → repo-github}/tools-checks.d.ts +0 -0
- /package/dist/{repo → repo-github}/tools-clone.d.ts +0 -0
- /package/dist/{repo → repo-github}/tools-worktree.d.ts +0 -0
- /package/dist/{repo → repo-github}/tools-worktree.js +0 -0
- /package/dist/{repo → repo-github}/tools-worktrees.d.ts +0 -0
- /package/dist/{repo → repo-github}/tools-worktrees.js +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the container is given instead of a credential.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code does not validate it locally: a run with this exact value
|
|
5
|
+
* succeeds, and the container only ever sends the placeholder. The egress
|
|
6
|
+
* gateway swaps in the real credential on the way out, so a `postinstall` script
|
|
7
|
+
* that dumps the environment learns this and nothing else.
|
|
8
|
+
*
|
|
9
|
+
* Shaped like a real token deliberately: something that looks obviously fake
|
|
10
|
+
* invites a future reader to "fix" it by putting the real one there.
|
|
11
|
+
*/
|
|
12
|
+
export declare const CREDENTIAL_PLACEHOLDER: string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the container is given instead of a credential.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code does not validate it locally: a run with this exact value
|
|
5
|
+
* succeeds, and the container only ever sends the placeholder. The egress
|
|
6
|
+
* gateway swaps in the real credential on the way out, so a `postinstall` script
|
|
7
|
+
* that dumps the environment learns this and nothing else.
|
|
8
|
+
*
|
|
9
|
+
* Shaped like a real token deliberately: something that looks obviously fake
|
|
10
|
+
* invites a future reader to "fix" it by putting the real one there.
|
|
11
|
+
*/
|
|
12
|
+
export const CREDENTIAL_PLACEHOLDER = "sk-ant-oat01-" + "0".repeat(24);
|
|
@@ -4,13 +4,13 @@ import { z } from "zod";
|
|
|
4
4
|
* The Claude Code writer, as data: what the parent's model is offered. The host
|
|
5
5
|
* binds it to a `SubAgent` class whose model is
|
|
6
6
|
* {@link file://./model.ts claudeCodeModel}, and adds the `prepare` and
|
|
7
|
-
* `settle` that claim and release its
|
|
7
|
+
* `settle` that claim and release its sandbox. A planner is the same spec
|
|
8
8
|
* under another name, description and input, whose model runs under `plan` —
|
|
9
|
-
* see
|
|
9
|
+
* see `./model.ts` `ClaudeCodeModelOptions.permissionMode`.
|
|
10
10
|
*
|
|
11
11
|
* A module of its own for its `zod` schemas. `zod` costs a Worker bundle
|
|
12
|
-
* ~460 KiB, and
|
|
13
|
-
*
|
|
12
|
+
* ~460 KiB, and a sandbox Durable Object imports this subpath for its egress
|
|
13
|
+
* rule and credential pool alone; with `sideEffects: false`, a module nothing it uses
|
|
14
14
|
* reaches is dropped from that bundle whole.
|
|
15
15
|
*
|
|
16
16
|
* It runs **detached**: a session runs for up to its `timeoutMs`, past the
|
package/dist/claude-code/spec.js
CHANGED
|
@@ -3,13 +3,13 @@ import { z } from "zod";
|
|
|
3
3
|
* The Claude Code writer, as data: what the parent's model is offered. The host
|
|
4
4
|
* binds it to a `SubAgent` class whose model is
|
|
5
5
|
* {@link file://./model.ts claudeCodeModel}, and adds the `prepare` and
|
|
6
|
-
* `settle` that claim and release its
|
|
6
|
+
* `settle` that claim and release its sandbox. A planner is the same spec
|
|
7
7
|
* under another name, description and input, whose model runs under `plan` —
|
|
8
|
-
* see
|
|
8
|
+
* see `./model.ts` `ClaudeCodeModelOptions.permissionMode`.
|
|
9
9
|
*
|
|
10
10
|
* A module of its own for its `zod` schemas. `zod` costs a Worker bundle
|
|
11
|
-
* ~460 KiB, and
|
|
12
|
-
*
|
|
11
|
+
* ~460 KiB, and a sandbox Durable Object imports this subpath for its egress
|
|
12
|
+
* rule and credential pool alone; with `sideEffects: false`, a module nothing it uses
|
|
13
13
|
* reaches is dropped from that bundle whole.
|
|
14
14
|
*
|
|
15
15
|
* It runs **detached**: a session runs for up to its `timeoutMs`, past the
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the computer tools use of where they run: a filesystem and a shell.
|
|
3
|
+
* A sandbox's is built by `./sandbox.ts`; the tools are written against this,
|
|
4
|
+
* so no RPC type reaches them.
|
|
5
|
+
*/
|
|
6
|
+
export interface GrepMatch {
|
|
7
|
+
path: string;
|
|
8
|
+
line: number;
|
|
9
|
+
text: string;
|
|
10
|
+
context?: {
|
|
11
|
+
line: number;
|
|
12
|
+
text: string;
|
|
13
|
+
isMatch: boolean;
|
|
14
|
+
}[];
|
|
15
|
+
}
|
|
16
|
+
export interface GrepOptions {
|
|
17
|
+
ignoreCase?: boolean;
|
|
18
|
+
regex?: boolean;
|
|
19
|
+
context?: number;
|
|
20
|
+
limit?: number;
|
|
21
|
+
offset?: number;
|
|
22
|
+
include?: string;
|
|
23
|
+
exclude?: string[];
|
|
24
|
+
}
|
|
25
|
+
export interface FoundEntry {
|
|
26
|
+
path: string;
|
|
27
|
+
type: "file" | "dir";
|
|
28
|
+
}
|
|
29
|
+
export interface StatResult {
|
|
30
|
+
size: number;
|
|
31
|
+
mtime: number;
|
|
32
|
+
isFile: boolean;
|
|
33
|
+
isDirectory: boolean;
|
|
34
|
+
isSymbolicLink: boolean;
|
|
35
|
+
}
|
|
36
|
+
export interface DirEntry {
|
|
37
|
+
name: string;
|
|
38
|
+
size?: number;
|
|
39
|
+
mtime?: number;
|
|
40
|
+
isFile: boolean;
|
|
41
|
+
isDirectory: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface ComputerFs {
|
|
44
|
+
readFile(path: string): Promise<ReadableStream<Uint8Array>>;
|
|
45
|
+
readFile(path: string, encoding: "utf8"): Promise<string>;
|
|
46
|
+
stat(path: string): Promise<StatResult>;
|
|
47
|
+
readdir(path: string, options?: {
|
|
48
|
+
limit?: number;
|
|
49
|
+
offset?: number;
|
|
50
|
+
}): Promise<DirEntry[]>;
|
|
51
|
+
find(directory: string, pattern?: string, options?: {
|
|
52
|
+
limit?: number;
|
|
53
|
+
offset?: number;
|
|
54
|
+
exclude?: string[];
|
|
55
|
+
}): Promise<FoundEntry[]>;
|
|
56
|
+
grep(pattern: string, path: string, options?: GrepOptions): Promise<GrepMatch[]>;
|
|
57
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
58
|
+
mkdir(path: string, options?: {
|
|
59
|
+
recursive?: boolean;
|
|
60
|
+
}): Promise<void>;
|
|
61
|
+
rm(path: string, options?: {
|
|
62
|
+
recursive?: boolean;
|
|
63
|
+
force?: boolean;
|
|
64
|
+
}): Promise<void>;
|
|
65
|
+
}
|
|
66
|
+
export interface CommandResult {
|
|
67
|
+
exitCode: number;
|
|
68
|
+
stdout: string;
|
|
69
|
+
stderr: string;
|
|
70
|
+
/** Anything but `completed` is said on the verdict line — see `./render.ts`. */
|
|
71
|
+
status?: "completed" | "failed" | "cancelled";
|
|
72
|
+
}
|
|
73
|
+
export interface CommandHandle extends Disposable {
|
|
74
|
+
result(): Promise<CommandResult>;
|
|
75
|
+
kill(signal?: "SIGTERM"): Promise<void>;
|
|
76
|
+
}
|
|
77
|
+
export interface ComputerRuntime {
|
|
78
|
+
exec(command: string, options: {
|
|
79
|
+
cwd: string;
|
|
80
|
+
encoding: "utf8";
|
|
81
|
+
timeoutMs: number;
|
|
82
|
+
env?: Record<string, string>;
|
|
83
|
+
}): Promise<CommandHandle>;
|
|
84
|
+
}
|
|
85
|
+
export interface ComputerClient extends Disposable {
|
|
86
|
+
readonly fs: ComputerFs;
|
|
87
|
+
readonly runtime: ComputerRuntime;
|
|
88
|
+
}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SandboxAdvisory } from "../sandbox/advisory.js";
|
|
2
|
+
import type { ComputerClient } from "./client.js";
|
|
2
3
|
import { type ExecGate } from "./gate.js";
|
|
3
|
-
import type {
|
|
4
|
-
import type { ComputerConfig } from "./index.js";
|
|
4
|
+
import type { ComputerToolsConfig } from "./index.js";
|
|
5
5
|
/** What {@link computerContext} hands each group of tools. */
|
|
6
6
|
export interface ComputerContext {
|
|
7
|
-
config:
|
|
7
|
+
config: ComputerToolsConfig;
|
|
8
8
|
cwd: string;
|
|
9
9
|
timeoutMs: number;
|
|
10
10
|
maxChars: number;
|
|
11
|
-
workspace: () => Promise<
|
|
11
|
+
workspace: () => Promise<ComputerClient>;
|
|
12
12
|
awaitAdvisories: (command: string, signal?: AbortSignal) => Promise<ExecGate>;
|
|
13
13
|
definedEnv: () => Record<string, string> | undefined;
|
|
14
|
-
inWorkspace: (gerund: string, subject: string, body: (fs:
|
|
14
|
+
inWorkspace: (gerund: string, subject: string, body: (fs: ComputerClient["fs"]) => Promise<string>) => Promise<string>;
|
|
15
15
|
refuseWrite: () => Promise<string | undefined>;
|
|
16
16
|
scope: () => string;
|
|
17
17
|
}
|
|
18
18
|
/** Takes what {@link file://./index.ts buildComputerTools} takes. */
|
|
19
|
-
export declare function computerContext(workspace: () => Promise<
|
|
19
|
+
export declare function computerContext(workspace: () => Promise<ComputerClient>, config: ComputerToolsConfig, advisories?: () => Promise<readonly SandboxAdvisory[]>, lockScope?: () => string): ComputerContext;
|
package/dist/computer/context.js
CHANGED
|
@@ -50,8 +50,9 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
50
50
|
var e = new Error(message);
|
|
51
51
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
52
|
});
|
|
53
|
-
import { DEFAULT_CWD, DEFAULT_TIMEOUT_MS } from "../workspace/exec.js";
|
|
54
53
|
import { withAbort } from "@dynamicagents/core";
|
|
54
|
+
import { DEFAULT_EXEC_TIMEOUT_MS } from "../sandbox/exec.js";
|
|
55
|
+
import { SANDBOX_ROOT } from "../sandbox/header.js";
|
|
55
56
|
import { execGate, writeGate } from "./gate.js";
|
|
56
57
|
/**
|
|
57
58
|
* The machinery every container tool closes over: the resolved config, the
|
|
@@ -76,9 +77,9 @@ const DEFAULT_MAX_OUTPUT_CHARS = 16_000;
|
|
|
76
77
|
*/
|
|
77
78
|
const INSTALL_POLL_MS = 3_000;
|
|
78
79
|
/** Takes what {@link file://./index.ts buildComputerTools} takes. */
|
|
79
|
-
export function computerContext(workspace, config, advisories,
|
|
80
|
-
const cwd = config.cwd ??
|
|
81
|
-
const timeoutMs = config.timeoutMs ??
|
|
80
|
+
export function computerContext(workspace, config, advisories, lockScope) {
|
|
81
|
+
const cwd = config.cwd ?? SANDBOX_ROOT;
|
|
82
|
+
const timeoutMs = config.timeoutMs ?? DEFAULT_EXEC_TIMEOUT_MS;
|
|
82
83
|
const maxChars = config.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
|
|
83
84
|
const gateMs = config.installGateMs ?? 90_000;
|
|
84
85
|
const env = config.env;
|
|
@@ -92,11 +93,9 @@ export function computerContext(workspace, config, advisories, fsWorkspace = wor
|
|
|
92
93
|
*
|
|
93
94
|
* **Read for every command, including the ones that need no dependencies.**
|
|
94
95
|
* The filter that spares `cat README.md` from queueing behind an `npm ci`
|
|
95
|
-
* lives in `execGate`, where it applies per advisory
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* commands whose writes it is dropping. The cost is one same-colo RPC per
|
|
99
|
-
* command, against a class of silent data loss.
|
|
96
|
+
* lives in `execGate`, where it applies per advisory, so an advisory that
|
|
97
|
+
* reaches every command is never filtered out by the one that does not. The
|
|
98
|
+
* cost is one same-colo RPC per command.
|
|
100
99
|
*/
|
|
101
100
|
const awaitAdvisories = async (command, signal) => {
|
|
102
101
|
if (!advisories)
|
|
@@ -142,8 +141,8 @@ export function computerContext(workspace, config, advisories, fsWorkspace = wor
|
|
|
142
141
|
*
|
|
143
142
|
* Separate from {@link awaitAdvisories} because the question is different:
|
|
144
143
|
* there is no command to classify and nothing worth waiting for. An install in
|
|
145
|
-
* flight does not stop a file being written
|
|
146
|
-
*
|
|
144
|
+
* flight does not stop a file being written; only an advisory whose writes do
|
|
145
|
+
* not persist does, and that never resolves on its own.
|
|
147
146
|
*
|
|
148
147
|
* Fails **open** for the same reason the exec gate does: a read of another
|
|
149
148
|
* Durable Object's state must not take out a working tool.
|
|
@@ -181,18 +180,15 @@ export function computerContext(workspace, config, advisories, fsWorkspace = wor
|
|
|
181
180
|
* only recover from what it is told, so this returns the sentence rather than
|
|
182
181
|
* throwing it.
|
|
183
182
|
*
|
|
184
|
-
* `bash` deliberately does not use it
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
* It opens through `fsWorkspace`, which is what makes the context block's
|
|
189
|
-
* sentence about these tools true: they answer while the container is down.
|
|
183
|
+
* `bash` deliberately does not use it: it logs, and carries an install
|
|
184
|
+
* warning through the failure path — a bespoke catch saying something this
|
|
185
|
+
* cannot.
|
|
190
186
|
*/
|
|
191
187
|
const inWorkspace = async (gerund, subject, body) => {
|
|
192
188
|
try {
|
|
193
189
|
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
194
190
|
try {
|
|
195
|
-
const ws = __addDisposableResource(env_1, await
|
|
191
|
+
const ws = __addDisposableResource(env_1, await workspace(), false);
|
|
196
192
|
return await body(ws.fs);
|
|
197
193
|
}
|
|
198
194
|
catch (e_1) {
|
package/dist/computer/gate.d.ts
CHANGED
|
@@ -1,32 +1,13 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type SandboxAdvisory } from "../sandbox/advisory.js";
|
|
2
2
|
/**
|
|
3
3
|
* What one shell command has to wait for, and what to say when it never ran.
|
|
4
4
|
*
|
|
5
|
-
* {@link file://../
|
|
5
|
+
* {@link file://../sandbox/advisory.ts} decides what is true about the sandbox and how
|
|
6
6
|
* to word it; this file decides what that means for a command about to run.
|
|
7
7
|
* {@link needsDependencies} is the part that cannot move there — it is a
|
|
8
8
|
* question about the *command*, and it is what stops `cat README.md` queueing
|
|
9
9
|
* behind an `npm ci` it has no use for.
|
|
10
|
-
*
|
|
11
|
-
* {@link execLostNote} covers a failure with no advisory behind it: a container
|
|
12
|
-
* replaced underneath a command that was running.
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* The one `bash` failure that is not the command's fault.
|
|
16
|
-
*
|
|
17
|
-
* An execution running when the container is replaced throws `EEXEC_LOST`.
|
|
18
|
-
* Unexplained, the model receives `Execution "…" was lost when its container
|
|
19
|
-
* runtime was replaced`, reads it as a crash, and goes looking at its command.
|
|
20
|
-
*
|
|
21
|
-
* What it needs is two facts: nothing ran to completion, and the workspace
|
|
22
|
-
* survived because the filesystem is the Durable Object's rather than the
|
|
23
|
-
* container's. So re-run it. `node_modules` is the container's, so the note
|
|
24
|
-
* says the replacement reinstalls it.
|
|
25
|
-
*
|
|
26
|
-
* Matched on `code` rather than the message — the property the package sets
|
|
27
|
-
* deliberately, and the one that survives a reworded string.
|
|
28
10
|
*/
|
|
29
|
-
export declare function execLostNote(err: unknown): string | undefined;
|
|
30
11
|
/**
|
|
31
12
|
* What the workspace's advisories mean for a command about to run.
|
|
32
13
|
*
|
|
@@ -71,7 +52,7 @@ export interface ExecGate {
|
|
|
71
52
|
* needs to hear about the ceiling — that is the fact that makes waiting
|
|
72
53
|
* pointless, and reporting only the blockage would send it round the loop again.
|
|
73
54
|
*/
|
|
74
|
-
export declare function execGate(advisories: readonly
|
|
55
|
+
export declare function execGate(advisories: readonly SandboxAdvisory[], command: string): ExecGate;
|
|
75
56
|
/**
|
|
76
57
|
* Whether a write should happen at all, and what to say when it should not.
|
|
77
58
|
*
|
|
@@ -90,7 +71,7 @@ export declare function execGate(advisories: readonly WorkspaceAdvisory[], comma
|
|
|
90
71
|
* available: the only thing refusing costs is a false "wrote 120 characters",
|
|
91
72
|
* and that message is worse than nothing because it is believed.
|
|
92
73
|
*/
|
|
93
|
-
export declare function writeGate(advisories: readonly
|
|
74
|
+
export declare function writeGate(advisories: readonly SandboxAdvisory[]): string | undefined;
|
|
94
75
|
/**
|
|
95
76
|
* Does this command plausibly read `node_modules`, and therefore have to wait for
|
|
96
77
|
* a dependency install to finish?
|
package/dist/computer/gate.js
CHANGED
|
@@ -1,40 +1,4 @@
|
|
|
1
|
-
import { renderAdvisory, shapeOf } from "../
|
|
2
|
-
/**
|
|
3
|
-
* What one shell command has to wait for, and what to say when it never ran.
|
|
4
|
-
*
|
|
5
|
-
* {@link file://../workspace/advisory.ts} decides what is true about the workspace and how
|
|
6
|
-
* to word it; this file decides what that means for a command about to run.
|
|
7
|
-
* {@link needsDependencies} is the part that cannot move there — it is a
|
|
8
|
-
* question about the *command*, and it is what stops `cat README.md` queueing
|
|
9
|
-
* behind an `npm ci` it has no use for.
|
|
10
|
-
*
|
|
11
|
-
* {@link execLostNote} covers a failure with no advisory behind it: a container
|
|
12
|
-
* replaced underneath a command that was running.
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* The one `bash` failure that is not the command's fault.
|
|
16
|
-
*
|
|
17
|
-
* An execution running when the container is replaced throws `EEXEC_LOST`.
|
|
18
|
-
* Unexplained, the model receives `Execution "…" was lost when its container
|
|
19
|
-
* runtime was replaced`, reads it as a crash, and goes looking at its command.
|
|
20
|
-
*
|
|
21
|
-
* What it needs is two facts: nothing ran to completion, and the workspace
|
|
22
|
-
* survived because the filesystem is the Durable Object's rather than the
|
|
23
|
-
* container's. So re-run it. `node_modules` is the container's, so the note
|
|
24
|
-
* says the replacement reinstalls it.
|
|
25
|
-
*
|
|
26
|
-
* Matched on `code` rather than the message — the property the package sets
|
|
27
|
-
* deliberately, and the one that survives a reworded string.
|
|
28
|
-
*/
|
|
29
|
-
export function execLostNote(err) {
|
|
30
|
-
if (err?.code !== "EEXEC_LOST")
|
|
31
|
-
return undefined;
|
|
32
|
-
return ("the container was replaced while this command was running, so it was lost — " +
|
|
33
|
-
"nothing ran to completion and no output survived. This is infrastructure, " +
|
|
34
|
-
"not your command: re-run it. The workspace is durable and is exactly as you " +
|
|
35
|
-
"left it. `node_modules` is not: the replacement reinstalls it, and a " +
|
|
36
|
-
"command that needs it waits for that.");
|
|
37
|
-
}
|
|
1
|
+
import { renderAdvisory, shapeOf } from "../sandbox/advisory.js";
|
|
38
2
|
/** Which advisories this particular command needs to hear about. */
|
|
39
3
|
function relevantTo(advisories, command) {
|
|
40
4
|
// Computed once for the whole set rather than per advisory: it parses the
|
package/dist/computer/index.d.ts
CHANGED
|
@@ -1,41 +1,51 @@
|
|
|
1
1
|
import type { ToolSet } from "ai";
|
|
2
2
|
import type { AgentPlugin } from "@dynamicagents/core";
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
5
|
-
import type { WorkspaceClientConfig } from "../workspace/exec.js";
|
|
3
|
+
import type { SandboxAdvisory } from "../sandbox/advisory.js";
|
|
4
|
+
import type { ComputerClient } from "./client.js";
|
|
6
5
|
/**
|
|
7
|
-
* `@dynamicagents/plugins/computer` — an agent's tools over a
|
|
8
|
-
*
|
|
6
|
+
* `@dynamicagents/plugins/computer` — an agent's tools over a `/sandbox`
|
|
7
|
+
* container: `bash`, `grep` and `edit`, and `computerWorkspace`, the agent's own
|
|
9
8
|
* `this.workspace`, which Think's `read`, `write`, `delete`, `find` and `list`
|
|
10
9
|
* go through.
|
|
11
10
|
*
|
|
12
|
-
* The
|
|
13
|
-
*
|
|
14
|
-
* through
|
|
15
|
-
*
|
|
11
|
+
* The container itself — the Durable Object, its git, its install — is
|
|
12
|
+
* `@dynamicagents/plugins/sandbox`. This plugin reaches it over RPC, never
|
|
13
|
+
* through its object, so an agent that holds the tools carries no container
|
|
14
|
+
* backend.
|
|
16
15
|
*
|
|
17
|
-
* The agent's own workspace has to be
|
|
16
|
+
* The agent's own workspace has to be the sandbox's — `computerWorkspace` — or
|
|
18
17
|
* Think's `read` and `write` would work on a different tree from `bash`. The
|
|
19
|
-
* plugin refuses to start otherwise; see `./proxy.ts`.
|
|
20
|
-
*
|
|
21
|
-
* ## The dependency tree is not in the workspace
|
|
22
|
-
*
|
|
23
|
-
* The one thing to internalise before reading further. A package root's
|
|
24
|
-
* `node_modules` is a bind mount of the container's disk, so installs never
|
|
25
|
-
* sync, and a new container reinstalls — see `/workspace`'s
|
|
26
|
-
* `container-deps.ts`. The file tools read the workspace, so they refuse it;
|
|
27
|
-
* `bash` reaches it. `paths.ts` owns what a path may be and what walks drop.
|
|
18
|
+
* plugin refuses to start otherwise; see `./proxy.ts`. `paths.ts` owns what a
|
|
19
|
+
* path may be and what walks drop.
|
|
28
20
|
*/
|
|
29
21
|
/**
|
|
30
22
|
* The helpers this plugin's own tests reach for, re-exported from the one
|
|
31
23
|
* entry point. `verify:exports` treats `./computer` as one realm, so what it
|
|
32
24
|
* exports must not move when the files behind it do.
|
|
33
25
|
*/
|
|
34
|
-
export { isGitInternal,
|
|
35
|
-
export { packBlocks, renderGrepMatches, renderResult
|
|
26
|
+
export { isGitInternal, WALK_SKIPS } from "./paths.js";
|
|
27
|
+
export { packBlocks, renderGrepMatches, renderResult } from "./render.js";
|
|
36
28
|
export { execGate, needsDependencies, writeGate, type ExecGate } from "./gate.js";
|
|
37
|
-
export { computerWorkspace,
|
|
38
|
-
export
|
|
29
|
+
export { sandboxClient, computerWorkspace, type SandboxComputerConfig, type SandboxComputerHost } from "./sandbox.js";
|
|
30
|
+
export type { ComputerClient } from "./client.js";
|
|
31
|
+
/** How the tools run: the shell, the limits, the environment. */
|
|
32
|
+
export interface ComputerToolsConfig {
|
|
33
|
+
/** Working directory for every command. Defaults to `/workspace`. */
|
|
34
|
+
cwd?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Run every command through this shell, e.g. `"bash"`. Unset, the command
|
|
37
|
+
* string goes to the runtime as-is and lands on whatever `/bin/sh` is.
|
|
38
|
+
*
|
|
39
|
+
* Worth setting, because "whatever `/bin/sh` is" is **dash** on Debian and
|
|
40
|
+
* Ubuntu, and a model writing shell writes *bash*. `${PIPESTATUS[0]}` is the
|
|
41
|
+
* one that costs most — a bash builtin a model reaches for to read a piped exit
|
|
42
|
+
* code, which dash fails with exit 2, after the command it wrapped has already
|
|
43
|
+
* run. Set it only if the image actually has that shell: a missing one fails
|
|
44
|
+
* *every* command rather than the bash-flavoured ones.
|
|
45
|
+
*/
|
|
46
|
+
shell?: string;
|
|
47
|
+
/** Per-command timeout. Defaults to ten minutes. */
|
|
48
|
+
timeoutMs?: number;
|
|
39
49
|
/**
|
|
40
50
|
* Output ceiling per command, in **characters**. Defaults to 16,000.
|
|
41
51
|
*
|
|
@@ -66,11 +76,6 @@ export interface ComputerConfig extends WorkspaceClientConfig {
|
|
|
66
76
|
* Environment merged into every command **these tools** run — a registry host,
|
|
67
77
|
* a `CI` flag, proxy settings. Host-supplied and never model input.
|
|
68
78
|
*
|
|
69
|
-
* "These tools" is exact: `/workspace`'s `workspaceExec` deliberately does not
|
|
70
|
-
* merge it, so a proxy set here does not reach the git that
|
|
71
|
-
* `@dynamicagents/plugins/repo` runs through that export. The reasoning, and
|
|
72
|
-
* how a host opts in explicitly, is written up there.
|
|
73
|
-
*
|
|
74
79
|
* ## Put no secret here
|
|
75
80
|
*
|
|
76
81
|
* *Every* command gets this environment, and `bash`'s command is written by
|
|
@@ -88,26 +93,22 @@ export interface ComputerConfig extends WorkspaceClientConfig {
|
|
|
88
93
|
*
|
|
89
94
|
* Do not hand the agent the credential; hand it the *action*. Keep the secret
|
|
90
95
|
* on the Worker and expose one tool that makes the call it is for.
|
|
91
|
-
* `@dynamicagents/plugins/repo` is the worked example: it holds a GitHub token, calls
|
|
92
|
-
* the API from the Worker, and
|
|
93
|
-
*
|
|
96
|
+
* `@dynamicagents/plugins/repo-github` is the worked example: it holds a GitHub token, calls
|
|
97
|
+
* the API from the Worker, and its container's git holds a placeholder the
|
|
98
|
+
* egress swaps. **No command in its container is ever given that token.**
|
|
94
99
|
*/
|
|
95
100
|
env?: () => Record<string, string | undefined>;
|
|
96
101
|
}
|
|
97
102
|
/**
|
|
98
|
-
* @param workspace Opens the
|
|
99
|
-
*
|
|
100
|
-
* @param fsWorkspace Opens it for the file tools, which touch nothing but the
|
|
101
|
-
* host's SQLite. Defaults to {@link workspace}, so a host with one way in
|
|
102
|
-
* behaves as before. See {@link openWorkspaceFs}.
|
|
103
|
-
* @param lockScope Names the workspace a write locks within — see
|
|
103
|
+
* @param workspace Opens the sandbox for a call.
|
|
104
|
+
* @param lockScope Names the sandbox a write locks within — see
|
|
104
105
|
* {@link file://./file-lock.ts}. Defaults to this tool set alone.
|
|
105
106
|
*/
|
|
106
|
-
export declare function buildComputerTools(workspace: () => Promise<
|
|
107
|
+
export declare function buildComputerTools(workspace: () => Promise<ComputerClient>, config: ComputerToolsConfig, advisories?: () => Promise<readonly SandboxAdvisory[]>, lockScope?: () => string): ToolSet;
|
|
107
108
|
/**
|
|
108
|
-
* The container plugin. Its tools reach the
|
|
109
|
-
* `computerWorkspace` — the one Think's `read` and
|
|
110
|
-
* never resolve different ones. `config` answers
|
|
111
|
-
*
|
|
109
|
+
* The container plugin over a `/sandbox` object. Its tools reach the sandbox
|
|
110
|
+
* through the agent's own `computerWorkspace` — the one Think's `read` and
|
|
111
|
+
* `write` use — so the two can never resolve different ones. `config` answers
|
|
112
|
+
* everything else.
|
|
112
113
|
*/
|
|
113
|
-
export declare function computer(config:
|
|
114
|
+
export declare function computer(config: ComputerToolsConfig): AgentPlugin;
|
package/dist/computer/index.js
CHANGED
|
@@ -1,84 +1,64 @@
|
|
|
1
1
|
import { definePlugin, PluginSetupError } from "@dynamicagents/core";
|
|
2
|
-
import { openWorkspace, openWorkspaceFs } from "../workspace/open.js";
|
|
3
2
|
import { workspaceRoute } from "./proxy.js";
|
|
4
3
|
import { computerContext } from "./context.js";
|
|
5
4
|
import { execTools } from "./tools-exec.js";
|
|
6
5
|
import { fileTools } from "./tools-file.js";
|
|
7
6
|
import { grepTools } from "./tools-grep.js";
|
|
8
7
|
/**
|
|
9
|
-
* `@dynamicagents/plugins/computer` — an agent's tools over a
|
|
10
|
-
*
|
|
8
|
+
* `@dynamicagents/plugins/computer` — an agent's tools over a `/sandbox`
|
|
9
|
+
* container: `bash`, `grep` and `edit`, and `computerWorkspace`, the agent's own
|
|
11
10
|
* `this.workspace`, which Think's `read`, `write`, `delete`, `find` and `list`
|
|
12
11
|
* go through.
|
|
13
12
|
*
|
|
14
|
-
* The
|
|
15
|
-
*
|
|
16
|
-
* through
|
|
17
|
-
*
|
|
13
|
+
* The container itself — the Durable Object, its git, its install — is
|
|
14
|
+
* `@dynamicagents/plugins/sandbox`. This plugin reaches it over RPC, never
|
|
15
|
+
* through its object, so an agent that holds the tools carries no container
|
|
16
|
+
* backend.
|
|
18
17
|
*
|
|
19
|
-
* The agent's own workspace has to be
|
|
18
|
+
* The agent's own workspace has to be the sandbox's — `computerWorkspace` — or
|
|
20
19
|
* Think's `read` and `write` would work on a different tree from `bash`. The
|
|
21
|
-
* plugin refuses to start otherwise; see `./proxy.ts`.
|
|
22
|
-
*
|
|
23
|
-
* ## The dependency tree is not in the workspace
|
|
24
|
-
*
|
|
25
|
-
* The one thing to internalise before reading further. A package root's
|
|
26
|
-
* `node_modules` is a bind mount of the container's disk, so installs never
|
|
27
|
-
* sync, and a new container reinstalls — see `/workspace`'s
|
|
28
|
-
* `container-deps.ts`. The file tools read the workspace, so they refuse it;
|
|
29
|
-
* `bash` reaches it. `paths.ts` owns what a path may be and what walks drop.
|
|
20
|
+
* plugin refuses to start otherwise; see `./proxy.ts`. `paths.ts` owns what a
|
|
21
|
+
* path may be and what walks drop.
|
|
30
22
|
*/
|
|
31
23
|
/**
|
|
32
24
|
* The helpers this plugin's own tests reach for, re-exported from the one
|
|
33
25
|
* entry point. `verify:exports` treats `./computer` as one realm, so what it
|
|
34
26
|
* exports must not move when the files behind it do.
|
|
35
27
|
*/
|
|
36
|
-
export { isGitInternal,
|
|
37
|
-
export { packBlocks, renderGrepMatches, renderResult
|
|
28
|
+
export { isGitInternal, WALK_SKIPS } from "./paths.js";
|
|
29
|
+
export { packBlocks, renderGrepMatches, renderResult } from "./render.js";
|
|
38
30
|
export { execGate, needsDependencies, writeGate } from "./gate.js";
|
|
39
|
-
export {
|
|
31
|
+
export { sandboxClient, computerWorkspace } from "./sandbox.js";
|
|
40
32
|
/**
|
|
41
|
-
* @param workspace Opens the
|
|
42
|
-
*
|
|
43
|
-
* @param fsWorkspace Opens it for the file tools, which touch nothing but the
|
|
44
|
-
* host's SQLite. Defaults to {@link workspace}, so a host with one way in
|
|
45
|
-
* behaves as before. See {@link openWorkspaceFs}.
|
|
46
|
-
* @param lockScope Names the workspace a write locks within — see
|
|
33
|
+
* @param workspace Opens the sandbox for a call.
|
|
34
|
+
* @param lockScope Names the sandbox a write locks within — see
|
|
47
35
|
* {@link file://./file-lock.ts}. Defaults to this tool set alone.
|
|
48
36
|
*/
|
|
49
|
-
export function buildComputerTools(workspace, config, advisories,
|
|
50
|
-
const ctx = computerContext(workspace, config, advisories,
|
|
37
|
+
export function buildComputerTools(workspace, config, advisories, lockScope) {
|
|
38
|
+
const ctx = computerContext(workspace, config, advisories, lockScope);
|
|
51
39
|
return {
|
|
52
40
|
...execTools(ctx),
|
|
53
41
|
...fileTools(ctx),
|
|
54
42
|
...grepTools(ctx)
|
|
55
43
|
};
|
|
56
44
|
}
|
|
45
|
+
/** What the model is told about the container, once per turn. */
|
|
57
46
|
const CONTEXT = [
|
|
58
47
|
"You have a Linux container with a shell, a package manager and network access:",
|
|
59
48
|
"- `bash` runs any shell command — builds, tests, installs, git.",
|
|
60
49
|
"- `read` / `write` / `edit` / `delete` work on files; `edit` replaces an exact unique string and is the right tool for a small change.",
|
|
61
50
|
"- `grep` searches file contents; `list` lists a directory; `find` finds files by glob.",
|
|
62
|
-
|
|
63
|
-
// tool description: these read the durable workspace directly, so they are
|
|
64
|
-
// the only tools that still answer while the container is unavailable.
|
|
65
|
-
"Search with `grep` and `find` rather than running them through `bash`. They read the workspace directly, so they keep working while the container is restarting or an install is still running, and they come back with line numbers and a bounded result instead of a wall of text.",
|
|
51
|
+
"Search with `grep` and `find` rather than running them through `bash`: they come back with line numbers and a bounded result instead of a wall of text.",
|
|
66
52
|
"Command output is truncated from the middle when large, so run targeted commands and read specific files rather than printing everything.",
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// out from a confusing result.
|
|
70
|
-
"The checkout is durable: it survives between tasks and is still there after the container restarts, so it may already contain work from an earlier task — check before assuming it is empty.",
|
|
71
|
-
"`node_modules` is not durable: it lives on the container's disk, so a new container reinstalls it, and the file tools cannot see inside it — use `bash` there. It is a mount point, so `rm -rf node_modules` fails; `npm ci` clears it itself.",
|
|
72
|
-
// Stated up front rather than left to a refusal, so the model does not spend
|
|
73
|
-
// a turn discovering it. The destination matters as much as the rule: a
|
|
74
|
-
// prohibition with nowhere to go gets worked around.
|
|
53
|
+
"The checkout is the container's own disk, and it is kept between tasks: a stopped container starts again from it, so it may already contain work from an earlier task — check before assuming it is empty.",
|
|
54
|
+
"`node_modules` is on that disk beside the checkout and is kept with it. The file tools leave it out — use `bash` there.",
|
|
75
55
|
"`.git` is off limits to these tools, and searches and recursive listings skip it. It is git's internal state — reading it tells you less than the repository tools do, and writing it corrupts the checkout. Repository work goes through the repo tools (`repo_status`, `repo_diff`, `repo_commit`, `repo_push`); if a task needs git state you cannot get that way, say so in your result rather than reaching into `.git` yourself."
|
|
76
56
|
].join("\n");
|
|
77
57
|
/**
|
|
78
|
-
* The container plugin. Its tools reach the
|
|
79
|
-
* `computerWorkspace` — the one Think's `read` and
|
|
80
|
-
* never resolve different ones. `config` answers
|
|
81
|
-
*
|
|
58
|
+
* The container plugin over a `/sandbox` object. Its tools reach the sandbox
|
|
59
|
+
* through the agent's own `computerWorkspace` — the one Think's `read` and
|
|
60
|
+
* `write` use — so the two can never resolve different ones. `config` answers
|
|
61
|
+
* everything else.
|
|
82
62
|
*/
|
|
83
63
|
export function computer(config) {
|
|
84
64
|
return definePlugin({
|
|
@@ -93,15 +73,14 @@ export function computer(config) {
|
|
|
93
73
|
"would work on a different tree. Set it on the agent class: " +
|
|
94
74
|
"`override workspace = computerWorkspace(config, () => this.pluginContext().runtime())`.");
|
|
95
75
|
// Resolved per call, not memoized: the name belongs to the turn — a
|
|
96
|
-
// sub-agent's comes from `runtime()`, see `/
|
|
97
|
-
// and a stale
|
|
98
|
-
// the first caller's files.
|
|
99
|
-
|
|
100
|
-
return buildComputerTools(() => openWorkspace(host()), config,
|
|
76
|
+
// sub-agent's comes from `runtime()`, see `/sandbox`'s
|
|
77
|
+
// `SANDBOX_RUNTIME_KEY` — and a stale one would silently route a second
|
|
78
|
+
// caller's commands into the first caller's files.
|
|
79
|
+
return buildComputerTools(() => route.open(route.name()), config,
|
|
101
80
|
// No try/catch here: `buildComputerTools` fails the gate open itself,
|
|
102
81
|
// so wrapping again would only make it look like the guarantee lives in
|
|
103
82
|
// two places.
|
|
104
|
-
() =>
|
|
83
|
+
() => route.advisories(route.name()), route.name);
|
|
105
84
|
},
|
|
106
85
|
context: [{ provider: { get: async () => CONTEXT } }]
|
|
107
86
|
});
|