@dynamicagents/plugins 0.13.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.
Files changed (141) hide show
  1. package/README.md +14 -14
  2. package/dist/claude-code/cards.d.ts +4 -0
  3. package/dist/claude-code/cards.js +22 -0
  4. package/dist/claude-code/config.d.ts +10 -83
  5. package/dist/claude-code/config.js +0 -2
  6. package/dist/claude-code/credentials.d.ts +32 -50
  7. package/dist/claude-code/credentials.js +115 -74
  8. package/dist/claude-code/daemon/index.d.ts +24 -0
  9. package/dist/claude-code/daemon/index.js +203 -0
  10. package/dist/claude-code/egress.d.ts +35 -117
  11. package/dist/claude-code/egress.js +80 -325
  12. package/dist/claude-code/events.d.ts +14 -17
  13. package/dist/claude-code/events.js +19 -15
  14. package/dist/claude-code/harness-wire.d.ts +45 -0
  15. package/dist/claude-code/harness-wire.js +47 -0
  16. package/dist/claude-code/index.d.ts +19 -29
  17. package/dist/claude-code/index.js +17 -25
  18. package/dist/claude-code/language-model.d.ts +19 -0
  19. package/dist/claude-code/language-model.js +71 -0
  20. package/dist/claude-code/model.d.ts +77 -70
  21. package/dist/claude-code/model.js +328 -304
  22. package/dist/claude-code/placeholder.d.ts +12 -0
  23. package/dist/claude-code/placeholder.js +12 -0
  24. package/dist/claude-code/spec.d.ts +4 -4
  25. package/dist/claude-code/spec.js +4 -4
  26. package/dist/claude-code/verified.d.ts +1 -1
  27. package/dist/claude-code/verified.js +1 -1
  28. package/dist/computer/client.d.ts +88 -0
  29. package/dist/computer/client.js +6 -0
  30. package/dist/computer/context.d.ts +7 -7
  31. package/dist/computer/context.js +14 -18
  32. package/dist/computer/gate.d.ts +4 -23
  33. package/dist/computer/gate.js +1 -37
  34. package/dist/computer/index.d.ts +44 -43
  35. package/dist/computer/index.js +29 -50
  36. package/dist/computer/paths.d.ts +5 -27
  37. package/dist/computer/paths.js +7 -30
  38. package/dist/computer/proxy.d.ts +17 -31
  39. package/dist/computer/proxy.js +9 -44
  40. package/dist/computer/render.d.ts +3 -40
  41. package/dist/computer/render.js +3 -36
  42. package/dist/computer/sandbox.d.ts +55 -0
  43. package/dist/computer/sandbox.js +236 -0
  44. package/dist/computer/shell.d.ts +36 -0
  45. package/dist/computer/shell.js +41 -0
  46. package/dist/computer/tools-exec.js +3 -9
  47. package/dist/computer/tools-file.d.ts +1 -1
  48. package/dist/computer/tools-file.js +1 -1
  49. package/dist/{repo → repo-github}/checkout.d.ts +3 -4
  50. package/dist/{repo → repo-github}/checkout.js +3 -4
  51. package/dist/{repo → repo-github}/context.d.ts +14 -32
  52. package/dist/{repo → repo-github}/context.js +46 -81
  53. package/dist/{repo → repo-github}/index.d.ts +30 -13
  54. package/dist/{repo → repo-github}/index.js +25 -6
  55. package/dist/repo-github/mcp.d.ts +95 -0
  56. package/dist/repo-github/mcp.js +107 -0
  57. package/dist/repo-github/sandbox-egress.d.ts +68 -0
  58. package/dist/repo-github/sandbox-egress.js +272 -0
  59. package/dist/repo-github/sandbox-git.d.ts +30 -0
  60. package/dist/repo-github/sandbox-git.js +113 -0
  61. package/dist/{repo → repo-github}/tools-checks.js +43 -4
  62. package/dist/{repo → repo-github}/tools-clone.js +2 -21
  63. package/dist/{repo → repo-github}/tools-pr.d.ts +0 -1
  64. package/dist/{repo → repo-github}/tools-pr.js +85 -20
  65. package/dist/{repo → repo-github}/tools-review.d.ts +6 -0
  66. package/dist/{repo → repo-github}/tools-review.js +62 -176
  67. package/dist/{repo → repo-github}/url.d.ts +11 -0
  68. package/dist/{repo → repo-github}/url.js +12 -1
  69. package/dist/{workspace → sandbox}/advisory.d.ts +16 -35
  70. package/dist/{workspace → sandbox}/advisory.js +2 -29
  71. package/dist/sandbox/agent-exec.d.ts +31 -0
  72. package/dist/sandbox/agent-exec.js +62 -0
  73. package/dist/sandbox/ca-trust.d.ts +9 -0
  74. package/dist/sandbox/ca-trust.js +21 -0
  75. package/dist/sandbox/constants.d.ts +22 -0
  76. package/dist/sandbox/constants.js +22 -0
  77. package/dist/sandbox/daemon/composite.d.ts +9 -0
  78. package/dist/sandbox/daemon/composite.js +35 -0
  79. package/dist/sandbox/daemon/index.d.ts +47 -0
  80. package/dist/sandbox/daemon/index.js +116 -0
  81. package/dist/sandbox/egress.d.ts +81 -0
  82. package/dist/sandbox/egress.js +109 -0
  83. package/dist/sandbox/exec.d.ts +57 -0
  84. package/dist/sandbox/exec.js +52 -0
  85. package/dist/sandbox/format.d.ts +2 -0
  86. package/dist/sandbox/format.js +7 -0
  87. package/dist/sandbox/header.d.ts +50 -0
  88. package/dist/sandbox/header.js +94 -0
  89. package/dist/sandbox/index.d.ts +17 -0
  90. package/dist/sandbox/index.js +17 -0
  91. package/dist/{workspace/install.d.ts → sandbox/install-plan.d.ts} +10 -1
  92. package/dist/{workspace/install.js → sandbox/install-plan.js} +22 -4
  93. package/dist/sandbox/name.d.ts +27 -0
  94. package/dist/sandbox/name.js +32 -0
  95. package/dist/sandbox/object.d.ts +244 -0
  96. package/dist/sandbox/object.js +1139 -0
  97. package/dist/scratch/index.d.ts +1 -9
  98. package/dist/scratch/index.js +2 -18
  99. package/package.json +26 -22
  100. package/dist/claude-code/github.d.ts +0 -39
  101. package/dist/claude-code/github.js +0 -82
  102. package/dist/claude-code/run.d.ts +0 -474
  103. package/dist/claude-code/run.js +0 -901
  104. package/dist/claude-code/session.d.ts +0 -131
  105. package/dist/claude-code/session.js +0 -285
  106. package/dist/workspace/ca-trust.d.ts +0 -95
  107. package/dist/workspace/ca-trust.js +0 -312
  108. package/dist/workspace/container-deps.d.ts +0 -54
  109. package/dist/workspace/container-deps.js +0 -224
  110. package/dist/workspace/container-fault.d.ts +0 -38
  111. package/dist/workspace/container-fault.js +0 -73
  112. package/dist/workspace/exec.d.ts +0 -94
  113. package/dist/workspace/exec.js +0 -153
  114. package/dist/workspace/format.d.ts +0 -36
  115. package/dist/workspace/format.js +0 -57
  116. package/dist/workspace/git-host.d.ts +0 -79
  117. package/dist/workspace/git-host.js +0 -226
  118. package/dist/workspace/git-identity.d.ts +0 -57
  119. package/dist/workspace/git-identity.js +0 -147
  120. package/dist/workspace/index.d.ts +0 -49
  121. package/dist/workspace/index.js +0 -58
  122. package/dist/workspace/install-job.d.ts +0 -121
  123. package/dist/workspace/install-job.js +0 -866
  124. package/dist/workspace/object.d.ts +0 -502
  125. package/dist/workspace/object.js +0 -1427
  126. package/dist/workspace/open.d.ts +0 -95
  127. package/dist/workspace/open.js +0 -63
  128. package/dist/workspace/read.d.ts +0 -16
  129. package/dist/workspace/read.js +0 -22
  130. package/dist/workspace/shell.d.ts +0 -45
  131. package/dist/workspace/shell.js +0 -80
  132. package/dist/workspace/sync.d.ts +0 -111
  133. package/dist/workspace/sync.js +0 -124
  134. package/dist/workspace/wake.d.ts +0 -19
  135. package/dist/workspace/wake.js +0 -10
  136. /package/dist/{repo → repo-github}/tools-checks.d.ts +0 -0
  137. /package/dist/{repo → repo-github}/tools-clone.d.ts +0 -0
  138. /package/dist/{repo → repo-github}/tools-worktree.d.ts +0 -0
  139. /package/dist/{repo → repo-github}/tools-worktree.js +0 -0
  140. /package/dist/{repo → repo-github}/tools-worktrees.d.ts +0 -0
  141. /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 workspace. A planner is the same spec
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 {@link file://./model.ts ClaudeCodeModelOptions.permissionMode}.
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 the workspace Durable Object imports this subpath for its
13
- * egress gateway alone; with `sideEffects: false`, a module nothing it uses
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
@@ -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 workspace. A planner is the same spec
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 {@link file://./model.ts ClaudeCodeModelOptions.permissionMode}.
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 the workspace Durable Object imports this subpath for its
12
- * egress gateway alone; with `sideEffects: false`, a module nothing it uses
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
@@ -12,4 +12,4 @@
12
12
  * and the capture move together. AGENTS.md's "Updating Claude Code" is the
13
13
  * procedure.
14
14
  */
15
- export declare const VERIFIED_CLAUDE_CODE_VERSION = "2.1.289";
15
+ export declare const VERIFIED_CLAUDE_CODE_VERSION = "2.1.296";
@@ -12,4 +12,4 @@
12
12
  * and the capture move together. AGENTS.md's "Updating Claude Code" is the
13
13
  * procedure.
14
14
  */
15
- export const VERIFIED_CLAUDE_CODE_VERSION = "2.1.289";
15
+ export const VERIFIED_CLAUDE_CODE_VERSION = "2.1.296";
@@ -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
+ }
@@ -0,0 +1,6 @@
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 {};
@@ -1,19 +1,19 @@
1
- import type { WorkspaceClient } from "@cloudflare/computer";
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 { WorkspaceAdvisory } from "../workspace/advisory.js";
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: ComputerConfig;
7
+ config: ComputerToolsConfig;
8
8
  cwd: string;
9
9
  timeoutMs: number;
10
10
  maxChars: number;
11
- workspace: () => Promise<WorkspaceClient>;
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: WorkspaceClient["fs"]) => Promise<string>) => Promise<string>;
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<WorkspaceClient>, config: ComputerConfig, advisories?: () => Promise<readonly WorkspaceAdvisory[]>, fsWorkspace?: () => Promise<WorkspaceClient>, lockScope?: () => string): ComputerContext;
19
+ export declare function computerContext(workspace: () => Promise<ComputerClient>, config: ComputerToolsConfig, advisories?: () => Promise<readonly SandboxAdvisory[]>, lockScope?: () => string): ComputerContext;
@@ -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, fsWorkspace = workspace, lockScope) {
80
- const cwd = config.cwd ?? DEFAULT_CWD;
81
- const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
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 — because a dependency
96
- * install and a full workspace are not relevant to the same commands, and one
97
- * filter over both leaves a workspace at its ceiling silent for exactly the
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, and the one thing that does —
146
- * a workspace that no longer accepts writes — never resolves on its own.
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. That one recognises `EEXEC_LOST`,
185
- * logs, and carries an install warning through the failure path — a bespoke
186
- * catch saying something this cannot.
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 fsWorkspace(), false);
191
+ const ws = __addDisposableResource(env_1, await workspace(), false);
196
192
  return await body(ws.fs);
197
193
  }
198
194
  catch (e_1) {
@@ -1,32 +1,13 @@
1
- import { type WorkspaceAdvisory } from "../workspace/advisory.js";
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://../workspace/advisory.ts} decides what is true about the workspace and how
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 WorkspaceAdvisory[], command: string): ExecGate;
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 WorkspaceAdvisory[]): string | undefined;
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?
@@ -1,40 +1,4 @@
1
- import { renderAdvisory, shapeOf } from "../workspace/advisory.js";
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
@@ -1,41 +1,51 @@
1
1
  import type { ToolSet } from "ai";
2
2
  import type { AgentPlugin } from "@dynamicagents/core";
3
- import type { WorkspaceClient } from "@cloudflare/computer";
4
- import type { WorkspaceAdvisory } from "../workspace/advisory.js";
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 container
8
- * workspace: `bash`, `grep` and `edit`, and `computerWorkspace`, the agent's own
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 workspace itself — the Durable Object, its container, its git, its
13
- * install — is `@dynamicagents/plugins/workspace`. This plugin reaches it only
14
- * through that subpath's client modules, never its object, so an agent that
15
- * holds the tools carries no container backend and no isomorphic-git.
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 this one — `computerWorkspace` — or
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, SESSION_STATE_DIR, WALK_SKIPS } from "./paths.js";
35
- export { packBlocks, renderGrepMatches, renderResult, syncPendingNote } from "./render.js";
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, isComputerWorkspace } from "./proxy.js";
38
- export interface ComputerConfig extends WorkspaceClientConfig {
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 runs clone, fetch and push on the host's side of
93
- * the boundary. **No command in its container is ever given that token.**
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 workspace for a command: container started, CA
99
- * installed.
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<WorkspaceClient>, config: ComputerConfig, advisories?: () => Promise<readonly WorkspaceAdvisory[]>, fsWorkspace?: () => Promise<WorkspaceClient>, lockScope?: () => string): ToolSet;
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 workspace through the agent's own
109
- * `computerWorkspace` — the one Think's `read` and `write` use — so the two can
110
- * never resolve different ones. `config` answers everything else: the shell,
111
- * the limits, the environment.
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: ComputerConfig): AgentPlugin;
114
+ export declare function computer(config: ComputerToolsConfig): AgentPlugin;
@@ -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 container
10
- * workspace: `bash`, `grep` and `edit`, and `computerWorkspace`, the agent's own
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 workspace itself — the Durable Object, its container, its git, its
15
- * install — is `@dynamicagents/plugins/workspace`. This plugin reaches it only
16
- * through that subpath's client modules, never its object, so an agent that
17
- * holds the tools carries no container backend and no isomorphic-git.
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 this one — `computerWorkspace` — or
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, SESSION_STATE_DIR, WALK_SKIPS } from "./paths.js";
37
- export { packBlocks, renderGrepMatches, renderResult, syncPendingNote } from "./render.js";
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 { computerWorkspace, isComputerWorkspace } from "./proxy.js";
31
+ export { sandboxClient, computerWorkspace } from "./sandbox.js";
40
32
  /**
41
- * @param workspace Opens the workspace for a command: container started, CA
42
- * installed.
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, fsWorkspace = workspace, lockScope) {
50
- const ctx = computerContext(workspace, config, advisories, fsWorkspace, lockScope);
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
- // The reason to prefer them is the one thing the model cannot infer from a
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
- // Both halves of this matter and they pull in opposite directions, which
68
- // is why they are stated together rather than left for the model to work
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 workspace through the agent's own
79
- * `computerWorkspace` — the one Think's `read` and `write` use — so the two can
80
- * never resolve different ones. `config` answers everything else: the shell,
81
- * the limits, the environment.
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 `/workspace`'s `WORKSPACE_RUNTIME_KEY` —
97
- // and a stale stub would silently route a second caller's commands into
98
- // the first caller's files.
99
- const host = () => route.host(route.name());
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
- () => host().advisories(), () => openWorkspaceFs(host()), route.name);
83
+ () => route.advisories(route.name()), route.name);
105
84
  },
106
85
  context: [{ provider: { get: async () => CONTEXT } }]
107
86
  });