@probelabs/probe 0.6.0-rc203 → 0.6.0-rc205
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/bin/binaries/probe-v0.6.0-rc205-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc205-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc205-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc205-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc205-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/ProbeAgent.d.ts +2 -0
- package/build/agent/ProbeAgent.js +233 -40
- package/build/agent/index.js +1566 -84
- package/build/agent/simpleTelemetry.js +12 -0
- package/build/agent/tasks/TaskManager.js +604 -0
- package/build/agent/tasks/index.js +15 -0
- package/build/agent/tasks/taskTool.js +476 -0
- package/build/agent/tools.js +11 -0
- package/build/delegate.js +7 -2
- package/build/index.js +14 -1
- package/build/search.js +19 -5
- package/build/tools/common.js +67 -0
- package/build/tools/vercel.js +28 -12
- package/build/utils/error-types.js +303 -0
- package/build/utils/path-validation.js +21 -3
- package/cjs/agent/ProbeAgent.cjs +8940 -6393
- package/cjs/agent/simpleTelemetry.cjs +10 -0
- package/cjs/index.cjs +8960 -6393
- package/package.json +2 -2
- package/src/agent/ProbeAgent.d.ts +2 -0
- package/src/agent/ProbeAgent.js +233 -40
- package/src/agent/index.js +14 -2
- package/src/agent/simpleTelemetry.js +12 -0
- package/src/agent/tasks/TaskManager.js +604 -0
- package/src/agent/tasks/index.js +15 -0
- package/src/agent/tasks/taskTool.js +476 -0
- package/src/agent/tools.js +11 -0
- package/src/delegate.js +7 -2
- package/src/index.js +14 -1
- package/src/search.js +19 -5
- package/src/tools/common.js +67 -0
- package/src/tools/vercel.js +28 -12
- package/src/utils/error-types.js +303 -0
- package/src/utils/path-validation.js +21 -3
- package/bin/binaries/probe-v0.6.0-rc203-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc203-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc203-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc203-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc203-x86_64-unknown-linux-musl.tar.gz +0 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task Tool - XML tool definition and executor for task management
|
|
3
|
+
* @module agent/tasks/taskTool
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Schema for a single task item in batch operations
|
|
10
|
+
*/
|
|
11
|
+
export const taskItemSchema = z.object({
|
|
12
|
+
id: z.string().optional(),
|
|
13
|
+
title: z.string().optional(),
|
|
14
|
+
description: z.string().optional(),
|
|
15
|
+
status: z.enum(['pending', 'in_progress', 'completed', 'cancelled']).optional(),
|
|
16
|
+
priority: z.enum(['low', 'medium', 'high', 'critical']).optional(),
|
|
17
|
+
dependencies: z.array(z.string()).optional(),
|
|
18
|
+
after: z.string().optional()
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Task schema for validation
|
|
23
|
+
*/
|
|
24
|
+
export const taskSchema = z.object({
|
|
25
|
+
action: z.enum(['create', 'update', 'complete', 'delete', 'list']),
|
|
26
|
+
tasks: z.array(z.union([z.string(), taskItemSchema])).optional(),
|
|
27
|
+
id: z.string().optional(),
|
|
28
|
+
title: z.string().optional(),
|
|
29
|
+
description: z.string().optional(),
|
|
30
|
+
status: z.enum(['pending', 'in_progress', 'completed', 'cancelled']).optional(),
|
|
31
|
+
priority: z.enum(['low', 'medium', 'high', 'critical']).optional(),
|
|
32
|
+
dependencies: z.array(z.string()).optional(),
|
|
33
|
+
after: z.string().optional()
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Task tool XML definition for system prompt
|
|
38
|
+
*/
|
|
39
|
+
export const taskToolDefinition = `## task
|
|
40
|
+
Manage tasks for tracking progress during code exploration and problem-solving. Create tasks to break down complex problems, track dependencies, and ensure all work is completed.
|
|
41
|
+
|
|
42
|
+
Parameters:
|
|
43
|
+
- action: (required) The action to perform: create, update, complete, delete, list
|
|
44
|
+
- tasks: (optional) JSON array for batch operations - alternative to single-task params
|
|
45
|
+
- id: (optional) Task ID for single operations (e.g., "task-1")
|
|
46
|
+
- title: (optional) Task title for create/update
|
|
47
|
+
- description: (optional) Task description for create/update
|
|
48
|
+
- status: (optional) Task status for update: pending, in_progress, completed, cancelled
|
|
49
|
+
- priority: (optional) Task priority: low, medium, high, critical
|
|
50
|
+
- dependencies: (optional) JSON array of task IDs that must be completed first
|
|
51
|
+
- after: (optional) Task ID to insert the new task after (for ordering). By default, new tasks are appended to the end
|
|
52
|
+
|
|
53
|
+
Usage Examples:
|
|
54
|
+
|
|
55
|
+
Creating a single task:
|
|
56
|
+
<task>
|
|
57
|
+
<action>create</action>
|
|
58
|
+
<title>Analyze authentication module</title>
|
|
59
|
+
<description>Search and understand how authentication works</description>
|
|
60
|
+
<priority>high</priority>
|
|
61
|
+
</task>
|
|
62
|
+
|
|
63
|
+
Creating multiple tasks with dependencies:
|
|
64
|
+
<task>
|
|
65
|
+
<action>create</action>
|
|
66
|
+
<tasks>[
|
|
67
|
+
{"title": "Search for user model", "priority": "high"},
|
|
68
|
+
{"title": "Analyze authentication flow", "dependencies": ["task-1"]},
|
|
69
|
+
{"title": "Review session management", "dependencies": ["task-2"]}
|
|
70
|
+
]</tasks>
|
|
71
|
+
</task>
|
|
72
|
+
|
|
73
|
+
Inserting a task after a specific task (instead of appending to end):
|
|
74
|
+
<task>
|
|
75
|
+
<action>create</action>
|
|
76
|
+
<title>Investigate error handling</title>
|
|
77
|
+
<after>task-2</after>
|
|
78
|
+
</task>
|
|
79
|
+
|
|
80
|
+
Updating a task status:
|
|
81
|
+
<task>
|
|
82
|
+
<action>update</action>
|
|
83
|
+
<id>task-1</id>
|
|
84
|
+
<status>in_progress</status>
|
|
85
|
+
</task>
|
|
86
|
+
|
|
87
|
+
Batch updating multiple tasks:
|
|
88
|
+
<task>
|
|
89
|
+
<action>update</action>
|
|
90
|
+
<tasks>[
|
|
91
|
+
{"id": "task-1", "status": "completed"},
|
|
92
|
+
{"id": "task-2", "status": "in_progress"}
|
|
93
|
+
]</tasks>
|
|
94
|
+
</task>
|
|
95
|
+
|
|
96
|
+
Completing a task:
|
|
97
|
+
<task>
|
|
98
|
+
<action>complete</action>
|
|
99
|
+
<id>task-1</id>
|
|
100
|
+
</task>
|
|
101
|
+
|
|
102
|
+
Cancelling a task:
|
|
103
|
+
<task>
|
|
104
|
+
<action>update</action>
|
|
105
|
+
<id>task-1</id>
|
|
106
|
+
<status>cancelled</status>
|
|
107
|
+
</task>
|
|
108
|
+
|
|
109
|
+
Deleting a task:
|
|
110
|
+
<task>
|
|
111
|
+
<action>delete</action>
|
|
112
|
+
<id>task-1</id>
|
|
113
|
+
</task>
|
|
114
|
+
|
|
115
|
+
Listing all tasks:
|
|
116
|
+
<task>
|
|
117
|
+
<action>list</action>
|
|
118
|
+
</task>
|
|
119
|
+
`;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Task system prompt addition - comprehensive guidance for AI
|
|
123
|
+
*/
|
|
124
|
+
export const taskSystemPrompt = `[Task Management System]
|
|
125
|
+
|
|
126
|
+
You have access to a task tracking tool to organize your work on complex requests.
|
|
127
|
+
|
|
128
|
+
## When to Create Tasks
|
|
129
|
+
|
|
130
|
+
CREATE TASKS when the request has **multiple distinct deliverables or goals**:
|
|
131
|
+
- "Fix bug A AND add feature B" → Two separate tasks
|
|
132
|
+
- "Investigate auth, payments, AND notifications" → Three independent areas
|
|
133
|
+
- "Implement X, then add tests, then update docs" → Sequential phases with different outputs
|
|
134
|
+
- User explicitly asks for a plan or task breakdown
|
|
135
|
+
|
|
136
|
+
SKIP TASKS for single-goal requests, even if they require multiple searches:
|
|
137
|
+
- "How does ranking work?" → Just investigate and answer (one goal)
|
|
138
|
+
- "What does function X do?" → Just look it up (one goal)
|
|
139
|
+
- "Explain the authentication flow" → Just trace and explain (one goal)
|
|
140
|
+
- "Find where errors are logged" → Just search and report (one goal)
|
|
141
|
+
|
|
142
|
+
**Key insight**: Multiple *internal steps* (search, read, analyze) are NOT the same as multiple *goals*.
|
|
143
|
+
A single investigation with many steps is still ONE task, not many.
|
|
144
|
+
|
|
145
|
+
MODIFY TASKS when (during execution):
|
|
146
|
+
- You discover the problem is more complex than expected → Add new tasks
|
|
147
|
+
- A single task covers too much scope → Split into smaller tasks
|
|
148
|
+
- You find related work that needs attention → Add dependent tasks
|
|
149
|
+
- A task becomes irrelevant based on findings → Cancel it
|
|
150
|
+
- Task priorities change based on discoveries → Update priority
|
|
151
|
+
- You learn new context → Update task description
|
|
152
|
+
|
|
153
|
+
## Task Workflow
|
|
154
|
+
|
|
155
|
+
**STEP 1 - Plan (at start):**
|
|
156
|
+
Analyze the request and create tasks for each logical step:
|
|
157
|
+
|
|
158
|
+
<task>
|
|
159
|
+
<action>create</action>
|
|
160
|
+
<tasks>[
|
|
161
|
+
{"title": "Search for authentication module", "priority": "high"},
|
|
162
|
+
{"title": "Analyze login flow implementation", "dependencies": ["task-1"]},
|
|
163
|
+
{"title": "Find session management code", "dependencies": ["task-1"]},
|
|
164
|
+
{"title": "Summarize authentication architecture", "dependencies": ["task-2", "task-3"]}
|
|
165
|
+
]</tasks>
|
|
166
|
+
</task>
|
|
167
|
+
|
|
168
|
+
**STEP 2 - Execute (during work):**
|
|
169
|
+
Update task status as you work:
|
|
170
|
+
|
|
171
|
+
<task>
|
|
172
|
+
<action>update</action>
|
|
173
|
+
<id>task-1</id>
|
|
174
|
+
<status>in_progress</status>
|
|
175
|
+
</task>
|
|
176
|
+
|
|
177
|
+
... do the work (search, extract, etc.) ...
|
|
178
|
+
|
|
179
|
+
<task>
|
|
180
|
+
<action>complete</action>
|
|
181
|
+
<id>task-1</id>
|
|
182
|
+
</task>
|
|
183
|
+
|
|
184
|
+
**STEP 2b - Adapt (when you discover new work):**
|
|
185
|
+
As you work, you may discover that:
|
|
186
|
+
- A task is more complex than expected → Split it into subtasks
|
|
187
|
+
- New areas need investigation → Add new tasks
|
|
188
|
+
- Some tasks are no longer needed → Cancel them
|
|
189
|
+
- Task order should change → Update dependencies
|
|
190
|
+
|
|
191
|
+
*Adding a new task when you discover more work:*
|
|
192
|
+
<task>
|
|
193
|
+
<action>create</action>
|
|
194
|
+
<title>Investigate caching layer</title>
|
|
195
|
+
<description>Found references to Redis caching in auth module</description>
|
|
196
|
+
</task>
|
|
197
|
+
|
|
198
|
+
*Inserting a task after a specific task (to maintain logical order):*
|
|
199
|
+
<task>
|
|
200
|
+
<action>create</action>
|
|
201
|
+
<title>Check rate limiting</title>
|
|
202
|
+
<after>task-2</after>
|
|
203
|
+
</task>
|
|
204
|
+
|
|
205
|
+
*Cancelling and splitting a complex task:*
|
|
206
|
+
<task>
|
|
207
|
+
<action>update</action>
|
|
208
|
+
<id>task-3</id>
|
|
209
|
+
<status>cancelled</status>
|
|
210
|
+
</task>
|
|
211
|
+
<task>
|
|
212
|
+
<action>create</action>
|
|
213
|
+
<tasks>[
|
|
214
|
+
{"title": "Review JWT token generation", "priority": "high"},
|
|
215
|
+
{"title": "Review token refresh logic"}
|
|
216
|
+
]</tasks>
|
|
217
|
+
</task>
|
|
218
|
+
|
|
219
|
+
**STEP 3 - Finish (before completion):**
|
|
220
|
+
Before calling attempt_completion, ensure ALL tasks are either:
|
|
221
|
+
- \`completed\` - you finished the work
|
|
222
|
+
- \`cancelled\` - no longer needed
|
|
223
|
+
|
|
224
|
+
If you created tasks, you MUST resolve them all before completing.
|
|
225
|
+
|
|
226
|
+
## Key Rules
|
|
227
|
+
|
|
228
|
+
1. **Dependencies are enforced**: A task cannot start until its dependencies are completed
|
|
229
|
+
2. **Circular dependencies are rejected**: task-1 → task-2 → task-1 is invalid
|
|
230
|
+
3. **Completion is blocked**: attempt_completion will fail if tasks remain unresolved
|
|
231
|
+
4. **List to review**: Use <task><action>list</action></task> to see current task status
|
|
232
|
+
5. **Tasks are living documents**: Add, split, or cancel tasks as you learn more about the problem
|
|
233
|
+
`;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Task guidance to inject at start of request
|
|
237
|
+
*/
|
|
238
|
+
export const taskGuidancePrompt = `<task_guidance>
|
|
239
|
+
Does this request have MULTIPLE DISTINCT GOALS?
|
|
240
|
+
- "Do A AND B AND C" (multiple goals) → Create tasks for each goal
|
|
241
|
+
- "Investigate/explain/find X" (single goal) → Skip tasks, just answer directly
|
|
242
|
+
|
|
243
|
+
Multiple internal steps (search, read, analyze) for ONE goal = NO tasks needed.
|
|
244
|
+
Only create tasks when there are separate deliverables the user is asking for.
|
|
245
|
+
|
|
246
|
+
If creating tasks, use the task tool with action="create" first.
|
|
247
|
+
</task_guidance>`;
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Create task completion blocked message
|
|
251
|
+
* @param {string} taskSummary - Summary of incomplete tasks
|
|
252
|
+
* @returns {string} Formatted message
|
|
253
|
+
*/
|
|
254
|
+
export function createTaskCompletionBlockedMessage(taskSummary) {
|
|
255
|
+
return `<task_completion_blocked>
|
|
256
|
+
You cannot complete yet. The following tasks are still unresolved:
|
|
257
|
+
|
|
258
|
+
${taskSummary}
|
|
259
|
+
|
|
260
|
+
Required action:
|
|
261
|
+
1. For each "pending" or "in_progress" task, either:
|
|
262
|
+
- Complete the work and mark it: <task><action>complete</action><id>task-X</id></task>
|
|
263
|
+
- Or cancel if no longer needed: <task><action>update</action><id>task-X</id><status>cancelled</status></task>
|
|
264
|
+
|
|
265
|
+
2. After ALL tasks are resolved (completed or cancelled), call attempt_completion again.
|
|
266
|
+
|
|
267
|
+
Use <task><action>list</action></task> to review current status.
|
|
268
|
+
</task_completion_blocked>`;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Create task tool instance
|
|
273
|
+
* @param {Object} options - Configuration options
|
|
274
|
+
* @param {import('./TaskManager.js').TaskManager} options.taskManager - TaskManager instance
|
|
275
|
+
* @param {Object} [options.tracer] - Optional tracer for telemetry
|
|
276
|
+
* @param {boolean} [options.debug=false] - Enable debug logging
|
|
277
|
+
* @returns {Object} Tool instance with execute function
|
|
278
|
+
*/
|
|
279
|
+
export function createTaskTool(options = {}) {
|
|
280
|
+
const { taskManager, tracer, debug = false } = options;
|
|
281
|
+
|
|
282
|
+
if (!taskManager) {
|
|
283
|
+
throw new Error('TaskManager instance is required');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Record task telemetry event
|
|
288
|
+
* @param {string} eventType - Event type (created, updated, completed, deleted, listed, error)
|
|
289
|
+
* @param {Object} data - Event data
|
|
290
|
+
*/
|
|
291
|
+
const recordTaskEvent = (eventType, data = {}) => {
|
|
292
|
+
if (tracer && typeof tracer.recordTaskEvent === 'function') {
|
|
293
|
+
tracer.recordTaskEvent(eventType, data);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
name: 'task',
|
|
299
|
+
description: 'Manage tasks for tracking progress during code exploration',
|
|
300
|
+
parameters: taskSchema,
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Execute task action
|
|
304
|
+
* @param {Object} params - Tool parameters
|
|
305
|
+
* @returns {string} Result message
|
|
306
|
+
*/
|
|
307
|
+
execute: async (params) => {
|
|
308
|
+
try {
|
|
309
|
+
const validation = taskSchema.safeParse(params);
|
|
310
|
+
if (!validation.success) {
|
|
311
|
+
recordTaskEvent('validation_error', {
|
|
312
|
+
'task.error': validation.error.message
|
|
313
|
+
});
|
|
314
|
+
return `Error: Invalid task parameters - ${validation.error.message}`;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const { action, tasks, id, title, description, status, priority, dependencies, after } = validation.data;
|
|
318
|
+
|
|
319
|
+
switch (action) {
|
|
320
|
+
case 'create': {
|
|
321
|
+
if (tasks && Array.isArray(tasks)) {
|
|
322
|
+
// Batch create
|
|
323
|
+
const created = taskManager.createTasks(tasks);
|
|
324
|
+
const ids = created.map(t => t.id).join(', ');
|
|
325
|
+
recordTaskEvent('batch_created', {
|
|
326
|
+
'task.action': 'create',
|
|
327
|
+
'task.count': created.length,
|
|
328
|
+
'task.ids': ids,
|
|
329
|
+
'task.total_count': taskManager.listTasks().length
|
|
330
|
+
});
|
|
331
|
+
return `Created ${created.length} tasks: ${ids}\n\n${taskManager.formatTasksForPrompt()}`;
|
|
332
|
+
} else if (title) {
|
|
333
|
+
// Single create
|
|
334
|
+
const task = taskManager.createTask({ title, description, priority, dependencies, after });
|
|
335
|
+
recordTaskEvent('created', {
|
|
336
|
+
'task.action': 'create',
|
|
337
|
+
'task.id': task.id,
|
|
338
|
+
'task.title': title,
|
|
339
|
+
'task.priority': priority || 'none',
|
|
340
|
+
'task.has_dependencies': dependencies && dependencies.length > 0,
|
|
341
|
+
'task.after': after || 'none',
|
|
342
|
+
'task.total_count': taskManager.listTasks().length
|
|
343
|
+
});
|
|
344
|
+
return `Created task ${task.id}: ${task.title}\n\n${taskManager.formatTasksForPrompt()}`;
|
|
345
|
+
} else {
|
|
346
|
+
return 'Error: Create action requires either "tasks" array or "title" parameter';
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
case 'update': {
|
|
351
|
+
if (tasks && Array.isArray(tasks)) {
|
|
352
|
+
// Batch update
|
|
353
|
+
const updated = taskManager.updateTasks(tasks);
|
|
354
|
+
const ids = updated.map(t => t.id).join(', ');
|
|
355
|
+
recordTaskEvent('batch_updated', {
|
|
356
|
+
'task.action': 'update',
|
|
357
|
+
'task.count': updated.length,
|
|
358
|
+
'task.ids': ids
|
|
359
|
+
});
|
|
360
|
+
return `Updated ${updated.length} tasks: ${ids}\n\n${taskManager.formatTasksForPrompt()}`;
|
|
361
|
+
} else if (id) {
|
|
362
|
+
// Single update
|
|
363
|
+
const updates = {};
|
|
364
|
+
if (status) updates.status = status;
|
|
365
|
+
if (title) updates.title = title;
|
|
366
|
+
if (description) updates.description = description;
|
|
367
|
+
if (priority) updates.priority = priority;
|
|
368
|
+
if (dependencies) updates.dependencies = dependencies;
|
|
369
|
+
|
|
370
|
+
const task = taskManager.updateTask(id, updates);
|
|
371
|
+
recordTaskEvent('updated', {
|
|
372
|
+
'task.action': 'update',
|
|
373
|
+
'task.id': id,
|
|
374
|
+
'task.new_status': status || 'unchanged',
|
|
375
|
+
'task.fields_updated': Object.keys(updates).join(', ')
|
|
376
|
+
});
|
|
377
|
+
return `Updated task ${task.id}\n\n${taskManager.formatTasksForPrompt()}`;
|
|
378
|
+
} else {
|
|
379
|
+
return 'Error: Update action requires either "tasks" array or "id" parameter';
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
case 'complete': {
|
|
384
|
+
if (tasks && Array.isArray(tasks)) {
|
|
385
|
+
// Batch complete - validate each item has an id
|
|
386
|
+
const ids = tasks.map((t, index) => {
|
|
387
|
+
if (typeof t === 'string') return t;
|
|
388
|
+
if (t && typeof t.id === 'string') return t.id;
|
|
389
|
+
throw new Error(`Invalid task item at index ${index}: must be a string ID or object with 'id' property`);
|
|
390
|
+
});
|
|
391
|
+
const completed = taskManager.completeTasks(ids);
|
|
392
|
+
recordTaskEvent('batch_completed', {
|
|
393
|
+
'task.action': 'complete',
|
|
394
|
+
'task.count': completed.length,
|
|
395
|
+
'task.ids': ids.join(', '),
|
|
396
|
+
'task.incomplete_remaining': taskManager.getIncompleteTasks().length
|
|
397
|
+
});
|
|
398
|
+
return `Completed ${completed.length} tasks\n\n${taskManager.formatTasksForPrompt()}`;
|
|
399
|
+
} else if (id) {
|
|
400
|
+
// Single complete
|
|
401
|
+
const task = taskManager.completeTask(id);
|
|
402
|
+
recordTaskEvent('completed', {
|
|
403
|
+
'task.action': 'complete',
|
|
404
|
+
'task.id': id,
|
|
405
|
+
'task.title': task.title,
|
|
406
|
+
'task.incomplete_remaining': taskManager.getIncompleteTasks().length
|
|
407
|
+
});
|
|
408
|
+
return `Completed task ${task.id}: ${task.title}\n\n${taskManager.formatTasksForPrompt()}`;
|
|
409
|
+
} else {
|
|
410
|
+
return 'Error: Complete action requires either "tasks" array or "id" parameter';
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
case 'delete': {
|
|
415
|
+
if (tasks && Array.isArray(tasks)) {
|
|
416
|
+
// Batch delete - validate each item has an id
|
|
417
|
+
const ids = tasks.map((t, index) => {
|
|
418
|
+
if (typeof t === 'string') return t;
|
|
419
|
+
if (t && typeof t.id === 'string') return t.id;
|
|
420
|
+
throw new Error(`Invalid task item at index ${index}: must be a string ID or object with 'id' property`);
|
|
421
|
+
});
|
|
422
|
+
const deleted = taskManager.deleteTasks(ids);
|
|
423
|
+
recordTaskEvent('batch_deleted', {
|
|
424
|
+
'task.action': 'delete',
|
|
425
|
+
'task.count': deleted.length,
|
|
426
|
+
'task.ids': deleted.join(', '),
|
|
427
|
+
'task.total_count': taskManager.listTasks().length
|
|
428
|
+
});
|
|
429
|
+
return `Deleted ${deleted.length} tasks: ${deleted.join(', ')}\n\n${taskManager.formatTasksForPrompt()}`;
|
|
430
|
+
} else if (id) {
|
|
431
|
+
// Single delete
|
|
432
|
+
taskManager.deleteTask(id);
|
|
433
|
+
recordTaskEvent('deleted', {
|
|
434
|
+
'task.action': 'delete',
|
|
435
|
+
'task.id': id,
|
|
436
|
+
'task.total_count': taskManager.listTasks().length
|
|
437
|
+
});
|
|
438
|
+
return `Deleted task ${id}\n\n${taskManager.formatTasksForPrompt()}`;
|
|
439
|
+
} else {
|
|
440
|
+
return 'Error: Delete action requires either "tasks" array or "id" parameter';
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
case 'list': {
|
|
445
|
+
const allTasks = taskManager.listTasks();
|
|
446
|
+
const incomplete = taskManager.getIncompleteTasks();
|
|
447
|
+
recordTaskEvent('listed', {
|
|
448
|
+
'task.action': 'list',
|
|
449
|
+
'task.total_count': allTasks.length,
|
|
450
|
+
'task.incomplete_count': incomplete.length,
|
|
451
|
+
'task.completed_count': allTasks.length - incomplete.length
|
|
452
|
+
});
|
|
453
|
+
return taskManager.formatTasksForPrompt();
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
default:
|
|
457
|
+
recordTaskEvent('unknown_action', {
|
|
458
|
+
'task.action': action
|
|
459
|
+
});
|
|
460
|
+
return `Error: Unknown action "${action}". Valid actions: create, update, complete, delete, list`;
|
|
461
|
+
}
|
|
462
|
+
} catch (error) {
|
|
463
|
+
recordTaskEvent('error', {
|
|
464
|
+
'task.error': error.message,
|
|
465
|
+
'task.action': params?.action || 'unknown'
|
|
466
|
+
});
|
|
467
|
+
if (debug) {
|
|
468
|
+
console.error('[TaskTool] Error:', error);
|
|
469
|
+
}
|
|
470
|
+
return `Error: ${error.message}`;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export default createTaskTool;
|
package/build/agent/tools.js
CHANGED
|
@@ -71,6 +71,17 @@ export function createTools(configOptions) {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
// Export tool definitions and schemas
|
|
74
|
+
// Export task tool from tasks module
|
|
75
|
+
export {
|
|
76
|
+
taskSchema,
|
|
77
|
+
taskToolDefinition,
|
|
78
|
+
taskSystemPrompt,
|
|
79
|
+
taskGuidancePrompt,
|
|
80
|
+
createTaskCompletionBlockedMessage,
|
|
81
|
+
createTaskTool,
|
|
82
|
+
TaskManager
|
|
83
|
+
} from './tasks/index.js';
|
|
84
|
+
|
|
74
85
|
export {
|
|
75
86
|
DEFAULT_SYSTEM_MESSAGE,
|
|
76
87
|
searchSchema,
|
package/build/delegate.js
CHANGED
|
@@ -185,6 +185,7 @@ const delegationManager = new DelegationManager();
|
|
|
185
185
|
* @param {boolean} [options.disableTools=false] - Disable all tools for the subagent
|
|
186
186
|
* @param {boolean} [options.searchDelegate] - Use delegated search in the subagent
|
|
187
187
|
* @param {Object|string} [options.schema] - Optional JSON schema to enforce response format
|
|
188
|
+
* @param {boolean} [options.enableTasks=false] - Enable task management for the subagent (isolated instance)
|
|
188
189
|
* @returns {Promise<string>} The response from the delegate agent
|
|
189
190
|
*/
|
|
190
191
|
export async function delegate({
|
|
@@ -206,7 +207,8 @@ export async function delegate({
|
|
|
206
207
|
allowedTools = null,
|
|
207
208
|
disableTools = false,
|
|
208
209
|
searchDelegate = undefined,
|
|
209
|
-
schema = null
|
|
210
|
+
schema = null,
|
|
211
|
+
enableTasks = false
|
|
210
212
|
}) {
|
|
211
213
|
if (!task || typeof task !== 'string') {
|
|
212
214
|
throw new Error('Task parameter is required and must be a string');
|
|
@@ -246,6 +248,8 @@ export async function delegate({
|
|
|
246
248
|
// IMPORTANT: We pass both path and cwd set to the same value (workspace root)
|
|
247
249
|
// to prevent path doubling issues. The parent's navigation context should not
|
|
248
250
|
// affect the subagent's path resolution - subagents always work from workspace root.
|
|
251
|
+
// Note: enableTasks creates an isolated TaskManager for the subagent.
|
|
252
|
+
// Tasks do not propagate back to the parent - each subagent has its own scope.
|
|
249
253
|
const subagent = new ProbeAgent({
|
|
250
254
|
sessionId,
|
|
251
255
|
promptType, // Clean prompt, not inherited from parent
|
|
@@ -265,7 +269,8 @@ export async function delegate({
|
|
|
265
269
|
architectureFileName,
|
|
266
270
|
allowedTools,
|
|
267
271
|
disableTools,
|
|
268
|
-
searchDelegate
|
|
272
|
+
searchDelegate,
|
|
273
|
+
enableTasks // Inherit from parent (subagent gets isolated TaskManager)
|
|
269
274
|
});
|
|
270
275
|
|
|
271
276
|
if (debug) {
|
package/build/index.js
CHANGED
|
@@ -49,6 +49,13 @@ import { SimpleTelemetry, SimpleAppTracer, initializeSimpleTelemetryFromOptions
|
|
|
49
49
|
import { listFilesToolInstance, searchFilesToolInstance } from './agent/probeTool.js';
|
|
50
50
|
import { StorageAdapter, InMemoryStorageAdapter } from './agent/storage/index.js';
|
|
51
51
|
import { HookManager, HOOK_TYPES } from './agent/hooks/index.js';
|
|
52
|
+
import {
|
|
53
|
+
TaskManager,
|
|
54
|
+
taskSchema,
|
|
55
|
+
taskToolDefinition,
|
|
56
|
+
taskSystemPrompt,
|
|
57
|
+
createTaskTool
|
|
58
|
+
} from './agent/tasks/index.js';
|
|
52
59
|
|
|
53
60
|
export {
|
|
54
61
|
search,
|
|
@@ -103,5 +110,11 @@ export {
|
|
|
103
110
|
editToolDefinition,
|
|
104
111
|
createToolDefinition,
|
|
105
112
|
// Export parser function
|
|
106
|
-
parseXmlToolCall
|
|
113
|
+
parseXmlToolCall,
|
|
114
|
+
// Export task management
|
|
115
|
+
TaskManager,
|
|
116
|
+
taskSchema,
|
|
117
|
+
taskToolDefinition,
|
|
118
|
+
taskSystemPrompt,
|
|
119
|
+
createTaskTool
|
|
107
120
|
};
|
package/build/search.js
CHANGED
|
@@ -7,6 +7,7 @@ import { execFile } from 'child_process';
|
|
|
7
7
|
import { promisify } from 'util';
|
|
8
8
|
import { getBinaryPath, buildCliArgs } from './utils.js';
|
|
9
9
|
import { validateCwdPath } from './utils/path-validation.js';
|
|
10
|
+
import { TimeoutError, categorizeError } from './utils/error-types.js';
|
|
10
11
|
|
|
11
12
|
const execFileAsync = promisify(execFile);
|
|
12
13
|
|
|
@@ -234,13 +235,26 @@ export async function search(options) {
|
|
|
234
235
|
} catch (error) {
|
|
235
236
|
// Check if the error is a timeout
|
|
236
237
|
if (error.code === 'ETIMEDOUT' || error.killed) {
|
|
237
|
-
const timeoutMessage = `Search operation timed out after ${options.timeout} seconds
|
|
238
|
+
const timeoutMessage = `Search operation timed out after ${options.timeout} seconds`;
|
|
238
239
|
console.error(timeoutMessage);
|
|
239
|
-
throw new
|
|
240
|
+
throw new TimeoutError(timeoutMessage, {
|
|
241
|
+
suggestion: 'The search operation timed out. Try a more specific query, reduce the search scope, or increase the timeout.',
|
|
242
|
+
details: {
|
|
243
|
+
timeout: options.timeout,
|
|
244
|
+
query: options.query,
|
|
245
|
+
path: options.path
|
|
246
|
+
}
|
|
247
|
+
});
|
|
240
248
|
}
|
|
241
249
|
|
|
242
|
-
//
|
|
243
|
-
const
|
|
244
|
-
|
|
250
|
+
// Categorize and enhance the error
|
|
251
|
+
const structuredError = categorizeError(error);
|
|
252
|
+
structuredError.details = {
|
|
253
|
+
...structuredError.details,
|
|
254
|
+
binary: binaryPath,
|
|
255
|
+
args: args.join(' '),
|
|
256
|
+
cwd: cwd
|
|
257
|
+
};
|
|
258
|
+
throw structuredError;
|
|
245
259
|
}
|
|
246
260
|
}
|
package/build/tools/common.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { z } from 'zod';
|
|
7
7
|
import { resolve, isAbsolute } from 'path';
|
|
8
8
|
import { editSchema, createSchema } from './edit.js';
|
|
9
|
+
import { taskSchema } from '../agent/tasks/taskTool.js';
|
|
9
10
|
|
|
10
11
|
// Common schemas for tool parameters (used for internal execution after XML parsing)
|
|
11
12
|
export const searchSchema = z.object({
|
|
@@ -347,6 +348,7 @@ export const DEFAULT_VALID_TOOLS = [
|
|
|
347
348
|
'searchFiles',
|
|
348
349
|
'implement',
|
|
349
350
|
'bash',
|
|
351
|
+
'task',
|
|
350
352
|
'attempt_completion'
|
|
351
353
|
];
|
|
352
354
|
|
|
@@ -380,6 +382,7 @@ function getValidParamsForTool(toolName) {
|
|
|
380
382
|
listSkills: listSkillsSchema,
|
|
381
383
|
useSkill: useSkillSchema,
|
|
382
384
|
bash: bashSchema,
|
|
385
|
+
task: taskSchema,
|
|
383
386
|
attempt_completion: attemptCompletionSchema,
|
|
384
387
|
edit: editSchema,
|
|
385
388
|
create: createSchema
|
|
@@ -553,6 +556,70 @@ export function createMessagePreview(message, charsPerSide = 200) {
|
|
|
553
556
|
return `${start}...${end}`;
|
|
554
557
|
}
|
|
555
558
|
|
|
559
|
+
/**
|
|
560
|
+
* Detect if the response contains an XML-style tool tag that wasn't recognized
|
|
561
|
+
* This helps identify when the AI tried to use a tool that's not in the validTools list
|
|
562
|
+
*
|
|
563
|
+
* @param {string} xmlString - The XML string to search
|
|
564
|
+
* @param {string[]} validTools - List of valid tool names that would have been recognized
|
|
565
|
+
* @returns {string|null} - The unrecognized tool name, or null if no unrecognized tools found
|
|
566
|
+
*/
|
|
567
|
+
export function detectUnrecognizedToolCall(xmlString, validTools) {
|
|
568
|
+
if (!xmlString || typeof xmlString !== 'string') {
|
|
569
|
+
return null;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Common tool names that AI might try to use (these should appear as top-level tags)
|
|
573
|
+
const knownToolNames = [
|
|
574
|
+
'search', 'query', 'extract', 'listFiles', 'searchFiles',
|
|
575
|
+
'listSkills', 'useSkill', 'readImage', 'implement', 'edit',
|
|
576
|
+
'create', 'delegate', 'bash', 'task', 'attempt_completion',
|
|
577
|
+
'attempt_complete', 'read_file', 'write_file', 'run_command',
|
|
578
|
+
'grep', 'find', 'cat', 'list_directory'
|
|
579
|
+
];
|
|
580
|
+
|
|
581
|
+
// Look for XML tags that match known tool patterns
|
|
582
|
+
// Only consider a tag as a tool call if:
|
|
583
|
+
// 1. It's not in the validTools list (wouldn't have been parsed)
|
|
584
|
+
// 2. It appears as a top-level tag (not nested inside another tool)
|
|
585
|
+
for (const toolName of knownToolNames) {
|
|
586
|
+
if (validTools.includes(toolName)) {
|
|
587
|
+
continue; // Skip valid tools - these would have been parsed
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const openTag = `<${toolName}>`;
|
|
591
|
+
const closeTag = `</${toolName}>`;
|
|
592
|
+
|
|
593
|
+
// Check if this tool tag exists
|
|
594
|
+
const openIndex = xmlString.indexOf(openTag);
|
|
595
|
+
if (openIndex === -1) {
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// Check if this tag is nested inside a valid tool tag
|
|
600
|
+
let isNested = false;
|
|
601
|
+
for (const validTool of validTools) {
|
|
602
|
+
const validOpenTag = `<${validTool}>`;
|
|
603
|
+
const validCloseTag = `</${validTool}>`;
|
|
604
|
+
const validOpenIndex = xmlString.indexOf(validOpenTag);
|
|
605
|
+
const validCloseIndex = xmlString.indexOf(validCloseTag);
|
|
606
|
+
|
|
607
|
+
// If the unrecognized tool tag is between a valid tool's open and close tags, it's nested (a parameter)
|
|
608
|
+
if (validOpenIndex !== -1 && validCloseIndex !== -1 &&
|
|
609
|
+
validOpenIndex < openIndex && openIndex < validCloseIndex) {
|
|
610
|
+
isNested = true;
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (!isNested) {
|
|
616
|
+
return toolName;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
|
|
556
623
|
/**
|
|
557
624
|
* Parse targets string into array of file specifications
|
|
558
625
|
* Handles both space-separated and comma-separated targets for extract tool
|