macro-agent 0.0.14 → 0.0.16

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 (154) hide show
  1. package/.claude/settings.local.json +59 -0
  2. package/dist/acp/index.d.ts +1 -1
  3. package/dist/acp/index.d.ts.map +1 -1
  4. package/dist/acp/index.js.map +1 -1
  5. package/dist/acp/macro-agent.d.ts +21 -0
  6. package/dist/acp/macro-agent.d.ts.map +1 -1
  7. package/dist/acp/macro-agent.js +182 -0
  8. package/dist/acp/macro-agent.js.map +1 -1
  9. package/dist/acp/types.d.ts +31 -2
  10. package/dist/acp/types.d.ts.map +1 -1
  11. package/dist/acp/types.js.map +1 -1
  12. package/dist/agent/agent-manager.d.ts.map +1 -1
  13. package/dist/agent/agent-manager.js +10 -4
  14. package/dist/agent/agent-manager.js.map +1 -1
  15. package/dist/cli/acp.d.ts +6 -0
  16. package/dist/cli/acp.d.ts.map +1 -1
  17. package/dist/cli/acp.js +16 -2
  18. package/dist/cli/acp.js.map +1 -1
  19. package/dist/map/adapter/acp-over-map.d.ts +5 -0
  20. package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
  21. package/dist/map/adapter/acp-over-map.js +47 -4
  22. package/dist/map/adapter/acp-over-map.js.map +1 -1
  23. package/dist/map/utils/address-translation.d.ts +99 -0
  24. package/dist/map/utils/address-translation.d.ts.map +1 -0
  25. package/dist/map/utils/address-translation.js +285 -0
  26. package/dist/map/utils/address-translation.js.map +1 -0
  27. package/dist/map/utils/index.d.ts +7 -0
  28. package/dist/map/utils/index.d.ts.map +1 -0
  29. package/dist/map/utils/index.js +7 -0
  30. package/dist/map/utils/index.js.map +1 -0
  31. package/dist/store/event-store.js +9 -2
  32. package/dist/store/event-store.js.map +1 -1
  33. package/dist/store/types/agents.d.ts +2 -0
  34. package/dist/store/types/agents.d.ts.map +1 -1
  35. package/package.json +4 -4
  36. package/references/acp-factory-ref/CHANGELOG.md +33 -0
  37. package/references/acp-factory-ref/LICENSE +21 -0
  38. package/references/acp-factory-ref/README.md +341 -0
  39. package/references/acp-factory-ref/package-lock.json +3102 -0
  40. package/references/acp-factory-ref/package.json +96 -0
  41. package/references/acp-factory-ref/python/CHANGELOG.md +33 -0
  42. package/references/acp-factory-ref/python/LICENSE +21 -0
  43. package/references/acp-factory-ref/python/Makefile +57 -0
  44. package/references/acp-factory-ref/python/README.md +253 -0
  45. package/references/acp-factory-ref/python/pyproject.toml +73 -0
  46. package/references/acp-factory-ref/python/tests/__init__.py +0 -0
  47. package/references/acp-factory-ref/python/tests/e2e/__init__.py +1 -0
  48. package/references/acp-factory-ref/python/tests/e2e/test_codex_e2e.py +349 -0
  49. package/references/acp-factory-ref/python/tests/e2e/test_gemini_e2e.py +165 -0
  50. package/references/acp-factory-ref/python/tests/e2e/test_opencode_e2e.py +296 -0
  51. package/references/acp-factory-ref/python/tests/test_client_handler.py +543 -0
  52. package/references/acp-factory-ref/python/tests/test_pushable.py +199 -0
  53. package/references/claude-code-acp/.github/workflows/ci.yml +45 -0
  54. package/references/claude-code-acp/.github/workflows/publish.yml +34 -0
  55. package/references/claude-code-acp/.prettierrc.json +4 -0
  56. package/references/claude-code-acp/CHANGELOG.md +249 -0
  57. package/references/claude-code-acp/LICENSE +222 -0
  58. package/references/claude-code-acp/README.md +53 -0
  59. package/references/claude-code-acp/docs/RELEASES.md +24 -0
  60. package/references/claude-code-acp/eslint.config.js +48 -0
  61. package/references/claude-code-acp/package-lock.json +4570 -0
  62. package/references/claude-code-acp/package.json +88 -0
  63. package/references/claude-code-acp/scripts/release.sh +119 -0
  64. package/references/claude-code-acp/src/acp-agent.ts +2079 -0
  65. package/references/claude-code-acp/src/index.ts +26 -0
  66. package/references/claude-code-acp/src/lib.ts +38 -0
  67. package/references/claude-code-acp/src/mcp-server.ts +911 -0
  68. package/references/claude-code-acp/src/settings.ts +522 -0
  69. package/references/claude-code-acp/src/tests/.claude/commands/quick-math.md +5 -0
  70. package/references/claude-code-acp/src/tests/.claude/commands/say-hello.md +6 -0
  71. package/references/claude-code-acp/src/tests/acp-agent-fork.test.ts +479 -0
  72. package/references/claude-code-acp/src/tests/acp-agent.test.ts +1502 -0
  73. package/references/claude-code-acp/src/tests/extract-lines.test.ts +103 -0
  74. package/references/claude-code-acp/src/tests/fork-session.test.ts +335 -0
  75. package/references/claude-code-acp/src/tests/replace-and-calculate-location.test.ts +334 -0
  76. package/references/claude-code-acp/src/tests/settings.test.ts +617 -0
  77. package/references/claude-code-acp/src/tests/skills-options.test.ts +187 -0
  78. package/references/claude-code-acp/src/tests/tools.test.ts +318 -0
  79. package/references/claude-code-acp/src/tests/typescript-declarations.test.ts +558 -0
  80. package/references/claude-code-acp/src/tools.ts +819 -0
  81. package/references/claude-code-acp/src/utils.ts +171 -0
  82. package/references/claude-code-acp/tsconfig.json +18 -0
  83. package/references/claude-code-acp/vitest.config.ts +19 -0
  84. package/references/multi-agent-protocol/.sudocode/issues.jsonl +111 -0
  85. package/references/multi-agent-protocol/.sudocode/specs.jsonl +13 -0
  86. package/references/multi-agent-protocol/LICENSE +21 -0
  87. package/references/multi-agent-protocol/README.md +113 -0
  88. package/references/multi-agent-protocol/docs/00-design-specification.md +496 -0
  89. package/references/multi-agent-protocol/docs/01-open-questions.md +1050 -0
  90. package/references/multi-agent-protocol/docs/02-wire-protocol.md +296 -0
  91. package/references/multi-agent-protocol/docs/03-streaming-semantics.md +252 -0
  92. package/references/multi-agent-protocol/docs/04-error-handling.md +231 -0
  93. package/references/multi-agent-protocol/docs/05-connection-model.md +244 -0
  94. package/references/multi-agent-protocol/docs/06-visibility-permissions.md +243 -0
  95. package/references/multi-agent-protocol/docs/07-federation.md +259 -0
  96. package/references/multi-agent-protocol/docs/08-macro-agent-migration.md +253 -0
  97. package/references/multi-agent-protocol/docs/09-authentication.md +680 -0
  98. package/references/multi-agent-protocol/docs/10-mail-protocol.md +553 -0
  99. package/references/multi-agent-protocol/docs/agent-iam-integration.md +877 -0
  100. package/references/multi-agent-protocol/docs/agentic-mesh-integration-draft.md +459 -0
  101. package/references/multi-agent-protocol/docs/git-transport-draft.md +251 -0
  102. package/references/multi-agent-protocol/docs-site/Gemfile +22 -0
  103. package/references/multi-agent-protocol/docs-site/README.md +82 -0
  104. package/references/multi-agent-protocol/docs-site/_config.yml +91 -0
  105. package/references/multi-agent-protocol/docs-site/_includes/head_custom.html +20 -0
  106. package/references/multi-agent-protocol/docs-site/_sass/color_schemes/map.scss +42 -0
  107. package/references/multi-agent-protocol/docs-site/_sass/custom/custom.scss +34 -0
  108. package/references/multi-agent-protocol/docs-site/examples/full-integration.md +510 -0
  109. package/references/multi-agent-protocol/docs-site/examples/index.md +138 -0
  110. package/references/multi-agent-protocol/docs-site/examples/simple-chat.md +282 -0
  111. package/references/multi-agent-protocol/docs-site/examples/task-queue.md +399 -0
  112. package/references/multi-agent-protocol/docs-site/getting-started/index.md +98 -0
  113. package/references/multi-agent-protocol/docs-site/getting-started/installation.md +219 -0
  114. package/references/multi-agent-protocol/docs-site/getting-started/overview.md +172 -0
  115. package/references/multi-agent-protocol/docs-site/getting-started/quickstart.md +237 -0
  116. package/references/multi-agent-protocol/docs-site/index.md +136 -0
  117. package/references/multi-agent-protocol/docs-site/protocol/authentication.md +391 -0
  118. package/references/multi-agent-protocol/docs-site/protocol/connection-model.md +376 -0
  119. package/references/multi-agent-protocol/docs-site/protocol/design.md +284 -0
  120. package/references/multi-agent-protocol/docs-site/protocol/error-handling.md +312 -0
  121. package/references/multi-agent-protocol/docs-site/protocol/federation.md +449 -0
  122. package/references/multi-agent-protocol/docs-site/protocol/index.md +129 -0
  123. package/references/multi-agent-protocol/docs-site/protocol/permissions.md +398 -0
  124. package/references/multi-agent-protocol/docs-site/protocol/streaming.md +353 -0
  125. package/references/multi-agent-protocol/docs-site/protocol/wire-protocol.md +369 -0
  126. package/references/multi-agent-protocol/docs-site/sdk/api/agent.md +357 -0
  127. package/references/multi-agent-protocol/docs-site/sdk/api/client.md +380 -0
  128. package/references/multi-agent-protocol/docs-site/sdk/api/index.md +62 -0
  129. package/references/multi-agent-protocol/docs-site/sdk/api/server.md +453 -0
  130. package/references/multi-agent-protocol/docs-site/sdk/api/types.md +468 -0
  131. package/references/multi-agent-protocol/docs-site/sdk/guides/agent.md +375 -0
  132. package/references/multi-agent-protocol/docs-site/sdk/guides/authentication.md +405 -0
  133. package/references/multi-agent-protocol/docs-site/sdk/guides/client.md +352 -0
  134. package/references/multi-agent-protocol/docs-site/sdk/guides/index.md +89 -0
  135. package/references/multi-agent-protocol/docs-site/sdk/guides/server.md +360 -0
  136. package/references/multi-agent-protocol/docs-site/sdk/guides/testing.md +446 -0
  137. package/references/multi-agent-protocol/docs-site/sdk/guides/transports.md +363 -0
  138. package/references/multi-agent-protocol/docs-site/sdk/index.md +206 -0
  139. package/references/multi-agent-protocol/package-lock.json +3886 -0
  140. package/references/multi-agent-protocol/package.json +56 -0
  141. package/references/multi-agent-protocol/schema/meta.json +467 -0
  142. package/references/multi-agent-protocol/schema/schema.json +2558 -0
  143. package/src/acp/__tests__/history.test.ts +526 -0
  144. package/src/acp/__tests__/integration.test.ts +2 -1
  145. package/src/acp/index.ts +4 -0
  146. package/src/acp/macro-agent.ts +329 -85
  147. package/src/acp/types.ts +39 -2
  148. package/src/agent/__tests__/agent-manager.test.ts +67 -1
  149. package/src/agent/agent-manager.ts +10 -4
  150. package/src/cli/__tests__/stable-instance-id.test.ts +57 -0
  151. package/src/cli/acp.ts +17 -2
  152. package/src/map/adapter/acp-over-map.ts +57 -2
  153. package/src/store/event-store.ts +10 -3
  154. package/src/store/types/agents.ts +2 -0
@@ -0,0 +1,819 @@
1
+ import {
2
+ ContentBlock,
3
+ PlanEntry,
4
+ ToolCallContent,
5
+ ToolCallLocation,
6
+ ToolKind,
7
+ } from "@agentclientprotocol/sdk";
8
+ import { SYSTEM_REMINDER } from "./mcp-server.js";
9
+ import * as diff from "diff";
10
+ import {
11
+ ImageBlockParam,
12
+ TextBlockParam,
13
+ ToolResultBlockParam,
14
+ WebSearchResultBlock,
15
+ WebSearchToolResultBlockParam,
16
+ WebSearchToolResultError,
17
+ } from "@anthropic-ai/sdk/resources";
18
+ import {
19
+ BetaBashCodeExecutionToolResultBlockParam,
20
+ BetaBashCodeExecutionResultBlock,
21
+ BetaBashCodeExecutionToolResultError,
22
+ BetaCodeExecutionToolResultBlockParam,
23
+ BetaCodeExecutionResultBlock,
24
+ BetaCodeExecutionToolResultError,
25
+ BetaRequestMCPToolResultBlockParam,
26
+ BetaTextEditorCodeExecutionToolResultBlockParam,
27
+ BetaTextEditorCodeExecutionViewResultBlock,
28
+ BetaTextEditorCodeExecutionCreateResultBlock,
29
+ BetaTextEditorCodeExecutionStrReplaceResultBlock,
30
+ BetaTextEditorCodeExecutionToolResultError,
31
+ BetaToolResultBlockParam,
32
+ BetaToolSearchToolResultBlockParam,
33
+ BetaToolReferenceBlock,
34
+ BetaToolSearchToolSearchResultBlock,
35
+ BetaToolSearchToolResultError,
36
+ BetaWebFetchToolResultBlockParam,
37
+ BetaWebFetchBlock,
38
+ BetaWebFetchToolResultErrorBlock,
39
+ BetaWebSearchToolResultBlockParam,
40
+ BetaImageBlockParam,
41
+ } from "@anthropic-ai/sdk/resources/beta.mjs";
42
+
43
+ const acpUnqualifiedToolNames = {
44
+ read: "Read",
45
+ edit: "Edit",
46
+ write: "Write",
47
+ bash: "Bash",
48
+ killShell: "KillShell",
49
+ bashOutput: "BashOutput",
50
+ };
51
+
52
+ export const ACP_TOOL_NAME_PREFIX = "mcp__acp__";
53
+ export const acpToolNames = {
54
+ read: ACP_TOOL_NAME_PREFIX + acpUnqualifiedToolNames.read,
55
+ edit: ACP_TOOL_NAME_PREFIX + acpUnqualifiedToolNames.edit,
56
+ write: ACP_TOOL_NAME_PREFIX + acpUnqualifiedToolNames.write,
57
+ bash: ACP_TOOL_NAME_PREFIX + acpUnqualifiedToolNames.bash,
58
+ killShell: ACP_TOOL_NAME_PREFIX + acpUnqualifiedToolNames.killShell,
59
+ bashOutput: ACP_TOOL_NAME_PREFIX + acpUnqualifiedToolNames.bashOutput,
60
+ };
61
+
62
+ export const EDIT_TOOL_NAMES = [acpToolNames.edit, acpToolNames.write];
63
+
64
+ /**
65
+ * Union of all possible content types that can appear in tool results from the Anthropic SDK.
66
+ * These are transformed to valid ACP ContentBlock types by toValidAcpContent().
67
+ */
68
+ type ToolResultContent =
69
+ | TextBlockParam
70
+ | ImageBlockParam
71
+ | BetaImageBlockParam
72
+ | BetaToolReferenceBlock
73
+ | BetaToolSearchToolSearchResultBlock
74
+ | BetaToolSearchToolResultError
75
+ | WebSearchResultBlock
76
+ | WebSearchToolResultError
77
+ | BetaWebFetchBlock
78
+ | BetaWebFetchToolResultErrorBlock
79
+ | BetaCodeExecutionResultBlock
80
+ | BetaCodeExecutionToolResultError
81
+ | BetaBashCodeExecutionResultBlock
82
+ | BetaBashCodeExecutionToolResultError
83
+ | BetaTextEditorCodeExecutionViewResultBlock
84
+ | BetaTextEditorCodeExecutionCreateResultBlock
85
+ | BetaTextEditorCodeExecutionStrReplaceResultBlock
86
+ | BetaTextEditorCodeExecutionToolResultError;
87
+ import { HookCallback } from "@anthropic-ai/claude-agent-sdk";
88
+ import { Logger } from "./acp-agent.js";
89
+ import { SettingsManager } from "./settings.js";
90
+
91
+ interface ToolInfo {
92
+ title: string;
93
+ kind: ToolKind;
94
+ content: ToolCallContent[];
95
+ locations?: ToolCallLocation[];
96
+ }
97
+
98
+ interface ToolUpdate {
99
+ title?: string;
100
+ content?: ToolCallContent[];
101
+ locations?: ToolCallLocation[];
102
+ }
103
+
104
+ export function toolInfoFromToolUse(toolUse: any): ToolInfo {
105
+ const name = toolUse.name;
106
+ const input = toolUse.input;
107
+
108
+ switch (name) {
109
+ case "Task":
110
+ return {
111
+ title: input?.description ? input.description : "Task",
112
+ kind: "think",
113
+ content:
114
+ input && input.prompt
115
+ ? [
116
+ {
117
+ type: "content",
118
+ content: { type: "text", text: input.prompt },
119
+ },
120
+ ]
121
+ : [],
122
+ };
123
+
124
+ case "NotebookRead":
125
+ return {
126
+ title: input?.notebook_path ? `Read Notebook ${input.notebook_path}` : "Read Notebook",
127
+ kind: "read",
128
+ content: [],
129
+ locations: input?.notebook_path ? [{ path: input.notebook_path }] : [],
130
+ };
131
+
132
+ case "NotebookEdit":
133
+ return {
134
+ title: input?.notebook_path ? `Edit Notebook ${input.notebook_path}` : "Edit Notebook",
135
+ kind: "edit",
136
+ content:
137
+ input && input.new_source
138
+ ? [
139
+ {
140
+ type: "content",
141
+ content: { type: "text", text: input.new_source },
142
+ },
143
+ ]
144
+ : [],
145
+ locations: input?.notebook_path ? [{ path: input.notebook_path }] : [],
146
+ };
147
+
148
+ case "Bash":
149
+ case acpToolNames.bash:
150
+ return {
151
+ title: input?.command ? "`" + input.command.replaceAll("`", "\\`") + "`" : "Terminal",
152
+ kind: "execute",
153
+ content:
154
+ input && input.description
155
+ ? [
156
+ {
157
+ type: "content",
158
+ content: { type: "text", text: input.description },
159
+ },
160
+ ]
161
+ : [],
162
+ };
163
+
164
+ case "BashOutput":
165
+ case acpToolNames.bashOutput:
166
+ return {
167
+ title: "Tail Logs",
168
+ kind: "execute",
169
+ content: [],
170
+ };
171
+
172
+ case "KillShell":
173
+ case acpToolNames.killShell:
174
+ return {
175
+ title: "Kill Process",
176
+ kind: "execute",
177
+ content: [],
178
+ };
179
+
180
+ case acpToolNames.read: {
181
+ let limit = "";
182
+ if (input.limit) {
183
+ limit =
184
+ " (" + ((input.offset ?? 0) + 1) + " - " + ((input.offset ?? 0) + input.limit) + ")";
185
+ } else if (input.offset) {
186
+ limit = " (from line " + (input.offset + 1) + ")";
187
+ }
188
+ return {
189
+ title: "Read " + (input.file_path ?? "File") + limit,
190
+ kind: "read",
191
+ locations: input.file_path
192
+ ? [
193
+ {
194
+ path: input.file_path,
195
+ line: input.offset ?? 0,
196
+ },
197
+ ]
198
+ : [],
199
+ content: [],
200
+ };
201
+ }
202
+
203
+ case "Read":
204
+ return {
205
+ title: "Read File",
206
+ kind: "read",
207
+ content: [],
208
+ locations: input.file_path
209
+ ? [
210
+ {
211
+ path: input.file_path,
212
+ line: input.offset ?? 0,
213
+ },
214
+ ]
215
+ : [],
216
+ };
217
+
218
+ case "LS":
219
+ return {
220
+ title: `List the ${input?.path ? "`" + input.path + "`" : "current"} directory's contents`,
221
+ kind: "search",
222
+ content: [],
223
+ locations: [],
224
+ };
225
+
226
+ case acpToolNames.edit:
227
+ case "Edit": {
228
+ const path = input?.file_path ?? input?.file_path;
229
+
230
+ return {
231
+ title: path ? `Edit \`${path}\`` : "Edit",
232
+ kind: "edit",
233
+ content:
234
+ input && path
235
+ ? [
236
+ {
237
+ type: "diff",
238
+ path,
239
+ oldText: input.old_string ?? null,
240
+ newText: input.new_string ?? "",
241
+ },
242
+ ]
243
+ : [],
244
+ locations: path ? [{ path }] : undefined,
245
+ };
246
+ }
247
+
248
+ case acpToolNames.write: {
249
+ let content: ToolCallContent[] = [];
250
+ if (input && input.file_path) {
251
+ content = [
252
+ {
253
+ type: "diff",
254
+ path: input.file_path,
255
+ oldText: null,
256
+ newText: input.content,
257
+ },
258
+ ];
259
+ } else if (input && input.content) {
260
+ content = [
261
+ {
262
+ type: "content",
263
+ content: { type: "text", text: input.content },
264
+ },
265
+ ];
266
+ }
267
+ return {
268
+ title: input?.file_path ? `Write ${input.file_path}` : "Write",
269
+ kind: "edit",
270
+ content,
271
+ locations: input?.file_path ? [{ path: input.file_path }] : [],
272
+ };
273
+ }
274
+
275
+ case "Write":
276
+ return {
277
+ title: input?.file_path ? `Write ${input.file_path}` : "Write",
278
+ kind: "edit",
279
+ content:
280
+ input && input.file_path
281
+ ? [
282
+ {
283
+ type: "diff",
284
+ path: input.file_path,
285
+ oldText: null,
286
+ newText: input.content,
287
+ },
288
+ ]
289
+ : [],
290
+ locations: input?.file_path ? [{ path: input.file_path }] : [],
291
+ };
292
+
293
+ case "Glob": {
294
+ let label = "Find";
295
+ if (input.path) {
296
+ label += ` \`${input.path}\``;
297
+ }
298
+ if (input.pattern) {
299
+ label += ` \`${input.pattern}\``;
300
+ }
301
+ return {
302
+ title: label,
303
+ kind: "search",
304
+ content: [],
305
+ locations: input.path ? [{ path: input.path }] : [],
306
+ };
307
+ }
308
+
309
+ case "Grep": {
310
+ let label = "grep";
311
+
312
+ if (input["-i"]) {
313
+ label += " -i";
314
+ }
315
+ if (input["-n"]) {
316
+ label += " -n";
317
+ }
318
+
319
+ if (input["-A"] !== undefined) {
320
+ label += ` -A ${input["-A"]}`;
321
+ }
322
+ if (input["-B"] !== undefined) {
323
+ label += ` -B ${input["-B"]}`;
324
+ }
325
+ if (input["-C"] !== undefined) {
326
+ label += ` -C ${input["-C"]}`;
327
+ }
328
+
329
+ if (input.output_mode) {
330
+ switch (input.output_mode) {
331
+ case "FilesWithMatches":
332
+ label += " -l";
333
+ break;
334
+ case "Count":
335
+ label += " -c";
336
+ break;
337
+ case "Content":
338
+ default:
339
+ break;
340
+ }
341
+ }
342
+
343
+ if (input.head_limit !== undefined) {
344
+ label += ` | head -${input.head_limit}`;
345
+ }
346
+
347
+ if (input.glob) {
348
+ label += ` --include="${input.glob}"`;
349
+ }
350
+
351
+ if (input.type) {
352
+ label += ` --type=${input.type}`;
353
+ }
354
+
355
+ if (input.multiline) {
356
+ label += " -P";
357
+ }
358
+
359
+ if (input.pattern) {
360
+ label += ` "${input.pattern}"`;
361
+ }
362
+
363
+ if (input.path) {
364
+ label += ` ${input.path}`;
365
+ }
366
+
367
+ return {
368
+ title: label,
369
+ kind: "search",
370
+ content: [],
371
+ };
372
+ }
373
+
374
+ case "WebFetch":
375
+ return {
376
+ title: input?.url ? `Fetch ${input.url}` : "Fetch",
377
+ kind: "fetch",
378
+ content:
379
+ input && input.prompt
380
+ ? [
381
+ {
382
+ type: "content",
383
+ content: { type: "text", text: input.prompt },
384
+ },
385
+ ]
386
+ : [],
387
+ };
388
+
389
+ case "WebSearch": {
390
+ let label = `"${input.query}"`;
391
+
392
+ if (input.allowed_domains && input.allowed_domains.length > 0) {
393
+ label += ` (allowed: ${input.allowed_domains.join(", ")})`;
394
+ }
395
+
396
+ if (input.blocked_domains && input.blocked_domains.length > 0) {
397
+ label += ` (blocked: ${input.blocked_domains.join(", ")})`;
398
+ }
399
+
400
+ return {
401
+ title: label,
402
+ kind: "fetch",
403
+ content: [],
404
+ };
405
+ }
406
+
407
+ case "TodoWrite":
408
+ return {
409
+ title: Array.isArray(input?.todos)
410
+ ? `Update TODOs: ${input.todos.map((todo: any) => todo.content).join(", ")}`
411
+ : "Update TODOs",
412
+ kind: "think",
413
+ content: [],
414
+ };
415
+
416
+ case "ExitPlanMode":
417
+ return {
418
+ title: "Ready to code?",
419
+ kind: "switch_mode",
420
+ content:
421
+ input && input.plan
422
+ ? [{ type: "content", content: { type: "text", text: input.plan } }]
423
+ : [],
424
+ };
425
+
426
+ case "Other": {
427
+ let output;
428
+ try {
429
+ output = JSON.stringify(input, null, 2);
430
+ } catch {
431
+ output = typeof input === "string" ? input : "{}";
432
+ }
433
+ return {
434
+ title: name || "Unknown Tool",
435
+ kind: "other",
436
+ content: [
437
+ {
438
+ type: "content",
439
+ content: {
440
+ type: "text",
441
+ text: `\`\`\`json\n${output}\`\`\``,
442
+ },
443
+ },
444
+ ],
445
+ };
446
+ }
447
+
448
+ default:
449
+ return {
450
+ title: name || "Unknown Tool",
451
+ kind: "other",
452
+ content: [],
453
+ };
454
+ }
455
+ }
456
+
457
+ export function toolUpdateFromToolResult(
458
+ toolResult:
459
+ | ToolResultBlockParam
460
+ | BetaToolResultBlockParam
461
+ | BetaWebSearchToolResultBlockParam
462
+ | BetaWebFetchToolResultBlockParam
463
+ | WebSearchToolResultBlockParam
464
+ | BetaCodeExecutionToolResultBlockParam
465
+ | BetaBashCodeExecutionToolResultBlockParam
466
+ | BetaTextEditorCodeExecutionToolResultBlockParam
467
+ | BetaRequestMCPToolResultBlockParam
468
+ | BetaToolSearchToolResultBlockParam,
469
+ toolUse: any | undefined,
470
+ ): ToolUpdate {
471
+ if (
472
+ "is_error" in toolResult &&
473
+ toolResult.is_error &&
474
+ toolResult.content &&
475
+ toolResult.content.length > 0
476
+ ) {
477
+ // Only return errors
478
+ return toAcpContentUpdate(toolResult.content, true);
479
+ }
480
+
481
+ switch (toolUse?.name) {
482
+ case "Read":
483
+ case acpToolNames.read:
484
+ if (Array.isArray(toolResult.content) && toolResult.content.length > 0) {
485
+ return {
486
+ content: toolResult.content.map((content: any) => ({
487
+ type: "content",
488
+ content:
489
+ content.type === "text"
490
+ ? {
491
+ type: "text",
492
+ text: markdownEscape(content.text.replace(SYSTEM_REMINDER, "")),
493
+ }
494
+ : content,
495
+ })),
496
+ };
497
+ } else if (typeof toolResult.content === "string" && toolResult.content.length > 0) {
498
+ return {
499
+ content: [
500
+ {
501
+ type: "content",
502
+ content: {
503
+ type: "text",
504
+ text: markdownEscape(toolResult.content.replace(SYSTEM_REMINDER, "")),
505
+ },
506
+ },
507
+ ],
508
+ };
509
+ }
510
+ return {};
511
+
512
+ case acpToolNames.edit: {
513
+ const content: ToolCallContent[] = [];
514
+ const locations: ToolCallLocation[] = [];
515
+
516
+ if (
517
+ Array.isArray(toolResult.content) &&
518
+ toolResult.content.length > 0 &&
519
+ "text" in toolResult.content[0] &&
520
+ typeof toolResult.content[0].text === "string"
521
+ ) {
522
+ const patches = diff.parsePatch(toolResult.content[0].text);
523
+ console.error(JSON.stringify(patches));
524
+ for (const { oldFileName, newFileName, hunks } of patches) {
525
+ for (const { lines, newStart } of hunks) {
526
+ const oldText = [];
527
+ const newText = [];
528
+ for (const line of lines) {
529
+ if (line.startsWith("-")) {
530
+ oldText.push(line.slice(1));
531
+ } else if (line.startsWith("+")) {
532
+ newText.push(line.slice(1));
533
+ } else {
534
+ oldText.push(line.slice(1));
535
+ newText.push(line.slice(1));
536
+ }
537
+ }
538
+ if (oldText.length > 0 || newText.length > 0) {
539
+ locations.push({ path: newFileName || oldFileName, line: newStart });
540
+ content.push({
541
+ type: "diff",
542
+ path: newFileName || oldFileName,
543
+ oldText: oldText.join("\n") || null,
544
+ newText: newText.join("\n"),
545
+ });
546
+ }
547
+ }
548
+ }
549
+ }
550
+
551
+ const result: ToolUpdate = {};
552
+ if (content.length > 0) {
553
+ result.content = content;
554
+ }
555
+ if (locations.length > 0) {
556
+ result.locations = locations;
557
+ }
558
+ return result;
559
+ }
560
+
561
+ case acpToolNames.bash:
562
+ case "edit":
563
+ case "Edit":
564
+ case acpToolNames.write:
565
+ case "Write": {
566
+ return {};
567
+ }
568
+
569
+ case "ExitPlanMode": {
570
+ return { title: "Exited Plan Mode" };
571
+ }
572
+
573
+ case "Task":
574
+ case "NotebookEdit":
575
+ case "NotebookRead":
576
+ case "TodoWrite":
577
+ case "exit_plan_mode":
578
+ case "Bash":
579
+ case "BashOutput":
580
+ case "KillBash":
581
+ case "LS":
582
+ case "Glob":
583
+ case "Grep":
584
+ case "WebFetch":
585
+ case "WebSearch":
586
+ case "Other":
587
+ default: {
588
+ return toAcpContentUpdate(
589
+ toolResult.content,
590
+ "is_error" in toolResult ? toolResult.is_error : false,
591
+ );
592
+ }
593
+ }
594
+ }
595
+
596
+ function toAcpContentUpdate(
597
+ content: any,
598
+ isError: boolean = false,
599
+ ): { content?: ToolCallContent[] } {
600
+ if (Array.isArray(content) && content.length > 0) {
601
+ return {
602
+ content: content.map((c: any) => ({
603
+ type: "content" as const,
604
+ content: toAcpContentBlock(c, isError),
605
+ })),
606
+ };
607
+ } else if (typeof content === "object" && content !== null && "type" in content) {
608
+ return {
609
+ content: [
610
+ {
611
+ type: "content" as const,
612
+ content: toAcpContentBlock(content, isError),
613
+ },
614
+ ],
615
+ };
616
+ } else if (typeof content === "string" && content.length > 0) {
617
+ return {
618
+ content: [
619
+ {
620
+ type: "content",
621
+ content: {
622
+ type: "text",
623
+ text: isError ? `\`\`\`\n${content}\n\`\`\`` : content,
624
+ },
625
+ },
626
+ ],
627
+ };
628
+ }
629
+ return {};
630
+ }
631
+
632
+ function toAcpContentBlock(content: ToolResultContent, isError: boolean): ContentBlock {
633
+ const wrapText = (text: string): ContentBlock => ({
634
+ type: "text" as const,
635
+ text: isError ? `\`\`\`\n${text}\n\`\`\`` : text,
636
+ });
637
+
638
+ switch (content.type) {
639
+ case "text":
640
+ return {
641
+ type: "text" as const,
642
+ text: isError ? `\`\`\`\n${content.text}\n\`\`\`` : content.text,
643
+ };
644
+ case "image":
645
+ if (content.source.type === "base64") {
646
+ return {
647
+ type: "image" as const,
648
+ data: content.source.data,
649
+ mimeType: content.source.media_type,
650
+ };
651
+ }
652
+ // URL and file-based images can't be converted to ACP format (requires data)
653
+ return wrapText(
654
+ content.source.type === "url"
655
+ ? `[image: ${content.source.url}]`
656
+ : "[image: file reference]",
657
+ );
658
+
659
+ case "tool_reference":
660
+ return wrapText(`Tool: ${content.tool_name}`);
661
+ case "tool_search_tool_search_result":
662
+ return wrapText(
663
+ `Tools found: ${content.tool_references.map((r) => r.tool_name).join(", ") || "none"}`,
664
+ );
665
+ case "tool_search_tool_result_error":
666
+ return wrapText(
667
+ `Error: ${content.error_code}${content.error_message ? ` - ${content.error_message}` : ""}`,
668
+ );
669
+ case "web_search_result":
670
+ return wrapText(`${content.title} (${content.url})`);
671
+ case "web_search_tool_result_error":
672
+ return wrapText(`Error: ${content.error_code}`);
673
+ case "web_fetch_result":
674
+ return wrapText(`Fetched: ${content.url}`);
675
+ case "web_fetch_tool_result_error":
676
+ return wrapText(`Error: ${content.error_code}`);
677
+ case "code_execution_result":
678
+ return wrapText(`Output: ${content.stdout || content.stderr || ""}`);
679
+ case "bash_code_execution_result":
680
+ return wrapText(`Output: ${content.stdout || content.stderr || ""}`);
681
+ case "code_execution_tool_result_error":
682
+ case "bash_code_execution_tool_result_error":
683
+ return wrapText(`Error: ${content.error_code}`);
684
+ case "text_editor_code_execution_view_result":
685
+ return wrapText(content.content);
686
+ case "text_editor_code_execution_create_result":
687
+ return wrapText(content.is_file_update ? "File updated" : "File created");
688
+ case "text_editor_code_execution_str_replace_result":
689
+ return wrapText(content.lines?.join("\n") || "");
690
+ case "text_editor_code_execution_tool_result_error":
691
+ return wrapText(
692
+ `Error: ${content.error_code}${content.error_message ? ` - ${content.error_message}` : ""}`,
693
+ );
694
+
695
+ default:
696
+ return wrapText(JSON.stringify(content));
697
+ }
698
+ }
699
+
700
+ export type ClaudePlanEntry = {
701
+ content: string;
702
+ status: "pending" | "in_progress" | "completed";
703
+ activeForm: string;
704
+ };
705
+
706
+ export function planEntries(input: { todos: ClaudePlanEntry[] }): PlanEntry[] {
707
+ return input.todos.map((input) => ({
708
+ content: input.content,
709
+ status: input.status,
710
+ priority: "medium",
711
+ }));
712
+ }
713
+
714
+ export function markdownEscape(text: string): string {
715
+ let escape = "```";
716
+ for (const [m] of text.matchAll(/^```+/gm)) {
717
+ while (m.length >= escape.length) {
718
+ escape += "`";
719
+ }
720
+ }
721
+ return escape + "\n" + text + (text.endsWith("\n") ? "" : "\n") + escape;
722
+ }
723
+
724
+ /* A global variable to store callbacks that should be executed when receiving hooks from Claude Code */
725
+ const toolUseCallbacks: {
726
+ [toolUseId: string]: {
727
+ onPostToolUseHook?: (
728
+ toolUseID: string,
729
+ toolInput: unknown,
730
+ toolResponse: unknown,
731
+ ) => Promise<void>;
732
+ };
733
+ } = {};
734
+
735
+ /* Setup callbacks that will be called when receiving hooks from Claude Code */
736
+ export const registerHookCallback = (
737
+ toolUseID: string,
738
+ {
739
+ onPostToolUseHook,
740
+ }: {
741
+ onPostToolUseHook?: (
742
+ toolUseID: string,
743
+ toolInput: unknown,
744
+ toolResponse: unknown,
745
+ ) => Promise<void>;
746
+ },
747
+ ) => {
748
+ toolUseCallbacks[toolUseID] = {
749
+ onPostToolUseHook,
750
+ };
751
+ };
752
+
753
+ /* A callback for Claude Code that is called when receiving a PostToolUse hook */
754
+ export const createPostToolUseHook =
755
+ (logger: Logger = console): HookCallback =>
756
+ async (input: any, toolUseID: string | undefined): Promise<{ continue: boolean }> => {
757
+ if (input.hook_event_name === "PostToolUse" && toolUseID) {
758
+ const onPostToolUseHook = toolUseCallbacks[toolUseID]?.onPostToolUseHook;
759
+ if (onPostToolUseHook) {
760
+ await onPostToolUseHook(toolUseID, input.tool_input, input.tool_response);
761
+ delete toolUseCallbacks[toolUseID]; // Cleanup after execution
762
+ } else {
763
+ logger.error(`No onPostToolUseHook found for tool use ID: ${toolUseID}`);
764
+ delete toolUseCallbacks[toolUseID];
765
+ }
766
+ }
767
+ return { continue: true };
768
+ };
769
+
770
+ /**
771
+ * Creates a PreToolUse hook that checks permissions using the SettingsManager.
772
+ * This runs before the SDK's built-in permission rules, allowing us to enforce
773
+ * our own permission settings for ACP-prefixed tools.
774
+ */
775
+ export const createPreToolUseHook =
776
+ (settingsManager: SettingsManager, logger: Logger = console): HookCallback =>
777
+ async (input: any, _toolUseID: string | undefined) => {
778
+ if (input.hook_event_name !== "PreToolUse") {
779
+ return { continue: true };
780
+ }
781
+
782
+ const toolName = input.tool_name;
783
+ const toolInput = input.tool_input;
784
+
785
+ const permissionCheck = settingsManager.checkPermission(toolName, toolInput);
786
+
787
+ if (permissionCheck.decision !== "ask") {
788
+ logger.log(
789
+ `[PreToolUseHook] Tool: ${toolName}, Decision: ${permissionCheck.decision}, Rule: ${permissionCheck.rule}`,
790
+ );
791
+ }
792
+
793
+ switch (permissionCheck.decision) {
794
+ case "allow":
795
+ return {
796
+ continue: true,
797
+ hookSpecificOutput: {
798
+ hookEventName: "PreToolUse" as const,
799
+ permissionDecision: "allow" as const,
800
+ permissionDecisionReason: `Allowed by settings rule: ${permissionCheck.rule}`,
801
+ },
802
+ };
803
+
804
+ case "deny":
805
+ return {
806
+ continue: true,
807
+ hookSpecificOutput: {
808
+ hookEventName: "PreToolUse" as const,
809
+ permissionDecision: "deny" as const,
810
+ permissionDecisionReason: `Denied by settings rule: ${permissionCheck.rule}`,
811
+ },
812
+ };
813
+
814
+ case "ask":
815
+ default:
816
+ // Let the normal permission flow continue
817
+ return { continue: true };
818
+ }
819
+ };