@posthog/agent 1.30.0 → 2.0.1

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 (144) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +221 -219
  3. package/dist/adapters/claude/conversion/tool-use-to-acp.d.ts +21 -0
  4. package/dist/adapters/claude/conversion/tool-use-to-acp.js +547 -0
  5. package/dist/adapters/claude/conversion/tool-use-to-acp.js.map +1 -0
  6. package/dist/adapters/claude/permissions/permission-options.d.ts +13 -0
  7. package/dist/adapters/claude/permissions/permission-options.js +117 -0
  8. package/dist/adapters/claude/permissions/permission-options.js.map +1 -0
  9. package/dist/adapters/claude/questions/utils.d.ts +132 -0
  10. package/dist/adapters/claude/questions/utils.js +63 -0
  11. package/dist/adapters/claude/questions/utils.js.map +1 -0
  12. package/dist/adapters/claude/tools.d.ts +18 -0
  13. package/dist/adapters/claude/tools.js +95 -0
  14. package/dist/adapters/claude/tools.js.map +1 -0
  15. package/dist/agent-DBQY1BfC.d.ts +123 -0
  16. package/dist/agent.d.ts +5 -0
  17. package/dist/agent.js +3656 -0
  18. package/dist/agent.js.map +1 -0
  19. package/dist/claude-cli/cli.js +3695 -2746
  20. package/dist/claude-cli/vendor/ripgrep/COPYING +3 -0
  21. package/dist/claude-cli/vendor/ripgrep/arm64-darwin/rg +0 -0
  22. package/dist/claude-cli/vendor/ripgrep/arm64-darwin/ripgrep.node +0 -0
  23. package/dist/claude-cli/vendor/ripgrep/arm64-linux/rg +0 -0
  24. package/dist/claude-cli/vendor/ripgrep/arm64-linux/ripgrep.node +0 -0
  25. package/dist/claude-cli/vendor/ripgrep/x64-darwin/rg +0 -0
  26. package/dist/claude-cli/vendor/ripgrep/x64-darwin/ripgrep.node +0 -0
  27. package/dist/claude-cli/vendor/ripgrep/x64-linux/rg +0 -0
  28. package/dist/claude-cli/vendor/ripgrep/x64-linux/ripgrep.node +0 -0
  29. package/dist/claude-cli/vendor/ripgrep/x64-win32/rg.exe +0 -0
  30. package/dist/claude-cli/vendor/ripgrep/x64-win32/ripgrep.node +0 -0
  31. package/dist/gateway-models.d.ts +24 -0
  32. package/dist/gateway-models.js +93 -0
  33. package/dist/gateway-models.js.map +1 -0
  34. package/dist/index.d.ts +172 -1203
  35. package/dist/index.js +3704 -6826
  36. package/dist/index.js.map +1 -1
  37. package/dist/logger-DDBiMOOD.d.ts +24 -0
  38. package/dist/posthog-api.d.ts +40 -0
  39. package/dist/posthog-api.js +175 -0
  40. package/dist/posthog-api.js.map +1 -0
  41. package/dist/server/agent-server.d.ts +41 -0
  42. package/dist/server/agent-server.js +4451 -0
  43. package/dist/server/agent-server.js.map +1 -0
  44. package/dist/server/bin.d.ts +1 -0
  45. package/dist/server/bin.js +4507 -0
  46. package/dist/server/bin.js.map +1 -0
  47. package/dist/types.d.ts +129 -0
  48. package/dist/types.js +1 -0
  49. package/dist/types.js.map +1 -0
  50. package/package.json +66 -14
  51. package/src/acp-extensions.ts +93 -61
  52. package/src/adapters/acp-connection.ts +494 -0
  53. package/src/adapters/base-acp-agent.ts +150 -0
  54. package/src/adapters/claude/claude-agent.ts +596 -0
  55. package/src/adapters/claude/conversion/acp-to-sdk.ts +102 -0
  56. package/src/adapters/claude/conversion/sdk-to-acp.ts +571 -0
  57. package/src/adapters/claude/conversion/tool-use-to-acp.ts +618 -0
  58. package/src/adapters/claude/hooks.ts +64 -0
  59. package/src/adapters/claude/mcp/tool-metadata.ts +102 -0
  60. package/src/adapters/claude/permissions/permission-handlers.ts +433 -0
  61. package/src/adapters/claude/permissions/permission-options.ts +103 -0
  62. package/src/adapters/claude/plan/utils.ts +56 -0
  63. package/src/adapters/claude/questions/utils.ts +92 -0
  64. package/src/adapters/claude/session/commands.ts +38 -0
  65. package/src/adapters/claude/session/mcp-config.ts +37 -0
  66. package/src/adapters/claude/session/models.ts +12 -0
  67. package/src/adapters/claude/session/options.ts +236 -0
  68. package/src/adapters/claude/tool-meta.ts +143 -0
  69. package/src/adapters/claude/tools.ts +53 -611
  70. package/src/adapters/claude/types.ts +61 -0
  71. package/src/adapters/codex/spawn.ts +130 -0
  72. package/src/agent.ts +97 -734
  73. package/src/execution-mode.ts +43 -0
  74. package/src/gateway-models.ts +135 -0
  75. package/src/index.ts +79 -0
  76. package/src/otel-log-writer.test.ts +105 -0
  77. package/src/otel-log-writer.ts +94 -0
  78. package/src/posthog-api.ts +75 -235
  79. package/src/resume.ts +115 -0
  80. package/src/sagas/apply-snapshot-saga.test.ts +690 -0
  81. package/src/sagas/apply-snapshot-saga.ts +88 -0
  82. package/src/sagas/capture-tree-saga.test.ts +892 -0
  83. package/src/sagas/capture-tree-saga.ts +141 -0
  84. package/src/sagas/resume-saga.test.ts +558 -0
  85. package/src/sagas/resume-saga.ts +332 -0
  86. package/src/sagas/test-fixtures.ts +250 -0
  87. package/src/server/agent-server.test.ts +220 -0
  88. package/src/server/agent-server.ts +748 -0
  89. package/src/server/bin.ts +88 -0
  90. package/src/server/jwt.ts +65 -0
  91. package/src/server/schemas.ts +47 -0
  92. package/src/server/types.ts +13 -0
  93. package/src/server/utils/retry.test.ts +122 -0
  94. package/src/server/utils/retry.ts +61 -0
  95. package/src/server/utils/sse-parser.test.ts +93 -0
  96. package/src/server/utils/sse-parser.ts +46 -0
  97. package/src/session-log-writer.test.ts +140 -0
  98. package/src/session-log-writer.ts +137 -0
  99. package/src/test/assertions.ts +114 -0
  100. package/src/test/controllers/sse-controller.ts +107 -0
  101. package/src/test/fixtures/api.ts +111 -0
  102. package/src/test/fixtures/config.ts +33 -0
  103. package/src/test/fixtures/notifications.ts +92 -0
  104. package/src/test/mocks/claude-sdk.ts +251 -0
  105. package/src/test/mocks/msw-handlers.ts +48 -0
  106. package/src/test/setup.ts +114 -0
  107. package/src/test/wait.ts +41 -0
  108. package/src/tree-tracker.ts +173 -0
  109. package/src/types.ts +51 -154
  110. package/src/utils/acp-content.ts +58 -0
  111. package/src/utils/async-mutex.test.ts +104 -0
  112. package/src/utils/async-mutex.ts +31 -0
  113. package/src/utils/common.ts +15 -0
  114. package/src/utils/gateway.ts +9 -6
  115. package/src/utils/logger.ts +0 -30
  116. package/src/utils/streams.ts +220 -0
  117. package/CLAUDE.md +0 -331
  118. package/dist/templates/plan-template.md +0 -41
  119. package/src/adapters/claude/claude.ts +0 -1543
  120. package/src/adapters/claude/mcp-server.ts +0 -810
  121. package/src/adapters/claude/utils.ts +0 -267
  122. package/src/agents/execution.ts +0 -37
  123. package/src/agents/planning.ts +0 -60
  124. package/src/agents/research.ts +0 -160
  125. package/src/file-manager.ts +0 -306
  126. package/src/git-manager.ts +0 -577
  127. package/src/prompt-builder.ts +0 -499
  128. package/src/schemas.ts +0 -241
  129. package/src/session-store.ts +0 -259
  130. package/src/task-manager.ts +0 -163
  131. package/src/template-manager.ts +0 -236
  132. package/src/templates/plan-template.md +0 -41
  133. package/src/todo-manager.ts +0 -180
  134. package/src/tools/registry.ts +0 -129
  135. package/src/tools/types.ts +0 -127
  136. package/src/utils/tapped-stream.ts +0 -60
  137. package/src/workflow/config.ts +0 -53
  138. package/src/workflow/steps/build.ts +0 -135
  139. package/src/workflow/steps/finalize.ts +0 -241
  140. package/src/workflow/steps/plan.ts +0 -167
  141. package/src/workflow/steps/research.ts +0 -223
  142. package/src/workflow/types.ts +0 -62
  143. package/src/workflow/utils.ts +0 -53
  144. package/src/worktree-manager.ts +0 -928
@@ -1,616 +1,58 @@
1
- import type {
2
- PlanEntry,
3
- ToolCallContent,
4
- ToolCallLocation,
5
- ToolKind,
6
- } from "@agentclientprotocol/sdk";
7
- import type { HookCallback, HookInput } from "@anthropic-ai/claude-agent-sdk";
8
- import type {
9
- ToolResultBlockParam,
10
- WebSearchToolResultBlockParam,
11
- } from "@anthropic-ai/sdk/resources";
12
- import type {
13
- BetaBashCodeExecutionToolResultBlockParam,
14
- BetaCodeExecutionToolResultBlockParam,
15
- BetaRequestMCPToolResultBlockParam,
16
- BetaTextEditorCodeExecutionToolResultBlockParam,
17
- BetaToolSearchToolResultBlockParam,
18
- BetaWebFetchToolResultBlockParam,
19
- BetaWebSearchToolResultBlockParam,
20
- } from "@anthropic-ai/sdk/resources/beta.mjs";
21
- import { Logger } from "@/utils/logger.js";
22
- import {
23
- replaceAndCalculateLocation,
24
- SYSTEM_REMINDER,
25
- toolNames,
26
- } from "./mcp-server.js";
27
-
28
- interface ToolInfo {
29
- title: string;
30
- kind: ToolKind;
31
- content: ToolCallContent[];
32
- locations?: ToolCallLocation[];
33
- }
34
-
35
- interface ToolUpdate {
36
- title?: string;
37
- content?: ToolCallContent[];
38
- locations?: ToolCallLocation[];
39
- }
40
-
41
- export function toolInfoFromToolUse(
42
- toolUse: any,
43
- cachedFileContent: { [key: string]: string },
44
- logger: Logger = new Logger({ debug: false, prefix: "[ClaudeTools]" }),
45
- ): ToolInfo {
46
- const name = toolUse.name;
47
- const input = toolUse.input;
48
-
49
- switch (name) {
50
- case "Task":
51
- return {
52
- title: input?.description ? input.description : "Task",
53
- kind: "think",
54
- content: input?.prompt
55
- ? [
56
- {
57
- type: "content",
58
- content: { type: "text", text: input.prompt },
59
- },
60
- ]
61
- : [],
62
- };
63
-
64
- case "NotebookRead":
65
- return {
66
- title: input?.notebook_path
67
- ? `Read Notebook ${input.notebook_path}`
68
- : "Read Notebook",
69
- kind: "read",
70
- content: [],
71
- locations: input?.notebook_path ? [{ path: input.notebook_path }] : [],
72
- };
73
-
74
- case "NotebookEdit":
75
- return {
76
- title: input?.notebook_path
77
- ? `Edit Notebook ${input.notebook_path}`
78
- : "Edit Notebook",
79
- kind: "edit",
80
- content: input?.new_source
81
- ? [
82
- {
83
- type: "content",
84
- content: { type: "text", text: input.new_source },
85
- },
86
- ]
87
- : [],
88
- locations: input?.notebook_path ? [{ path: input.notebook_path }] : [],
89
- };
90
-
91
- case "Bash":
92
- case toolNames.bash:
93
- return {
94
- title: input?.command
95
- ? `\`${input.command.replaceAll("`", "\\`")}\``
96
- : "Terminal",
97
- kind: "execute",
98
- content: input?.description
99
- ? [
100
- {
101
- type: "content",
102
- content: { type: "text", text: input.description },
103
- },
104
- ]
105
- : [],
106
- };
107
-
108
- case "BashOutput":
109
- case toolNames.bashOutput:
110
- return {
111
- title: "Tail Logs",
112
- kind: "execute",
113
- content: [],
114
- };
115
-
116
- case "KillShell":
117
- case toolNames.killShell:
118
- return {
119
- title: "Kill Process",
120
- kind: "execute",
121
- content: [],
122
- };
123
-
124
- case toolNames.read: {
125
- let limit = "";
126
- if (input.limit) {
127
- limit =
128
- " (" +
129
- ((input.offset ?? 0) + 1) +
130
- " - " +
131
- ((input.offset ?? 0) + input.limit) +
132
- ")";
133
- } else if (input.offset) {
134
- limit = ` (from line ${input.offset + 1})`;
135
- }
136
- return {
137
- title: `Read ${input.file_path ?? "File"}${limit}`,
138
- kind: "read",
139
- locations: input.file_path
140
- ? [
141
- {
142
- path: input.file_path,
143
- line: input.offset ?? 0,
144
- },
145
- ]
146
- : [],
147
- content: [],
148
- };
149
- }
150
-
151
- case "Read":
152
- return {
153
- title: "Read File",
154
- kind: "read",
155
- content: [],
156
- locations: input.file_path
157
- ? [
158
- {
159
- path: input.file_path,
160
- line: input.offset ?? 0,
161
- },
162
- ]
163
- : [],
164
- };
165
-
166
- case "LS":
167
- return {
168
- title: `List the ${input?.path ? `\`${input.path}\`` : "current"} directory's contents`,
169
- kind: "search",
170
- content: [],
171
- locations: [],
172
- };
173
-
174
- case toolNames.edit:
175
- case "Edit": {
176
- const path = input?.file_path ?? input?.file_path;
177
- let oldText = input.old_string ?? null;
178
- let newText = input.new_string ?? "";
179
- let affectedLines: number[] = [];
180
-
181
- if (path && oldText) {
182
- try {
183
- const oldContent = cachedFileContent[path] || "";
184
- const newContent = replaceAndCalculateLocation(oldContent, [
185
- {
186
- oldText,
187
- newText,
188
- replaceAll: false,
189
- },
190
- ]);
191
- oldText = oldContent;
192
- newText = newContent.newContent;
193
- affectedLines = newContent.lineNumbers;
194
- } catch (e) {
195
- logger.error("Failed to edit file", e);
196
- }
197
- }
198
- return {
199
- title: path ? `Edit \`${path}\`` : "Edit",
200
- kind: "edit",
201
- content:
202
- input && path
203
- ? [
204
- {
205
- type: "diff",
206
- path,
207
- oldText,
208
- newText,
209
- },
210
- ]
211
- : [],
212
- locations: path
213
- ? affectedLines.length > 0
214
- ? affectedLines.map((line) => ({ line, path }))
215
- : [{ path }]
216
- : [],
217
- };
218
- }
219
-
220
- case toolNames.write: {
221
- let content: ToolCallContent[] = [];
222
- if (input?.file_path) {
223
- content = [
224
- {
225
- type: "diff",
226
- path: input.file_path,
227
- oldText: null,
228
- newText: input.content,
229
- },
230
- ];
231
- } else if (input?.content) {
232
- content = [
233
- {
234
- type: "content",
235
- content: { type: "text", text: input.content },
236
- },
237
- ];
238
- }
239
- return {
240
- title: input?.file_path ? `Write ${input.file_path}` : "Write",
241
- kind: "edit",
242
- content,
243
- locations: input?.file_path ? [{ path: input.file_path }] : [],
244
- };
245
- }
246
-
247
- case "Write":
248
- return {
249
- title: input?.file_path ? `Write ${input.file_path}` : "Write",
250
- kind: "edit",
251
- content: input?.file_path
252
- ? [
253
- {
254
- type: "diff",
255
- path: input.file_path,
256
- oldText: null,
257
- newText: input.content,
258
- },
259
- ]
260
- : [],
261
- locations: input?.file_path ? [{ path: input.file_path }] : [],
262
- };
263
-
264
- case "Glob": {
265
- let label = "Find";
266
- if (input.path) {
267
- label += ` \`${input.path}\``;
268
- }
269
- if (input.pattern) {
270
- label += ` \`${input.pattern}\``;
271
- }
272
- return {
273
- title: label,
274
- kind: "search",
275
- content: [],
276
- locations: input.path ? [{ path: input.path }] : [],
277
- };
278
- }
279
-
280
- case "Grep": {
281
- let label = "grep";
282
-
283
- if (input["-i"]) {
284
- label += " -i";
285
- }
286
- if (input["-n"]) {
287
- label += " -n";
288
- }
289
-
290
- if (input["-A"] !== undefined) {
291
- label += ` -A ${input["-A"]}`;
292
- }
293
- if (input["-B"] !== undefined) {
294
- label += ` -B ${input["-B"]}`;
295
- }
296
- if (input["-C"] !== undefined) {
297
- label += ` -C ${input["-C"]}`;
298
- }
299
-
300
- if (input.output_mode) {
301
- switch (input.output_mode) {
302
- case "FilesWithMatches":
303
- label += " -l";
304
- break;
305
- case "Count":
306
- label += " -c";
307
- break;
308
- default:
309
- break;
310
- }
311
- }
312
-
313
- if (input.head_limit !== undefined) {
314
- label += ` | head -${input.head_limit}`;
315
- }
316
-
317
- if (input.glob) {
318
- label += ` --include="${input.glob}"`;
319
- }
320
-
321
- if (input.type) {
322
- label += ` --type=${input.type}`;
323
- }
324
-
325
- if (input.multiline) {
326
- label += " -P";
327
- }
328
-
329
- label += ` "${input.pattern}"`;
330
-
331
- if (input.path) {
332
- label += ` ${input.path}`;
333
- }
334
-
335
- return {
336
- title: label,
337
- kind: "search",
338
- content: [],
339
- };
340
- }
341
-
342
- case "WebFetch":
343
- return {
344
- title: input?.url ? `Fetch ${input.url}` : "Fetch",
345
- kind: "fetch",
346
- content: input?.prompt
347
- ? [
348
- {
349
- type: "content",
350
- content: { type: "text", text: input.prompt },
351
- },
352
- ]
353
- : [],
354
- };
355
-
356
- case "WebSearch": {
357
- let label = `"${input.query}"`;
358
-
359
- if (input.allowed_domains && input.allowed_domains.length > 0) {
360
- label += ` (allowed: ${input.allowed_domains.join(", ")})`;
361
- }
362
-
363
- if (input.blocked_domains && input.blocked_domains.length > 0) {
364
- label += ` (blocked: ${input.blocked_domains.join(", ")})`;
365
- }
366
-
367
- return {
368
- title: label,
369
- kind: "fetch",
370
- content: [],
371
- };
372
- }
373
-
374
- case "TodoWrite":
375
- return {
376
- title: Array.isArray(input?.todos)
377
- ? `Update TODOs: ${input.todos.map((todo: any) => todo.content).join(", ")}`
378
- : "Update TODOs",
379
- kind: "think",
380
- content: [],
381
- };
382
-
383
- case "ExitPlanMode":
384
- return {
385
- title: "Ready to code?",
386
- kind: "switch_mode",
387
- content: input?.plan
388
- ? [{ type: "content", content: { type: "text", text: input.plan } }]
389
- : [],
390
- };
391
-
392
- case "Other": {
393
- let output: string;
394
- try {
395
- output = JSON.stringify(input, null, 2);
396
- } catch {
397
- output = typeof input === "string" ? input : "{}";
398
- }
399
- return {
400
- title: name || "Unknown Tool",
401
- kind: "other",
402
- content: [
403
- {
404
- type: "content",
405
- content: {
406
- type: "text",
407
- text: `\`\`\`json\n${output}\`\`\``,
408
- },
409
- },
410
- ],
411
- };
412
- }
413
-
414
- default:
415
- return {
416
- title: name || "Unknown Tool",
417
- kind: "other",
418
- content: [],
419
- };
420
- }
421
- }
422
-
423
- export function toolUpdateFromToolResult(
424
- toolResult:
425
- | ToolResultBlockParam
426
- | BetaWebSearchToolResultBlockParam
427
- | BetaWebFetchToolResultBlockParam
428
- | WebSearchToolResultBlockParam
429
- | BetaCodeExecutionToolResultBlockParam
430
- | BetaBashCodeExecutionToolResultBlockParam
431
- | BetaTextEditorCodeExecutionToolResultBlockParam
432
- | BetaRequestMCPToolResultBlockParam
433
- | BetaToolSearchToolResultBlockParam,
434
- toolUse: any | undefined,
435
- ): ToolUpdate {
436
- switch (toolUse?.name) {
437
- case "Read":
438
- case toolNames.read:
439
- if (Array.isArray(toolResult.content) && toolResult.content.length > 0) {
440
- return {
441
- content: toolResult.content.map((content: any) => ({
442
- type: "content",
443
- content:
444
- content.type === "text"
445
- ? {
446
- type: "text",
447
- text: markdownEscape(
448
- content.text.replace(SYSTEM_REMINDER, ""),
449
- ),
450
- }
451
- : content,
452
- })),
453
- };
454
- } else if (
455
- typeof toolResult.content === "string" &&
456
- toolResult.content.length > 0
457
- ) {
458
- return {
459
- content: [
460
- {
461
- type: "content",
462
- content: {
463
- type: "text",
464
- text: markdownEscape(
465
- toolResult.content.replace(SYSTEM_REMINDER, ""),
466
- ),
467
- },
468
- },
469
- ],
470
- };
471
- }
472
- return {};
473
-
474
- case toolNames.bash:
475
- case "edit":
476
- case "Edit":
477
- case toolNames.edit:
478
- case toolNames.write:
479
- case "Write": {
480
- if (
481
- "is_error" in toolResult &&
482
- toolResult.is_error &&
483
- toolResult.content &&
484
- toolResult.content.length > 0
485
- ) {
486
- // Only return errors
487
- return toAcpContentUpdate(toolResult.content, true);
488
- }
489
- return {};
490
- }
1
+ export {
2
+ getAvailableModes,
3
+ type ModeInfo,
4
+ TWIG_EXECUTION_MODES,
5
+ type TwigExecutionMode,
6
+ } from "../../execution-mode.js";
7
+
8
+ import type { TwigExecutionMode } from "../../execution-mode.js";
9
+ import { isMcpToolReadOnly } from "./mcp/tool-metadata.js";
10
+
11
+ export const READ_TOOLS: Set<string> = new Set(["Read", "NotebookRead"]);
12
+
13
+ export const WRITE_TOOLS: Set<string> = new Set([
14
+ "Edit",
15
+ "Write",
16
+ "NotebookEdit",
17
+ ]);
18
+
19
+ export const BASH_TOOLS: Set<string> = new Set([
20
+ "Bash",
21
+ "BashOutput",
22
+ "KillShell",
23
+ ]);
24
+
25
+ export const SEARCH_TOOLS: Set<string> = new Set(["Glob", "Grep", "LS"]);
26
+
27
+ export const WEB_TOOLS: Set<string> = new Set(["WebSearch", "WebFetch"]);
28
+
29
+ export const AGENT_TOOLS: Set<string> = new Set(["Task", "TodoWrite"]);
30
+
31
+ const BASE_ALLOWED_TOOLS = [
32
+ ...READ_TOOLS,
33
+ ...SEARCH_TOOLS,
34
+ ...WEB_TOOLS,
35
+ ...AGENT_TOOLS,
36
+ ];
37
+
38
+ const AUTO_ALLOWED_TOOLS: Record<string, Set<string>> = {
39
+ default: new Set(BASE_ALLOWED_TOOLS),
40
+ acceptEdits: new Set([...BASE_ALLOWED_TOOLS, ...WRITE_TOOLS]),
41
+ plan: new Set(BASE_ALLOWED_TOOLS),
42
+ };
491
43
 
492
- case "ExitPlanMode": {
493
- return { title: "Exited Plan Mode" };
494
- }
495
- default: {
496
- return toAcpContentUpdate(
497
- toolResult.content,
498
- "is_error" in toolResult ? toolResult.is_error : false,
499
- );
500
- }
44
+ export function isToolAllowedForMode(
45
+ toolName: string,
46
+ mode: TwigExecutionMode,
47
+ ): boolean {
48
+ if (mode === "bypassPermissions") {
49
+ return true;
501
50
  }
502
- }
503
-
504
- function toAcpContentUpdate(
505
- content: any,
506
- isError: boolean = false,
507
- ): { content?: ToolCallContent[] } {
508
- if (Array.isArray(content) && content.length > 0) {
509
- return {
510
- content: content.map((content: any) => ({
511
- type: "content",
512
- content:
513
- isError && content.type === "text"
514
- ? {
515
- ...content,
516
- text: `\`\`\`\n${content.text}\n\`\`\``,
517
- }
518
- : content,
519
- })),
520
- };
521
- } else if (typeof content === "string" && content.length > 0) {
522
- return {
523
- content: [
524
- {
525
- type: "content",
526
- content: {
527
- type: "text",
528
- text: isError ? `\`\`\`\n${content}\n\`\`\`` : content,
529
- },
530
- },
531
- ],
532
- };
51
+ if (AUTO_ALLOWED_TOOLS[mode]?.has(toolName) === true) {
52
+ return true;
533
53
  }
534
- return {};
535
- }
536
-
537
- export type ClaudePlanEntry = {
538
- content: string;
539
- status: "pending" | "in_progress" | "completed";
540
- activeForm: string;
541
- };
542
-
543
- export function planEntries(input: { todos: ClaudePlanEntry[] }): PlanEntry[] {
544
- return input.todos.map((input) => ({
545
- content: input.content,
546
- status: input.status,
547
- priority: "medium",
548
- }));
549
- }
550
-
551
- export function markdownEscape(text: string): string {
552
- let escapedText = "```";
553
- for (const [m] of text.matchAll(/^```+/gm)) {
554
- while (m.length >= escapedText.length) {
555
- escapedText += "`";
556
- }
54
+ if (isMcpToolReadOnly(toolName)) {
55
+ return true;
557
56
  }
558
- return `${escapedText}\n${text}${text.endsWith("\n") ? "" : "\n"}${escapedText}`;
57
+ return false;
559
58
  }
560
-
561
- /* A global variable to store callbacks that should be executed when receiving hooks from Claude Code */
562
- const toolUseCallbacks: {
563
- [toolUseId: string]: {
564
- onPostToolUseHook?: (
565
- toolUseID: string,
566
- toolInput: unknown,
567
- toolResponse: unknown,
568
- ) => Promise<void>;
569
- };
570
- } = {};
571
-
572
- /* Setup callbacks that will be called when receiving hooks from Claude Code */
573
- export const registerHookCallback = (
574
- toolUseID: string,
575
- {
576
- onPostToolUseHook,
577
- }: {
578
- onPostToolUseHook?: (
579
- toolUseID: string,
580
- toolInput: unknown,
581
- toolResponse: unknown,
582
- ) => Promise<void>;
583
- },
584
- ) => {
585
- toolUseCallbacks[toolUseID] = {
586
- onPostToolUseHook,
587
- };
588
- };
589
-
590
- /* A callback for Claude Code that is called when receiving a PostToolUse hook */
591
- export const createPostToolUseHook =
592
- (
593
- logger: Logger = new Logger({ prefix: "[createPostToolUseHook]" }),
594
- ): HookCallback =>
595
- async (
596
- input: HookInput,
597
- toolUseID: string | undefined,
598
- ): Promise<{ continue: boolean }> => {
599
- if (input.hook_event_name === "PostToolUse" && toolUseID) {
600
- const onPostToolUseHook = toolUseCallbacks[toolUseID]?.onPostToolUseHook;
601
- if (onPostToolUseHook) {
602
- await onPostToolUseHook(
603
- toolUseID,
604
- input.tool_input,
605
- input.tool_response,
606
- );
607
- delete toolUseCallbacks[toolUseID]; // Cleanup after execution
608
- } else {
609
- logger.error(
610
- `No onPostToolUseHook found for tool use ID: ${toolUseID}`,
611
- );
612
- delete toolUseCallbacks[toolUseID];
613
- }
614
- }
615
- return { continue: true };
616
- };