@dynamicagents/plugins 0.11.0 → 0.12.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 (95) hide show
  1. package/README.md +12 -13
  2. package/dist/arc-agi/analysis.d.ts +16 -30
  3. package/dist/arc-agi/analysis.js +20 -47
  4. package/dist/arc-agi/client.d.ts +3 -2
  5. package/dist/arc-agi/game-tools.d.ts +5 -6
  6. package/dist/arc-agi/index.d.ts +6 -8
  7. package/dist/arc-agi/index.js +3 -3
  8. package/dist/arc-agi/main-agent.d.ts +5 -8
  9. package/dist/arc-agi/main-agent.js +5 -8
  10. package/dist/arc-agi/recipe.d.ts +17 -20
  11. package/dist/arc-agi/recipe.js +17 -20
  12. package/dist/arc-agi/soul.d.ts +16 -27
  13. package/dist/arc-agi/soul.js +16 -27
  14. package/dist/arc-agi/store.d.ts +4 -5
  15. package/dist/arc-agi/store.js +4 -5
  16. package/dist/arc-agi/tools.d.ts +1 -1
  17. package/dist/arc-agi/tools.js +26 -19
  18. package/dist/arc-agi/types.d.ts +6 -7
  19. package/dist/claude-code/config.d.ts +88 -11
  20. package/dist/claude-code/config.js +11 -2
  21. package/dist/claude-code/credentials.d.ts +21 -9
  22. package/dist/claude-code/credentials.js +43 -9
  23. package/dist/claude-code/egress.d.ts +7 -13
  24. package/dist/claude-code/egress.js +9 -10
  25. package/dist/claude-code/events.d.ts +47 -0
  26. package/dist/claude-code/events.js +96 -10
  27. package/dist/claude-code/index.d.ts +31 -13
  28. package/dist/claude-code/index.js +62 -15
  29. package/dist/claude-code/run.d.ts +135 -41
  30. package/dist/claude-code/run.js +366 -61
  31. package/dist/computer/advisory.d.ts +31 -18
  32. package/dist/computer/advisory.js +30 -5
  33. package/dist/computer/context.d.ts +23 -0
  34. package/dist/computer/context.js +225 -0
  35. package/dist/computer/file-lock.d.ts +5 -0
  36. package/dist/computer/file-lock.js +39 -0
  37. package/dist/computer/gate.d.ts +8 -8
  38. package/dist/computer/gate.js +7 -7
  39. package/dist/computer/host/ca-trust.d.ts +88 -0
  40. package/dist/computer/host/ca-trust.js +303 -0
  41. package/dist/computer/host/container-deps.d.ts +54 -0
  42. package/dist/computer/host/container-deps.js +224 -0
  43. package/dist/computer/host/container-fault.d.ts +38 -0
  44. package/dist/computer/host/container-fault.js +58 -0
  45. package/dist/computer/host/git-host.d.ts +62 -0
  46. package/dist/computer/host/git-host.js +156 -0
  47. package/dist/computer/host/git-identity.d.ts +57 -0
  48. package/dist/computer/host/git-identity.js +147 -0
  49. package/dist/computer/host/install-job.d.ts +121 -0
  50. package/dist/computer/host/install-job.js +766 -0
  51. package/dist/computer/host/sync.d.ts +111 -0
  52. package/dist/computer/host/sync.js +124 -0
  53. package/dist/computer/host/wake.d.ts +19 -0
  54. package/dist/computer/host/wake.js +10 -0
  55. package/dist/computer/host/workspace.d.ts +424 -0
  56. package/dist/computer/host/workspace.js +1258 -0
  57. package/dist/computer/index.d.ts +70 -50
  58. package/dist/computer/index.js +79 -767
  59. package/dist/computer/install.d.ts +8 -12
  60. package/dist/computer/install.js +6 -9
  61. package/dist/computer/paths.d.ts +21 -16
  62. package/dist/computer/paths.js +40 -38
  63. package/dist/computer/read.d.ts +18 -22
  64. package/dist/computer/read.js +24 -27
  65. package/dist/computer/render.d.ts +28 -0
  66. package/dist/computer/render.js +34 -1
  67. package/dist/computer/shell.d.ts +45 -0
  68. package/dist/computer/shell.js +80 -0
  69. package/dist/computer/tools-exec.d.ts +3 -0
  70. package/dist/computer/tools-exec.js +269 -0
  71. package/dist/computer/tools-file.d.ts +4 -0
  72. package/dist/computer/tools-file.js +124 -0
  73. package/dist/computer/tools-find.d.ts +3 -0
  74. package/dist/computer/tools-find.js +216 -0
  75. package/dist/recall/index.d.ts +9 -3
  76. package/dist/recall/index.js +22 -12
  77. package/dist/repo/checkout.d.ts +34 -4
  78. package/dist/repo/checkout.js +36 -1
  79. package/dist/repo/context.d.ts +109 -0
  80. package/dist/repo/context.js +530 -0
  81. package/dist/repo/index.d.ts +6 -29
  82. package/dist/repo/index.js +17 -1027
  83. package/dist/repo/tools-clone.d.ts +4 -0
  84. package/dist/repo/tools-clone.js +192 -0
  85. package/dist/repo/tools-forge.d.ts +4 -0
  86. package/dist/repo/tools-forge.js +179 -0
  87. package/dist/repo/tools-review.d.ts +3 -0
  88. package/dist/repo/tools-review.js +373 -0
  89. package/dist/repo/tools-worktree.d.ts +4 -0
  90. package/dist/repo/tools-worktree.js +221 -0
  91. package/dist/triage/index.d.ts +6 -1
  92. package/dist/triage/index.js +32 -23
  93. package/dist/workspace/index.d.ts +3 -3
  94. package/dist/workspace/index.js +3 -3
  95. package/package.json +21 -15
@@ -2,7 +2,7 @@ import { embedMany, tool } from "ai";
2
2
  import { z } from "zod";
3
3
  import { createWorkersAI } from "workers-ai-provider";
4
4
  import { definePlugin } from "@dynamicagents/core";
5
- import { parseTurn, sessionText } from "@dynamicagents/core/agent";
5
+ import { gatewayLogFields, parseTurn, sessionText } from "@dynamicagents/core/agent";
6
6
  /**
7
7
  * `@dynamicagents/plugins/recall` — episodic memory over Cloudflare Vectorize.
8
8
  *
@@ -14,11 +14,11 @@ import { parseTurn, sessionText } from "@dynamicagents/core/agent";
14
14
  * context window.
15
15
  *
16
16
  * Core carries **no embedding code at all**: no `Embed` type, no
17
- * `embeddingModelId`, no recall config. It used to, without a single caller, and
18
- * that had fixed the shape of embedding for every future plugin — one model, one
19
- * dimension, `string[] → number[][]` — while `ModelConfig` could only say "the
20
- * agent has an embedding model" when the true statement is "*this plugin* uses
21
- * this embedding model". So the whole embedding path lives here.
17
+ * `embeddingModelId`, no recall config. Putting any of it there fixes the shape
18
+ * of embedding for every future plugin — one model, one dimension,
19
+ * `string[] → number[][]` — and lets `ModelConfig` say only "the agent has an
20
+ * embedding model" when the true statement is "*this plugin* uses this embedding
21
+ * model". So the whole embedding path lives here.
22
22
  *
23
23
  * The namespace is always bound in code from the caller's verified identity,
24
24
  * never model input, so one caller can never read another's archive.
@@ -101,24 +101,34 @@ export async function recallSearch(index, namespace, query, embed, topK = DEFAUL
101
101
  });
102
102
  }
103
103
  export function recall(config) {
104
- const { ai, index, namespace, aiGatewayId, embeddingModelId = DEFAULT_EMBEDDING_MODEL_ID, topK = DEFAULT_TOP_K, metadataTextMax = DEFAULT_METADATA_TEXT_MAX } = config;
104
+ const { ai, index, namespace, aiGatewayId, agentName, embeddingModelId = DEFAULT_EMBEDDING_MODEL_ID, topK = DEFAULT_TOP_K, metadataTextMax = DEFAULT_METADATA_TEXT_MAX } = config;
105
105
  /**
106
106
  * Built on first use, never at module scope. Cloudflare evaluates module scope
107
107
  * during `wrangler deploy` to validate the new version, and bindings are not
108
108
  * populated at that point — constructing eagerly makes `createWorkersAI` throw
109
109
  * "you must provide either a binding or credentials".
110
+ *
111
+ * The gateway goes on the embedding model and never on the provider — see
112
+ * core's Workers AI runtime for why setting it on both discards the model's.
113
+ * Only `agent` and `phase` are known here: one call embeds a whole displaced
114
+ * range, which can span channels, or a query the model wrote.
110
115
  */
111
116
  let provider;
117
+ const embeddingSettings = aiGatewayId
118
+ ? {
119
+ gateway: {
120
+ id: aiGatewayId,
121
+ ...gatewayLogFields({ agent: agentName, phase: "embed" })
122
+ }
123
+ }
124
+ : {};
112
125
  const embed = config.embed ??
113
126
  (async (texts) => {
114
127
  if (texts.length === 0)
115
128
  return [];
116
- provider ??= createWorkersAI({
117
- binding: ai,
118
- ...(aiGatewayId ? { gateway: { id: aiGatewayId } } : {})
119
- });
129
+ provider ??= createWorkersAI({ binding: ai });
120
130
  const { embeddings } = await embedMany({
121
- model: provider.embedding(embeddingModelId),
131
+ model: provider.embedding(embeddingModelId, embeddingSettings),
122
132
  values: texts
123
133
  });
124
134
  return embeddings;
@@ -21,8 +21,8 @@ interface RefreshOutcome {
21
21
  /**
22
22
  * What this module needs back from a git command.
23
23
  *
24
- * A structural subset of `index.ts`'s `RunResult` rather than an import of it,
25
- * so the dependency runs one way: `index.ts` knows about this file, and this file
24
+ * A structural subset of `context.ts`'s `RunResult` rather than an import of it,
25
+ * so the dependency runs one way: the tools know about this file, and this file
26
26
  * knows only about the shape of an answer. `unreachable` is the field that earns
27
27
  * the type — a command that *ran and failed* has answered the question it was
28
28
  * asked, and one that never ran has answered nothing, which two refusals below
@@ -34,7 +34,7 @@ export interface GitAnswer {
34
34
  stderr: string;
35
35
  unreachable?: true;
36
36
  }
37
- /** The two runners `refreshCheckout` borrows from `repoSurface`. */
37
+ /** The two runners `refreshCheckout` borrows from `repoContext`. */
38
38
  export interface GitRunners {
39
39
  plain: (args: string, cwd: string, vars?: Record<string, string>) => Promise<GitAnswer>;
40
40
  /**
@@ -64,6 +64,32 @@ export declare function resolveDefaultBranch(plain: GitRunners["plain"], dir: st
64
64
  branch?: string;
65
65
  unreachable?: string;
66
66
  }>;
67
+ /**
68
+ * Write the identity this checkout's commits carry into its own config.
69
+ *
70
+ * **Repo-local, and repeated on every clone and every refresh.** The workspace
71
+ * outlives the deployment's configuration: written once, at the clone, it is
72
+ * frozen — change the configured name and every checkout that already exists
73
+ * goes on committing under the old one, with nothing in the tree saying why.
74
+ * Rewriting costs two commands against a checkout that is being set up anyway.
75
+ *
76
+ * It is the *nearest* answer, not the only one. `repo_commit` names the identity
77
+ * on the commit itself, which is the one layer a stale config cannot outrank.
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 the computer plugin, whose workspace writes one
81
+ * per container; see
82
+ * {@link file://../computer/host/git-identity.ts}. `exec` is an arbitrary
83
+ * runner, so that is a property of a deployment, not of this plugin.
84
+ *
85
+ * Unchecked, like the config pins at the clone: git's identity is needed by the
86
+ * first commit, and a failure to write it fails that commit with a sentence far
87
+ * clearer than anything this could report from here.
88
+ */
89
+ export declare function writeGitIdentity(plain: GitRunners["plain"], dir: string, author: {
90
+ name: string;
91
+ email: string;
92
+ }): Promise<void>;
67
93
  /**
68
94
  * Bring an existing checkout back to a clean, current state.
69
95
  *
@@ -78,9 +104,13 @@ export declare function resolveDefaultBranch(plain: GitRunners["plain"], dir: st
78
104
  * one outcome nobody could recover from. Refusing costs a round; discarding
79
105
  * costs the work.
80
106
  */
81
- export declare function refreshCheckout({ dir, url, branch, plain, fetchOrigin }: GitRunners & {
107
+ export declare function refreshCheckout({ dir, url, branch, author, plain, fetchOrigin }: GitRunners & {
82
108
  dir: string;
83
109
  url: string;
84
110
  branch: string | undefined;
111
+ author: {
112
+ name: string;
113
+ email: string;
114
+ };
85
115
  }): Promise<RefreshOutcome>;
86
116
  export {};
@@ -21,6 +21,36 @@ export async function resolveDefaultBranch(plain, dir) {
21
21
  ? { branch: head.stdout.trim().replace(/^origin\//, "") }
22
22
  : {};
23
23
  }
24
+ /**
25
+ * Write the identity this checkout's commits carry into its own config.
26
+ *
27
+ * **Repo-local, and repeated on every clone and every refresh.** The workspace
28
+ * outlives the deployment's configuration: written once, at the clone, it is
29
+ * frozen — change the configured name and every checkout that already exists
30
+ * goes on committing under the old one, with nothing in the tree saying why.
31
+ * Rewriting costs two commands against a checkout that is being set up anyway.
32
+ *
33
+ * It is the *nearest* answer, not the only one. `repo_commit` names the identity
34
+ * on the commit itself, which is the one layer a stale config cannot outrank.
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 the computer plugin, whose workspace writes one
38
+ * per container; see
39
+ * {@link file://../computer/host/git-identity.ts}. `exec` is an arbitrary
40
+ * runner, so that is a property of a deployment, not of this plugin.
41
+ *
42
+ * Unchecked, like the config pins at the clone: git's identity is needed by the
43
+ * first commit, and a failure to write it fails that commit with a sentence far
44
+ * clearer than anything this could report from here.
45
+ */
46
+ export async function writeGitIdentity(plain, dir, author) {
47
+ // Through the environment, so a configured name containing a quote stays a
48
+ // value rather than becoming shell.
49
+ await plain(`config user.name "$GIT_NAME"`, dir, { GIT_NAME: author.name });
50
+ await plain(`config user.email "$GIT_EMAIL"`, dir, {
51
+ GIT_EMAIL: author.email
52
+ });
53
+ }
24
54
  /**
25
55
  * Bring an existing checkout back to a clean, current state.
26
56
  *
@@ -35,7 +65,7 @@ export async function resolveDefaultBranch(plain, dir) {
35
65
  * one outcome nobody could recover from. Refusing costs a round; discarding
36
66
  * costs the work.
37
67
  */
38
- export async function refreshCheckout({ dir, url, branch, plain, fetchOrigin }) {
68
+ export async function refreshCheckout({ dir, url, branch, author, plain, fetchOrigin }) {
39
69
  const remote = await plain("remote get-url origin", dir);
40
70
  // Interrogated, for the same reason the `status` below is: a question that went
41
71
  // unanswered is not the answer "some other repository". Read as one, an
@@ -59,6 +89,11 @@ export async function refreshCheckout({ dir, url, branch, plain, fetchOrigin })
59
89
  `not ${url}. Pick a different directory or work with the checkout that is there.`
60
90
  };
61
91
  }
92
+ // Ahead of every refusal below, and deliberately: a refusal leaves the *tree*
93
+ // untouched, which is what those sentences promise. An identity is not tree
94
+ // state, and a dirty tree is the case where somebody is about to commit —
95
+ // exactly when a stale name would be spent.
96
+ await writeGitIdentity(plain, dir, author);
62
97
  // Interrogated, not assumed. Empty stdout from a `status` that *failed* is not
63
98
  // a clean tree, it is no answer at all — and the next two commands are `fetch`
64
99
  // and `reset --hard`. Conflating the two lets a permissions error or a
@@ -0,0 +1,109 @@
1
+ import type { RepoCheckout, RepoConfig, RepoExec, RepoGitResult } from "./index.js";
2
+ /**
3
+ * How many items one forge list call asks for. GitHub's maximum, and its default
4
+ * without this is 30.
5
+ *
6
+ * One page, never a Link-header walk: an unbounded fetch loop does not belong
7
+ * inside a model turn, and the render is bounded by `maxOutputChars` anyway. What
8
+ * matters is that a full page is *reported* as possibly partial rather than
9
+ * presented as the whole answer — a pull request's newest review comments are on
10
+ * the last page, and those are exactly the ones an agent was sent to act on.
11
+ */
12
+ export declare const FORGE_PAGE_SIZE = 100;
13
+ /**
14
+ * Where GraphQL lives, given where REST does.
15
+ *
16
+ * On github.com they share an origin and `/graphql` hangs off it. On GitHub
17
+ * Enterprise — which {@link RepoConfig.apiBase} exists for — REST is under
18
+ * `/api/v3` and GraphQL is its **sibling** at `/api/graphql`, not a path beneath
19
+ * it. Appending blindly yields `/api/v3/graphql`, which is a 404 on every
20
+ * Enterprise install and no failure at all on the public API, so the mistake
21
+ * survives every test written against github.com.
22
+ */
23
+ export declare function graphqlEndpoint(apiBase: string): string;
24
+ /**
25
+ * Middle-out truncation, so both the head of a diff and its tail survive.
26
+ *
27
+ * Deliberately a **copy** of the computer plugin's function of the same name,
28
+ * not an import. `npm run verify:exports` fails any subpath whose module graph
29
+ * reaches a sibling's directory, and its own advice for a shared helper is to
30
+ * duplicate it — installing `repo` must not drag `computer` (and therefore
31
+ * `@cloudflare/computer`) into a consumer's bundle. Fifteen lines is a cheaper
32
+ * price than that coupling, and it is the same reason `exec` is injected here
33
+ * rather than imported.
34
+ *
35
+ * The `half < 1` guard is not padding: without it a small `max` makes `half`
36
+ * zero or negative, and `slice(-0)` is `slice(0)` — the whole string — so the
37
+ * function would return *more* than it was given.
38
+ */
39
+ export declare function truncateOutput(text: string, max: number): string;
40
+ /**
41
+ * What one container command produced — plus one bit the container did not.
42
+ *
43
+ * `exec` does not only *return* failures, it throws them: `@cloudflare/computer`
44
+ * throws `EEXEC_LOST` when a container is replaced mid-command, and any call to a
45
+ * Durable Object can fail outright. Both are caught at the seam
46
+ * ({@link repoContext}'s `run`) and turned into a failed result, so every tool
47
+ * answers in its own `!success` branch — "could not read the status of /w/r: …"
48
+ * is a better sentence than any wrapper one level up could write, and a seventh
49
+ * tool calling `plain` gets it without being told.
50
+ *
51
+ * `unreachable` is for the branches that must tell the two apart. A command that
52
+ * ran and failed has answered the question it was asked; a command that never ran
53
+ * has answered nothing, and three places here read a failure as an answer.
54
+ */
55
+ export type RunResult = Awaited<ReturnType<RepoExec>> & {
56
+ unreachable?: true;
57
+ };
58
+ /** What {@link repoContext} hands each group of tools. */
59
+ export interface RepoContext {
60
+ config: RepoConfig;
61
+ workdir: string;
62
+ author: {
63
+ name: string;
64
+ email: string;
65
+ };
66
+ allowedHosts: string[];
67
+ bounded: (text: string) => string;
68
+ notifyCheckout: (checkout: RepoCheckout) => Promise<void>;
69
+ logFailure: (tool: string, detail: {
70
+ exitCode?: number;
71
+ stderr?: string;
72
+ stdout?: string;
73
+ }) => void;
74
+ plain: (args: string, cwd: string, vars?: Record<string, string>) => Promise<RunResult>;
75
+ runGit: (op: () => Promise<RepoGitResult>) => Promise<RunResult>;
76
+ fetchOrigin: (dir: string, url: string) => Promise<RunResult>;
77
+ pushBranch: (dir: string, url: string, branch: string) => Promise<RunResult>;
78
+ origin: (dir: string) => Promise<{
79
+ remote?: {
80
+ host: string;
81
+ url: string;
82
+ };
83
+ unreachable?: string;
84
+ }>;
85
+ forge: (tool: string, path: string, init?: {
86
+ method: string;
87
+ body: unknown;
88
+ }) => Promise<{
89
+ ok: true;
90
+ data: unknown;
91
+ } | {
92
+ ok: false;
93
+ message: string;
94
+ }>;
95
+ forgeGraphql: (tool: string, query: string, variables: Record<string, unknown>) => Promise<{
96
+ ok: true;
97
+ data: unknown;
98
+ } | {
99
+ ok: false;
100
+ message: string;
101
+ }>;
102
+ forgeRepo: (dir: string) => Promise<{
103
+ owner: string;
104
+ repo: string;
105
+ } | {
106
+ refusal: string;
107
+ }>;
108
+ }
109
+ export declare function repoContext(config: RepoConfig, runtime?: unknown): RepoContext;