deepagentsdk 0.9.2
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/LICENSE +21 -0
- package/README.md +159 -0
- package/package.json +95 -0
- package/src/agent.ts +1230 -0
- package/src/backends/composite.ts +273 -0
- package/src/backends/filesystem.ts +692 -0
- package/src/backends/index.ts +22 -0
- package/src/backends/local-sandbox.ts +175 -0
- package/src/backends/persistent.ts +593 -0
- package/src/backends/sandbox.ts +510 -0
- package/src/backends/state.ts +244 -0
- package/src/backends/utils.ts +287 -0
- package/src/checkpointer/file-saver.ts +98 -0
- package/src/checkpointer/index.ts +5 -0
- package/src/checkpointer/kv-saver.ts +82 -0
- package/src/checkpointer/memory-saver.ts +82 -0
- package/src/checkpointer/types.ts +125 -0
- package/src/cli/components/ApiKeyInput.tsx +300 -0
- package/src/cli/components/FilePreview.tsx +237 -0
- package/src/cli/components/Input.tsx +277 -0
- package/src/cli/components/Message.tsx +93 -0
- package/src/cli/components/ModelSelection.tsx +338 -0
- package/src/cli/components/SlashMenu.tsx +101 -0
- package/src/cli/components/StatusBar.tsx +89 -0
- package/src/cli/components/Subagent.tsx +91 -0
- package/src/cli/components/TodoList.tsx +133 -0
- package/src/cli/components/ToolApproval.tsx +70 -0
- package/src/cli/components/ToolCall.tsx +144 -0
- package/src/cli/components/ToolCallSummary.tsx +175 -0
- package/src/cli/components/Welcome.tsx +75 -0
- package/src/cli/components/index.ts +24 -0
- package/src/cli/hooks/index.ts +12 -0
- package/src/cli/hooks/useAgent.ts +933 -0
- package/src/cli/index.tsx +1066 -0
- package/src/cli/theme.ts +205 -0
- package/src/cli/utils/model-list.ts +365 -0
- package/src/constants/errors.ts +29 -0
- package/src/constants/limits.ts +195 -0
- package/src/index.ts +176 -0
- package/src/middleware/agent-memory.ts +330 -0
- package/src/prompts.ts +196 -0
- package/src/skills/index.ts +2 -0
- package/src/skills/load.ts +191 -0
- package/src/skills/types.ts +53 -0
- package/src/tools/execute.ts +167 -0
- package/src/tools/filesystem.ts +418 -0
- package/src/tools/index.ts +39 -0
- package/src/tools/subagent.ts +443 -0
- package/src/tools/todos.ts +101 -0
- package/src/tools/web.ts +567 -0
- package/src/types/backend.ts +177 -0
- package/src/types/core.ts +220 -0
- package/src/types/events.ts +429 -0
- package/src/types/index.ts +94 -0
- package/src/types/structured-output.ts +43 -0
- package/src/types/subagent.ts +96 -0
- package/src/types.ts +22 -0
- package/src/utils/approval.ts +213 -0
- package/src/utils/events.ts +416 -0
- package/src/utils/eviction.ts +181 -0
- package/src/utils/index.ts +34 -0
- package/src/utils/model-parser.ts +38 -0
- package/src/utils/patch-tool-calls.ts +233 -0
- package/src/utils/project-detection.ts +32 -0
- package/src/utils/summarization.ts +254 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subagent tool for task delegation using AI SDK v6 ToolLoopAgent.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { tool, ToolLoopAgent, stepCountIs, Output, type ToolSet, type LanguageModel } from "ai";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import type {
|
|
8
|
+
SubAgent,
|
|
9
|
+
DeepAgentState,
|
|
10
|
+
BackendProtocol,
|
|
11
|
+
BackendFactory,
|
|
12
|
+
EventCallback,
|
|
13
|
+
InterruptOnConfig,
|
|
14
|
+
CreateDeepAgentParams,
|
|
15
|
+
BuiltinToolCreator,
|
|
16
|
+
SubagentToolConfig,
|
|
17
|
+
} from "../types";
|
|
18
|
+
import { applyInterruptConfig } from "../utils/approval";
|
|
19
|
+
import {
|
|
20
|
+
DEFAULT_SUBAGENT_MAX_STEPS,
|
|
21
|
+
DEFAULT_TIMEOUT_SECONDS,
|
|
22
|
+
} from "../constants/limits";
|
|
23
|
+
import {
|
|
24
|
+
getTaskToolDescription,
|
|
25
|
+
DEFAULT_GENERAL_PURPOSE_DESCRIPTION,
|
|
26
|
+
DEFAULT_SUBAGENT_PROMPT,
|
|
27
|
+
TODO_SYSTEM_PROMPT,
|
|
28
|
+
FILESYSTEM_SYSTEM_PROMPT,
|
|
29
|
+
BASE_PROMPT,
|
|
30
|
+
} from "../prompts";
|
|
31
|
+
import {
|
|
32
|
+
createSubagentStartEvent,
|
|
33
|
+
createSubagentStepEvent,
|
|
34
|
+
createSubagentFinishEvent,
|
|
35
|
+
} from "../utils/events";
|
|
36
|
+
import { createTodosTool } from "./todos";
|
|
37
|
+
import { createFilesystemTools } from "./filesystem";
|
|
38
|
+
import {
|
|
39
|
+
createWebSearchTool,
|
|
40
|
+
createHttpRequestTool,
|
|
41
|
+
createFetchUrlTool,
|
|
42
|
+
} from "./web";
|
|
43
|
+
import {
|
|
44
|
+
createLsTool,
|
|
45
|
+
createReadFileTool,
|
|
46
|
+
createWriteFileTool,
|
|
47
|
+
createEditFileTool,
|
|
48
|
+
createGlobTool,
|
|
49
|
+
createGrepTool,
|
|
50
|
+
} from "./filesystem";
|
|
51
|
+
import { createExecuteTool } from "./execute";
|
|
52
|
+
|
|
53
|
+
// ============================================================================
|
|
54
|
+
// Helper Functions for Builtin Tool Instantiation
|
|
55
|
+
// ============================================================================
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check if a value is a builtin tool creator function.
|
|
59
|
+
*/
|
|
60
|
+
function isBuiltinToolCreator(value: any): value is BuiltinToolCreator {
|
|
61
|
+
return typeof value === "function" && (
|
|
62
|
+
value === createWebSearchTool ||
|
|
63
|
+
value === createHttpRequestTool ||
|
|
64
|
+
value === createFetchUrlTool ||
|
|
65
|
+
value === createLsTool ||
|
|
66
|
+
value === createReadFileTool ||
|
|
67
|
+
value === createWriteFileTool ||
|
|
68
|
+
value === createEditFileTool ||
|
|
69
|
+
value === createGlobTool ||
|
|
70
|
+
value === createGrepTool ||
|
|
71
|
+
value === createTodosTool ||
|
|
72
|
+
value === createExecuteTool
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Instantiate a builtin tool creator with the given context.
|
|
78
|
+
*/
|
|
79
|
+
function instantiateBuiltinTool(
|
|
80
|
+
creator: BuiltinToolCreator,
|
|
81
|
+
state: DeepAgentState,
|
|
82
|
+
options: {
|
|
83
|
+
backend?: BackendProtocol | BackendFactory;
|
|
84
|
+
onEvent?: EventCallback;
|
|
85
|
+
toolResultEvictionLimit?: number;
|
|
86
|
+
}
|
|
87
|
+
): ToolSet {
|
|
88
|
+
const { backend, onEvent, toolResultEvictionLimit } = options;
|
|
89
|
+
|
|
90
|
+
// Web tools - require API key and timeout defaults
|
|
91
|
+
const tavilyApiKey = process.env.TAVILY_API_KEY || "";
|
|
92
|
+
const defaultTimeout = DEFAULT_TIMEOUT_SECONDS;
|
|
93
|
+
|
|
94
|
+
if (creator === createWebSearchTool) {
|
|
95
|
+
if (!tavilyApiKey) {
|
|
96
|
+
console.warn("web_search tool requested but TAVILY_API_KEY not set");
|
|
97
|
+
return {};
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
web_search: createWebSearchTool(state, { backend, onEvent, toolResultEvictionLimit, tavilyApiKey }),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (creator === createHttpRequestTool) {
|
|
104
|
+
return {
|
|
105
|
+
http_request: createHttpRequestTool(state, { backend, onEvent, toolResultEvictionLimit, defaultTimeout }),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (creator === createFetchUrlTool) {
|
|
109
|
+
return {
|
|
110
|
+
fetch_url: createFetchUrlTool(state, { backend, onEvent, toolResultEvictionLimit, defaultTimeout }),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Filesystem tools
|
|
115
|
+
if (creator === createLsTool) {
|
|
116
|
+
return {
|
|
117
|
+
ls: createLsTool(state, backend!, onEvent),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (creator === createReadFileTool) {
|
|
121
|
+
return {
|
|
122
|
+
read_file: createReadFileTool(state, backend!, toolResultEvictionLimit, onEvent),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (creator === createWriteFileTool) {
|
|
126
|
+
return {
|
|
127
|
+
write_file: createWriteFileTool(state, backend!, onEvent),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (creator === createEditFileTool) {
|
|
131
|
+
return {
|
|
132
|
+
edit_file: createEditFileTool(state, backend!, onEvent),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (creator === createGlobTool) {
|
|
136
|
+
return {
|
|
137
|
+
glob: createGlobTool(state, backend!, onEvent),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
if (creator === createGrepTool) {
|
|
141
|
+
return {
|
|
142
|
+
grep: createGrepTool(state, backend!, toolResultEvictionLimit, onEvent),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Utility tools
|
|
147
|
+
if (creator === createTodosTool) {
|
|
148
|
+
return {
|
|
149
|
+
write_todos: createTodosTool(state, onEvent),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
if (creator === createExecuteTool) {
|
|
153
|
+
// Execute tool requires special handling - needs a sandbox backend
|
|
154
|
+
throw new Error("execute tool cannot be used via selective tools - it requires a SandboxBackendProtocol");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
throw new Error(`Unknown builtin tool creator: ${creator}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Process subagent tool configuration (array or ToolSet) into a ToolSet.
|
|
162
|
+
*/
|
|
163
|
+
function processSubagentTools(
|
|
164
|
+
toolConfig: ToolSet | SubagentToolConfig[] | undefined,
|
|
165
|
+
state: DeepAgentState,
|
|
166
|
+
options: {
|
|
167
|
+
backend?: BackendProtocol | BackendFactory;
|
|
168
|
+
onEvent?: EventCallback;
|
|
169
|
+
toolResultEvictionLimit?: number;
|
|
170
|
+
}
|
|
171
|
+
): ToolSet {
|
|
172
|
+
if (!toolConfig) {
|
|
173
|
+
return {};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// If it's already a ToolSet object, return as-is
|
|
177
|
+
if (!Array.isArray(toolConfig)) {
|
|
178
|
+
return toolConfig;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Process array of SubagentToolConfig items
|
|
182
|
+
let result: ToolSet = {};
|
|
183
|
+
for (const item of toolConfig) {
|
|
184
|
+
if (isBuiltinToolCreator(item)) {
|
|
185
|
+
// Instantiate builtin tool creator
|
|
186
|
+
const instantiated = instantiateBuiltinTool(item, state, options);
|
|
187
|
+
result = { ...result, ...instantiated };
|
|
188
|
+
} else if (typeof item === "object" && item !== null) {
|
|
189
|
+
// Assume it's a ToolSet object
|
|
190
|
+
result = { ...result, ...item };
|
|
191
|
+
}
|
|
192
|
+
// Silently skip invalid items
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Options for creating the subagent tool.
|
|
200
|
+
*/
|
|
201
|
+
export interface CreateSubagentToolOptions {
|
|
202
|
+
/** Default model for subagents (AI SDK LanguageModel instance) */
|
|
203
|
+
defaultModel: LanguageModel;
|
|
204
|
+
/** Default tools available to all subagents */
|
|
205
|
+
defaultTools?: ToolSet;
|
|
206
|
+
/** List of custom subagent specifications */
|
|
207
|
+
subagents?: SubAgent[];
|
|
208
|
+
/** Whether to include the general-purpose agent */
|
|
209
|
+
includeGeneralPurposeAgent?: boolean;
|
|
210
|
+
/** Backend for filesystem operations */
|
|
211
|
+
backend?: BackendProtocol | BackendFactory;
|
|
212
|
+
/** Custom description for the task tool */
|
|
213
|
+
taskDescription?: string | null;
|
|
214
|
+
/** Optional callback for emitting events */
|
|
215
|
+
onEvent?: EventCallback;
|
|
216
|
+
/** Interrupt config to pass to subagents */
|
|
217
|
+
interruptOn?: InterruptOnConfig;
|
|
218
|
+
/** Parent agent options to pass through to subagents */
|
|
219
|
+
parentGenerationOptions?: CreateDeepAgentParams["generationOptions"];
|
|
220
|
+
parentAdvancedOptions?: CreateDeepAgentParams["advancedOptions"];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Build the system prompt for a subagent.
|
|
225
|
+
*/
|
|
226
|
+
function buildSubagentSystemPrompt(customPrompt: string): string {
|
|
227
|
+
return `${customPrompt}
|
|
228
|
+
|
|
229
|
+
${BASE_PROMPT}
|
|
230
|
+
|
|
231
|
+
${TODO_SYSTEM_PROMPT}
|
|
232
|
+
|
|
233
|
+
${FILESYSTEM_SYSTEM_PROMPT}`;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Create the task tool for spawning subagents using ToolLoopAgent.
|
|
238
|
+
*/
|
|
239
|
+
export function createSubagentTool(
|
|
240
|
+
state: DeepAgentState,
|
|
241
|
+
options: CreateSubagentToolOptions
|
|
242
|
+
) {
|
|
243
|
+
const {
|
|
244
|
+
defaultModel,
|
|
245
|
+
defaultTools = {},
|
|
246
|
+
subagents = [],
|
|
247
|
+
includeGeneralPurposeAgent = true,
|
|
248
|
+
backend,
|
|
249
|
+
taskDescription = null,
|
|
250
|
+
onEvent,
|
|
251
|
+
interruptOn,
|
|
252
|
+
parentGenerationOptions,
|
|
253
|
+
parentAdvancedOptions,
|
|
254
|
+
} = options;
|
|
255
|
+
|
|
256
|
+
// Build subagent registry (store raw tool config, process during execution)
|
|
257
|
+
const subagentRegistry: Record<
|
|
258
|
+
string,
|
|
259
|
+
{
|
|
260
|
+
systemPrompt: string;
|
|
261
|
+
toolConfig: ToolSet | SubagentToolConfig[] | undefined;
|
|
262
|
+
model: LanguageModel;
|
|
263
|
+
output?: { schema: z.ZodType<any>; description?: string };
|
|
264
|
+
}
|
|
265
|
+
> = {};
|
|
266
|
+
const subagentDescriptions: string[] = [];
|
|
267
|
+
|
|
268
|
+
// Add general-purpose agent if enabled
|
|
269
|
+
if (includeGeneralPurposeAgent) {
|
|
270
|
+
subagentRegistry["general-purpose"] = {
|
|
271
|
+
systemPrompt: buildSubagentSystemPrompt(DEFAULT_SUBAGENT_PROMPT),
|
|
272
|
+
toolConfig: defaultTools,
|
|
273
|
+
model: defaultModel,
|
|
274
|
+
};
|
|
275
|
+
subagentDescriptions.push(
|
|
276
|
+
`- general-purpose: ${DEFAULT_GENERAL_PURPOSE_DESCRIPTION}`
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Add custom subagents (store raw tool config)
|
|
281
|
+
for (const subagent of subagents) {
|
|
282
|
+
subagentRegistry[subagent.name] = {
|
|
283
|
+
systemPrompt: buildSubagentSystemPrompt(subagent.systemPrompt),
|
|
284
|
+
toolConfig: subagent.tools || defaultTools,
|
|
285
|
+
model: subagent.model || defaultModel,
|
|
286
|
+
output: subagent.output,
|
|
287
|
+
};
|
|
288
|
+
subagentDescriptions.push(`- ${subagent.name}: ${subagent.description}`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const finalTaskDescription =
|
|
292
|
+
taskDescription || getTaskToolDescription(subagentDescriptions);
|
|
293
|
+
|
|
294
|
+
return tool({
|
|
295
|
+
description: finalTaskDescription,
|
|
296
|
+
inputSchema: z.object({
|
|
297
|
+
description: z
|
|
298
|
+
.string()
|
|
299
|
+
.describe("The task to execute with the selected agent"),
|
|
300
|
+
subagent_type: z
|
|
301
|
+
.string()
|
|
302
|
+
.describe(
|
|
303
|
+
`Name of the agent to use. Available: ${Object.keys(subagentRegistry).join(", ")}`
|
|
304
|
+
),
|
|
305
|
+
}),
|
|
306
|
+
execute: async ({ description, subagent_type }) => {
|
|
307
|
+
// Validate subagent type
|
|
308
|
+
if (!(subagent_type in subagentRegistry)) {
|
|
309
|
+
const allowedTypes = Object.keys(subagentRegistry)
|
|
310
|
+
.map((k) => `\`${k}\``)
|
|
311
|
+
.join(", ");
|
|
312
|
+
return `Error: invoked agent of type ${subagent_type}, the only allowed types are ${allowedTypes}`;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const subagentConfig = subagentRegistry[subagent_type]!;
|
|
316
|
+
|
|
317
|
+
// Find the subagent spec to get its specific options
|
|
318
|
+
const subagentSpec = subagents.find((sa) => sa.name === subagent_type);
|
|
319
|
+
const subagentInterruptOn = subagentSpec?.interruptOn ?? interruptOn;
|
|
320
|
+
|
|
321
|
+
// Merge options: subagent-specific options override parent options
|
|
322
|
+
const mergedGenerationOptions = {
|
|
323
|
+
...parentGenerationOptions,
|
|
324
|
+
...subagentSpec?.generationOptions,
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const mergedAdvancedOptions = {
|
|
328
|
+
...parentAdvancedOptions,
|
|
329
|
+
...subagentSpec?.advancedOptions,
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
// Emit subagent start event
|
|
333
|
+
if (onEvent) {
|
|
334
|
+
onEvent(createSubagentStartEvent(subagent_type, description));
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Create a fresh state for the subagent (shares files but have own todos)
|
|
338
|
+
const subagentState: DeepAgentState = {
|
|
339
|
+
todos: [],
|
|
340
|
+
files: state.files, // Share files with parent
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
// Process subagent tool configuration (handles both arrays and ToolSet objects)
|
|
344
|
+
const customTools = processSubagentTools(
|
|
345
|
+
subagentConfig.toolConfig,
|
|
346
|
+
subagentState,
|
|
347
|
+
{ backend, onEvent }
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
// Build default tools (todos + filesystem) that all subagents get
|
|
351
|
+
const todosTool = createTodosTool(subagentState, onEvent);
|
|
352
|
+
const filesystemTools = createFilesystemTools(subagentState, backend, onEvent);
|
|
353
|
+
|
|
354
|
+
// Combine default tools with custom tools
|
|
355
|
+
// Custom tools come last so they can override defaults if needed
|
|
356
|
+
let allTools: ToolSet = {
|
|
357
|
+
write_todos: todosTool,
|
|
358
|
+
...filesystemTools,
|
|
359
|
+
...customTools,
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
// Apply interruptOn config - use subagent's own config if provided, otherwise parent's
|
|
363
|
+
allTools = applyInterruptConfig(allTools, subagentInterruptOn);
|
|
364
|
+
|
|
365
|
+
try {
|
|
366
|
+
// Create and run a ToolLoopAgent for the subagent
|
|
367
|
+
const subagentSettings: any = {
|
|
368
|
+
model: subagentConfig.model,
|
|
369
|
+
instructions: subagentConfig.systemPrompt,
|
|
370
|
+
tools: allTools,
|
|
371
|
+
stopWhen: stepCountIs(DEFAULT_SUBAGENT_MAX_STEPS), // Enforce max steps limit for subagents
|
|
372
|
+
// Pass output configuration if subagent has one using AI SDK Output helper
|
|
373
|
+
...(subagentConfig.output ? { output: Output.object(subagentConfig.output) } : {}),
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
// Add merged generation options
|
|
377
|
+
if (Object.keys(mergedGenerationOptions).length > 0) {
|
|
378
|
+
Object.assign(subagentSettings, mergedGenerationOptions);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Add merged advanced options (excluding toolChoice and activeTools as per plan)
|
|
382
|
+
if (mergedAdvancedOptions) {
|
|
383
|
+
const { toolChoice, activeTools, ...safeAdvancedOptions } = mergedAdvancedOptions;
|
|
384
|
+
Object.assign(subagentSettings, safeAdvancedOptions);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Track subagent step count for events
|
|
388
|
+
let subagentStepCount = 0;
|
|
389
|
+
|
|
390
|
+
// Add onStepFinish callback to settings to capture steps
|
|
391
|
+
subagentSettings.onStepFinish = async ({ toolCalls, toolResults }: { toolCalls: any[]; toolResults: any[] }) => {
|
|
392
|
+
// Emit subagent step event with tool calls
|
|
393
|
+
if (onEvent && toolCalls && toolCalls.length > 0) {
|
|
394
|
+
// Map tool calls with their results
|
|
395
|
+
const toolCallsWithResults = toolCalls.map((tc: any, index: number) => ({
|
|
396
|
+
toolName: tc.toolName,
|
|
397
|
+
args: tc.args,
|
|
398
|
+
result: toolResults[index],
|
|
399
|
+
}));
|
|
400
|
+
|
|
401
|
+
onEvent(createSubagentStepEvent(subagentStepCount++, toolCallsWithResults));
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
const subagentAgent = new ToolLoopAgent(subagentSettings);
|
|
406
|
+
|
|
407
|
+
const result = await subagentAgent.generate({
|
|
408
|
+
prompt: description,
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
// Merge any file changes back to parent state
|
|
412
|
+
state.files = { ...state.files, ...subagentState.files };
|
|
413
|
+
|
|
414
|
+
const resultText = result.text || "Task completed successfully.";
|
|
415
|
+
|
|
416
|
+
// Format output for parent agent
|
|
417
|
+
let formattedResult = resultText;
|
|
418
|
+
|
|
419
|
+
// If subagent has structured output, include it in the response
|
|
420
|
+
if (subagentConfig.output && 'output' in result && result.output) {
|
|
421
|
+
formattedResult = `${resultText}\n\n[Structured Output]\n${JSON.stringify(result.output, null, 2)}`;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Emit subagent finish event
|
|
425
|
+
if (onEvent) {
|
|
426
|
+
onEvent(createSubagentFinishEvent(subagent_type, formattedResult));
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return formattedResult;
|
|
430
|
+
} catch (error: unknown) {
|
|
431
|
+
const err = error as Error;
|
|
432
|
+
const errorMessage = `Error executing subagent: ${err.message}`;
|
|
433
|
+
|
|
434
|
+
// Emit subagent finish event with error
|
|
435
|
+
if (onEvent) {
|
|
436
|
+
onEvent(createSubagentFinishEvent(subagent_type, errorMessage));
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return errorMessage;
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
});
|
|
443
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Todo list tool for task planning and tracking.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { tool } from "ai";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import type { DeepAgentState, TodoItem, EventCallback } from "../types";
|
|
8
|
+
import { createTodosChangedEvent } from "../utils/events";
|
|
9
|
+
|
|
10
|
+
const TodoItemSchema = z.object({
|
|
11
|
+
id: z.string().describe("Unique identifier for the todo item"),
|
|
12
|
+
content: z
|
|
13
|
+
.string()
|
|
14
|
+
.max(100)
|
|
15
|
+
.describe("The description/content of the todo item (max 100 chars)"),
|
|
16
|
+
status: z
|
|
17
|
+
.enum(["pending", "in_progress", "completed", "cancelled"])
|
|
18
|
+
.describe("The current status of the todo item"),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Create the write_todos tool for task planning.
|
|
23
|
+
* @param state - The shared agent state
|
|
24
|
+
* @param onEvent - Optional callback for emitting events
|
|
25
|
+
*/
|
|
26
|
+
export function createTodosTool(state: DeepAgentState, onEvent?: EventCallback) {
|
|
27
|
+
return tool({
|
|
28
|
+
description: `Manage and plan tasks using a structured todo list. Use this tool for:
|
|
29
|
+
- Complex multi-step tasks (3+ steps)
|
|
30
|
+
- After receiving new instructions - capture requirements
|
|
31
|
+
- When starting tasks - mark as in_progress (only one at a time)
|
|
32
|
+
- After completing tasks - mark complete immediately
|
|
33
|
+
|
|
34
|
+
Task states: pending, in_progress, completed, cancelled
|
|
35
|
+
|
|
36
|
+
When merge=true, updates are merged with existing todos by id.
|
|
37
|
+
When merge=false, the new todos replace all existing todos.`,
|
|
38
|
+
inputSchema: z.object({
|
|
39
|
+
todos: z
|
|
40
|
+
.array(TodoItemSchema)
|
|
41
|
+
.min(1)
|
|
42
|
+
.describe("Array of todo items to write"),
|
|
43
|
+
merge: z
|
|
44
|
+
.boolean()
|
|
45
|
+
.default(true)
|
|
46
|
+
.describe(
|
|
47
|
+
"Whether to merge with existing todos (true) or replace all (false)"
|
|
48
|
+
),
|
|
49
|
+
}),
|
|
50
|
+
execute: async ({ todos, merge }) => {
|
|
51
|
+
if (merge) {
|
|
52
|
+
// Merge by id
|
|
53
|
+
const existingMap = new Map<string, TodoItem>();
|
|
54
|
+
for (const todo of state.todos) {
|
|
55
|
+
existingMap.set(todo.id, todo);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for (const newTodo of todos) {
|
|
59
|
+
const existing = existingMap.get(newTodo.id);
|
|
60
|
+
if (existing) {
|
|
61
|
+
// Update existing todo
|
|
62
|
+
existingMap.set(newTodo.id, {
|
|
63
|
+
...existing,
|
|
64
|
+
...newTodo,
|
|
65
|
+
});
|
|
66
|
+
} else {
|
|
67
|
+
// Add new todo
|
|
68
|
+
existingMap.set(newTodo.id, newTodo);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
state.todos = Array.from(existingMap.values());
|
|
73
|
+
} else {
|
|
74
|
+
// Replace all
|
|
75
|
+
state.todos = todos;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Emit event if callback provided
|
|
79
|
+
if (onEvent) {
|
|
80
|
+
onEvent(createTodosChangedEvent([...state.todos]));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Format current todo list for response
|
|
84
|
+
const todoList = state.todos
|
|
85
|
+
.map((t) => `- [${t.status}] ${t.id}: ${t.content}`)
|
|
86
|
+
.join("\n");
|
|
87
|
+
|
|
88
|
+
return `Todo list updated successfully.\n\nCurrent todos:\n${todoList}`;
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ============================================================================
|
|
94
|
+
// Individual Tool Reference
|
|
95
|
+
// ============================================================================
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Individual builtin tool reference for selective subagent configuration.
|
|
99
|
+
* This is a reference to the creator function, not an instance.
|
|
100
|
+
*/
|
|
101
|
+
export const write_todos = createTodosTool;
|