wave-agent-sdk 1.0.5 → 1.0.7

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 (202) hide show
  1. package/dist/agent.d.ts +34 -1
  2. package/dist/agent.js +49 -4
  3. package/dist/index.d.ts +3 -1
  4. package/dist/index.js +1 -1
  5. package/dist/managers/aiManager.d.ts +36 -0
  6. package/dist/managers/aiManager.js +240 -11
  7. package/dist/managers/messageManager.d.ts +2 -4
  8. package/dist/managers/messageManager.js +14 -7
  9. package/dist/managers/slashCommandManager.js +28 -0
  10. package/dist/managers/subagentManager.d.ts +0 -2
  11. package/dist/services/aiService.d.ts +2 -3
  12. package/dist/services/aiService.js +4 -18
  13. package/dist/services/configurationService.d.ts +7 -0
  14. package/dist/services/configurationService.js +10 -0
  15. package/dist/telemetry/instrumentation.d.ts +1 -1
  16. package/dist/telemetry/instrumentation.js +57 -28
  17. package/dist/tools/bashTool.js +16 -6
  18. package/dist/tools/grepTool.js +11 -3
  19. package/dist/types/core.d.ts +1 -34
  20. package/dist/types/index.d.ts +4 -0
  21. package/dist/types/messaging.d.ts +2 -0
  22. package/dist/utils/cacheControlUtils.d.ts +5 -27
  23. package/dist/utils/cacheControlUtils.js +12 -73
  24. package/dist/utils/containerSetup.js +0 -3
  25. package/dist/utils/messageOperations.d.ts +2 -1
  26. package/dist/utils/messageOperations.js +2 -1
  27. package/dist/utils/notificationXml.js +5 -0
  28. package/dist/utils/ripgrep.d.ts +4 -4
  29. package/dist/utils/ripgrep.js +4 -27
  30. package/package.json +20 -8
  31. package/bin/rg +0 -79
  32. package/dist/managers/forkedAgentManager.d.ts +0 -50
  33. package/dist/managers/forkedAgentManager.js +0 -130
  34. package/scripts/install_ripgrep.js +0 -111
  35. package/scripts/postinstall.js +0 -38
  36. package/src/agent.ts +0 -1271
  37. package/src/constants/subagents.ts +0 -4
  38. package/src/constants/toolLimits.ts +0 -15
  39. package/src/constants/tools.ts +0 -24
  40. package/src/core/plugin.ts +0 -237
  41. package/src/core/session.ts +0 -9
  42. package/src/index.ts +0 -38
  43. package/src/managers/MemoryRuleManager.ts +0 -198
  44. package/src/managers/aiManager.ts +0 -2429
  45. package/src/managers/backgroundTaskManager.ts +0 -480
  46. package/src/managers/bangManager.ts +0 -119
  47. package/src/managers/cronManager.ts +0 -401
  48. package/src/managers/foregroundTaskManager.ts +0 -30
  49. package/src/managers/forkedAgentManager.ts +0 -216
  50. package/src/managers/hookManager.ts +0 -1089
  51. package/src/managers/liveConfigManager.ts +0 -445
  52. package/src/managers/lspManager.ts +0 -470
  53. package/src/managers/mcpManager.ts +0 -1010
  54. package/src/managers/messageManager.ts +0 -1184
  55. package/src/managers/messageQueue.ts +0 -157
  56. package/src/managers/permissionManager.ts +0 -1140
  57. package/src/managers/planManager.ts +0 -112
  58. package/src/managers/pluginManager.ts +0 -330
  59. package/src/managers/pluginScopeManager.ts +0 -124
  60. package/src/managers/reversionManager.ts +0 -178
  61. package/src/managers/skillManager.ts +0 -578
  62. package/src/managers/slashCommandManager.ts +0 -564
  63. package/src/managers/subagentManager.ts +0 -915
  64. package/src/managers/toolManager.ts +0 -489
  65. package/src/managers/workflowManager.ts +0 -493
  66. package/src/prompts/autoMemory.ts +0 -33
  67. package/src/prompts/autoMemoryExtraction.ts +0 -146
  68. package/src/prompts/index.ts +0 -506
  69. package/src/prompts/planModeReminders.ts +0 -126
  70. package/src/services/GitService.ts +0 -131
  71. package/src/services/MarketplaceService.ts +0 -1052
  72. package/src/services/MemoryRuleService.ts +0 -71
  73. package/src/services/aiService.ts +0 -919
  74. package/src/services/authService.ts +0 -565
  75. package/src/services/autoMemoryService.ts +0 -280
  76. package/src/services/configurationService.ts +0 -1520
  77. package/src/services/fileWatcher.ts +0 -319
  78. package/src/services/hook.ts +0 -348
  79. package/src/services/initializationService.ts +0 -357
  80. package/src/services/interactionService.ts +0 -229
  81. package/src/services/jsonlHandler.ts +0 -295
  82. package/src/services/memory.ts +0 -234
  83. package/src/services/pluginLoader.ts +0 -252
  84. package/src/services/remoteSettingsService.ts +0 -366
  85. package/src/services/reversionService.ts +0 -122
  86. package/src/services/session.ts +0 -917
  87. package/src/services/taskManager.ts +0 -321
  88. package/src/telemetry/events.ts +0 -65
  89. package/src/telemetry/instrumentation.ts +0 -499
  90. package/src/telemetry/sessionTracing.ts +0 -348
  91. package/src/tools/agentTool.ts +0 -322
  92. package/src/tools/askUserQuestion.ts +0 -160
  93. package/src/tools/bashTool.ts +0 -621
  94. package/src/tools/buildTool.ts +0 -61
  95. package/src/tools/cronCreateTool.ts +0 -161
  96. package/src/tools/cronDeleteTool.ts +0 -51
  97. package/src/tools/cronListTool.ts +0 -47
  98. package/src/tools/editTool.ts +0 -313
  99. package/src/tools/enterPlanMode.ts +0 -124
  100. package/src/tools/enterWorktreeTool.ts +0 -190
  101. package/src/tools/exitPlanMode.ts +0 -139
  102. package/src/tools/exitWorktreeTool.ts +0 -236
  103. package/src/tools/globTool.ts +0 -167
  104. package/src/tools/grepTool.ts +0 -399
  105. package/src/tools/lspTool.ts +0 -883
  106. package/src/tools/readTool.ts +0 -426
  107. package/src/tools/skillTool.ts +0 -248
  108. package/src/tools/taskManagementTools.ts +0 -574
  109. package/src/tools/taskStopTool.ts +0 -78
  110. package/src/tools/types.ts +0 -137
  111. package/src/tools/webFetchTool.ts +0 -373
  112. package/src/tools/workflowTool.ts +0 -205
  113. package/src/tools/writeTool.ts +0 -267
  114. package/src/types/agent.ts +0 -128
  115. package/src/types/auth.ts +0 -19
  116. package/src/types/commands.ts +0 -30
  117. package/src/types/config.ts +0 -43
  118. package/src/types/configuration.ts +0 -168
  119. package/src/types/core.ts +0 -105
  120. package/src/types/cron.ts +0 -13
  121. package/src/types/environment.ts +0 -106
  122. package/src/types/fileSearch.ts +0 -4
  123. package/src/types/history.ts +0 -7
  124. package/src/types/hooks.ts +0 -256
  125. package/src/types/index.ts +0 -41
  126. package/src/types/lsp.ts +0 -98
  127. package/src/types/marketplace.ts +0 -71
  128. package/src/types/mcp.ts +0 -43
  129. package/src/types/memoryRule.ts +0 -31
  130. package/src/types/messaging.ts +0 -114
  131. package/src/types/permissions.ts +0 -80
  132. package/src/types/plugins.ts +0 -39
  133. package/src/types/processes.ts +0 -99
  134. package/src/types/reversion.ts +0 -29
  135. package/src/types/session.ts +0 -12
  136. package/src/types/skills.ts +0 -108
  137. package/src/types/tasks.ts +0 -13
  138. package/src/types/telemetry.ts +0 -98
  139. package/src/types/tools.ts +0 -44
  140. package/src/types/workflow.ts +0 -6
  141. package/src/utils/abortUtils.ts +0 -118
  142. package/src/utils/atomicWrite.ts +0 -61
  143. package/src/utils/bashParser.ts +0 -899
  144. package/src/utils/cacheControlUtils.ts +0 -426
  145. package/src/utils/commandArgumentParser.ts +0 -161
  146. package/src/utils/commandPathResolver.ts +0 -88
  147. package/src/utils/configPaths.ts +0 -220
  148. package/src/utils/configValidator.ts +0 -100
  149. package/src/utils/constants.ts +0 -39
  150. package/src/utils/container.ts +0 -92
  151. package/src/utils/containerSetup.ts +0 -371
  152. package/src/utils/convertMessagesForAPI.ts +0 -326
  153. package/src/utils/cronTasks.ts +0 -128
  154. package/src/utils/cronTasksLock.ts +0 -182
  155. package/src/utils/cronToHuman.ts +0 -99
  156. package/src/utils/customCommands.ts +0 -95
  157. package/src/utils/editUtils.ts +0 -20
  158. package/src/utils/fileFormat.ts +0 -40
  159. package/src/utils/fileSearch.ts +0 -151
  160. package/src/utils/fileUtils.ts +0 -223
  161. package/src/utils/gitUtils.ts +0 -315
  162. package/src/utils/globalLogger.ts +0 -128
  163. package/src/utils/groupMessagesByApiRound.ts +0 -121
  164. package/src/utils/hookMatcher.ts +0 -176
  165. package/src/utils/markdownParser.ts +0 -317
  166. package/src/utils/mcpUtils.ts +0 -138
  167. package/src/utils/messageOperations.ts +0 -659
  168. package/src/utils/modelCapabilities.ts +0 -30
  169. package/src/utils/nameGenerator.ts +0 -99
  170. package/src/utils/notificationXml.ts +0 -57
  171. package/src/utils/openaiClient.ts +0 -251
  172. package/src/utils/parseCronExpression.ts +0 -78
  173. package/src/utils/path.ts +0 -72
  174. package/src/utils/pathEncoder.ts +0 -400
  175. package/src/utils/pathSafety.ts +0 -40
  176. package/src/utils/promptHistory.ts +0 -168
  177. package/src/utils/ripgrep.ts +0 -35
  178. package/src/utils/shellResolver.ts +0 -190
  179. package/src/utils/skillParser.ts +0 -246
  180. package/src/utils/stringUtils.ts +0 -156
  181. package/src/utils/subagentParser.ts +0 -279
  182. package/src/utils/taskReminder.ts +0 -96
  183. package/src/utils/tokenCalculation.ts +0 -43
  184. package/src/utils/tokenEstimate.ts +0 -34
  185. package/src/utils/toolResultStorage.ts +0 -117
  186. package/src/utils/worktreeSession.ts +0 -26
  187. package/src/utils/worktreeUtils.ts +0 -786
  188. package/src/workflow/budgetTracker.ts +0 -34
  189. package/src/workflow/concurrencyLimiter.ts +0 -47
  190. package/src/workflow/journal.ts +0 -95
  191. package/src/workflow/progressReporter.ts +0 -141
  192. package/src/workflow/runState.ts +0 -65
  193. package/src/workflow/scriptRuntime.ts +0 -274
  194. package/src/workflow/structuredOutput.ts +0 -123
  195. package/src/workflow/types.ts +0 -101
  196. package/src/workflow/workflowApis.ts +0 -410
  197. package/vendor/ripgrep/linux-aarch64/rg +0 -0
  198. package/vendor/ripgrep/linux-x86_64/rg +0 -0
  199. package/vendor/ripgrep/macos-aarch64/rg +0 -0
  200. package/vendor/ripgrep/macos-x86_64/rg +0 -0
  201. package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
  202. package/vendor/ripgrep/windows-x86_64/rg.exe +0 -0
@@ -1,348 +0,0 @@
1
- /**
2
- * Session Tracing -- OpenTelemetry Span Management
3
- *
4
- * Provides span creation/ending APIs for interactions, LLM requests, and tool
5
- * executions. Uses two independent AsyncLocalStorage contexts:
6
- * - interactionContext: holds the interaction span for the entire turn
7
- * - toolContext: holds the current tool span (cleared on end)
8
- *
9
- * LLM request spans do not enter any ALS; they are passed explicitly to
10
- * endLLMRequestSpan, aligning with Claude Code's approach.
11
- */
12
-
13
- import { AsyncLocalStorage } from "node:async_hooks";
14
- import { createHash } from "node:crypto";
15
- import type { Span } from "@opentelemetry/api";
16
- import {
17
- getOTELApi,
18
- isInitialized,
19
- getCurrentConfig,
20
- } from "./instrumentation.js";
21
- import { logOTelEvent } from "./events.js";
22
- import type {
23
- LLMRequestMetadata,
24
- LLMRequestInput,
25
- ToolMetadata,
26
- } from "../types/telemetry.js";
27
-
28
- // -- Constants --
29
-
30
- /** Max content length for span attributes (60KB, aligning with Claude Code) */
31
- const MAX_CONTENT_LENGTH = 60000;
32
-
33
- // -- AsyncLocalStorage for context propagation --
34
-
35
- const interactionContext = new AsyncLocalStorage<Span | undefined>();
36
- const toolContext = new AsyncLocalStorage<Span | undefined>();
37
-
38
- // -- Incremental tracking state (module-level, per session) --
39
-
40
- /** Number of messages already reported in new_context; only deltas are sent */
41
- let lastReportedMessageCount = 0;
42
- /** Set of system prompt hashes already emitted via logOTelEvent */
43
- const seenSystemPromptHashes = new Set<string>();
44
- /** Set of tool schema hashes already emitted via logOTelEvent */
45
- const seenToolSchemaHashes = new Set<string>();
46
-
47
- // -- Helpers --
48
-
49
- /** Computes SHA-256 hash of content, returns first 12 hex characters */
50
- function hashContent(content: string): string {
51
- return createHash("sha256").update(content).digest("hex").substring(0, 12);
52
- }
53
-
54
- /** Truncates content to MAX_CONTENT_LENGTH, returns [truncated, originalLength] */
55
- function truncateContent(content: string): [string, number] {
56
- const originalLength = content.length;
57
- if (originalLength > MAX_CONTENT_LENGTH) {
58
- return [content.substring(0, MAX_CONTENT_LENGTH), originalLength];
59
- }
60
- return [content, originalLength];
61
- }
62
-
63
- // -- Tracer accessor --
64
-
65
- function getTracer() {
66
- if (!isInitialized()) return undefined;
67
- const otelApi = getOTELApi();
68
- if (!otelApi) return undefined;
69
- return otelApi.trace.getTracer("wave");
70
- }
71
-
72
- // -- Public API --
73
-
74
- /**
75
- * Creates an interaction span for a user turn.
76
- * The span is stored in interactionContext for the duration of the turn.
77
- */
78
- export function startInteractionSpan(
79
- userPrompt: string,
80
- sequence: number,
81
- ): Span | undefined {
82
- const tracer = getTracer();
83
- if (!tracer) return undefined;
84
-
85
- const config = getCurrentConfig();
86
- const attributes: Record<string, string | number> = {
87
- "span.type": "interaction",
88
- user_prompt_length: userPrompt.length,
89
- "interaction.sequence": sequence,
90
- };
91
- if (config?.logUserPrompts) {
92
- attributes.user_prompt = userPrompt;
93
- }
94
-
95
- const span = tracer.startSpan("interaction", { attributes });
96
- interactionContext.enterWith(span);
97
- return span;
98
- }
99
-
100
- /**
101
- * Ends the current interaction span and clears the context.
102
- */
103
- export function endInteractionSpan(): void {
104
- const span = interactionContext.getStore();
105
- if (!span) return;
106
- span.end();
107
- interactionContext.enterWith(undefined);
108
- }
109
-
110
- /**
111
- * Resets incremental tracing state. Should be called after compaction to
112
- * prevent incorrect delta calculations.
113
- */
114
- export function resetTracingState(): void {
115
- lastReportedMessageCount = 0;
116
- seenSystemPromptHashes.clear();
117
- seenToolSchemaHashes.clear();
118
- }
119
-
120
- /**
121
- * Creates an LLM request span as a child of the interaction span.
122
- * Does NOT enter any ALS — the span must be passed explicitly to endLLMRequestSpan.
123
- *
124
- * When logToolContent is enabled, captures:
125
- * - system_prompt_hash / system_prompt_preview / system_prompt_length
126
- * - new_context (incremental messages delta)
127
- * - tools (JSON array of {name, hash}) / tools_count
128
- */
129
- export function startLLMRequestSpan(
130
- model: string,
131
- options?: LLMRequestInput,
132
- ): Span | undefined {
133
- const tracer = getTracer();
134
- if (!tracer) return undefined;
135
-
136
- const attributes: Record<string, string | number | boolean> = {
137
- "span.type": "llm_request",
138
- model,
139
- };
140
- if (options?.context) {
141
- attributes["llm_request.context"] = options.context;
142
- }
143
-
144
- const config = getCurrentConfig();
145
-
146
- // Content capture gated by logToolContent
147
- if (config?.logToolContent) {
148
- // System prompt: hash + preview + length
149
- if (options?.systemPrompt) {
150
- const hash = hashContent(options.systemPrompt);
151
- attributes.system_prompt_hash = hash;
152
- attributes.system_prompt_length = options.systemPrompt.length;
153
- const preview =
154
- options.systemPrompt.length > 500
155
- ? options.systemPrompt.substring(0, 500)
156
- : options.systemPrompt;
157
- attributes.system_prompt_preview = preview;
158
-
159
- // Emit full system prompt via event only once per hash
160
- if (!seenSystemPromptHashes.has(hash)) {
161
- seenSystemPromptHashes.add(hash);
162
- logOTelEvent("system_prompt", {
163
- system_prompt_hash: hash,
164
- system_prompt: options.systemPrompt,
165
- }).catch(() => {});
166
- }
167
- }
168
-
169
- // Incremental input messages: only send new messages since last report
170
- if (options?.inputMessages && options.inputMessages.length > 0) {
171
- const totalCount = options.inputMessages.length;
172
- const deltaCount = totalCount - lastReportedMessageCount;
173
- if (deltaCount > 0) {
174
- const newMessages = options.inputMessages.slice(
175
- lastReportedMessageCount,
176
- );
177
- const newContextStr = JSON.stringify(newMessages);
178
- const [truncated, originalLength] = truncateContent(newContextStr);
179
- attributes.new_context = truncated;
180
- attributes.new_context_message_count = deltaCount;
181
- if (originalLength > MAX_CONTENT_LENGTH) {
182
- attributes.new_context_truncated = true;
183
- attributes.new_context_original_length = originalLength;
184
- }
185
- }
186
- lastReportedMessageCount = totalCount;
187
- }
188
-
189
- // Tool schemas: hash per tool + count
190
- if (options?.toolsSchema) {
191
- try {
192
- const tools = JSON.parse(options.toolsSchema);
193
- if (Array.isArray(tools)) {
194
- const toolHashes = tools.map(
195
- (tool: { function?: { name?: string } }) => {
196
- const name = tool.function?.name || "unknown";
197
- const toolStr = JSON.stringify(tool);
198
- const hash = hashContent(toolStr);
199
- return { name, hash };
200
- },
201
- );
202
- attributes.tools = JSON.stringify(toolHashes);
203
- attributes.tools_count = toolHashes.length;
204
-
205
- // Emit full tool schema via event only once per hash
206
- for (const { name, hash } of toolHashes) {
207
- if (!seenToolSchemaHashes.has(hash)) {
208
- seenToolSchemaHashes.add(hash);
209
- const fullTool = tools.find(
210
- (t: { function?: { name?: string } }) =>
211
- t.function?.name === name,
212
- );
213
- if (fullTool) {
214
- logOTelEvent("tool_schema", {
215
- tool_name: name,
216
- tool_hash: hash,
217
- tool_schema: JSON.stringify(fullTool),
218
- }).catch(() => {});
219
- }
220
- }
221
- }
222
- }
223
- } catch {
224
- // toolsSchema is not valid JSON — skip
225
- }
226
- }
227
- }
228
-
229
- const parent = interactionContext.getStore();
230
- let span: Span;
231
- if (parent) {
232
- const otelApi = getOTELApi()!;
233
- const ctx = otelApi.trace.setSpan(otelApi.context.active(), parent);
234
- span = tracer.startSpan("llm.request", { attributes }, ctx);
235
- } else {
236
- span = tracer.startSpan("llm.request", { attributes });
237
- }
238
- return span;
239
- }
240
-
241
- /**
242
- * Ends an LLM request span with response metadata.
243
- * The span is passed explicitly — no ALS is read or modified.
244
- *
245
- * When logToolContent is enabled, captures response.model_output.
246
- */
247
- export function endLLMRequestSpan(
248
- span: Span | undefined,
249
- metadata: LLMRequestMetadata,
250
- ): void {
251
- if (!span) return;
252
-
253
- if (metadata.inputTokens != null)
254
- span.setAttribute("input_tokens", metadata.inputTokens);
255
- if (metadata.outputTokens != null)
256
- span.setAttribute("output_tokens", metadata.outputTokens);
257
- if (metadata.cacheReadTokens != null)
258
- span.setAttribute("cache_read_tokens", metadata.cacheReadTokens);
259
- if (metadata.cacheCreationTokens != null)
260
- span.setAttribute("cache_creation_tokens", metadata.cacheCreationTokens);
261
- if (metadata.ttftMs != null) span.setAttribute("ttft_ms", metadata.ttftMs);
262
- if (metadata.ttltMs != null) span.setAttribute("ttlt_ms", metadata.ttltMs);
263
- span.setAttribute("success", metadata.success);
264
- if (metadata.error) span.setAttribute("error", metadata.error);
265
- if (metadata.hasToolCall != null)
266
- span.setAttribute("has_tool_call", metadata.hasToolCall);
267
-
268
- // Content capture gated by logToolContent
269
- const config = getCurrentConfig();
270
- if (config?.logToolContent && metadata.modelOutput) {
271
- const [truncated, originalLength] = truncateContent(metadata.modelOutput);
272
- span.setAttribute("response.model_output", truncated);
273
- if (originalLength > MAX_CONTENT_LENGTH) {
274
- span.setAttribute("response.model_output_truncated", true);
275
- span.setAttribute(
276
- "response.model_output_original_length",
277
- originalLength,
278
- );
279
- }
280
- }
281
-
282
- span.end();
283
- }
284
-
285
- /**
286
- * Creates a tool execution span as a child of the interaction span.
287
- * Enters toolContext with the new span.
288
- */
289
- export function startToolSpan(
290
- toolName: string,
291
- input?: unknown,
292
- ): Span | undefined {
293
- const tracer = getTracer();
294
- if (!tracer) return undefined;
295
-
296
- const config = getCurrentConfig();
297
- const attributes: Record<string, string | number | boolean> = {
298
- "span.type": "tool",
299
- tool_name: toolName,
300
- };
301
- if (config?.logToolContent && input !== undefined) {
302
- const inputStr = typeof input === "string" ? input : JSON.stringify(input);
303
- const [truncated, originalLength] = truncateContent(inputStr);
304
- attributes.tool_input = truncated;
305
- if (originalLength > MAX_CONTENT_LENGTH) {
306
- attributes.tool_input_truncated = true;
307
- attributes.tool_input_original_length = originalLength;
308
- }
309
- }
310
-
311
- const parent = interactionContext.getStore();
312
- let span: Span;
313
- if (parent) {
314
- const otelApi = getOTELApi()!;
315
- const ctx = otelApi.trace.setSpan(otelApi.context.active(), parent);
316
- span = tracer.startSpan(`tool.${toolName}`, { attributes }, ctx);
317
- } else {
318
- span = tracer.startSpan(`tool.${toolName}`, { attributes });
319
- }
320
- toolContext.enterWith(span);
321
- return span;
322
- }
323
-
324
- /**
325
- * Ends the current tool span with execution metadata.
326
- * Reads the span from toolContext, then clears it.
327
- */
328
- export function endToolSpan(metadata: ToolMetadata): void {
329
- const span = toolContext.getStore();
330
- if (!span) return;
331
-
332
- span.setAttribute("success", metadata.success);
333
- if (metadata.error) span.setAttribute("error", metadata.error);
334
- span.setAttribute("duration_ms", metadata.durationMs);
335
-
336
- const config = getCurrentConfig();
337
- if (config?.logToolContent && metadata.output) {
338
- const [truncated, originalLength] = truncateContent(metadata.output);
339
- span.setAttribute("tool_output", truncated);
340
- if (originalLength > MAX_CONTENT_LENGTH) {
341
- span.setAttribute("tool_output_truncated", true);
342
- span.setAttribute("tool_output_original_length", originalLength);
343
- }
344
- }
345
-
346
- span.end();
347
- toolContext.enterWith(undefined);
348
- }
@@ -1,322 +0,0 @@
1
- import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
2
- import { EXPLORE_SUBAGENT_TYPE } from "../constants/subagents.js";
3
- import { AGENT_TOOL_NAME } from "../constants/tools.js";
4
- import type { SubagentConfiguration } from "../utils/subagentParser.js";
5
- import {
6
- countToolBlocks,
7
- formatToolTokenSummary,
8
- } from "../utils/messageOperations.js";
9
-
10
- /**
11
- * Agent tool plugin for launching specialized agents to handle complex tasks
12
- */
13
- export const agentTool: ToolPlugin = {
14
- name: AGENT_TOOL_NAME,
15
- config: {
16
- type: "function" as const,
17
- function: {
18
- name: AGENT_TOOL_NAME,
19
- description:
20
- "Launch a new agent to handle complex, multi-step tasks autonomously.",
21
- parameters: {
22
- type: "object",
23
- properties: {
24
- description: {
25
- type: "string",
26
- description: "A short (3-5 word) description of the task",
27
- },
28
- prompt: {
29
- type: "string",
30
- description: "The task for the agent to perform",
31
- },
32
- subagent_type: {
33
- type: "string",
34
- description: "The type of specialized agent to use for this task",
35
- },
36
- run_in_background: {
37
- type: "boolean",
38
- description:
39
- "Set to true to run this command in the background. Use Read to read the output later.",
40
- },
41
- },
42
- required: ["description", "prompt", "subagent_type"],
43
- },
44
- },
45
- },
46
-
47
- prompt: (args?: { availableSubagents?: SubagentConfiguration[] }) => {
48
- const subagentList = args?.availableSubagents
49
- ? args.availableSubagents
50
- .map((config) => {
51
- let toolsStr = "";
52
- if (config.tools && config.tools.length > 0) {
53
- toolsStr = ` (Tools: ${config.tools.join(", ")})`;
54
- } else {
55
- toolsStr = " (Tools: *)";
56
- }
57
-
58
- return `- ${config.name}: ${config.description}${toolsStr}`;
59
- })
60
- .join("\n")
61
- : "";
62
-
63
- return `Launch a new agent to handle complex, multi-step tasks autonomously.
64
-
65
- The Agent tool launches specialized agents (subprocesses) that autonomously handle complex tasks. Each agent type has specific capabilities and tools available to it.
66
-
67
- Available agent types and the tools they have access to:
68
- ${subagentList || "No agents configured"}
69
-
70
- When using the Agent tool, you must specify a subagent_type parameter to select which agent type to use from the list above. Choose the agent whose description best matches the task you want to delegate.
71
-
72
- - When doing file search, prefer to use the ${AGENT_TOOL_NAME} tool in order to reduce context usage.
73
- - You should proactively use the ${AGENT_TOOL_NAME} tool with specialized agents when the task at hand matches the agent's description.
74
- - VERY IMPORTANT: When exploring the codebase to gather context or to answer a question that is not a needle query for a specific file/class/function, it is CRITICAL that you use the ${AGENT_TOOL_NAME} tool with subagent_type=${EXPLORE_SUBAGENT_TYPE} instead of running search commands directly.
75
- - You can optionally run agents in the background using the run_in_background parameter. When an agent runs in the background, you will be automatically notified when it completes — do NOT sleep, poll, or proactively check on its progress. Continue with other work or respond to the user instead.
76
- - **Foreground vs background**: Use foreground (default) when you need the agent's results before you can proceed — e.g., research agents whose findings inform your next steps. Use background when you have genuinely independent work to do in parallel.
77
- - **Don't peek.** The tool result includes an output file path — do not Read or tail it unless the user explicitly asks for a progress check. You get a completion notification; trust it. Reading the transcript mid-flight pulls the agent's tool noise into your context, which defeats the point of backgrounding.
78
- - **Don't race.** After launching, you know nothing about what the agent found. Never fabricate or predict agent results in any format — not as prose, summary, or structured output. The notification arrives as a user-role message in a later turn; it is never something you write yourself. If the user asks a follow-up before the notification lands, tell them the agent is still running — give status, not a guess.`;
79
- },
80
-
81
- execute: async (
82
- args: Record<string, unknown>,
83
- context: ToolContext,
84
- ): Promise<ToolResult> => {
85
- const subagentManager = context.subagentManager;
86
- if (!subagentManager) {
87
- return {
88
- success: false,
89
- content: "",
90
- error: "Subagent manager not available in tool context",
91
- shortResult: "Agent delegation failed",
92
- };
93
- }
94
-
95
- // Input validation
96
- const description = args.description as string;
97
- const prompt = args.prompt as string;
98
- const subagent_type = args.subagent_type as string;
99
- const run_in_background = args.run_in_background as boolean;
100
-
101
- if (!description || typeof description !== "string") {
102
- return {
103
- success: false,
104
- content: "",
105
- error: "description parameter is required and must be a string",
106
- shortResult: "Agent delegation failed",
107
- };
108
- }
109
-
110
- if (!prompt || typeof prompt !== "string") {
111
- return {
112
- success: false,
113
- content: "",
114
- error: "prompt parameter is required and must be a string",
115
- shortResult: "Agent delegation failed",
116
- };
117
- }
118
-
119
- if (!subagent_type || typeof subagent_type !== "string") {
120
- return {
121
- success: false,
122
- content: "",
123
- error: "subagent_type parameter is required and must be a string",
124
- shortResult: "Agent delegation failed",
125
- };
126
- }
127
-
128
- try {
129
- // Subagent selection logic with explicit name matching only
130
- const configuration = await subagentManager.findSubagent(subagent_type);
131
-
132
- if (!configuration) {
133
- // Error handling for nonexistent subagents with available subagents listing
134
- const allConfigs = subagentManager.getConfigurations();
135
- const availableNames = allConfigs.map((c) => c.name).join(", ");
136
-
137
- return {
138
- success: false,
139
- content: "",
140
- error: `No agent found matching "${subagent_type}". Available agents: ${availableNames || "none"}`,
141
- shortResult: "Agent not found",
142
- };
143
- }
144
-
145
- let isBackgrounded = false;
146
-
147
- // Create subagent instance and execute agent
148
- const instance = await subagentManager.createInstance(
149
- configuration,
150
- {
151
- description,
152
- prompt,
153
- subagent_type,
154
- },
155
- run_in_background,
156
- () => {
157
- // Do not update shortResult if it is running in background
158
- if (run_in_background || isBackgrounded) return;
159
-
160
- const messages = instance.messageManager.getMessages();
161
- const tokens = instance.messageManager.getLatestTotalTokens();
162
- const usedTools = instance.usedTools;
163
-
164
- const toolCount = countToolBlocks(messages);
165
- const summary = formatToolTokenSummary(toolCount, tokens);
166
-
167
- const getDisplayParam = (t: {
168
- name: string;
169
- parameters: string;
170
- compactParams?: string;
171
- stage?: string;
172
- }) => {
173
- if (
174
- (t.stage === "end" || t.stage === "running") &&
175
- t.compactParams
176
- ) {
177
- return t.compactParams;
178
- }
179
- const flat = t.parameters.replace(/\n/g, "\\n");
180
- return flat.length > 30 ? `…${flat.slice(-30)}` : flat;
181
- };
182
-
183
- let shortResult = "";
184
- if (toolCount > 2) {
185
- shortResult += "... ";
186
- }
187
- shortResult += summary;
188
- if (usedTools.length > 0) {
189
- shortResult +=
190
- "\n" +
191
- usedTools
192
- .map((t) => `${t.name} ${getDisplayParam(t)}`)
193
- .join("\n");
194
- }
195
-
196
- context.onShortResultUpdate?.(shortResult);
197
- },
198
- );
199
-
200
- return new Promise<ToolResult>((resolve) => {
201
- (async () => {
202
- // Register for backgrounding if not already in background
203
- if (!run_in_background && context.foregroundTaskManager) {
204
- context.foregroundTaskManager.registerForegroundTask({
205
- id: instance.subagentId,
206
- backgroundHandler: async () => {
207
- isBackgrounded = true;
208
- const taskId = await subagentManager.backgroundInstance(
209
- instance.subagentId,
210
- );
211
- const task = context.backgroundTaskManager?.getTask(taskId);
212
- const outputPath = task?.outputPath;
213
- resolve({
214
- success: true,
215
- content: `Agent backgrounded with ID: ${taskId}.${outputPath ? ` Real-time output: ${outputPath}` : ""}`,
216
- shortResult: "Agent backgrounded",
217
- isManuallyBackgrounded: true,
218
- });
219
- },
220
- });
221
- }
222
-
223
- try {
224
- const result = await subagentManager.executeAgent(
225
- instance,
226
- prompt,
227
- context.abortSignal,
228
- run_in_background,
229
- );
230
-
231
- if (isBackgrounded) {
232
- return;
233
- }
234
-
235
- if (run_in_background) {
236
- const task = context.backgroundTaskManager?.getTask(result);
237
- const outputPath = task?.outputPath;
238
- const backgroundMsg = [
239
- `Agent started in background with ID: ${result}.`,
240
- `The agent is working in the background. You will be notified automatically when it completes.`,
241
- `Do not duplicate this agent's work — avoid working with the same files or topics it is using.`,
242
- outputPath
243
- ? `output_file: ${outputPath}`
244
- : `Briefly tell the user what you launched and end your response.`,
245
- ].join("\n");
246
- resolve({
247
- success: true,
248
- content: backgroundMsg,
249
- shortResult: `Agent started in background: ${result}${outputPath ? ` → ${outputPath}` : ""}`,
250
- });
251
- return;
252
- }
253
-
254
- // Cleanup subagent instance after agent completion
255
- subagentManager.cleanupInstance(instance.subagentId);
256
-
257
- const messages = instance.messageManager.getMessages();
258
- const tokens = instance.messageManager.getLatestTotalTokens();
259
- const toolCount = countToolBlocks(messages);
260
- const summary = formatToolTokenSummary(toolCount, tokens);
261
-
262
- resolve({
263
- success: true,
264
- content: result,
265
- shortResult: `Agent completed${summary ? ` ${summary}` : ""}`,
266
- });
267
- } catch (error) {
268
- if (!isBackgrounded) {
269
- // Extract content from the subagent's last assistant message.
270
- // aiManager.sendAIMessage() catch block already added an error
271
- // block via addErrorBlock(), so the error info is in the message
272
- // history — just return it directly.
273
- let content = "";
274
- try {
275
- const messages = instance.messageManager.getMessages();
276
- const lastAssistant = messages
277
- .filter((m) => m.role === "assistant")
278
- .pop();
279
- if (lastAssistant) {
280
- content = lastAssistant.blocks
281
- .filter((b) => b.type === "text" || b.type === "error")
282
- .map((b) => (b as { content: string }).content)
283
- .join("\n")
284
- .trim();
285
- }
286
- } catch {
287
- // Ignore errors when extracting messages
288
- }
289
-
290
- const errorMsg = `Agent delegation failed: ${error instanceof Error ? error.message : String(error)}`;
291
- resolve({
292
- success: false,
293
- content: content || errorMsg,
294
- error: errorMsg,
295
- shortResult: "Delegation error",
296
- });
297
- }
298
- } finally {
299
- if (!run_in_background && context.foregroundTaskManager) {
300
- context.foregroundTaskManager.unregisterForegroundTask(
301
- instance.subagentId,
302
- );
303
- }
304
- }
305
- })();
306
- });
307
- } catch (error) {
308
- return {
309
- success: false,
310
- content: "",
311
- error: `Agent delegation failed: ${error instanceof Error ? error.message : String(error)}`,
312
- shortResult: "Delegation error",
313
- };
314
- }
315
- },
316
-
317
- formatCompactParams: (params: Record<string, unknown>) => {
318
- const subagent_type = params.subagent_type as string;
319
- const description = params.description as string;
320
- return `${subagent_type || "unknown"}: ${description || "no description"}`;
321
- },
322
- };