@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.
Files changed (44) hide show
  1. package/bin/binaries/probe-v0.6.0-rc205-aarch64-apple-darwin.tar.gz +0 -0
  2. package/bin/binaries/probe-v0.6.0-rc205-aarch64-unknown-linux-musl.tar.gz +0 -0
  3. package/bin/binaries/probe-v0.6.0-rc205-x86_64-apple-darwin.tar.gz +0 -0
  4. package/bin/binaries/probe-v0.6.0-rc205-x86_64-pc-windows-msvc.zip +0 -0
  5. package/bin/binaries/probe-v0.6.0-rc205-x86_64-unknown-linux-musl.tar.gz +0 -0
  6. package/build/agent/ProbeAgent.d.ts +2 -0
  7. package/build/agent/ProbeAgent.js +233 -40
  8. package/build/agent/index.js +1566 -84
  9. package/build/agent/simpleTelemetry.js +12 -0
  10. package/build/agent/tasks/TaskManager.js +604 -0
  11. package/build/agent/tasks/index.js +15 -0
  12. package/build/agent/tasks/taskTool.js +476 -0
  13. package/build/agent/tools.js +11 -0
  14. package/build/delegate.js +7 -2
  15. package/build/index.js +14 -1
  16. package/build/search.js +19 -5
  17. package/build/tools/common.js +67 -0
  18. package/build/tools/vercel.js +28 -12
  19. package/build/utils/error-types.js +303 -0
  20. package/build/utils/path-validation.js +21 -3
  21. package/cjs/agent/ProbeAgent.cjs +8940 -6393
  22. package/cjs/agent/simpleTelemetry.cjs +10 -0
  23. package/cjs/index.cjs +8960 -6393
  24. package/package.json +2 -2
  25. package/src/agent/ProbeAgent.d.ts +2 -0
  26. package/src/agent/ProbeAgent.js +233 -40
  27. package/src/agent/index.js +14 -2
  28. package/src/agent/simpleTelemetry.js +12 -0
  29. package/src/agent/tasks/TaskManager.js +604 -0
  30. package/src/agent/tasks/index.js +15 -0
  31. package/src/agent/tasks/taskTool.js +476 -0
  32. package/src/agent/tools.js +11 -0
  33. package/src/delegate.js +7 -2
  34. package/src/index.js +14 -1
  35. package/src/search.js +19 -5
  36. package/src/tools/common.js +67 -0
  37. package/src/tools/vercel.js +28 -12
  38. package/src/utils/error-types.js +303 -0
  39. package/src/utils/path-validation.js +21 -3
  40. package/bin/binaries/probe-v0.6.0-rc203-aarch64-apple-darwin.tar.gz +0 -0
  41. package/bin/binaries/probe-v0.6.0-rc203-aarch64-unknown-linux-musl.tar.gz +0 -0
  42. package/bin/binaries/probe-v0.6.0-rc203-x86_64-apple-darwin.tar.gz +0 -0
  43. package/bin/binaries/probe-v0.6.0-rc203-x86_64-pc-windows-msvc.zip +0 -0
  44. 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;
@@ -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.\nBinary: ${binaryPath}\nArgs: ${args.join(' ')}\nCwd: ${cwd}`;
238
+ const timeoutMessage = `Search operation timed out after ${options.timeout} seconds`;
238
239
  console.error(timeoutMessage);
239
- throw new Error(timeoutMessage);
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
- // Enhance error message with command details
243
- const errorMessage = `Error executing search command: ${error.message}\nBinary: ${binaryPath}\nArgs: ${args.join(' ')}\nCwd: ${cwd}`;
244
- throw new Error(errorMessage);
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
  }
@@ -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