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.
- package/dist/agent.d.ts +34 -1
- package/dist/agent.js +49 -4
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -1
- package/dist/managers/aiManager.d.ts +36 -0
- package/dist/managers/aiManager.js +240 -11
- package/dist/managers/messageManager.d.ts +2 -4
- package/dist/managers/messageManager.js +14 -7
- package/dist/managers/slashCommandManager.js +28 -0
- package/dist/managers/subagentManager.d.ts +0 -2
- package/dist/services/aiService.d.ts +2 -3
- package/dist/services/aiService.js +4 -18
- package/dist/services/configurationService.d.ts +7 -0
- package/dist/services/configurationService.js +10 -0
- package/dist/telemetry/instrumentation.d.ts +1 -1
- package/dist/telemetry/instrumentation.js +57 -28
- package/dist/tools/bashTool.js +16 -6
- package/dist/tools/grepTool.js +11 -3
- package/dist/types/core.d.ts +1 -34
- package/dist/types/index.d.ts +4 -0
- package/dist/types/messaging.d.ts +2 -0
- package/dist/utils/cacheControlUtils.d.ts +5 -27
- package/dist/utils/cacheControlUtils.js +12 -73
- package/dist/utils/containerSetup.js +0 -3
- package/dist/utils/messageOperations.d.ts +2 -1
- package/dist/utils/messageOperations.js +2 -1
- package/dist/utils/notificationXml.js +5 -0
- package/dist/utils/ripgrep.d.ts +4 -4
- package/dist/utils/ripgrep.js +4 -27
- package/package.json +20 -8
- package/bin/rg +0 -79
- package/dist/managers/forkedAgentManager.d.ts +0 -50
- package/dist/managers/forkedAgentManager.js +0 -130
- package/scripts/install_ripgrep.js +0 -111
- package/scripts/postinstall.js +0 -38
- package/src/agent.ts +0 -1271
- package/src/constants/subagents.ts +0 -4
- package/src/constants/toolLimits.ts +0 -15
- package/src/constants/tools.ts +0 -24
- package/src/core/plugin.ts +0 -237
- package/src/core/session.ts +0 -9
- package/src/index.ts +0 -38
- package/src/managers/MemoryRuleManager.ts +0 -198
- package/src/managers/aiManager.ts +0 -2429
- package/src/managers/backgroundTaskManager.ts +0 -480
- package/src/managers/bangManager.ts +0 -119
- package/src/managers/cronManager.ts +0 -401
- package/src/managers/foregroundTaskManager.ts +0 -30
- package/src/managers/forkedAgentManager.ts +0 -216
- package/src/managers/hookManager.ts +0 -1089
- package/src/managers/liveConfigManager.ts +0 -445
- package/src/managers/lspManager.ts +0 -470
- package/src/managers/mcpManager.ts +0 -1010
- package/src/managers/messageManager.ts +0 -1184
- package/src/managers/messageQueue.ts +0 -157
- package/src/managers/permissionManager.ts +0 -1140
- package/src/managers/planManager.ts +0 -112
- package/src/managers/pluginManager.ts +0 -330
- package/src/managers/pluginScopeManager.ts +0 -124
- package/src/managers/reversionManager.ts +0 -178
- package/src/managers/skillManager.ts +0 -578
- package/src/managers/slashCommandManager.ts +0 -564
- package/src/managers/subagentManager.ts +0 -915
- package/src/managers/toolManager.ts +0 -489
- package/src/managers/workflowManager.ts +0 -493
- package/src/prompts/autoMemory.ts +0 -33
- package/src/prompts/autoMemoryExtraction.ts +0 -146
- package/src/prompts/index.ts +0 -506
- package/src/prompts/planModeReminders.ts +0 -126
- package/src/services/GitService.ts +0 -131
- package/src/services/MarketplaceService.ts +0 -1052
- package/src/services/MemoryRuleService.ts +0 -71
- package/src/services/aiService.ts +0 -919
- package/src/services/authService.ts +0 -565
- package/src/services/autoMemoryService.ts +0 -280
- package/src/services/configurationService.ts +0 -1520
- package/src/services/fileWatcher.ts +0 -319
- package/src/services/hook.ts +0 -348
- package/src/services/initializationService.ts +0 -357
- package/src/services/interactionService.ts +0 -229
- package/src/services/jsonlHandler.ts +0 -295
- package/src/services/memory.ts +0 -234
- package/src/services/pluginLoader.ts +0 -252
- package/src/services/remoteSettingsService.ts +0 -366
- package/src/services/reversionService.ts +0 -122
- package/src/services/session.ts +0 -917
- package/src/services/taskManager.ts +0 -321
- package/src/telemetry/events.ts +0 -65
- package/src/telemetry/instrumentation.ts +0 -499
- package/src/telemetry/sessionTracing.ts +0 -348
- package/src/tools/agentTool.ts +0 -322
- package/src/tools/askUserQuestion.ts +0 -160
- package/src/tools/bashTool.ts +0 -621
- package/src/tools/buildTool.ts +0 -61
- package/src/tools/cronCreateTool.ts +0 -161
- package/src/tools/cronDeleteTool.ts +0 -51
- package/src/tools/cronListTool.ts +0 -47
- package/src/tools/editTool.ts +0 -313
- package/src/tools/enterPlanMode.ts +0 -124
- package/src/tools/enterWorktreeTool.ts +0 -190
- package/src/tools/exitPlanMode.ts +0 -139
- package/src/tools/exitWorktreeTool.ts +0 -236
- package/src/tools/globTool.ts +0 -167
- package/src/tools/grepTool.ts +0 -399
- package/src/tools/lspTool.ts +0 -883
- package/src/tools/readTool.ts +0 -426
- package/src/tools/skillTool.ts +0 -248
- package/src/tools/taskManagementTools.ts +0 -574
- package/src/tools/taskStopTool.ts +0 -78
- package/src/tools/types.ts +0 -137
- package/src/tools/webFetchTool.ts +0 -373
- package/src/tools/workflowTool.ts +0 -205
- package/src/tools/writeTool.ts +0 -267
- package/src/types/agent.ts +0 -128
- package/src/types/auth.ts +0 -19
- package/src/types/commands.ts +0 -30
- package/src/types/config.ts +0 -43
- package/src/types/configuration.ts +0 -168
- package/src/types/core.ts +0 -105
- package/src/types/cron.ts +0 -13
- package/src/types/environment.ts +0 -106
- package/src/types/fileSearch.ts +0 -4
- package/src/types/history.ts +0 -7
- package/src/types/hooks.ts +0 -256
- package/src/types/index.ts +0 -41
- package/src/types/lsp.ts +0 -98
- package/src/types/marketplace.ts +0 -71
- package/src/types/mcp.ts +0 -43
- package/src/types/memoryRule.ts +0 -31
- package/src/types/messaging.ts +0 -114
- package/src/types/permissions.ts +0 -80
- package/src/types/plugins.ts +0 -39
- package/src/types/processes.ts +0 -99
- package/src/types/reversion.ts +0 -29
- package/src/types/session.ts +0 -12
- package/src/types/skills.ts +0 -108
- package/src/types/tasks.ts +0 -13
- package/src/types/telemetry.ts +0 -98
- package/src/types/tools.ts +0 -44
- package/src/types/workflow.ts +0 -6
- package/src/utils/abortUtils.ts +0 -118
- package/src/utils/atomicWrite.ts +0 -61
- package/src/utils/bashParser.ts +0 -899
- package/src/utils/cacheControlUtils.ts +0 -426
- package/src/utils/commandArgumentParser.ts +0 -161
- package/src/utils/commandPathResolver.ts +0 -88
- package/src/utils/configPaths.ts +0 -220
- package/src/utils/configValidator.ts +0 -100
- package/src/utils/constants.ts +0 -39
- package/src/utils/container.ts +0 -92
- package/src/utils/containerSetup.ts +0 -371
- package/src/utils/convertMessagesForAPI.ts +0 -326
- package/src/utils/cronTasks.ts +0 -128
- package/src/utils/cronTasksLock.ts +0 -182
- package/src/utils/cronToHuman.ts +0 -99
- package/src/utils/customCommands.ts +0 -95
- package/src/utils/editUtils.ts +0 -20
- package/src/utils/fileFormat.ts +0 -40
- package/src/utils/fileSearch.ts +0 -151
- package/src/utils/fileUtils.ts +0 -223
- package/src/utils/gitUtils.ts +0 -315
- package/src/utils/globalLogger.ts +0 -128
- package/src/utils/groupMessagesByApiRound.ts +0 -121
- package/src/utils/hookMatcher.ts +0 -176
- package/src/utils/markdownParser.ts +0 -317
- package/src/utils/mcpUtils.ts +0 -138
- package/src/utils/messageOperations.ts +0 -659
- package/src/utils/modelCapabilities.ts +0 -30
- package/src/utils/nameGenerator.ts +0 -99
- package/src/utils/notificationXml.ts +0 -57
- package/src/utils/openaiClient.ts +0 -251
- package/src/utils/parseCronExpression.ts +0 -78
- package/src/utils/path.ts +0 -72
- package/src/utils/pathEncoder.ts +0 -400
- package/src/utils/pathSafety.ts +0 -40
- package/src/utils/promptHistory.ts +0 -168
- package/src/utils/ripgrep.ts +0 -35
- package/src/utils/shellResolver.ts +0 -190
- package/src/utils/skillParser.ts +0 -246
- package/src/utils/stringUtils.ts +0 -156
- package/src/utils/subagentParser.ts +0 -279
- package/src/utils/taskReminder.ts +0 -96
- package/src/utils/tokenCalculation.ts +0 -43
- package/src/utils/tokenEstimate.ts +0 -34
- package/src/utils/toolResultStorage.ts +0 -117
- package/src/utils/worktreeSession.ts +0 -26
- package/src/utils/worktreeUtils.ts +0 -786
- package/src/workflow/budgetTracker.ts +0 -34
- package/src/workflow/concurrencyLimiter.ts +0 -47
- package/src/workflow/journal.ts +0 -95
- package/src/workflow/progressReporter.ts +0 -141
- package/src/workflow/runState.ts +0 -65
- package/src/workflow/scriptRuntime.ts +0 -274
- package/src/workflow/structuredOutput.ts +0 -123
- package/src/workflow/types.ts +0 -101
- package/src/workflow/workflowApis.ts +0 -410
- package/vendor/ripgrep/linux-aarch64/rg +0 -0
- package/vendor/ripgrep/linux-x86_64/rg +0 -0
- package/vendor/ripgrep/macos-aarch64/rg +0 -0
- package/vendor/ripgrep/macos-x86_64/rg +0 -0
- package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
- 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
|
-
}
|
package/src/tools/agentTool.ts
DELETED
|
@@ -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
|
-
};
|