@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,236 @@
1
+ import { RUN_DIR } from "../sandbox/constants.js";
2
+ import { sandboxNameFromRuntime } from "../sandbox/name.js";
3
+ import { routedWorkspace } from "./proxy.js";
4
+ /** After `timeout`'s TERM, how long before its KILL. */
5
+ const KILL_AFTER_S = 5;
6
+ /** `timeout`'s exits for a command it ended: TERM taken, or KILL after it was not. */
7
+ const TIMED_OUT = new Set([124, 137]);
8
+ /**
9
+ * A command's processes carry a marker, for `kill` to find them by — through
10
+ * the launch and after it, since `exec` keeps it in every command line.
11
+ * `timeout` ends the command where it runs at its limit, which a Worker-side
12
+ * signal cannot.
13
+ */
14
+ const MARKER = "da-exec-";
15
+ const CANCELLED = `${RUN_DIR}/cancelled`;
16
+ /**
17
+ * The launch and `kill` are a handshake, because a command can still be on its
18
+ * way in — starting the container — when it is cancelled: `kill` marks the
19
+ * command cancelled before it signals, and the launch checks the mark before
20
+ * it starts. Either the command never starts, or it is running when `kill`
21
+ * signals it.
22
+ */
23
+ const LAUNCH = [
24
+ `[ -e "${CANCELLED}/$0" ] && exit 130`,
25
+ `exec timeout --kill-after=${KILL_AFTER_S} "$2" sh -c "$1" "$0"`
26
+ ].join("\n");
27
+ /** The marker arrives in two halves, so this script's own command line is not one `pkill` matches. */
28
+ const CANCEL = [
29
+ 'm="$1$2"',
30
+ `mkdir -p ${CANCELLED} && : > "${CANCELLED}/$m"`,
31
+ 'pkill -TERM -f "$m"',
32
+ "exit 0"
33
+ ].join("\n");
34
+ /** What one `find` or `grep` returns at most, with no `limit` of its own. */
35
+ const MAX_FOUND = 10_000;
36
+ export function computerWorkspace(config, runtime) {
37
+ const host = (name) => config.binding.getByName(name);
38
+ return routedWorkspace({
39
+ name: () => sandboxNameFromRuntime(runtime?.()) ?? config.sandboxName(),
40
+ open: async (name) => sandboxClient(host(name)),
41
+ advisories: (name) => host(name).advisories()
42
+ }, config.cwd ?? "/workspace");
43
+ }
44
+ function missing(path) {
45
+ return Object.assign(new Error(`ENOENT: no such file or directory, ${path}`), { code: "ENOENT" });
46
+ }
47
+ /** The computer tools' client over one sandbox. */
48
+ export function sandboxClient(host) {
49
+ const readFile = async (path, encoding) => {
50
+ const bytes = await host.readFile(path);
51
+ if (bytes === null)
52
+ throw missing(path);
53
+ return encoding === "utf8"
54
+ ? new TextDecoder().decode(bytes)
55
+ : new Blob([bytes]).stream();
56
+ };
57
+ const fs = {
58
+ readFile: readFile,
59
+ async stat(path) {
60
+ const stat = await host.stat(path);
61
+ if (!stat)
62
+ throw missing(path);
63
+ return {
64
+ size: stat.size,
65
+ mtime: stat.modifiedAt,
66
+ isFile: stat.type === "file",
67
+ isDirectory: stat.type === "directory",
68
+ isSymbolicLink: stat.type === "symlink"
69
+ };
70
+ },
71
+ async readdir(path, options = {}) {
72
+ const from = options.offset ?? 0;
73
+ return (await host.readDir(path))
74
+ .slice(from, options.limit === undefined ? undefined : from + options.limit)
75
+ .map((entry) => ({
76
+ name: entry.name,
77
+ isFile: entry.type === "file",
78
+ isDirectory: entry.type === "directory"
79
+ }));
80
+ },
81
+ find: (directory, pattern, options = {}) => find(host, directory, pattern, options),
82
+ grep: (pattern, path, options = {}) => grep(host, pattern, path, options),
83
+ writeFile: (path, content) => host.writeFile(path, content),
84
+ mkdir: (path) => host.mkdir(path),
85
+ rm: (path, options = {}) => host.rm(path, options.recursive ? { recursive: true } : {})
86
+ };
87
+ return {
88
+ fs,
89
+ runtime: {
90
+ async exec(command, options) {
91
+ const id = crypto.randomUUID();
92
+ const marker = `${MARKER}${id}`;
93
+ const startedAt = Date.now();
94
+ let killed = false;
95
+ const running = host.exec([
96
+ "sh",
97
+ "-c",
98
+ LAUNCH,
99
+ marker,
100
+ command,
101
+ // At least a second: `timeout 0` is no limit at all.
102
+ String(Math.max(1, Math.ceil(options.timeoutMs / 1000)))
103
+ ], {
104
+ cwd: options.cwd,
105
+ ...(options.env ? { env: options.env } : {}),
106
+ timeoutMs: options.timeoutMs + (KILL_AFTER_S + 5) * 1000
107
+ });
108
+ running.catch(() => undefined);
109
+ return {
110
+ async result() {
111
+ const result = await running;
112
+ const timedOut = TIMED_OUT.has(result.exitCode) &&
113
+ Date.now() - startedAt >= options.timeoutMs;
114
+ return {
115
+ ...result,
116
+ status: killed || timedOut
117
+ ? "cancelled"
118
+ : "completed"
119
+ };
120
+ },
121
+ async kill() {
122
+ killed = true;
123
+ await host.exec(["sh", "-c", CANCEL, "da-cancel", MARKER, id]);
124
+ },
125
+ [Symbol.dispose]() { }
126
+ };
127
+ }
128
+ },
129
+ [Symbol.dispose]() { }
130
+ };
131
+ }
132
+ /**
133
+ * `rg` over `target`, from where its globs anchor: a directory's own, with
134
+ * `include` applied, or a file's parent. The script prints that directory
135
+ * first, then `rg`'s output cut at `lines`, so a search of a whole checkout
136
+ * returns what the tool can show rather than everything.
137
+ */
138
+ const RG_SCRIPT = [
139
+ 't="$1"; include="$2"; lines="$3"; shift 3',
140
+ '[ -e "$t" ] || { echo "ENOENT" >&2; exit 2; }',
141
+ 'if [ -d "$t" ]; then cd "$t" || exit 2; a=.',
142
+ ' if [ -n "$include" ]; then set -- -g "/$include" "$@"; fi',
143
+ 'else cd "$(dirname "$t")" || exit 2; a="./$(basename "$t")"; fi',
144
+ "pwd",
145
+ 'rg "$@" -- "$a" < /dev/null | head -n "$lines"'
146
+ ].join("\n");
147
+ async function rg(host, target, include, args, lines) {
148
+ const run = await host.exec([
149
+ "sh",
150
+ "-c",
151
+ RG_SCRIPT,
152
+ "rg",
153
+ target,
154
+ (include ?? "").replace(/^\/+/, ""),
155
+ String(lines),
156
+ ...args
157
+ ]);
158
+ if (run.exitCode === 2 && /ENOENT/.test(run.stderr))
159
+ throw missing(target);
160
+ const [cwd = target, ...out] = run.stdout.split("\n");
161
+ if (out.every((line) => line === "") && run.stderr.trim() !== "")
162
+ throw new Error(run.stderr.trim());
163
+ return { cwd, out: out.filter(Boolean) };
164
+ }
165
+ function absolute(cwd, printed) {
166
+ const rest = printed.replace(/^\.\//, "");
167
+ return cwd === "/" ? `/${rest}` : `${cwd}/${rest}`;
168
+ }
169
+ /** Hidden and ignored files too, as the old walk read them; `exclude` pruned. */
170
+ function scope(exclude) {
171
+ return [
172
+ "--hidden",
173
+ "--no-ignore",
174
+ "--sort",
175
+ "path",
176
+ ...(exclude ?? []).flatMap((glob) => ["-g", `!${glob}`])
177
+ ];
178
+ }
179
+ async function find(host, directory, pattern, options) {
180
+ const from = options.offset ?? 0;
181
+ const take = options.limit ?? MAX_FOUND;
182
+ const { cwd, out } = await rg(host, directory, pattern, ["--files", ...scope(options.exclude)], Math.min(from + take, MAX_FOUND));
183
+ // Files only: `rg --files` lists no directories.
184
+ return out
185
+ .slice(from, from + take)
186
+ .map((path) => ({ path: absolute(cwd, path), type: "file" }));
187
+ }
188
+ async function grep(host, pattern, path, options) {
189
+ const around = options.context ?? 0;
190
+ const from = options.offset ?? 0;
191
+ const take = options.limit ?? MAX_FOUND;
192
+ const { cwd, out } = await rg(host, path, options.include, [
193
+ "--json",
194
+ ...scope(options.exclude),
195
+ ...(options.regex ? [] : ["--fixed-strings"]),
196
+ ...(options.ignoreCase ? ["--ignore-case"] : []),
197
+ ...(around > 0 ? ["--context", String(around)] : []),
198
+ "-e",
199
+ pattern
200
+ ],
201
+ // Enough for every match it can return to arrive whole: each brings at
202
+ // most its context, and opens and closes a file.
203
+ (from + take) * (2 * around + 3) + 16);
204
+ // Per file, every line rg printed, so each match takes its context from them.
205
+ const files = new Map();
206
+ const order = [];
207
+ for (const raw of out) {
208
+ let entry;
209
+ try {
210
+ entry = JSON.parse(raw);
211
+ }
212
+ catch {
213
+ continue;
214
+ }
215
+ if (entry.type !== "match" && entry.type !== "context")
216
+ continue;
217
+ const file = absolute(cwd, entry.data?.path?.text ?? "");
218
+ const line = entry.data?.line_number ?? 0;
219
+ const text = (entry.data?.lines?.text ?? "").replace(/\r?\n$/, "");
220
+ const lines = files.get(file) ?? [];
221
+ lines.push({ line, text, isMatch: entry.type === "match" });
222
+ files.set(file, lines);
223
+ if (entry.type === "match")
224
+ order.push({ file, line, text });
225
+ }
226
+ return order.slice(from, from + take).map(({ file, line, text }) => ({
227
+ path: file,
228
+ line,
229
+ text,
230
+ ...(around > 0
231
+ ? {
232
+ context: (files.get(file) ?? []).filter((other) => Math.abs(other.line - line) <= around)
233
+ }
234
+ : {})
235
+ }));
236
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Run a command under {@link file://./index.ts ComputerToolsConfig.shell} with its
3
+ * two output streams merged into one transcript, in the order they were
4
+ * written, or hand it back untouched when no shell is configured.
5
+ *
6
+ * `bash`'s consumer is **a model reading output**. A project's check is a chain
7
+ * — `wrangler types && prettier && eslint && tsc` — and *which tool spoke last*
8
+ * is how you know which one failed. A stdout block and a separate stderr block
9
+ * destroy that ordering, and a model that wants it back re-runs the whole gate
10
+ * as `npm run check > /tmp/out 2>&1; cat /tmp/out`. This is that workaround,
11
+ * done once, for free.
12
+ *
13
+ * One quoted argument, not string concatenation: the command is model-authored
14
+ * and routinely contains quotes of its own (`git commit -m "…"`), so anything
15
+ * less than `shellQuote` re-parses the model's quoting and mangles it. The
16
+ * redirect binds to the wrapper process, so it applies to everything the
17
+ * command writes however deeply nested.
18
+ *
19
+ * ## Why `-o pipefail`
20
+ *
21
+ * Without it a pipeline's exit status is its **last** stage's, so
22
+ * `npm run check | tail -100` reports `exit 0` for a gate that failed outright —
23
+ * and models pipe into `tail` constantly. A tool that reports success for a
24
+ * failed build is worse than one that reports nothing.
25
+ *
26
+ * The trade is worth stating. `pipefail` surfaces SIGPIPE too, so
27
+ * `ls big-dir | head -1` reports 141 rather than 0 — noisy, but *visible*. The
28
+ * tool description tells the model not to pipe into `head`/`tail` at all, since
29
+ * output is already truncated with both ends kept. It needs a shell that
30
+ * implements it — `bash`, `zsh`, `ksh`, not `sh`/dash.
31
+ *
32
+ * With no shell configured the two streams arrive separate, and
33
+ * {@link file://./render.ts renderResult} renders them as a labelled
34
+ * `--- stderr ---` block.
35
+ */
36
+ export declare function withShellTranscript(command: string, shell: string | undefined): string;
@@ -0,0 +1,41 @@
1
+ import { shellQuote } from "../sandbox/agent-exec.js";
2
+ /**
3
+ * Run a command under {@link file://./index.ts ComputerToolsConfig.shell} with its
4
+ * two output streams merged into one transcript, in the order they were
5
+ * written, or hand it back untouched when no shell is configured.
6
+ *
7
+ * `bash`'s consumer is **a model reading output**. A project's check is a chain
8
+ * — `wrangler types && prettier && eslint && tsc` — and *which tool spoke last*
9
+ * is how you know which one failed. A stdout block and a separate stderr block
10
+ * destroy that ordering, and a model that wants it back re-runs the whole gate
11
+ * as `npm run check > /tmp/out 2>&1; cat /tmp/out`. This is that workaround,
12
+ * done once, for free.
13
+ *
14
+ * One quoted argument, not string concatenation: the command is model-authored
15
+ * and routinely contains quotes of its own (`git commit -m "…"`), so anything
16
+ * less than `shellQuote` re-parses the model's quoting and mangles it. The
17
+ * redirect binds to the wrapper process, so it applies to everything the
18
+ * command writes however deeply nested.
19
+ *
20
+ * ## Why `-o pipefail`
21
+ *
22
+ * Without it a pipeline's exit status is its **last** stage's, so
23
+ * `npm run check | tail -100` reports `exit 0` for a gate that failed outright —
24
+ * and models pipe into `tail` constantly. A tool that reports success for a
25
+ * failed build is worse than one that reports nothing.
26
+ *
27
+ * The trade is worth stating. `pipefail` surfaces SIGPIPE too, so
28
+ * `ls big-dir | head -1` reports 141 rather than 0 — noisy, but *visible*. The
29
+ * tool description tells the model not to pipe into `head`/`tail` at all, since
30
+ * output is already truncated with both ends kept. It needs a shell that
31
+ * implements it — `bash`, `zsh`, `ksh`, not `sh`/dash.
32
+ *
33
+ * With no shell configured the two streams arrive separate, and
34
+ * {@link file://./render.ts renderResult} renders them as a labelled
35
+ * `--- stderr ---` block.
36
+ */
37
+ export function withShellTranscript(command, shell) {
38
+ return shell
39
+ ? `${shell} -o pipefail -c ${shellQuote(command)} 2>&1`
40
+ : command;
41
+ }
@@ -54,8 +54,7 @@ import { tool } from "ai";
54
54
  import { z } from "zod";
55
55
  import { withAbort } from "@dynamicagents/core";
56
56
  import { renderResult } from "./render.js";
57
- import { execLostNote } from "./gate.js";
58
- import { withShellTranscript } from "../workspace/shell.js";
57
+ import { withShellTranscript } from "./shell.js";
59
58
  /** Running one command in the container. */
60
59
  /**
61
60
  * Wait for a disposable resource without outliving `signal`.
@@ -96,7 +95,7 @@ export function execTools(ctx) {
96
95
  *
97
96
  * Two of these sentences are only true when a shell is configured, so the
98
97
  * description says whichever is. `withShellTranscript` is a no-op without
99
- * {@link file://./index.ts ComputerConfig.shell}: no `2>&1`, so the
98
+ * {@link file://./index.ts ComputerToolsConfig.shell}: no `2>&1`, so the
100
99
  * streams arrive separate and {@link renderResult} labels them; no
101
100
  * `-o pipefail`, so `/bin/sh` reports a pipeline's *last* stage. Promising
102
101
  * a transcript and first-failure semantics there would be the lie
@@ -246,21 +245,16 @@ export function execTools(ctx) {
246
245
  catch (err) {
247
246
  if (abortSignal?.aborted)
248
247
  return note(stopped(gateMsWaited, started));
249
- const lost = execLostNote(err);
250
248
  console.warn("[computer] bash failed", {
251
249
  command,
252
250
  gateMs: gateMsWaited,
253
251
  durationMs: Date.now() - startedAtMs - gateMsWaited,
254
252
  timeoutMs,
255
- // Distinguished in the log for the same reason it is distinguished
256
- // for the model: a container replacement and a broken command want
257
- // different people looking at them.
258
- ...(lost ? { lost: true } : {}),
259
253
  err: String(err)
260
254
  });
261
255
  // Returned, not thrown: a failed command is usually the model's to
262
256
  // recover from, and it can only recover from what it is told.
263
- return note(lost ?? `error running command: ${String(err)}`);
257
+ return note(`error running command: ${String(err)}`);
264
258
  }
265
259
  }
266
260
  })
@@ -3,7 +3,7 @@ import type { ComputerContext } from "./context.js";
3
3
  /**
4
4
  * Editing a file in place — the one file tool this plugin keeps. Think's
5
5
  * `read`, `write` and `delete` reach the workspace through the agent's own
6
- * `this.workspace`, which is `computerWorkspace` (`./proxy.ts`).
6
+ * `this.workspace`, which is `computerWorkspace` (`./sandbox.ts`).
7
7
  *
8
8
  * Not Think's `edit`, because that one is two calls through the workspace — a
9
9
  * `readFile`, then a `writeFile` — and a lock inside the workspace cannot span
@@ -5,7 +5,7 @@ import { withFileLock } from "./file-lock.js";
5
5
  /**
6
6
  * Editing a file in place — the one file tool this plugin keeps. Think's
7
7
  * `read`, `write` and `delete` reach the workspace through the agent's own
8
- * `this.workspace`, which is `computerWorkspace` (`./proxy.ts`).
8
+ * `this.workspace`, which is `computerWorkspace` (`./sandbox.ts`).
9
9
  *
10
10
  * Not Think's `edit`, because that one is two calls through the workspace — a
11
11
  * `readFile`, then a `writeFile` — and a lock inside the workspace cannot span
@@ -76,10 +76,9 @@ export declare function resolveDefaultBranch(plain: GitRunners["plain"], dir: st
76
76
  * It is the *nearest* answer, not the only one. `repo_commit` names the identity
77
77
  * on the commit itself, which is the one layer a stale config cannot outrank.
78
78
  * Underneath, a repository this plugin never cloned falls back to whatever the
79
- * shell's own git is configured with — which is an identity rather than nothing
80
- * only where `exec` is backed by `/workspace`, whose object writes one per
81
- * container; see {@link file://../workspace/git-identity.ts}. `exec` is an arbitrary
82
- * runner, so that is a property of a deployment, not of this plugin.
79
+ * shell's own git is configured with, or the environment a deployment gives
80
+ * its commands. `exec` is an arbitrary runner, so that is a property of a
81
+ * deployment, not of this plugin.
83
82
  *
84
83
  * Unchecked, like the config pins at the clone: git's identity is needed by the
85
84
  * first commit, and a failure to write it fails that commit with a sentence far
@@ -33,10 +33,9 @@ export async function resolveDefaultBranch(plain, dir) {
33
33
  * It is the *nearest* answer, not the only one. `repo_commit` names the identity
34
34
  * on the commit itself, which is the one layer a stale config cannot outrank.
35
35
  * Underneath, a repository this plugin never cloned falls back to whatever the
36
- * shell's own git is configured with — which is an identity rather than nothing
37
- * only where `exec` is backed by `/workspace`, whose object writes one per
38
- * container; see {@link file://../workspace/git-identity.ts}. `exec` is an arbitrary
39
- * runner, so that is a property of a deployment, not of this plugin.
36
+ * shell's own git is configured with, or the environment a deployment gives
37
+ * its commands. `exec` is an arbitrary runner, so that is a property of a
38
+ * deployment, not of this plugin.
40
39
  *
41
40
  * Unchecked, like the config pins at the clone: git's identity is needed by the
42
41
  * first commit, and a failure to write it fails that commit with a sentence far
@@ -1,4 +1,5 @@
1
1
  import type { ActionContext } from "@cloudflare/think";
2
+ import { type GithubMcpCall } from "./mcp.js";
2
3
  import type { RepoCheckout, RepoConfig, RepoExec, RepoGitResult } from "./index.js";
3
4
  /**
4
5
  * How many items one GitHub list call asks for. GitHub's maximum, and its default
@@ -11,39 +12,12 @@ import type { RepoCheckout, RepoConfig, RepoExec, RepoGitResult } from "./index.
11
12
  * the last page, and those are exactly the ones an agent was sent to act on.
12
13
  */
13
14
  export declare const GITHUB_PAGE_SIZE = 100;
14
- /**
15
- * Where GraphQL lives, given where REST does.
16
- *
17
- * On github.com they share an origin and `/graphql` hangs off it. On GitHub
18
- * Enterprise — which {@link RepoConfig.apiBase} exists for — REST is under
19
- * `/api/v3` and GraphQL is its **sibling** at `/api/graphql`, not a path beneath
20
- * it. Appending blindly yields `/api/v3/graphql`, which is a 404 on every
21
- * Enterprise install and no failure at all on the public API, so the mistake
22
- * survives every test written against github.com.
23
- */
24
- export declare function graphqlEndpoint(apiBase: string): string;
25
- /**
26
- * Middle-out truncation, so both the head of a diff and its tail survive.
27
- *
28
- * Deliberately a **copy** of `/workspace`'s function of the same name, not an
29
- * import. `npm run verify:exports` fails any subpath whose module graph
30
- * reaches a sibling's directory, and its own advice for a shared helper is to
31
- * duplicate it — installing `repo` must not drag `/workspace` (and therefore
32
- * `@cloudflare/computer`) into a consumer's bundle. Fifteen lines is a cheaper
33
- * price than that coupling, and it is the same reason `exec` is injected here
34
- * rather than imported.
35
- *
36
- * The `half < 1` guard is not padding: without it a small `max` makes `half`
37
- * zero or negative, and `slice(-0)` is `slice(0)` — the whole string — so the
38
- * function would return *more* than it was given.
39
- */
40
- export declare function truncateOutput(text: string, max: number): string;
41
15
  /**
42
16
  * What one container command produced — plus one bit the container did not.
43
17
  *
44
- * `exec` does not only *return* failures, it throws them: `@cloudflare/computer`
45
- * throws `EEXEC_LOST` when a container is replaced mid-command, and any call to a
46
- * Durable Object can fail outright. Both are caught at the seam
18
+ * `exec` does not only *return* failures, it throws them: any call to a Durable
19
+ * Object can fail outright, a container lost mid-command among them. That is
20
+ * caught at the seam
47
21
  * ({@link repoContext}'s `run`) and turned into a failed result, so every tool
48
22
  * answers in its own `!success` branch — "could not read the status of /w/r: …"
49
23
  * is a better sentence than any wrapper one level up could write, and a seventh
@@ -83,6 +57,16 @@ export interface RepoContext {
83
57
  };
84
58
  unreachable?: string;
85
59
  }>;
60
+ /**
61
+ * One call to GitHub's hosted MCP server — every read this plugin makes.
62
+ * See {@link file://./mcp.ts GithubMcpCall}.
63
+ */
64
+ mcp: GithubMcpCall;
65
+ /**
66
+ * One REST call, for what is left on it: opening a pull request and
67
+ * commenting. Both are writes, and both are `coding`'s rather than
68
+ * claude-coordinator's.
69
+ */
86
70
  github: (tool: string, path: string, init?: {
87
71
  method: string;
88
72
  body: unknown;
@@ -125,8 +109,6 @@ export declare const GITHUB_ACTION_TIMEOUT_MS = 120000;
125
109
  * version of that fault.
126
110
  */
127
111
  export declare function actionTask(ctx: ActionContext): string;
128
- /** A hex SHA-256, so a key names a body without carrying it. */
129
- export declare function sha256(text: string): Promise<string>;
130
112
  export declare function repoContext(config: RepoConfig,
131
113
  /**
132
114
  * The running sub-agent's `runtime()`, read per command. Forwarded to every