wave-agent-sdk 0.4.0 → 0.6.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 (190) hide show
  1. package/dist/agent.d.ts +42 -11
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +114 -115
  4. package/dist/constants/prompts.d.ts +18 -14
  5. package/dist/constants/prompts.d.ts.map +1 -1
  6. package/dist/constants/prompts.js +130 -54
  7. package/dist/constants/tools.d.ts +6 -3
  8. package/dist/constants/tools.d.ts.map +1 -1
  9. package/dist/constants/tools.js +6 -3
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +1 -0
  13. package/dist/managers/MemoryRuleManager.js +1 -1
  14. package/dist/managers/aiManager.d.ts +5 -3
  15. package/dist/managers/aiManager.d.ts.map +1 -1
  16. package/dist/managers/aiManager.js +57 -20
  17. package/dist/managers/backgroundBashManager.d.ts.map +1 -1
  18. package/dist/managers/backgroundBashManager.js +1 -0
  19. package/dist/managers/backgroundTaskManager.d.ts +35 -0
  20. package/dist/managers/backgroundTaskManager.d.ts.map +1 -0
  21. package/dist/managers/backgroundTaskManager.js +255 -0
  22. package/dist/managers/foregroundTaskManager.d.ts +9 -0
  23. package/dist/managers/foregroundTaskManager.d.ts.map +1 -0
  24. package/dist/managers/foregroundTaskManager.js +21 -0
  25. package/dist/managers/liveConfigManager.d.ts +1 -1
  26. package/dist/managers/lspManager.d.ts.map +1 -1
  27. package/dist/managers/lspManager.js +3 -1
  28. package/dist/managers/mcpManager.d.ts.map +1 -1
  29. package/dist/managers/messageManager.d.ts +26 -12
  30. package/dist/managers/messageManager.d.ts.map +1 -1
  31. package/dist/managers/messageManager.js +138 -64
  32. package/dist/managers/permissionManager.d.ts.map +1 -1
  33. package/dist/managers/permissionManager.js +26 -22
  34. package/dist/managers/planManager.d.ts +1 -1
  35. package/dist/managers/planManager.d.ts.map +1 -1
  36. package/dist/managers/planManager.js +2 -2
  37. package/dist/managers/pluginManager.d.ts.map +1 -1
  38. package/dist/managers/pluginManager.js +3 -2
  39. package/dist/managers/slashCommandManager.d.ts +6 -0
  40. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  41. package/dist/managers/slashCommandManager.js +8 -2
  42. package/dist/managers/subagentManager.d.ts +15 -2
  43. package/dist/managers/subagentManager.d.ts.map +1 -1
  44. package/dist/managers/subagentManager.js +153 -39
  45. package/dist/managers/toolManager.d.ts +18 -1
  46. package/dist/managers/toolManager.d.ts.map +1 -1
  47. package/dist/managers/toolManager.js +29 -5
  48. package/dist/services/GitService.d.ts.map +1 -1
  49. package/dist/services/GitService.js +6 -2
  50. package/dist/services/MarketplaceService.d.ts +2 -2
  51. package/dist/services/MarketplaceService.d.ts.map +1 -1
  52. package/dist/services/MarketplaceService.js +18 -11
  53. package/dist/services/MemoryRuleService.d.ts +1 -1
  54. package/dist/services/MemoryRuleService.d.ts.map +1 -1
  55. package/dist/services/MemoryRuleService.js +13 -2
  56. package/dist/services/aiService.d.ts +0 -1
  57. package/dist/services/aiService.d.ts.map +1 -1
  58. package/dist/services/aiService.js +4 -140
  59. package/dist/services/memory.d.ts +0 -3
  60. package/dist/services/memory.d.ts.map +1 -1
  61. package/dist/services/memory.js +1 -60
  62. package/dist/services/session.d.ts +15 -1
  63. package/dist/services/session.d.ts.map +1 -1
  64. package/dist/services/session.js +57 -1
  65. package/dist/services/taskManager.d.ts +21 -0
  66. package/dist/services/taskManager.d.ts.map +1 -0
  67. package/dist/services/taskManager.js +158 -0
  68. package/dist/tools/askUserQuestion.d.ts.map +1 -1
  69. package/dist/tools/askUserQuestion.js +39 -25
  70. package/dist/tools/bashTool.d.ts +0 -8
  71. package/dist/tools/bashTool.d.ts.map +1 -1
  72. package/dist/tools/bashTool.js +48 -172
  73. package/dist/tools/editTool.d.ts.map +1 -1
  74. package/dist/tools/editTool.js +8 -6
  75. package/dist/tools/exitPlanMode.d.ts.map +1 -1
  76. package/dist/tools/exitPlanMode.js +25 -1
  77. package/dist/tools/globTool.d.ts.map +1 -1
  78. package/dist/tools/globTool.js +8 -2
  79. package/dist/tools/grepTool.d.ts.map +1 -1
  80. package/dist/tools/grepTool.js +17 -6
  81. package/dist/tools/lsTool.d.ts.map +1 -1
  82. package/dist/tools/lsTool.js +3 -1
  83. package/dist/tools/multiEditTool.d.ts.map +1 -1
  84. package/dist/tools/multiEditTool.js +7 -6
  85. package/dist/tools/readTool.d.ts.map +1 -1
  86. package/dist/tools/readTool.js +16 -1
  87. package/dist/tools/taskManagementTools.d.ts +6 -0
  88. package/dist/tools/taskManagementTools.d.ts.map +1 -0
  89. package/dist/tools/taskManagementTools.js +453 -0
  90. package/dist/tools/taskOutputTool.d.ts +3 -0
  91. package/dist/tools/taskOutputTool.d.ts.map +1 -0
  92. package/dist/tools/taskOutputTool.js +173 -0
  93. package/dist/tools/taskStopTool.d.ts +3 -0
  94. package/dist/tools/taskStopTool.d.ts.map +1 -0
  95. package/dist/tools/taskStopTool.js +71 -0
  96. package/dist/tools/taskTool.d.ts.map +1 -1
  97. package/dist/tools/taskTool.js +110 -63
  98. package/dist/tools/types.d.ts +12 -0
  99. package/dist/tools/types.d.ts.map +1 -1
  100. package/dist/tools/writeTool.d.ts.map +1 -1
  101. package/dist/tools/writeTool.js +9 -1
  102. package/dist/types/index.d.ts +1 -0
  103. package/dist/types/index.d.ts.map +1 -1
  104. package/dist/types/index.js +1 -0
  105. package/dist/types/marketplace.d.ts +1 -0
  106. package/dist/types/marketplace.d.ts.map +1 -1
  107. package/dist/types/messaging.d.ts +3 -8
  108. package/dist/types/messaging.d.ts.map +1 -1
  109. package/dist/types/processes.d.ts +29 -4
  110. package/dist/types/processes.d.ts.map +1 -1
  111. package/dist/types/tasks.d.ts +13 -0
  112. package/dist/types/tasks.d.ts.map +1 -0
  113. package/dist/types/tasks.js +1 -0
  114. package/dist/types/tools.d.ts +4 -1
  115. package/dist/types/tools.d.ts.map +1 -1
  116. package/dist/utils/builtinSubagents.d.ts.map +1 -1
  117. package/dist/utils/builtinSubagents.js +38 -1
  118. package/dist/utils/cacheControlUtils.d.ts.map +1 -1
  119. package/dist/utils/cacheControlUtils.js +18 -12
  120. package/dist/utils/constants.d.ts +0 -4
  121. package/dist/utils/constants.d.ts.map +1 -1
  122. package/dist/utils/constants.js +0 -4
  123. package/dist/utils/convertMessagesForAPI.js +2 -2
  124. package/dist/utils/editUtils.d.ts +2 -11
  125. package/dist/utils/editUtils.d.ts.map +1 -1
  126. package/dist/utils/editUtils.js +52 -79
  127. package/dist/utils/messageOperations.d.ts +5 -36
  128. package/dist/utils/messageOperations.d.ts.map +1 -1
  129. package/dist/utils/messageOperations.js +9 -98
  130. package/dist/utils/nameGenerator.d.ts +1 -1
  131. package/dist/utils/nameGenerator.d.ts.map +1 -1
  132. package/dist/utils/nameGenerator.js +19 -3
  133. package/package.json +5 -5
  134. package/src/agent.ts +157 -134
  135. package/src/constants/prompts.ts +156 -65
  136. package/src/constants/tools.ts +6 -3
  137. package/src/index.ts +1 -0
  138. package/src/managers/MemoryRuleManager.ts +1 -1
  139. package/src/managers/aiManager.ts +77 -35
  140. package/src/managers/backgroundBashManager.ts +1 -0
  141. package/src/managers/backgroundTaskManager.ts +305 -0
  142. package/src/managers/foregroundTaskManager.ts +27 -0
  143. package/src/managers/lspManager.ts +3 -1
  144. package/src/managers/mcpManager.ts +6 -3
  145. package/src/managers/messageManager.ts +185 -75
  146. package/src/managers/permissionManager.ts +33 -28
  147. package/src/managers/planManager.ts +2 -2
  148. package/src/managers/pluginManager.ts +4 -3
  149. package/src/managers/slashCommandManager.ts +15 -2
  150. package/src/managers/subagentManager.ts +194 -35
  151. package/src/managers/toolManager.ts +48 -6
  152. package/src/services/GitService.ts +6 -2
  153. package/src/services/MarketplaceService.ts +30 -12
  154. package/src/services/MemoryRuleService.ts +18 -6
  155. package/src/services/aiService.ts +3 -145
  156. package/src/services/memory.ts +1 -73
  157. package/src/services/session.ts +73 -0
  158. package/src/services/taskManager.ts +188 -0
  159. package/src/tools/askUserQuestion.ts +51 -29
  160. package/src/tools/bashTool.ts +63 -196
  161. package/src/tools/editTool.ts +9 -18
  162. package/src/tools/exitPlanMode.ts +26 -2
  163. package/src/tools/globTool.ts +10 -2
  164. package/src/tools/grepTool.ts +17 -6
  165. package/src/tools/lsTool.ts +3 -1
  166. package/src/tools/multiEditTool.ts +7 -18
  167. package/src/tools/readTool.ts +17 -1
  168. package/src/tools/taskManagementTools.ts +498 -0
  169. package/src/tools/taskOutputTool.ts +196 -0
  170. package/src/tools/taskStopTool.ts +78 -0
  171. package/src/tools/taskTool.ts +136 -74
  172. package/src/tools/types.ts +13 -0
  173. package/src/tools/writeTool.ts +9 -2
  174. package/src/types/index.ts +1 -0
  175. package/src/types/marketplace.ts +1 -0
  176. package/src/types/messaging.ts +2 -9
  177. package/src/types/processes.ts +39 -4
  178. package/src/types/tasks.ts +13 -0
  179. package/src/types/tools.ts +4 -1
  180. package/src/utils/builtinSubagents.ts +47 -1
  181. package/src/utils/cacheControlUtils.ts +26 -18
  182. package/src/utils/constants.ts +0 -5
  183. package/src/utils/convertMessagesForAPI.ts +2 -2
  184. package/src/utils/editUtils.ts +65 -103
  185. package/src/utils/messageOperations.ts +12 -136
  186. package/src/utils/nameGenerator.ts +20 -3
  187. package/dist/tools/todoWriteTool.d.ts +0 -6
  188. package/dist/tools/todoWriteTool.d.ts.map +0 -1
  189. package/dist/tools/todoWriteTool.js +0 -220
  190. package/src/tools/todoWriteTool.ts +0 -257
@@ -0,0 +1,13 @@
1
+ export type TaskStatus = "pending" | "in_progress" | "completed" | "deleted";
2
+
3
+ export interface Task {
4
+ id: string;
5
+ subject: string;
6
+ description: string;
7
+ status: TaskStatus;
8
+ activeForm?: string;
9
+ owner?: string;
10
+ blocks: string[];
11
+ blockedBy: string[];
12
+ metadata: Record<string, unknown>;
13
+ }
@@ -8,7 +8,6 @@
8
8
  export interface AskUserQuestionOption {
9
9
  label: string;
10
10
  description?: string;
11
- isRecommended?: boolean;
12
11
  }
13
12
 
14
13
  export interface AskUserQuestion {
@@ -20,6 +19,10 @@ export interface AskUserQuestion {
20
19
 
21
20
  export interface AskUserQuestionInput {
22
21
  questions: AskUserQuestion[];
22
+ answers?: Record<string, string>;
23
+ metadata?: {
24
+ source?: string;
25
+ };
23
26
  }
24
27
 
25
28
  /**
@@ -1,4 +1,9 @@
1
- import { GENERAL_PURPOSE_SYSTEM_PROMPT } from "../constants/prompts.js";
1
+ import {
2
+ BASH_SUBAGENT_SYSTEM_PROMPT,
3
+ GENERAL_PURPOSE_SYSTEM_PROMPT,
4
+ PLAN_SUBAGENT_SYSTEM_PROMPT,
5
+ } from "../constants/prompts.js";
6
+ import { BASH_TOOL_NAME } from "../constants/tools.js";
2
7
  import type { SubagentConfiguration } from "./subagentParser.js";
3
8
 
4
9
  /**
@@ -7,12 +12,32 @@ import type { SubagentConfiguration } from "./subagentParser.js";
7
12
  */
8
13
  export function getBuiltinSubagents(): SubagentConfiguration[] {
9
14
  return [
15
+ createBashSubagent(),
10
16
  createExploreSubagent(),
11
17
  createGeneralPurposeSubagent(),
18
+ createPlanSubagent(),
12
19
  // Add more built-in subagents here as needed
13
20
  ];
14
21
  }
15
22
 
23
+ /**
24
+ * Create the Bash built-in subagent configuration
25
+ * Specialized for executing bash commands and git operations
26
+ */
27
+ function createBashSubagent(): SubagentConfiguration {
28
+ return {
29
+ name: "Bash",
30
+ description:
31
+ "Command execution specialist for running bash commands. Use this for git operations, command execution, and other terminal tasks.",
32
+ systemPrompt: BASH_SUBAGENT_SYSTEM_PROMPT,
33
+ tools: [BASH_TOOL_NAME],
34
+ model: "inherit",
35
+ filePath: "<builtin:Bash>",
36
+ scope: "builtin",
37
+ priority: 3,
38
+ };
39
+ }
40
+
16
41
  /**
17
42
  * Create the General-Purpose built-in subagent configuration
18
43
  * Specialized for multi-step research and implementation tasks
@@ -87,3 +112,24 @@ Complete the user's search request efficiently and report your findings clearly.
87
112
  priority: 3, // Lowest priority - can be overridden by user configs
88
113
  };
89
114
  }
115
+
116
+ /**
117
+ * Create the Plan built-in subagent configuration
118
+ * Specialized for designing implementation plans and exploring codebases in read-only mode
119
+ */
120
+ function createPlanSubagent(): SubagentConfiguration {
121
+ // Define allowed tools for read-only operations
122
+ const allowedTools = ["Glob", "Grep", "Read", "Bash", "LS", "LSP"];
123
+
124
+ return {
125
+ name: "Plan",
126
+ description:
127
+ "Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs.",
128
+ systemPrompt: PLAN_SUBAGENT_SYSTEM_PROMPT,
129
+ tools: allowedTools,
130
+ model: "inherit", // Uses parent agent's model
131
+ filePath: "<builtin:Plan>",
132
+ scope: "builtin",
133
+ priority: 3, // Lowest priority - can be overridden by user configs
134
+ };
135
+ }
@@ -316,14 +316,8 @@ export function transformMessagesForClaudeCache(
316
316
  // Find the latest interval message index (20th, 40th, 60th, etc.)
317
317
  const intervalMessageIndex = findIntervalMessageIndex(messages);
318
318
 
319
- // Find last system message index
320
- let lastSystemIndex = -1;
321
- for (let i = messages.length - 1; i >= 0; i--) {
322
- if (messages[i].role === "system") {
323
- lastSystemIndex = i;
324
- break;
325
- }
326
- }
319
+ // Find first system message index
320
+ const firstSystemIndex = messages.findIndex((m) => m.role === "system");
327
321
 
328
322
  const result = messages.map((message, index) => {
329
323
  // Validate message structure
@@ -332,14 +326,27 @@ export function transformMessagesForClaudeCache(
332
326
  return message; // Return as-is to avoid breaking the flow
333
327
  }
334
328
 
335
- // Last system message: always cached (hardcoded)
336
- if (message.role === "system" && index === lastSystemIndex) {
329
+ // First system message: always cached (hardcoded)
330
+ if (message.role === "system" && index === firstSystemIndex) {
331
+ const content =
332
+ (message.content as string | ChatCompletionContentPart[]) || "";
333
+ // If content is already an array, check if it already has cache control
334
+ if (Array.isArray(content)) {
335
+ const hasCacheControl = content.some(
336
+ (part) =>
337
+ part.type === "text" &&
338
+ (part as ClaudeChatCompletionContentPartText).cache_control,
339
+ );
340
+ if (hasCacheControl) {
341
+ return message;
342
+ }
343
+ }
344
+
345
+ const transformedContent = addCacheControlToContent(content, true);
346
+
337
347
  return {
338
348
  ...message,
339
- content: addCacheControlToContent(
340
- (message.content as string | ChatCompletionContentPart[]) || "",
341
- true,
342
- ),
349
+ content: transformedContent,
343
350
  } as ChatCompletionMessageParam;
344
351
  }
345
352
 
@@ -364,12 +371,13 @@ export function transformMessagesForClaudeCache(
364
371
  } as ChatCompletionMessageParam;
365
372
  } else {
366
373
  // For other message types without tool calls, cache the content
374
+ const content =
375
+ (message.content as string | ChatCompletionContentPart[]) || "";
376
+ const transformedContent = addCacheControlToContent(content, true);
377
+
367
378
  return {
368
379
  ...message,
369
- content: addCacheControlToContent(
370
- (message.content as string | ChatCompletionContentPart[]) || "",
371
- true,
372
- ),
380
+ content: transformedContent,
373
381
  } as ChatCompletionMessageParam;
374
382
  }
375
383
  }
@@ -31,8 +31,3 @@ export const USER_MEMORY_FILE = path.join(DATA_DIRECTORY, "AGENTS.md");
31
31
  */
32
32
  export const DEFAULT_WAVE_MAX_INPUT_TOKENS = 96000; // Default token limit
33
33
  export const DEFAULT_WAVE_MAX_OUTPUT_TOKENS = 8192; // Default output token limit
34
-
35
- /**
36
- * Default number of messages to keep uncompressed
37
- */
38
- export const DEFAULT_KEEP_LAST_MESSAGES_COUNT = 7;
@@ -57,8 +57,8 @@ export function convertMessagesForAPI(
57
57
  );
58
58
  if (compressBlock && compressBlock.type === "compress") {
59
59
  recentMessages.unshift({
60
- role: "system",
61
- content: `[Compressed Message Summary] ${compressBlock.content}`,
60
+ role: "assistant",
61
+ content: compressBlock.content,
62
62
  });
63
63
  }
64
64
  break;
@@ -1,128 +1,90 @@
1
- import { mkdir, writeFile } from "fs/promises";
2
- import path from "path";
3
- import os from "os";
4
- import { logger } from "./globalLogger.js";
5
-
6
1
  /**
7
2
  * Utility functions for file editing tools
8
3
  */
9
4
 
10
5
  /**
11
- * Find a match in content that is identical to searchString except for a consistent indentation offset.
12
- *
13
- * Priority:
14
- * 1. If exact matches exist, returns searchString (letting the tool handle uniqueness/replaceAll).
15
- * 2. If no exact match, but exactly one unique indentation-insensitive match exists, returns that match.
16
- * 3. Otherwise returns null.
6
+ * Escape regular expression special characters
7
+ */
8
+ export function escapeRegExp(string: string): string {
9
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10
+ }
11
+
12
+ /**
13
+ * Analyze why an edit failed by finding the best partial match and highlighting mismatches.
17
14
  */
18
- export function findIndentationInsensitiveMatch(
15
+ export function analyzeEditMismatch(
19
16
  content: string,
20
17
  searchString: string,
21
- ): string | null {
22
- // 1. If exact match exists, return it
23
- if (content.includes(searchString)) {
24
- return searchString;
25
- }
26
-
18
+ ): string {
19
+ const contentLines = content.split("\n");
27
20
  const searchLines = searchString.split("\n");
28
- if (searchLines.length === 0) return null;
29
21
 
30
- const contentLines = content.split("\n");
31
- let foundMatch: string | null = null;
22
+ if (searchLines.length === 0 || contentLines.length === 0) {
23
+ return "old_string not found in file (empty search or content)";
24
+ }
32
25
 
33
- for (let i = 0; i <= contentLines.length - searchLines.length; i++) {
34
- let offset: number | null = null;
35
- let isMatch = true;
26
+ let bestMatchIndex = -1;
27
+ let bestMatchScore = -1;
36
28
 
29
+ // Sliding window to find the best partial match
30
+ for (let i = 0; i <= contentLines.length - searchLines.length; i++) {
31
+ let currentScore = 0;
37
32
  for (let j = 0; j < searchLines.length; j++) {
38
- const sLine = searchLines[j];
39
- const cLine = contentLines[i + j];
40
-
41
- const sTrimmed = sLine.trimStart();
42
- const cTrimmed = cLine.trimStart();
43
-
44
- // If trimmed content doesn't match, it's not a match
45
- if (sTrimmed !== cTrimmed) {
46
- isMatch = false;
47
- break;
33
+ if (contentLines[i + j] === searchLines[j]) {
34
+ currentScore++;
48
35
  }
36
+ }
49
37
 
50
- // For non-empty lines, check for consistent indentation offset
51
- if (sTrimmed !== "") {
52
- const sIndent = sLine.length - sTrimmed.length;
53
- const cIndent = cLine.length - cTrimmed.length;
54
- const currentOffset = cIndent - sIndent;
38
+ // Heuristic: prioritize matches where first or last lines match
39
+ if (contentLines[i] === searchLines[0]) currentScore += 0.5;
40
+ if (
41
+ contentLines[i + searchLines.length - 1] ===
42
+ searchLines[searchLines.length - 1]
43
+ )
44
+ currentScore += 0.5;
55
45
 
56
- if (offset === null) {
57
- offset = currentOffset;
58
- } else if (offset !== currentOffset) {
59
- isMatch = false;
60
- break;
61
- }
46
+ // Also consider trimmed matches to catch indentation issues
47
+ for (let j = 0; j < searchLines.length; j++) {
48
+ if (
49
+ contentLines[i + j].trim() === searchLines[j].trim() &&
50
+ contentLines[i + j] !== searchLines[j]
51
+ ) {
52
+ currentScore += 0.1;
62
53
  }
63
54
  }
64
55
 
65
- if (isMatch) {
66
- const matchCandidate = contentLines
67
- .slice(i, i + searchLines.length)
68
- .join("\n");
69
- if (foundMatch !== null && foundMatch !== matchCandidate) {
70
- // Multiple different smart matches found
71
- return null;
72
- }
73
- foundMatch = matchCandidate;
56
+ if (currentScore > bestMatchScore) {
57
+ bestMatchScore = currentScore;
58
+ bestMatchIndex = i;
74
59
  }
75
60
  }
76
61
 
77
- // If we found exactly one unique smart match (or multiple instances of the same smart match)
78
- // return it. The tool will then check for uniqueness if replaceAll is false.
79
- return foundMatch;
80
- }
81
-
82
- /**
83
- * Escape regular expression special characters
84
- */
85
- export function escapeRegExp(string: string): string {
86
- return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
87
- }
88
-
89
- /**
90
- * Save a snapshot of the old string and current file content for debugging
91
- */
92
- export async function saveEditErrorSnapshot(
93
- filePath: string,
94
- oldString: string,
95
- currentContent: string,
96
- toolName: string,
97
- ): Promise<string | null> {
98
- try {
99
- const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
100
- const fileName = path.basename(filePath);
101
- const snapshotDirName = `${timestamp}_${toolName}_${fileName}`;
102
- const snapshotDir = path.join(
103
- os.tmpdir(),
104
- "wave-agent-edit-errors",
105
- snapshotDirName,
106
- );
107
-
108
- await mkdir(snapshotDir, { recursive: true });
109
-
110
- await Promise.all([
111
- writeFile(path.join(snapshotDir, "old_string.txt"), oldString, "utf-8"),
112
- writeFile(
113
- path.join(snapshotDir, "file_content.txt"),
114
- currentContent,
115
- "utf-8",
116
- ),
117
- ]);
118
-
119
- logger.error(
120
- `Edit error snapshot saved to: ${snapshotDir}\nFile: ${filePath}\nTool: ${toolName}`,
121
- );
62
+ // If no decent match found (score <= 0), return generic message
63
+ if (bestMatchScore <= 0) {
64
+ return "old_string not found in file (no similar block found)";
65
+ }
122
66
 
123
- return snapshotDir;
124
- } catch (error) {
125
- logger.error(`Failed to save edit error snapshot: ${error}`);
126
- return null;
67
+ // Generate detailed report
68
+ const reportLines: string[] = [
69
+ `old_string not found in file. Best partial match found at line ${bestMatchIndex + 1}:`,
70
+ ];
71
+
72
+ for (let j = 0; j < searchLines.length; j++) {
73
+ const lineNum = bestMatchIndex + j + 1;
74
+ const actualLine = contentLines[bestMatchIndex + j];
75
+ const expectedLine = searchLines[j];
76
+
77
+ if (actualLine === expectedLine) {
78
+ reportLines.push(`${lineNum.toString().padStart(4)} | ${actualLine}`);
79
+ } else {
80
+ reportLines.push(
81
+ `${lineNum.toString().padStart(4)} | - ${expectedLine} (expected)`,
82
+ );
83
+ reportLines.push(
84
+ `${lineNum.toString().padStart(4)} | + ${actualLine} (actual)`,
85
+ );
86
+ }
127
87
  }
88
+
89
+ return reportLines.join("\n");
128
90
  }
@@ -1,6 +1,5 @@
1
1
  import type { Message, Usage } from "../types/index.js";
2
2
  import { MessageSource } from "../types/index.js";
3
- import { DEFAULT_KEEP_LAST_MESSAGES_COUNT } from "./constants.js";
4
3
  import type { SubagentConfiguration } from "./subagentParser.js";
5
4
  import { readFileSync } from "fs";
6
5
  import { extname } from "path";
@@ -40,6 +39,7 @@ export interface UpdateToolBlockParams {
40
39
  images?: Array<{ data: string; mediaType?: string }>;
41
40
  compactParams?: string;
42
41
  parametersChunk?: string; // Incremental parameter updates for streaming
42
+ isManuallyBackgrounded?: boolean;
43
43
  }
44
44
 
45
45
  // Agent specific interfaces (without messages parameter)
@@ -53,14 +53,6 @@ export interface AddErrorBlockParams {
53
53
  error: string;
54
54
  }
55
55
 
56
- export interface AddMemoryBlockParams {
57
- messages: Message[];
58
- content: string;
59
- isSuccess: boolean;
60
- memoryType?: "project" | "user";
61
- storagePath?: string;
62
- }
63
-
64
56
  export interface AddCommandOutputParams {
65
57
  messages: Message[];
66
58
  command: string;
@@ -78,26 +70,6 @@ export interface CompleteCommandParams {
78
70
  exitCode: number;
79
71
  }
80
72
 
81
- /**
82
- * Extract text content from user messages in the messages array
83
- * Excludes messages with source HOOK to prevent hook-generated content from entering user history
84
- */
85
- export const extractUserInputHistory = (messages: Message[]): string[] => {
86
- return messages
87
- .filter((message) => message.role === "user")
88
- .map((message) => {
89
- // Extract text block content, excluding HOOK-sourced blocks
90
- const textBlocks = message.blocks.filter(
91
- (block) => block.type === "text" && block.source !== MessageSource.HOOK,
92
- );
93
- return textBlocks
94
- .map((block) => (block as { content: string }).content)
95
- .join(" ")
96
- .trim();
97
- })
98
- .filter((text) => text.length > 0); // Filter out empty text
99
- };
100
-
101
73
  /**
102
74
  * Convert image file path to base64 format
103
75
  * @param imagePath Image file path
@@ -230,6 +202,7 @@ export const updateToolBlockInMessage = ({
230
202
  images,
231
203
  compactParams,
232
204
  parametersChunk,
205
+ isManuallyBackgrounded,
233
206
  }: UpdateToolBlockParams): Message[] => {
234
207
  const newMessages = [...messages];
235
208
  // Find the last assistant message
@@ -253,6 +226,8 @@ export const updateToolBlockInMessage = ({
253
226
  toolBlock.compactParams = compactParams;
254
227
  if (parametersChunk !== undefined)
255
228
  toolBlock.parametersChunk = parametersChunk;
229
+ if (isManuallyBackgrounded !== undefined)
230
+ toolBlock.isManuallyBackgrounded = isManuallyBackgrounded;
256
231
  }
257
232
  } else {
258
233
  // If existing block not found, create new one
@@ -270,6 +245,7 @@ export const updateToolBlockInMessage = ({
270
245
  stage: stage ?? "start",
271
246
  compactParams: compactParams,
272
247
  parametersChunk: parametersChunk,
248
+ isManuallyBackgrounded: isManuallyBackgrounded,
273
249
  });
274
250
  }
275
251
  break;
@@ -315,113 +291,6 @@ export const addErrorBlockToMessage = ({
315
291
  return newMessages;
316
292
  };
317
293
 
318
- // Add Memory Block as new assistant message
319
- export const addMemoryBlockToMessage = ({
320
- messages,
321
- content,
322
- isSuccess,
323
- memoryType,
324
- storagePath,
325
- }: AddMemoryBlockParams): Message[] => {
326
- const newMessages = [...messages];
327
-
328
- // Create new assistant message containing MemoryBlock
329
- const memoryMessage: Message = {
330
- role: "assistant",
331
- blocks: [
332
- {
333
- type: "memory",
334
- content,
335
- isSuccess,
336
- memoryType,
337
- storagePath,
338
- },
339
- ],
340
- };
341
-
342
- // Add to end of message list
343
- newMessages.push(memoryMessage);
344
- return newMessages;
345
- };
346
-
347
- /**
348
- * Count valid blocks from the end
349
- * Only text, image, and tool type blocks are counted
350
- * @param messages Message array
351
- * @param targetCount Number of valid blocks to count
352
- * @returns { messageIndex: number, blockCount: number } Message index and actual counted block count
353
- */
354
- export const countValidBlocksFromEnd = (
355
- messages: Message[],
356
- targetCount: number,
357
- ): { messageIndex: number; blockCount: number } => {
358
- let validBlockCount = 0;
359
-
360
- // Iterate messages from end to beginning
361
- for (let i = messages.length - 1; i >= 0; i--) {
362
- const message = messages[i];
363
-
364
- // Iterate through all blocks of current message
365
- for (const block of message.blocks) {
366
- // Only count valid block types
367
- if (
368
- block.type === "text" ||
369
- block.type === "image" ||
370
- block.type === "tool"
371
- ) {
372
- validBlockCount++;
373
-
374
- // If target count reached, return current message index
375
- if (validBlockCount >= targetCount) {
376
- return { messageIndex: i, blockCount: validBlockCount };
377
- }
378
- }
379
- }
380
- }
381
-
382
- // If target count not reached, return index 0
383
- return { messageIndex: 0, blockCount: validBlockCount };
384
- };
385
-
386
- /**
387
- * Get messages to be compressed and insertion position
388
- * @param messages Message array
389
- * @param keepLastCount Keep the last few valid blocks uncompressed
390
- * @returns { messagesToCompress: Message[], insertIndex: number }
391
- */
392
- export const getMessagesToCompress = (
393
- messages: Message[],
394
- keepLastCount: number = DEFAULT_KEEP_LAST_MESSAGES_COUNT,
395
- ): { messagesToCompress: Message[]; insertIndex: number } => {
396
- // Calculate message position to keep from end to beginning
397
- const { messageIndex } = countValidBlocksFromEnd(messages, keepLastCount);
398
-
399
- // Find the last message containing compression block
400
- let lastCompressIndex = -1;
401
- for (let i = messages.length - 1; i >= 0; i--) {
402
- const hasCompressBlock = messages[i].blocks.some(
403
- (block) => block.type === "compress",
404
- );
405
- if (hasCompressBlock) {
406
- lastCompressIndex = i;
407
- break;
408
- }
409
- }
410
-
411
- // Determine compression start position
412
- // If compression block exists, start from compression block position (include compression block)
413
- // If no compression block, start from beginning
414
- const startIndex = lastCompressIndex >= 0 ? lastCompressIndex : 0;
415
-
416
- // Messages to compress are all messages from start position to before calculated position
417
- const messagesToCompress = messages.slice(startIndex, messageIndex);
418
-
419
- // Change insertion position to negative number, indicating position from end
420
- const insertIndex = messageIndex - messages.length;
421
-
422
- return { messagesToCompress, insertIndex };
423
- };
424
-
425
294
  // Add command output block to message list
426
295
  export const addCommandOutputMessage = ({
427
296
  messages,
@@ -504,6 +373,7 @@ export interface AddSubagentBlockParams {
504
373
  status: "active" | "completed" | "error" | "aborted";
505
374
  sessionId: string;
506
375
  configuration: SubagentConfiguration;
376
+ runInBackground?: boolean;
507
377
  }
508
378
 
509
379
  export interface UpdateSubagentBlockParams {
@@ -520,6 +390,7 @@ export const addSubagentBlockToMessage = ({
520
390
  status,
521
391
  sessionId,
522
392
  configuration,
393
+ runInBackground,
523
394
  }: AddSubagentBlockParams): Message[] => {
524
395
  const newMessages = [...messages];
525
396
 
@@ -544,6 +415,7 @@ export const addSubagentBlockToMessage = ({
544
415
  status,
545
416
  sessionId,
546
417
  configuration,
418
+ runInBackground,
547
419
  });
548
420
 
549
421
  return newMessages;
@@ -555,6 +427,7 @@ export const updateSubagentBlockInMessage = (
555
427
  updates: Partial<{
556
428
  status: "active" | "completed" | "error" | "aborted";
557
429
  sessionId: string;
430
+ runInBackground: boolean;
558
431
  }>,
559
432
  ): Message[] => {
560
433
  const newMessages = [...messages];
@@ -571,6 +444,9 @@ export const updateSubagentBlockInMessage = (
571
444
  if (updates.sessionId !== undefined) {
572
445
  block.sessionId = updates.sessionId;
573
446
  }
447
+ if (updates.runInBackground !== undefined) {
448
+ block.runInBackground = updates.runInBackground;
449
+ }
574
450
  return newMessages;
575
451
  }
576
452
  }
@@ -71,8 +71,25 @@ const nouns = [
71
71
  /**
72
72
  * Generates a random English name (adjective-noun)
73
73
  */
74
- export function generateRandomName(): string {
75
- const adj = adjectives[Math.floor(Math.random() * adjectives.length)];
76
- const noun = nouns[Math.floor(Math.random() * nouns.length)];
74
+ export function generateRandomName(seed?: string): string {
75
+ let adjIndex: number;
76
+ let nounIndex: number;
77
+
78
+ if (seed) {
79
+ // Simple hash function to derive indices from seed
80
+ let hash = 0;
81
+ for (let i = 0; i < seed.length; i++) {
82
+ hash = (hash << 5) - hash + seed.charCodeAt(i);
83
+ hash |= 0; // Convert to 32bit integer
84
+ }
85
+ adjIndex = Math.abs(hash) % adjectives.length;
86
+ nounIndex = Math.abs(hash >> 8) % nouns.length;
87
+ } else {
88
+ adjIndex = Math.floor(Math.random() * adjectives.length);
89
+ nounIndex = Math.floor(Math.random() * nouns.length);
90
+ }
91
+
92
+ const adj = adjectives[adjIndex];
93
+ const noun = nouns[nounIndex];
77
94
  return `${adj}-${noun}`;
78
95
  }
@@ -1,6 +0,0 @@
1
- import type { ToolPlugin } from "./types.js";
2
- /**
3
- * TodoWrite tool for creating and managing structured task lists
4
- */
5
- export declare const todoWriteTool: ToolPlugin;
6
- //# sourceMappingURL=todoWriteTool.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"todoWriteTool.d.ts","sourceRoot":"","sources":["../../src/tools/todoWriteTool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAStE;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,UAoP3B,CAAC"}