@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,311 +0,0 @@
1
- import { generateText, Output, tool } from "ai";
2
- import { z } from "zod";
3
- import { createWorkersAI } from "workers-ai-provider";
4
- import { definePlugin } from "@dynamicagents/core";
5
- import { gatewayLogFields, parseTurn, sessionText } from "@dynamicagents/core/agent";
6
- /**
7
- * `@dynamicagents/plugins/triage` — decide whether an inbound message deserves a
8
- * reply at all, *before* the tool loop runs.
9
- *
10
- * An agent in a shared channel sees every message, and most are not for it. Left
11
- * to the main loop, that judgement is made by a model simultaneously trying to
12
- * be helpful, with memory, history and half a dozen tools in view — and it
13
- * degrades exactly there, and degrades **invisibly**: failing to call a
14
- * decline-tool looks identical to deciding not to.
15
- *
16
- * Triage moves the decision somewhere it cannot be skipped. A structured-output
17
- * call has no "just answer instead" escape hatch — the schema must be filled —
18
- * so the question is always actually asked.
19
- *
20
- * Two moments, and both are needed. The gate here judges the *message*; the
21
- * `no_reply` tool below lets the agent decline *late*, after looking something
22
- * up and concluding there was nothing worth adding. Wiring `no_reply` into a
23
- * loop is the loop's business (see {@link noReplyTool}); this module ships the
24
- * pieces rather than the loop.
25
- */
26
- /** Small, fast classifier. High-volume and narrow — not the chat model. */
27
- const DEFAULT_TRIAGE_MODEL_ID = "@cf/qwen/qwen3-30b-a3b-fp8";
28
- /** Trailing history the classifier sees, including the message being judged. */
29
- const DEFAULT_HISTORY_MESSAGES = 12;
30
- /** Per-message char cap in the prompt — keeps the classify call small. */
31
- const DEFAULT_MESSAGE_MAX_CHARS = 500;
32
- /**
33
- * The classifier's output. The booleans are **not** metadata for the caller —
34
- * only `should_reply` is ever read. They exist to shape the generation itself:
35
- * constrained decoding fills properties in schema order, so declaring them ahead
36
- * of `should_reply` forces the model to commit to the sub-judgements that
37
- * determine the answer — *who is this for*, and *do I have something worth
38
- * adding* — before it can name a verdict. Reordering these fields, or dropping
39
- * one, changes what the model reasons about; it is not cosmetic.
40
- *
41
- * `reason` comes last, after the verdict, on purpose: generated afterwards it
42
- * justifies the decision rather than steering it, so a logged reason can never
43
- * contradict the `should_reply` it explains. That is what makes it worth reading
44
- * when a misclassification needs debugging.
45
- */
46
- export const triageSchema = z.object({
47
- for_other_people: z
48
- .boolean()
49
- .describe("Is this message aimed at other people in the channel rather than at you?"),
50
- for_another_agent: z
51
- .boolean()
52
- .describe("Is this message aimed at a different bot or agent, not at you?"),
53
- can_contribute: z
54
- .boolean()
55
- .describe("Is there something this conversation clearly needs right now that you can add?"),
56
- should_reply: z
57
- .boolean()
58
- .describe("Given all of the above, should you reply at all?"),
59
- reason: z.string().describe("One short sentence justifying the verdict.")
60
- });
61
- /**
62
- * The classifier's instructions. Aimed squarely at the failure this exists to
63
- * fix: the agent joins a conversation legitimately, then keeps answering
64
- * afterwards as unrelated messages go by. Every rule here is about *audience*
65
- * and *value* — the hard, per-message calls — rather than about obeying
66
- * instructions, which was never the part that broke.
67
- */
68
- const TRIAGE_RULES = [
69
- "You are the assistant in a shared chat channel, and you see every message — including the many that are not for you.",
70
- "Decide one thing only: should you reply to the LAST message?",
71
- "",
72
- "Rules:",
73
- "- People talking to each other, chatter, and acknowledgements need no reply from you. In a busy channel this is the common case.",
74
- "- Having replied recently does not entitle you to reply now. Judge the last message on its own audience; being part of the conversation a moment ago is not a licence to answer whatever follows.",
75
- "- Reply when you are directly asked something, addressed by name or @mention, or when the previous message was your own and this one answers or follows up on it.",
76
- "- `can_contribute` is a high bar: something the conversation clearly needs, not merely something true you could say. Restating, agreeing, praising, or acknowledging is not contributing.",
77
- "- These rules apply in every language, including when the channel is not in English.",
78
- "- When you are genuinely unsure, reply. A wrong silence is invisible to the person who needed you; a wrong reply is only noise."
79
- ].join("\n");
80
- /** Trim a message body to the triage char cap, marking it when cut. */
81
- function truncate(text, max) {
82
- const flat = text.trim();
83
- return flat.length > max ? `${flat.slice(0, max)}…` : flat;
84
- }
85
- /**
86
- * Render one stored message as a transcript line. User turns carry the gatekeeper's
87
- * `<turn from="…">` provenance, so the speaker's name is surfaced rather than a
88
- * flat "user" — without it the classifier cannot tell one participant from
89
- * another, which is most of what it has to reason about. Assistant turns are
90
- * labelled "you" so "the previous message was your own" is legible.
91
- */
92
- function transcriptLine(message, max) {
93
- const text = sessionText(message);
94
- if (message.role === "assistant")
95
- return `you: ${truncate(text, max)}`;
96
- const turn = parseTurn(text);
97
- const speaker = turn ? turn.from : "someone";
98
- return `${speaker}: ${truncate(turn ? turn.body : text, max)}`;
99
- }
100
- /**
101
- * Build the classifier prompt from the tail of the conversation.
102
- *
103
- * A bare message is frequently unclassifiable — "yes", "thanks", "and the second
104
- * one?" only resolve against what came before — and the drift being fixed is
105
- * *only* visible in context, where the classifier can see the agent has been
106
- * talking and that the speaker has since turned to someone else.
107
- *
108
- * Exported for its own tests: it is pure, so the prompt can be asserted without
109
- * an LLM.
110
- */
111
- export function buildTriagePrompt(history, options = {}) {
112
- const { historyMessages = DEFAULT_HISTORY_MESSAGES, messageMaxChars = DEFAULT_MESSAGE_MAX_CHARS } = options;
113
- const recent = history
114
- .filter((m) => m.role === "user" || m.role === "assistant")
115
- .slice(-historyMessages);
116
- const transcript = recent
117
- .map((m) => transcriptLine(m, messageMaxChars))
118
- .join("\n");
119
- return [
120
- TRIAGE_RULES,
121
- "",
122
- "Recent messages, oldest first. The last line is the message you are judging:",
123
- transcript
124
- ].join("\n");
125
- }
126
- /**
127
- * Classify whether the agent should reply to the newest message in `history`.
128
- *
129
- * **Fails open.** Every error is caught here and answered `true`, because the
130
- * two mistakes are not symmetric: a wrong reply is noise the user can see and
131
- * ignore, while a wrong silence is invisible — the person who needed the agent
132
- * simply never hears back and has no way to tell that anything happened. A
133
- * triage outage must therefore degrade to the previous behaviour (let the loop
134
- * decide), never to a silent agent.
135
- *
136
- * Only `should_reply` is read; the other fields exist to steer the generation
137
- * (see {@link triageSchema}) and are logged, not re-derived in code.
138
- */
139
- export async function shouldReply(model, history, options = {}) {
140
- try {
141
- // `generateText` + `Output.object` rather than `generateObject`, which is
142
- // deprecated in AI SDK 7. Same effect: the provider is sent a json_schema
143
- // response format, so the model cannot answer with anything but the schema.
144
- const { output } = await generateText({
145
- model,
146
- output: Output.object({ schema: triageSchema }),
147
- prompt: buildTriagePrompt(history, options)
148
- });
149
- // Log the whole verdict, not just the outcome. The booleans are the
150
- // reasoning — a correct verdict reached through wrong booleans means the
151
- // decomposition is not carrying the decision, and will drift again on
152
- // untested cases.
153
- console.debug("[triage] verdict", output);
154
- return output.should_reply;
155
- }
156
- catch (err) {
157
- console.warn("[triage] classify failed, replying by default", {
158
- error: String(err)
159
- });
160
- return true;
161
- }
162
- }
163
- export function triage(config) {
164
- const { ai, aiGatewayId, agentName, modelId = DEFAULT_TRIAGE_MODEL_ID, historyMessages, messageMaxChars } = config;
165
- /**
166
- * Built on first use, never at module scope, for the reason
167
- * {@link file://../recall/index.ts} gives on its own provider — and the
168
- * gateway goes on the model, never on the provider, for the reason it gives
169
- * there too.
170
- *
171
- * The provider is what is memoized, not the model: the model carries the
172
- * channel of the turn it judges, which differs turn to turn.
173
- *
174
- * No fallback model: triage fails open, so a second attempt on a different
175
- * model buys nothing a `true` does not already buy, at twice the latency in
176
- * front of every turn.
177
- */
178
- let provider;
179
- const classifier = (history) => {
180
- if (config.model)
181
- return config.model;
182
- provider ??= createWorkersAI({ binding: ai });
183
- if (!aiGatewayId)
184
- return provider(modelId);
185
- const judged = history.filter((m) => m.role === "user").at(-1);
186
- const fields = gatewayLogFields({
187
- agent: agentName,
188
- phase: "triage",
189
- channel: judged ? parseTurn(sessionText(judged))?.channel : undefined
190
- });
191
- return provider(modelId, { gateway: { id: aiGatewayId, ...fields } });
192
- };
193
- return definePlugin({
194
- key: "triage",
195
- /**
196
- * Building the classifier is inside the guarantee, not in front of it.
197
- *
198
- * `shouldReply` catches its own failures, but only what happens after it is
199
- * entered — and `createWorkersAI` throws synchronously when the binding is
200
- * missing, before the call is ever made. A throw out of this hook is the one
201
- * outcome the whole design forbids: a triage outage must degrade to *running
202
- * the turn*, never to a silent agent. A host that also treats a rejected gate
203
- * as `true` is a second layer, not a substitute — a plugin documenting
204
- * fail-open cannot rely on its host to make that true.
205
- */
206
- shouldHandleTurn: async ({ history }) => {
207
- let model;
208
- try {
209
- model = classifier(history);
210
- }
211
- catch (error) {
212
- console.warn("[triage] classifier unavailable, replying by default", {
213
- error: String(error)
214
- });
215
- return true;
216
- }
217
- return shouldReply(model, history, { historyMessages, messageMaxChars });
218
- }
219
- // No `requires`. `AI` is core's own mandatory binding, not this plugin's to
220
- // demand — declaring it would report a core misconfiguration under this
221
- // plugin's name.
222
- });
223
- }
224
- // --- declining late: the `no_reply` tool ------------------------------------
225
- /** The tool the model calls to end a turn without replying. */
226
- export const NO_REPLY_TOOL_NAME = "no_reply";
227
- /**
228
- * Tool result when a `no_reply` call is ignored. This is only ever read by the
229
- * model in one case: it has already sent something this turn (so the tool was
230
- * withdrawn) yet named it anyway — the call is ignored and it must finish with a
231
- * reply. When a `no_reply` call is instead honoured it ends the turn, and this
232
- * result is discarded unread.
233
- */
234
- export const NO_REPLY_IGNORED = "Ignored: you have already sent a message this turn — finish with a reply.";
235
- /**
236
- * End the turn without replying — the *late* counterpart to the pre-turn gate.
237
- * The agent looks something up, concludes there is nothing worth adding, and
238
- * declines.
239
- *
240
- * Deliberately an ordinary tool with an ordinary no-op `execute`: the loop, not
241
- * the tool, decides what a `no_reply` call means (see {@link isNoReplyTurn}).
242
- * Two things here are load-bearing:
243
- *
244
- * - **`execute` must exist.** Once the agent has spoken the tool is withdrawn,
245
- * but the model can still name it (the SDK resolves calls against the
246
- * unfiltered map); that call is ignored and the loop must continue to a real
247
- * reply. `generateText` only continues while every tool call in a step
248
- * produced an output, so without `execute` that step would halt with no reply
249
- * text and surface as a failure. Executing normally keeps the counts equal.
250
- * - **`reason` must stay optional.** A required field the model omits makes the
251
- * SDK mark the call invalid and skip execution, halting the loop the same way.
252
- *
253
- * Wiring is the loop's: offer this tool until the agent has spoken, withdraw it
254
- * after, and stop the run when {@link isNoReplyTurn} holds.
255
- */
256
- export const noReplyTool = tool({
257
- description: "End this turn without replying at all, when the message needs no answer from you (chatter, people talking to each other, anything not addressed to you) — or when you looked into it and there is genuinely nothing worth adding. Calling this ends the turn immediately, so do not pair it with a tool whose result you still need. Only available until you have sent anything this turn.",
258
- inputSchema: z.object({
259
- reason: z
260
- .string()
261
- .optional()
262
- .describe("One short phrase: why this message needs no reply.")
263
- }),
264
- execute: async () => NO_REPLY_IGNORED
265
- });
266
- /**
267
- * What the agent is told about {@link noReplyTool}, as a system-prompt suffix.
268
- *
269
- * It lives here rather than in a host's prompt file for the same reason a subtask
270
- * type carries its own `capability`: everything the model is told about a
271
- * capability is declared by whatever owns that capability, so uninstalling the
272
- * plugin takes its advice with it. A host describing a `no_reply` tool it no
273
- * longer installs is the exact drift those fields exist to end.
274
- *
275
- * Not a `capability` on the plugin, though, and the distinction is the design.
276
- * `capability` is rendered into the soul once and is true for the whole
277
- * conversation; this has to be **withdrawn mid-turn**, the moment the agent
278
- * speaks, in the same `prepareStep` that withdraws the tool. Advice for a tool
279
- * that is no longer on the call is worse than no advice — so the loop injects it,
280
- * and the loop is what knows when.
281
- */
282
- export const NO_REPLY_GUIDANCE = [
283
- "",
284
- "",
285
- "You see every message in this channel, including the many that are not for you.",
286
- "Call the `no_reply` tool to end your turn without replying whenever the message needs no answer from you: people talking to each other, chatter, acknowledgements, or anything you were not asked about. You may also look something up first and then call `no_reply` if, having looked, there is genuinely nothing worth adding.",
287
- "Staying silent is the right default in a busy channel. Reply only when you are addressed, asked a question, or can add something the conversation clearly needs.",
288
- "Calling `no_reply` ends your turn at once — don't pair it with a tool whose result you still need, and don't write any text with it (that text is discarded). It is unavailable once you have already sent something this turn."
289
- ].join("\n");
290
- /** Whether a step calls `no_reply` at all — on its own or beside other tools. */
291
- export function stepCallsNoReply(step) {
292
- return step.toolCalls.some((c) => c.toolName === NO_REPLY_TOOL_NAME);
293
- }
294
- /**
295
- * Whether a run declined to reply: the agent has **not sent anything this turn**
296
- * (`!repliedAny`) and its final step called `no_reply`. A `no_reply` call ends
297
- * the turn the moment it appears — even beside another tool, which still runs
298
- * but whose result is discarded — so the first step to call it is the last. A
299
- * `no_reply` after the agent already streamed content is the one thing ignored.
300
- *
301
- * `!repliedAny` is the runtime guard that lets `no_reply` be a *late* decision
302
- * while still forbidding it once the agent has spoken. Hiding the tool from the
303
- * model merely hides it; the SDK still resolves a tool call against the full,
304
- * unfiltered map, so a model that names `no_reply` after speaking executes it
305
- * happily — this flag is what stops that from discarding a turn the user has
306
- * already seen.
307
- */
308
- export function isNoReplyTurn(repliedAny, steps) {
309
- const last = steps.at(-1);
310
- return !repliedAny && last !== undefined && stepCallsNoReply(last);
311
- }
File without changes
File without changes
File without changes