@dynamicagents/plugins 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/README.md +58 -77
  2. package/dist/browser/index.d.ts +0 -2
  3. package/dist/browser/index.js +21 -27
  4. package/dist/claude-code/cards.d.ts +55 -0
  5. package/dist/claude-code/cards.js +283 -0
  6. package/dist/claude-code/config.d.ts +28 -63
  7. package/dist/claude-code/config.js +0 -11
  8. package/dist/claude-code/egress.d.ts +8 -1
  9. package/dist/claude-code/egress.js +42 -4
  10. package/dist/claude-code/events.d.ts +106 -9
  11. package/dist/claude-code/events.js +201 -56
  12. package/dist/claude-code/github.d.ts +39 -0
  13. package/dist/claude-code/github.js +82 -0
  14. package/dist/claude-code/index.d.ts +16 -90
  15. package/dist/claude-code/index.js +14 -276
  16. package/dist/claude-code/model.d.ts +152 -0
  17. package/dist/claude-code/model.js +366 -0
  18. package/dist/claude-code/run.d.ts +230 -116
  19. package/dist/claude-code/run.js +380 -167
  20. package/dist/claude-code/session.d.ts +131 -0
  21. package/dist/claude-code/session.js +288 -0
  22. package/dist/claude-code/spec.d.ts +46 -0
  23. package/dist/claude-code/spec.js +83 -0
  24. package/dist/claude-code/verified.d.ts +15 -0
  25. package/dist/claude-code/verified.js +15 -0
  26. package/dist/computer/context.d.ts +1 -5
  27. package/dist/computer/context.js +7 -10
  28. package/dist/computer/file-lock.js +5 -4
  29. package/dist/computer/gate.d.ts +4 -4
  30. package/dist/computer/gate.js +5 -5
  31. package/dist/computer/index.d.ts +40 -258
  32. package/dist/computer/index.js +71 -324
  33. package/dist/computer/paths.d.ts +36 -16
  34. package/dist/computer/paths.js +42 -29
  35. package/dist/computer/proxy.d.ts +45 -0
  36. package/dist/computer/proxy.js +234 -0
  37. package/dist/computer/render.d.ts +1 -40
  38. package/dist/computer/render.js +2 -68
  39. package/dist/computer/tools-exec.js +12 -13
  40. package/dist/computer/tools-file.d.ts +9 -1
  41. package/dist/computer/tools-file.js +22 -82
  42. package/dist/computer/{tools-find.d.ts → tools-grep.d.ts} +1 -1
  43. package/dist/computer/tools-grep.js +129 -0
  44. package/dist/repo/checkout.d.ts +4 -5
  45. package/dist/repo/checkout.js +31 -8
  46. package/dist/repo/context.d.ts +35 -9
  47. package/dist/repo/context.js +101 -49
  48. package/dist/repo/index.d.ts +73 -28
  49. package/dist/repo/index.js +112 -96
  50. package/dist/repo/tools-checks.d.ts +3 -0
  51. package/dist/repo/tools-checks.js +112 -0
  52. package/dist/repo/tools-clone.js +1 -1
  53. package/dist/repo/tools-pr.d.ts +14 -0
  54. package/dist/repo/{tools-forge.js → tools-pr.js} +53 -36
  55. package/dist/repo/tools-review.d.ts +9 -0
  56. package/dist/repo/tools-review.js +67 -47
  57. package/dist/repo/tools-worktree.js +144 -60
  58. package/dist/repo/tools-worktrees.d.ts +12 -0
  59. package/dist/repo/tools-worktrees.js +48 -0
  60. package/dist/repo/url.d.ts +2 -2
  61. package/dist/repo/url.js +7 -7
  62. package/dist/scratch/index.d.ts +4 -4
  63. package/dist/scratch/index.js +12 -11
  64. package/dist/{computer → workspace}/advisory.d.ts +4 -4
  65. package/dist/{computer → workspace}/advisory.js +2 -2
  66. package/dist/{computer/host → workspace}/ca-trust.d.ts +8 -1
  67. package/dist/{computer/host → workspace}/ca-trust.js +12 -3
  68. package/dist/{computer/host → workspace}/container-deps.d.ts +4 -0
  69. package/dist/{computer/host → workspace}/container-deps.js +26 -1
  70. package/dist/{computer/host → workspace}/container-fault.d.ts +3 -3
  71. package/dist/{computer/host → workspace}/container-fault.js +23 -8
  72. package/dist/workspace/deps-snapshot.d.ts +110 -0
  73. package/dist/workspace/deps-snapshot.js +424 -0
  74. package/dist/workspace/exec.d.ts +94 -0
  75. package/dist/workspace/exec.js +153 -0
  76. package/dist/workspace/format.d.ts +36 -0
  77. package/dist/workspace/format.js +57 -0
  78. package/dist/{computer/host → workspace}/git-host.d.ts +21 -4
  79. package/dist/{computer/host → workspace}/git-host.js +72 -2
  80. package/dist/{computer/host → workspace}/git-identity.d.ts +1 -1
  81. package/dist/{computer/host → workspace}/git-identity.js +1 -1
  82. package/dist/workspace/index.d.ts +44 -47
  83. package/dist/workspace/index.js +52 -112
  84. package/dist/{computer/host → workspace}/install-job.d.ts +53 -4
  85. package/dist/{computer/host → workspace}/install-job.js +346 -178
  86. package/dist/{computer → workspace}/install.d.ts +17 -8
  87. package/dist/{computer → workspace}/install.js +24 -6
  88. package/dist/{computer/host/workspace.d.ts → workspace/object.d.ts} +125 -27
  89. package/dist/{computer/host/workspace.js → workspace/object.js} +341 -58
  90. package/dist/workspace/open.d.ts +95 -0
  91. package/dist/workspace/open.js +63 -0
  92. package/dist/workspace/read.d.ts +16 -0
  93. package/dist/workspace/read.js +22 -0
  94. package/dist/{computer → workspace}/shell.d.ts +5 -5
  95. package/dist/{computer → workspace}/shell.js +6 -6
  96. package/dist/{computer/host → workspace}/wake.d.ts +3 -1
  97. package/package.json +17 -37
  98. package/dist/arc-agi/analysis.d.ts +0 -322
  99. package/dist/arc-agi/analysis.js +0 -802
  100. package/dist/arc-agi/client.d.ts +0 -82
  101. package/dist/arc-agi/client.js +0 -113
  102. package/dist/arc-agi/game-tools.d.ts +0 -24
  103. package/dist/arc-agi/game-tools.js +0 -26
  104. package/dist/arc-agi/index.d.ts +0 -40
  105. package/dist/arc-agi/index.js +0 -175
  106. package/dist/arc-agi/main-agent.d.ts +0 -24
  107. package/dist/arc-agi/main-agent.js +0 -62
  108. package/dist/arc-agi/recipe.d.ts +0 -48
  109. package/dist/arc-agi/recipe.js +0 -97
  110. package/dist/arc-agi/scorecard.d.ts +0 -118
  111. package/dist/arc-agi/scorecard.js +0 -163
  112. package/dist/arc-agi/soul.d.ts +0 -29
  113. package/dist/arc-agi/soul.js +0 -79
  114. package/dist/arc-agi/store.d.ts +0 -153
  115. package/dist/arc-agi/store.js +0 -215
  116. package/dist/arc-agi/tools.d.ts +0 -36
  117. package/dist/arc-agi/tools.js +0 -574
  118. package/dist/arc-agi/types.d.ts +0 -211
  119. package/dist/arc-agi/types.js +0 -19
  120. package/dist/claude-code/recipe.d.ts +0 -53
  121. package/dist/claude-code/recipe.js +0 -100
  122. package/dist/computer/read.d.ts +0 -133
  123. package/dist/computer/read.js +0 -200
  124. package/dist/computer/tools-find.js +0 -216
  125. package/dist/recall/index.d.ts +0 -105
  126. package/dist/recall/index.js +0 -181
  127. package/dist/repo/tools-forge.d.ts +0 -4
  128. package/dist/triage/index.d.ts +0 -360
  129. package/dist/triage/index.js +0 -311
  130. /package/dist/{computer/host → workspace}/sync.d.ts +0 -0
  131. /package/dist/{computer/host → workspace}/sync.js +0 -0
  132. /package/dist/{computer/host → workspace}/wake.js +0 -0
@@ -1,7 +1,7 @@
1
1
  import { DEFAULT_ALLOWED_HOSTS, parseRepo, repoLocation } from "./url.js";
2
2
  /**
3
3
  * The machinery every repo tool closes over: the resolved config, the git
4
- * runners, and the forge client.
4
+ * runners, and the GitHub client.
5
5
  */
6
6
  const DEFAULT_WORKDIR = "/workspace";
7
7
  const DEFAULT_API_BASE = "https://api.github.com";
@@ -11,15 +11,15 @@ const DEFAULT_AUTHOR = {
11
11
  };
12
12
  const DEFAULT_MAX_OUTPUT_CHARS = 16_000;
13
13
  /**
14
- * Ceiling on every call this plugin makes to the forge's API.
14
+ * Ceiling on every call this plugin makes to GitHub's API.
15
15
  *
16
16
  * Not configurable, because it is not a tuning knob: it exists so an API that
17
- * stops answering costs a round rather than the task. These are single small
17
+ * stops answering costs a tool call rather than the task. These are single small
18
18
  * requests, and thirty seconds is already generous for one.
19
19
  */
20
- const FORGE_TIMEOUT_MS = 30_000;
20
+ const GITHUB_TIMEOUT_MS = 30_000;
21
21
  /**
22
- * How many items one forge list call asks for. GitHub's maximum, and its default
22
+ * How many items one GitHub list call asks for. GitHub's maximum, and its default
23
23
  * without this is 30.
24
24
  *
25
25
  * One page, never a Link-header walk: an unbounded fetch loop does not belong
@@ -28,7 +28,7 @@ const FORGE_TIMEOUT_MS = 30_000;
28
28
  * presented as the whole answer — a pull request's newest review comments are on
29
29
  * the last page, and those are exactly the ones an agent was sent to act on.
30
30
  */
31
- export const FORGE_PAGE_SIZE = 100;
31
+ export const GITHUB_PAGE_SIZE = 100;
32
32
  /**
33
33
  * Where GraphQL lives, given where REST does.
34
34
  *
@@ -47,10 +47,10 @@ export function graphqlEndpoint(apiBase) {
47
47
  /**
48
48
  * Middle-out truncation, so both the head of a diff and its tail survive.
49
49
  *
50
- * Deliberately a **copy** of the computer plugin's function of the same name,
51
- * not an import. `npm run verify:exports` fails any subpath whose module graph
50
+ * Deliberately a **copy** of `/workspace`'s function of the same name, not an
51
+ * import. `npm run verify:exports` fails any subpath whose module graph
52
52
  * reaches a sibling's directory, and its own advice for a shared helper is to
53
- * duplicate it — installing `repo` must not drag `computer` (and therefore
53
+ * duplicate it — installing `repo` must not drag `/workspace` (and therefore
54
54
  * `@cloudflare/computer`) into a consumer's bundle. Fifteen lines is a cheaper
55
55
  * price than that coupling, and it is the same reason `exec` is injected here
56
56
  * rather than imported.
@@ -74,7 +74,7 @@ export function truncateOutput(text, max) {
74
74
  * A sibling of the computer plugin's `execLostNote` rather than an import, for
75
75
  * the same reason {@link truncateOutput} is a copy: `verify:exports` fails any
76
76
  * subpath whose module graph reaches a sibling's, and installing `repo` must
77
- * not drag `computer` in behind it. The wording differs anyway, and the
77
+ * not drag `/computer` in behind it. The wording differs anyway, and the
78
78
  * difference is the point — that plugin cannot know the lost command was git,
79
79
  * and this one does. What a model needs after a lost `git push` is not "re-run
80
80
  * it" but whether re-running it is *safe*.
@@ -85,7 +85,7 @@ function unreachableNote(err) {
85
85
  "is known to have finished. This is infrastructure — not git, and not what " +
86
86
  "you asked for. The checkout is durable and is exactly as you left it. " +
87
87
  "Retry, and read what the retry says rather than assuming it starts from " +
88
- "nothing: a command that had already reached the forge may have taken " +
88
+ "nothing: a command that had already reached GitHub may have taken " +
89
89
  "effect. Retrying is safe either way — these commands push one branch to " +
90
90
  "the branch of the same name, and never force.");
91
91
  }
@@ -102,13 +102,13 @@ function unreachableNote(err) {
102
102
  * than waiting if it is already held. A model may emit several tool calls in one
103
103
  * turn and the SDK runs them concurrently, so unserialised a `repo_commit` and a
104
104
  * `repo_push` race: the commit fails, the push succeeds against the previous
105
- * state, and the round reports work that never landed.
105
+ * state, and the turn reports work that never landed.
106
106
  *
107
107
  * Held here rather than in {@link repoContext}'s closure, because that closure
108
- * is too short-lived to be the boundary. Core rebuilds `mainAgentTools` every
109
- * turn and gives each subagent execution its own tool family, so a per-call queue
110
- * leaves a subagent racing its parent over one checkout — the same collision, one
111
- * level up. The config object is the plugin instance, and a `WeakMap` means a
108
+ * is too short-lived to be the boundary. The tools are built for every turn, and
109
+ * a sub-agent builds its own from the same config, so a per-call queue leaves a
110
+ * sub-agent racing its parent over one checkout — the same collision, one level
111
+ * up. The config object is the plugin instance, and a `WeakMap` means a
112
112
  * discarded agent takes its queue with it.
113
113
  *
114
114
  * A promise chain rather than a lock, because that is all the scope needs: one
@@ -145,11 +145,10 @@ const gitQueues = new WeakMap();
145
145
  *
146
146
  * A command whose container or host never answers — a workspace object that
147
147
  * reset under it — would otherwise hold every git command behind it for the life
148
- * of the isolate, each abandoned in turn at core's per-call limit. Past this the
149
- * command is reported unreachable and the queue moves on. It may still be
150
- * running; a later command that meets its `index.lock` fails fast and says so.
151
- * Under that per-call limit, so the tool answers for itself rather than being
152
- * abandoned, and far longer than any git command here takes.
148
+ * of the isolate. Past this the command is reported unreachable and the queue
149
+ * moves on. It may still be running; a later command that meets its
150
+ * `index.lock` fails fast and says so. Far longer than any git command here
151
+ * takes.
153
152
  */
154
153
  const GIT_SLOT_MS = 8 * 60_000;
155
154
  /** `work`, rejected once {@link GIT_SLOT_MS} passes without it settling. */
@@ -162,7 +161,43 @@ function withinSlot(work) {
162
161
  work.catch(() => { });
163
162
  return Promise.race([work, expired]).finally(() => clearTimeout(timer));
164
163
  }
165
- export function repoContext(config, runtime) {
164
+ /**
165
+ * How long an action that calls GitHub may run before Think gives up on it.
166
+ *
167
+ * An action's default is thirty seconds, which is {@link GITHUB_TIMEOUT_MS}
168
+ * alone — and a thread reply is three GitHub calls, after an `origin` read that
169
+ * waits its turn in the git queue.
170
+ */
171
+ export const GITHUB_ACTION_TIMEOUT_MS = 120_000;
172
+ /**
173
+ * The task an action runs for, which leads its idempotency key.
174
+ *
175
+ * Think replays a settled key for as long as it keeps the row
176
+ * (`actionLedgerRetention`), so a key without the task would replay, not post,
177
+ * the same comment in a later task. Every turn core runs carries a task id, so
178
+ * a missing one is a wiring fault — and keying without it would be the silent
179
+ * version of that fault.
180
+ */
181
+ export function actionTask(ctx) {
182
+ const task = ctx.agent.activeTurnMetadata?.taskId;
183
+ if (typeof task !== "string")
184
+ throw new Error("this turn carries no task id, so a GitHub write cannot be keyed to it — " +
185
+ "the repo plugin's actions run only in turns core started for a task");
186
+ return task;
187
+ }
188
+ /** A hex SHA-256, so a key names a body without carrying it. */
189
+ export async function sha256(text) {
190
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
191
+ return [...new Uint8Array(digest)]
192
+ .map((byte) => byte.toString(16).padStart(2, "0"))
193
+ .join("");
194
+ }
195
+ export function repoContext(config,
196
+ /**
197
+ * The running sub-agent's `runtime()`, read per command. Forwarded to every
198
+ * `exec`; see {@link RepoExec}'s `runtime` option.
199
+ */
200
+ runtime = () => undefined) {
166
201
  const workdir = config.workdir ?? DEFAULT_WORKDIR;
167
202
  const apiBase = config.apiBase ?? DEFAULT_API_BASE;
168
203
  const graphqlUrl = graphqlEndpoint(apiBase);
@@ -194,7 +229,7 @@ export function repoContext(config, runtime) {
194
229
  }
195
230
  };
196
231
  /**
197
- * The forge credential, or the reason there is none.
232
+ * The GitHub credential, or the reason there is none.
198
233
  *
199
234
  * `config.token` is the host's thunk, and an unresolvable secret throws at the
200
235
  * moment of use rather than at startup. Every caller here has a sentence to
@@ -208,7 +243,7 @@ export function repoContext(config, runtime) {
208
243
  }
209
244
  catch (err) {
210
245
  return {
211
- failure: `the forge credential could not be read: ${String(err)}. That is the ` +
246
+ failure: `the GitHub credential could not be read: ${String(err)}. That is the ` +
212
247
  `host's configuration rather than anything you passed — report it ` +
213
248
  `rather than working around it.`
214
249
  };
@@ -229,8 +264,8 @@ export function repoContext(config, runtime) {
229
264
  * of an investigation and findable only by timestamp.
230
265
  *
231
266
  * The token is scrubbed rather than trusted. It has no route into a container
232
- * command, and the one channel that could carry it is the forge API's error
233
- * body, which {@link forge} logs through this same function. So stderr *should*
267
+ * command, and the one channel that could carry it is the GitHub API's error
268
+ * body, which {@link github} logs through this same function. So stderr *should*
234
269
  * be clean — but "should be" is not the standard for something that writes a
235
270
  * credential into a log that outlives the request, and the scrub costs a
236
271
  * `split`/`join` on a path that already failed.
@@ -308,7 +343,7 @@ export function repoContext(config, runtime) {
308
343
  */
309
344
  const shell = (script, cwd, vars = {}) => run(script, () => ({
310
345
  cwd,
311
- runtime,
346
+ runtime: runtime(),
312
347
  env: { GIT_TERMINAL_PROMPT: "0", ...vars }
313
348
  }));
314
349
  const plain = (args, cwd, vars = {}) => shell(`git ${args}`, cwd, vars);
@@ -366,9 +401,9 @@ export function repoContext(config, runtime) {
366
401
  /** Push one branch to the same-named branch on the remote. Never forced. */
367
402
  const pushBranch = (dir, url, branch) => runGit(() => config.git.push({ url, dir, branch, allowedHosts }));
368
403
  /**
369
- * One call to the forge's API, from the Worker.
404
+ * One call to GitHub's API, from the Worker.
370
405
  *
371
- * Every tool here that talks to the forge goes through this, so the credential
406
+ * Every tool here that talks to GitHub goes through this, so the credential
372
407
  * stays on the Worker's side of the boundary and the bound, the header set and
373
408
  * the refusal to let an unreadable body replace an explanatory status are
374
409
  * written once.
@@ -377,14 +412,14 @@ export function repoContext(config, runtime) {
377
412
  * its own sentence to add: a POST whose answer never arrived may have been
378
413
  * received, which a GET has no reason to warn about.
379
414
  */
380
- const forge = (tool, path, init) => forgeAt(tool, `${apiBase}${path}`, init);
415
+ const github = (tool, path, init) => githubAt(tool, `${apiBase}${path}`, init);
381
416
  /**
382
417
  * The same call, against a URL rather than a path under {@link apiBase}.
383
418
  *
384
419
  * GraphQL needs it: on Enterprise the REST base carries `/api/v3` and GraphQL
385
420
  * does not live under it — see {@link graphqlEndpoint}.
386
421
  */
387
- const forgeAt = async (tool, url, init) => {
422
+ const githubAt = async (tool, url, init) => {
388
423
  const secret = credential();
389
424
  if ("failure" in secret) {
390
425
  logFailure(tool, { stderr: secret.failure });
@@ -401,7 +436,7 @@ export function repoContext(config, runtime) {
401
436
  "user-agent": "da-coder"
402
437
  },
403
438
  ...(init ? { body: JSON.stringify(init.body) } : {}),
404
- signal: AbortSignal.timeout(FORGE_TIMEOUT_MS)
439
+ signal: AbortSignal.timeout(GITHUB_TIMEOUT_MS)
405
440
  });
406
441
  }
407
442
  catch (err) {
@@ -419,7 +454,7 @@ export function repoContext(config, runtime) {
419
454
  return { ok: true, data: await response.json().catch(() => ({})) };
420
455
  };
421
456
  /**
422
- * One GraphQL query or mutation, over {@link forge} so the credential, the
457
+ * One GraphQL query or mutation, over {@link github} so the credential, the
423
458
  * bound, the headers and the scrubbed failure log stay written once.
424
459
  *
425
460
  * **A failed GraphQL query answers `200`.** The errors are in the body, so
@@ -434,8 +469,8 @@ export function repoContext(config, runtime) {
434
469
  * `repo_issue_view` reads, and a review *request* naming an app is invisible to
435
470
  * REST — see {@link file://./tools-review.ts REVIEW_REQUESTS_QUERY}.
436
471
  */
437
- const forgeGraphql = async (tool, query, variables) => {
438
- const answered = await forgeAt(tool, graphqlUrl, {
472
+ const githubGraphql = async (tool, query, variables) => {
473
+ const answered = await githubAt(tool, graphqlUrl, {
439
474
  method: "POST",
440
475
  body: { query, variables }
441
476
  });
@@ -455,7 +490,7 @@ export function repoContext(config, runtime) {
455
490
  return { ok: true, data: body.data };
456
491
  };
457
492
  /**
458
- * Which repository the tools that read the forge are talking about.
493
+ * Which repository the tools that read GitHub are talking about.
459
494
  *
460
495
  * The checkout's own origin, never a URL the model supplies. Three reasons, in
461
496
  * order of how much they matter: the origin has already been through the host
@@ -465,7 +500,7 @@ export function repoContext(config, runtime) {
465
500
  * "issue 42" almost always means the repository it is working in, so the extra
466
501
  * parameter would mostly be an extra way to be wrong.
467
502
  */
468
- const forgeRepo = async (dir) => {
503
+ const githubRepo = async (dir) => {
469
504
  const { remote, unreachable } = await origin(dir);
470
505
  if (unreachable)
471
506
  return { refusal: bounded(unreachable) };
@@ -491,15 +526,32 @@ export function repoContext(config, runtime) {
491
526
  * `repo_clone` only ever writes after the allowlist has passed.
492
527
  */
493
528
  const origin = async (dir) => {
494
- const result = await plain("remote get-url origin", dir);
495
- // Two different nothings. `repo_push`'s answer to "no origin here" is to
496
- // tell the model to clone the repository first — advice that is actively
497
- // wrong when the truth is that no command ran at all.
498
- if (result.unreachable)
499
- return { unreachable: result.stderr };
500
- if (!result.success)
501
- return {};
502
- const url = result.stdout.trim();
529
+ let url;
530
+ if (config.git.origin) {
531
+ let read;
532
+ try {
533
+ read = await config.git.origin(dir);
534
+ }
535
+ catch (err) {
536
+ return {
537
+ unreachable: `the workspace could not be reached to read ${dir}'s origin: ${String(err)}`
538
+ };
539
+ }
540
+ if (!read)
541
+ return {};
542
+ url = read.trim();
543
+ }
544
+ else {
545
+ const result = await plain("remote get-url origin", dir);
546
+ // Two different nothings. `repo_push`'s answer to "no origin here" is to
547
+ // tell the model to clone the repository first — advice that is actively
548
+ // wrong when the truth is that no command ran at all.
549
+ if (result.unreachable)
550
+ return { unreachable: result.stderr };
551
+ if (!result.success)
552
+ return {};
553
+ url = result.stdout.trim();
554
+ }
503
555
  const location = repoLocation(url);
504
556
  if (!location || !allowedHosts.includes(location.host))
505
557
  return {};
@@ -523,8 +575,8 @@ export function repoContext(config, runtime) {
523
575
  fetchOrigin,
524
576
  pushBranch,
525
577
  origin,
526
- forge,
527
- forgeGraphql,
528
- forgeRepo
578
+ github,
579
+ githubGraphql,
580
+ githubRepo
529
581
  };
530
582
  }
@@ -1,4 +1,3 @@
1
- import type { ToolSet } from "ai";
2
1
  import type { AgentPlugin } from "@dynamicagents/core";
3
2
  /**
4
3
  * The URL parsing, re-exported from the one entry point.
@@ -13,8 +12,8 @@ export { graphqlEndpoint, truncateOutput } from "./context.js";
13
12
  * `@dynamicagents/plugins/repo` — clone, commit, push, open a pull request.
14
13
  *
15
14
  * Layered over a container rather than owning one: it needs a shell with `git`
16
- * on it, and `@dynamicagents/plugins/computer` provides exactly that through
17
- * `computerExec`. Passing `exec` in rather than importing that plugin keeps the
15
+ * on it, and `@dynamicagents/plugins/workspace` provides exactly that through
16
+ * `workspaceExec`. Passing `exec` in rather than importing that plugin keeps the
18
17
  * two independent — a host with its own container can use this against that
19
18
  * instead, and the tests here need no container at all.
20
19
  *
@@ -27,7 +26,7 @@ export { graphqlEndpoint, truncateOutput } from "./context.js";
27
26
  * none of it needs to authenticate.
28
27
  *
29
28
  * That split is the one thing to preserve when changing this file: if an
30
- * operation talks to the forge it does not belong on `exec`, and if it does not,
29
+ * operation talks to GitHub it does not belong on `exec`, and if it does not,
31
30
  * it has no business anywhere else.
32
31
  *
33
32
  * It is absolute rather than careful because a narrower version does not hold.
@@ -41,9 +40,9 @@ export { graphqlEndpoint, truncateOutput } from "./context.js";
41
40
  * the length of one command is readable at `/proc/<pid>/environ` by anything
42
41
  * else in the container.
43
42
  *
44
- * Two further rules, which are about the forge rather than the container:
43
+ * Two further rules, which are about GitHub rather than the container:
45
44
  *
46
- * 1. **The forge is an allowlist, checked before anything runs.** A clone URL is
45
+ * 1. **GitHub's hosts are an allowlist, checked before anything runs.** A clone URL is
47
46
  * model input — a repository README, an issue body, or a page fetched by a
48
47
  * co-installed browser plugin is enough to choose it — and a credential
49
48
  * offered to a host of the model's choosing is the whole game. So the URL's
@@ -52,7 +51,7 @@ export { graphqlEndpoint, truncateOutput } from "./context.js";
52
51
  * check to the moment the token would actually be handed over. `origin` is
53
52
  * re-derived and re-checked on every push rather than remembered, because the
54
53
  * checkout's `.git/config` is a file the container can rewrite.
55
- * 2. **The forge API is called from the Worker**, so the credential that can
54
+ * 2. **The GitHub API is called from the Worker**, so the credential that can
56
55
  * write to a repository through the API never crosses the boundary either.
57
56
  * Every tool that reads or writes through it resolves the repository from the
58
57
  * checkout's own origin rather than from a parameter, so there is no way to
@@ -67,23 +66,20 @@ export { graphqlEndpoint, truncateOutput } from "./context.js";
67
66
  * repository. Whatever the token can reach, an agent talked into naming it can
68
67
  * reach. See the README — the token wants to be fine-grained.
69
68
  */
70
- /** This plugin's tool-family name, as a recipe's `toolFamilies` lists it. */
71
- export declare const REPO_FAMILY = "repo";
72
- /** Run one command in the host's container. Matches `computerExec`'s shape. */
69
+ /** Run one command in the host's container. Matches `workspaceExec`'s shape. */
73
70
  export type RepoExec = (command: string, options?: {
74
71
  cwd?: string;
75
72
  env?: Record<string, string | undefined>;
76
73
  timeout?: number;
77
74
  /**
78
- * The executing subtask's runtime state, forwarded **opaquely**.
75
+ * The running sub-agent's `runtime()`, forwarded **opaquely**.
79
76
  *
80
- * This plugin never looks inside it. A delegated subtask has to reach the
81
- * container its parent prepared, and the key for that lives in the runtime
82
- * state the parent resolved — but decoding it is the container plugin's
77
+ * This plugin never looks inside it. A sub-agent has to reach the
78
+ * container its parent prepared, and the key for that lives in what the
79
+ * parent's `prepare` returned — but decoding it is the container plugin's
83
80
  * business, not git's. Passing it through untouched is what lets the two
84
- * plugins compose
85
- * without either importing the other, which is the whole reason `exec` is
86
- * injected rather than imported.
81
+ * plugins compose without either importing the other, which is the whole
82
+ * reason `exec` is injected rather than imported.
87
83
  */
88
84
  runtime?: unknown;
89
85
  }) => Promise<{
@@ -93,7 +89,7 @@ export type RepoExec = (command: string, options?: {
93
89
  exitCode: number;
94
90
  }>;
95
91
  /**
96
- * The three git operations that need the forge token, run by the host.
92
+ * The three git operations that need the GitHub token, run by the host.
97
93
  *
98
94
  * Injected for the same reason {@link RepoExec} is — this plugin owns the
99
95
  * policy, not the plumbing — but the split between the two is not arbitrary. It
@@ -102,7 +98,7 @@ export type RepoExec = (command: string, options?: {
102
98
  * keeps its secret and never touches the container at all.
103
99
  *
104
100
  * That is why `clone`, `fetch` and `push` are the whole interface. They are
105
- * exactly the operations that talk to the forge. `status`, `diff`, `add`,
101
+ * exactly the operations that talk to GitHub. `status`, `diff`, `add`,
106
102
  * `commit` and `checkout` are local, need nothing to authenticate with, and stay
107
103
  * on `exec` where they are cheap and where the model can see them work.
108
104
  *
@@ -155,6 +151,14 @@ export interface RepoGit {
155
151
  branch: string;
156
152
  allowedHosts: string[];
157
153
  }): Promise<RepoGitResult>;
154
+ /**
155
+ * The checkout's `origin` URL, read on the host's side; `undefined` for none.
156
+ *
157
+ * Optional. Set, the GitHub tools resolve their repository without the
158
+ * container, so an agent polling a pull request does not keep one running.
159
+ * Unset, the origin is read through {@link RepoConfig.exec}.
160
+ */
161
+ origin?(dir: string): Promise<string | undefined>;
158
162
  }
159
163
  /** What a {@link RepoGit} operation reports. */
160
164
  export type RepoGitResult = {
@@ -169,14 +173,14 @@ export interface RepoConfig {
169
173
  /**
170
174
  * Runs commands in the container holding the checkout.
171
175
  *
172
- * Uncredentialed, always. Nothing this runs is ever given the forge token —
176
+ * Uncredentialed, always. Nothing this runs is ever given the GitHub token —
173
177
  * see {@link RepoGit} for the operations that need one.
174
178
  */
175
179
  exec: RepoExec;
176
180
  /** Runs the three credentialed operations, on the host's side of the boundary. */
177
181
  git: RepoGit;
178
182
  /**
179
- * A forge token with contents+pull-request write. A thunk so a rotated secret
183
+ * A GitHub token with contents+pull-request write. A thunk so a rotated secret
180
184
  * is picked up without rebuilding the plugin list.
181
185
  */
182
186
  token: () => string;
@@ -205,9 +209,9 @@ export interface RepoConfig {
205
209
  * 16,000 non-ASCII characters is three times that many UTF-8 bytes.
206
210
  *
207
211
  * `repo_diff` is the main input for an agent that
208
- * reviews rather than writes — a delegating coder whose subagents hold the
209
- * shell — and an unbounded diff on a large change is precisely the context
210
- * blowup that design exists to prevent.
212
+ * reviews rather than writes — a coder whose sub-agents hold the shell — and
213
+ * an unbounded diff on a large change is precisely the context blowup that
214
+ * design exists to prevent.
211
215
  */
212
216
  maxOutputChars?: number;
213
217
  /**
@@ -228,7 +232,7 @@ export interface RepoConfig {
228
232
  * not — a host that stored the path alongside its install state found that a
229
233
  * repository the resolver had nothing to install in (no `package.json`) cloned
230
234
  * perfectly, reported itself correctly, and could never be worked in, because
231
- * the one path its subagents needed was written on a branch that never ran.
235
+ * the one path its sub-agents needed was written on a branch that never ran.
232
236
  *
233
237
  * Awaited, so it can record intent durably, but it must **return quickly**:
234
238
  * it runs inside `repo_clone`, which runs inside a model turn. A host that
@@ -265,6 +269,50 @@ export interface RepoConfig {
265
269
  owner: string;
266
270
  repo: string;
267
271
  }) => void;
272
+ /**
273
+ * The worktrees a host keeps for its sub-agents' work, and the way between
274
+ * them.
275
+ *
276
+ * Set, the plugin offers `repo_worktrees` and `repo_worktree`. A switch moves
277
+ * the installing agent's own tools, so a host sets this on the parent's
278
+ * install and never on a sub-agent's. See {@link RepoWorktrees}.
279
+ */
280
+ worktrees?: RepoWorktrees;
281
+ /**
282
+ * The tools this install offers, and with them the lines of context that
283
+ * describe them. Unset, every tool. A selection rather than core's
284
+ * `restrictTools`, which drops a plugin's context whole: an agent that
285
+ * coordinates others' work reads about the tools it has and no others.
286
+ */
287
+ tools?: readonly RepoToolName[];
288
+ }
289
+ /** Every tool and action this plugin can offer, by the name the model sees. */
290
+ export declare const REPO_TOOL_NAMES: readonly ["repo_clone", "repo_fetch", "repo_status", "repo_diff", "repo_commit", "repo_push", "repo_open_pr", "repo_issue_view", "repo_pr_view", "repo_pr_comment", "repo_pr_review_status", "repo_pr_threads", "repo_pr_thread_reply", "repo_pr_checks", "repo_worktrees", "repo_worktree"];
291
+ export type RepoToolName = (typeof REPO_TOOL_NAMES)[number];
292
+ /**
293
+ * Checkouts a host keeps apart from the main one — each holding a branch its
294
+ * writing sub-agents committed on — and the switch that points every repo tool
295
+ * at one of them.
296
+ *
297
+ * The host renders every answer: which worktrees exist, where their checkouts
298
+ * sit and what is safe to do in them are its facts, and a sentence is what the
299
+ * model reads.
300
+ */
301
+ export interface RepoWorktrees {
302
+ /** Every worktree, with its branch and state. */
303
+ list(): Promise<string>;
304
+ /**
305
+ * Point every repo tool — and whatever else the host routes the same way — at
306
+ * the worktree holding `branch`, or back at the main checkout when it is absent.
307
+ * Answers what the model now has in front of it.
308
+ *
309
+ * The switch holds until the next one. This answer is what the model
310
+ * remembers, so a host that moves the tools on its own says so where the
311
+ * model reads it every turn.
312
+ */
313
+ use(branch?: string): Promise<string>;
314
+ /** Give up `branch`'s worktree, and the unpushed work in it. */
315
+ release(branch: string): Promise<string>;
268
316
  }
269
317
  /** What {@link RepoConfig.afterCheckout} is told about a checkout. */
270
318
  export interface RepoCheckout {
@@ -294,9 +342,6 @@ export interface RepoCheckout {
294
342
  /** True for a first clone, false when an existing tree was refreshed. */
295
343
  fresh: boolean;
296
344
  }
297
- export declare function buildRepoTools(config: RepoConfig,
298
- /** Forwarded to every `exec`; see {@link RepoExec}'s `runtime` option. */
299
- runtime?: unknown): ToolSet;
300
345
  /**
301
346
  * No tool here is held for a person's approval — see this plugin's README for
302
347
  * why, and for what a fork that wants one does instead.