wave-agent-sdk 0.17.1 → 0.17.3

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 (173) hide show
  1. package/builtin/skills/deep-research/SKILL.md +90 -0
  2. package/builtin/skills/settings/ENV.md +6 -3
  3. package/dist/agent.d.ts +28 -1
  4. package/dist/agent.d.ts.map +1 -1
  5. package/dist/agent.js +128 -34
  6. package/dist/constants/goalPrompts.d.ts +2 -0
  7. package/dist/constants/goalPrompts.d.ts.map +1 -0
  8. package/dist/constants/goalPrompts.js +10 -0
  9. package/dist/constants/tools.d.ts +1 -0
  10. package/dist/constants/tools.d.ts.map +1 -1
  11. package/dist/constants/tools.js +1 -0
  12. package/dist/managers/aiManager.d.ts +7 -0
  13. package/dist/managers/aiManager.d.ts.map +1 -1
  14. package/dist/managers/aiManager.js +77 -41
  15. package/dist/managers/backgroundTaskManager.d.ts.map +1 -1
  16. package/dist/managers/backgroundTaskManager.js +10 -2
  17. package/dist/managers/goalManager.d.ts +43 -0
  18. package/dist/managers/goalManager.d.ts.map +1 -0
  19. package/dist/managers/goalManager.js +177 -0
  20. package/dist/managers/messageManager.d.ts +2 -2
  21. package/dist/managers/messageManager.d.ts.map +1 -1
  22. package/dist/managers/messageQueue.d.ts +10 -0
  23. package/dist/managers/messageQueue.d.ts.map +1 -1
  24. package/dist/managers/messageQueue.js +53 -1
  25. package/dist/managers/pluginManager.d.ts.map +1 -1
  26. package/dist/managers/pluginManager.js +7 -1
  27. package/dist/managers/skillManager.d.ts +2 -0
  28. package/dist/managers/skillManager.d.ts.map +1 -1
  29. package/dist/managers/skillManager.js +19 -9
  30. package/dist/managers/slashCommandManager.d.ts +6 -0
  31. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  32. package/dist/managers/slashCommandManager.js +105 -0
  33. package/dist/managers/toolManager.d.ts.map +1 -1
  34. package/dist/managers/toolManager.js +5 -0
  35. package/dist/managers/workflowManager.d.ts +65 -0
  36. package/dist/managers/workflowManager.d.ts.map +1 -0
  37. package/dist/managers/workflowManager.js +380 -0
  38. package/dist/prompts/index.d.ts +2 -1
  39. package/dist/prompts/index.d.ts.map +1 -1
  40. package/dist/prompts/index.js +3 -3
  41. package/dist/services/MarketplaceService.d.ts +2 -2
  42. package/dist/services/MarketplaceService.d.ts.map +1 -1
  43. package/dist/services/MarketplaceService.js +11 -32
  44. package/dist/services/aiService.d.ts +23 -0
  45. package/dist/services/aiService.d.ts.map +1 -1
  46. package/dist/services/aiService.js +102 -9
  47. package/dist/services/configurationService.d.ts +1 -1
  48. package/dist/services/configurationService.d.ts.map +1 -1
  49. package/dist/services/configurationService.js +3 -16
  50. package/dist/services/hook.d.ts.map +1 -1
  51. package/dist/services/hook.js +4 -0
  52. package/dist/services/session.d.ts +9 -1
  53. package/dist/services/session.d.ts.map +1 -1
  54. package/dist/services/session.js +28 -1
  55. package/dist/tools/bashTool.d.ts.map +1 -1
  56. package/dist/tools/bashTool.js +49 -7
  57. package/dist/tools/readTool.d.ts.map +1 -1
  58. package/dist/tools/readTool.js +1 -1
  59. package/dist/tools/taskManagementTools.d.ts.map +1 -1
  60. package/dist/tools/taskManagementTools.js +103 -157
  61. package/dist/tools/types.d.ts +2 -0
  62. package/dist/tools/types.d.ts.map +1 -1
  63. package/dist/tools/webFetchTool.d.ts.map +1 -1
  64. package/dist/tools/webFetchTool.js +0 -9
  65. package/dist/tools/workflowTool.d.ts +11 -0
  66. package/dist/tools/workflowTool.d.ts.map +1 -0
  67. package/dist/tools/workflowTool.js +190 -0
  68. package/dist/types/agent.d.ts +2 -0
  69. package/dist/types/agent.d.ts.map +1 -1
  70. package/dist/types/commands.d.ts +4 -0
  71. package/dist/types/commands.d.ts.map +1 -1
  72. package/dist/types/config.d.ts +2 -2
  73. package/dist/types/config.d.ts.map +1 -1
  74. package/dist/types/core.d.ts +1 -1
  75. package/dist/types/core.d.ts.map +1 -1
  76. package/dist/types/hooks.d.ts +2 -0
  77. package/dist/types/hooks.d.ts.map +1 -1
  78. package/dist/types/index.d.ts +1 -0
  79. package/dist/types/index.d.ts.map +1 -1
  80. package/dist/types/index.js +1 -0
  81. package/dist/types/messaging.d.ts +2 -2
  82. package/dist/types/messaging.d.ts.map +1 -1
  83. package/dist/types/processes.d.ts +6 -2
  84. package/dist/types/processes.d.ts.map +1 -1
  85. package/dist/types/workflow.d.ts +2 -0
  86. package/dist/types/workflow.d.ts.map +1 -0
  87. package/dist/types/workflow.js +1 -0
  88. package/dist/utils/cacheControlUtils.d.ts +13 -8
  89. package/dist/utils/cacheControlUtils.d.ts.map +1 -1
  90. package/dist/utils/cacheControlUtils.js +73 -102
  91. package/dist/utils/containerSetup.d.ts.map +1 -1
  92. package/dist/utils/containerSetup.js +7 -0
  93. package/dist/utils/markdownParser.d.ts.map +1 -1
  94. package/dist/utils/markdownParser.js +21 -6
  95. package/dist/utils/messageOperations.d.ts +2 -2
  96. package/dist/utils/messageOperations.d.ts.map +1 -1
  97. package/dist/utils/notificationXml.d.ts.map +1 -1
  98. package/dist/workflow/budgetTracker.d.ts +12 -0
  99. package/dist/workflow/budgetTracker.d.ts.map +1 -0
  100. package/dist/workflow/budgetTracker.js +30 -0
  101. package/dist/workflow/concurrencyLimiter.d.ts +14 -0
  102. package/dist/workflow/concurrencyLimiter.d.ts.map +1 -0
  103. package/dist/workflow/concurrencyLimiter.js +39 -0
  104. package/dist/workflow/journal.d.ts +19 -0
  105. package/dist/workflow/journal.d.ts.map +1 -0
  106. package/dist/workflow/journal.js +74 -0
  107. package/dist/workflow/progressReporter.d.ts +21 -0
  108. package/dist/workflow/progressReporter.d.ts.map +1 -0
  109. package/dist/workflow/progressReporter.js +118 -0
  110. package/dist/workflow/runState.d.ts +16 -0
  111. package/dist/workflow/runState.d.ts.map +1 -0
  112. package/dist/workflow/runState.js +57 -0
  113. package/dist/workflow/scriptRuntime.d.ts +35 -0
  114. package/dist/workflow/scriptRuntime.d.ts.map +1 -0
  115. package/dist/workflow/scriptRuntime.js +196 -0
  116. package/dist/workflow/structuredOutput.d.ts +27 -0
  117. package/dist/workflow/structuredOutput.d.ts.map +1 -0
  118. package/dist/workflow/structuredOutput.js +106 -0
  119. package/dist/workflow/types.d.ts +81 -0
  120. package/dist/workflow/types.d.ts.map +1 -0
  121. package/dist/workflow/types.js +1 -0
  122. package/dist/workflow/workflowApis.d.ts +46 -0
  123. package/dist/workflow/workflowApis.d.ts.map +1 -0
  124. package/dist/workflow/workflowApis.js +280 -0
  125. package/package.json +1 -1
  126. package/src/agent.ts +144 -34
  127. package/src/constants/goalPrompts.ts +10 -0
  128. package/src/constants/tools.ts +1 -0
  129. package/src/managers/aiManager.ts +91 -47
  130. package/src/managers/backgroundTaskManager.ts +16 -4
  131. package/src/managers/goalManager.ts +232 -0
  132. package/src/managers/messageManager.ts +2 -2
  133. package/src/managers/messageQueue.ts +59 -1
  134. package/src/managers/pluginManager.ts +8 -1
  135. package/src/managers/skillManager.ts +20 -9
  136. package/src/managers/slashCommandManager.ts +119 -0
  137. package/src/managers/toolManager.ts +7 -0
  138. package/src/managers/workflowManager.ts +491 -0
  139. package/src/prompts/index.ts +4 -2
  140. package/src/services/MarketplaceService.ts +14 -38
  141. package/src/services/aiService.ts +166 -12
  142. package/src/services/configurationService.ts +2 -22
  143. package/src/services/hook.ts +5 -0
  144. package/src/services/session.ts +42 -2
  145. package/src/tools/bashTool.ts +64 -9
  146. package/src/tools/readTool.ts +1 -2
  147. package/src/tools/taskManagementTools.ts +146 -195
  148. package/src/tools/types.ts +2 -0
  149. package/src/tools/webFetchTool.ts +0 -12
  150. package/src/tools/workflowTool.ts +205 -0
  151. package/src/types/agent.ts +6 -0
  152. package/src/types/commands.ts +4 -0
  153. package/src/types/config.ts +2 -2
  154. package/src/types/core.ts +3 -3
  155. package/src/types/hooks.ts +2 -0
  156. package/src/types/index.ts +1 -0
  157. package/src/types/messaging.ts +2 -2
  158. package/src/types/processes.ts +10 -2
  159. package/src/types/workflow.ts +5 -0
  160. package/src/utils/cacheControlUtils.ts +106 -131
  161. package/src/utils/containerSetup.ts +9 -0
  162. package/src/utils/markdownParser.ts +26 -8
  163. package/src/utils/messageOperations.ts +2 -2
  164. package/src/utils/notificationXml.ts +6 -1
  165. package/src/workflow/budgetTracker.ts +34 -0
  166. package/src/workflow/concurrencyLimiter.ts +47 -0
  167. package/src/workflow/journal.ts +95 -0
  168. package/src/workflow/progressReporter.ts +141 -0
  169. package/src/workflow/runState.ts +65 -0
  170. package/src/workflow/scriptRuntime.ts +274 -0
  171. package/src/workflow/structuredOutput.ts +123 -0
  172. package/src/workflow/types.ts +95 -0
  173. package/src/workflow/workflowApis.ts +412 -0
@@ -1,4 +1,21 @@
1
1
  import { TASK_CREATE_TOOL_NAME, TASK_GET_TOOL_NAME, TASK_UPDATE_TOOL_NAME, TASK_LIST_TOOL_NAME, } from "../constants/tools.js";
2
+ /**
3
+ * Helper to record and commit a reversion snapshot for a task file.
4
+ */
5
+ async function recordSnapshot(context, taskManager, taskId, operation) {
6
+ if (!context.reversionManager || !context.messageId)
7
+ return undefined;
8
+ const snapshotId = await context.reversionManager.recordSnapshot(context.messageId, taskManager.getTaskPath(taskId), operation);
9
+ await context.reversionManager.commitSnapshot(snapshotId);
10
+ return snapshotId;
11
+ }
12
+ /**
13
+ * Helper to update a target task's blocks/blockedBy array and record a reversion snapshot.
14
+ */
15
+ async function updateTaskField(context, taskManager, targetTask, field, value) {
16
+ await recordSnapshot(context, taskManager, targetTask.id, "modify");
17
+ await taskManager.updateTask({ ...targetTask, [field]: value });
18
+ }
2
19
  export const taskCreateTool = {
3
20
  name: TASK_CREATE_TOOL_NAME,
4
21
  config: {
@@ -15,31 +32,12 @@ export const taskCreateTool = {
15
32
  },
16
33
  description: {
17
34
  type: "string",
18
- description: "A detailed description of what needs to be done",
19
- },
20
- status: {
21
- type: "string",
22
- enum: ["pending", "in_progress", "completed", "deleted"],
23
- description: "Initial status of the task. Defaults to 'pending'.",
35
+ description: "What needs to be done",
24
36
  },
25
37
  activeForm: {
26
38
  type: "string",
27
39
  description: 'Present continuous form shown in spinner when in_progress (e.g., "Running tests")',
28
40
  },
29
- owner: {
30
- type: "string",
31
- description: "Optional owner of the task.",
32
- },
33
- blocks: {
34
- type: "array",
35
- items: { type: "string" },
36
- description: "List of task IDs that this task blocks.",
37
- },
38
- blockedBy: {
39
- type: "array",
40
- items: { type: "string" },
41
- description: "List of task IDs that block this task.",
42
- },
43
41
  metadata: {
44
42
  type: "object",
45
43
  description: "Arbitrary metadata to attach to the task",
@@ -78,7 +76,7 @@ NOTE that you should not use this tool if there is only one trivial task to do.
78
76
  ## Task Fields
79
77
 
80
78
  - **subject**: A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")
81
- - **description**: Detailed description of what needs to be done, including context and acceptance criteria
79
+ - **description**: What needs to be done, including context and acceptance criteria
82
80
  - **activeForm**: Present continuous form shown in spinner when task is in_progress (e.g., "Fixing authentication bug"). This is displayed to the user while you work on the task.
83
81
 
84
82
  **IMPORTANT**: Always provide activeForm when creating tasks. The subject should be imperative ("Run tests") while activeForm should be present continuous ("Running tests"). All tasks are created with status \`pending\`.
@@ -91,32 +89,21 @@ NOTE that you should not use this tool if there is only one trivial task to do.
91
89
  - Check TaskList first to avoid creating duplicate tasks`,
92
90
  execute: async (args, context) => {
93
91
  const taskManager = context.taskManager;
94
- if (args.status === "deleted") {
95
- return {
96
- success: true,
97
- content: `Task creation skipped because status was set to 'deleted'.`,
98
- shortResult: `Skipped deleted task`,
99
- };
100
- }
101
92
  const task = {
102
93
  subject: args.subject,
103
94
  description: args.description,
104
- status: args.status || "pending",
95
+ status: "pending",
105
96
  activeForm: args.activeForm,
106
- owner: args.owner,
107
- blocks: args.blocks || [],
108
- blockedBy: args.blockedBy || [],
97
+ owner: undefined,
98
+ blocks: [],
99
+ blockedBy: [],
109
100
  metadata: args.metadata || {},
110
101
  };
111
102
  const taskId = await taskManager.createTask(task);
112
- if (context.reversionManager && context.messageId) {
113
- const taskPath = taskManager.getTaskPath(taskId);
114
- const snapshotId = await context.reversionManager.recordSnapshot(context.messageId, taskPath, "create");
115
- await context.reversionManager.commitSnapshot(snapshotId);
116
- }
103
+ await recordSnapshot(context, taskManager, taskId, "create");
117
104
  return {
118
105
  success: true,
119
- content: `Task created with ID: ${taskId}`,
106
+ content: `Task #${taskId} created successfully: ${task.subject}`,
120
107
  shortResult: `Created task ${taskId}: ${task.subject}`,
121
108
  };
122
109
  },
@@ -168,12 +155,24 @@ Returns full task details:
168
155
  if (!task) {
169
156
  return {
170
157
  success: false,
171
- content: `Task with ID ${taskId} not found.`,
158
+ content: `Task not found`,
172
159
  };
173
160
  }
161
+ // Format like Claude Code: structured text instead of raw JSON
162
+ const lines = [
163
+ `Task #${task.id}: ${task.subject}`,
164
+ `Status: ${task.status}`,
165
+ `Description: ${task.description}`,
166
+ ];
167
+ if (task.blockedBy.length > 0) {
168
+ lines.push(`Blocked by: ${task.blockedBy.map((id) => `#${id}`).join(", ")}`);
169
+ }
170
+ if (task.blocks.length > 0) {
171
+ lines.push(`Blocks: ${task.blocks.map((id) => `#${id}`).join(", ")}`);
172
+ }
174
173
  return {
175
174
  success: true,
176
- content: JSON.stringify(task, null, 2),
175
+ content: lines.join("\n"),
177
176
  };
178
177
  },
179
178
  };
@@ -313,160 +312,106 @@ Set up task dependencies:
313
312
  if (!existingTask) {
314
313
  return {
315
314
  success: false,
316
- content: `Task with ID ${taskId} not found.`,
315
+ content: `Task #${taskId} not found`,
317
316
  };
318
317
  }
318
+ // Handle deletion — just delete the task file (like Claude Code)
319
319
  if (args.status === "deleted") {
320
- // Reciprocal Dependency Cleanup
321
- // For each task in the deleted task's blocks list, remove the deleted task's ID from their blockedBy list.
322
- for (const targetId of existingTask.blocks) {
323
- const targetTask = await taskManager.getTask(targetId);
324
- if (targetTask && targetTask.blockedBy.includes(taskId)) {
325
- let targetSnapshotId;
326
- if (context.reversionManager && context.messageId) {
327
- const targetPath = taskManager.getTaskPath(targetId);
328
- targetSnapshotId = await context.reversionManager.recordSnapshot(context.messageId, targetPath, "modify");
329
- }
330
- await taskManager.updateTask({
331
- ...targetTask,
332
- blockedBy: targetTask.blockedBy.filter((id) => id !== taskId),
333
- });
334
- if (context.reversionManager && targetSnapshotId) {
335
- await context.reversionManager.commitSnapshot(targetSnapshotId);
336
- }
337
- }
338
- }
339
- // For each task in the deleted task's blockedBy list, remove the deleted task's ID from their blocks list.
340
- for (const targetId of existingTask.blockedBy) {
341
- const targetTask = await taskManager.getTask(targetId);
342
- if (targetTask && targetTask.blocks.includes(taskId)) {
343
- let targetSnapshotId;
344
- if (context.reversionManager && context.messageId) {
345
- const targetPath = taskManager.getTaskPath(targetId);
346
- targetSnapshotId = await context.reversionManager.recordSnapshot(context.messageId, targetPath, "modify");
347
- }
348
- await taskManager.updateTask({
349
- ...targetTask,
350
- blocks: targetTask.blocks.filter((id) => id !== taskId),
351
- });
352
- if (context.reversionManager && targetSnapshotId) {
353
- await context.reversionManager.commitSnapshot(targetSnapshotId);
354
- }
355
- }
356
- }
357
- // Record delete snapshot for the task itself
358
- if (context.reversionManager && context.messageId) {
359
- const taskPath = taskManager.getTaskPath(taskId);
360
- const deleteSnapshotId = await context.reversionManager.recordSnapshot(context.messageId, taskPath, "delete");
361
- await context.reversionManager.commitSnapshot(deleteSnapshotId);
362
- }
320
+ await recordSnapshot(context, taskManager, taskId, "delete");
363
321
  await taskManager.deleteTask(taskId);
364
322
  return {
365
323
  success: true,
366
- content: `Task #${taskId} deleted and removed from disk.`,
324
+ content: `Task #${taskId} deleted`,
367
325
  shortResult: `Deleted task ${taskId}`,
368
326
  };
369
327
  }
370
- let snapshotId;
371
- if (context.reversionManager && context.messageId) {
372
- const taskPath = taskManager.getTaskPath(taskId);
373
- snapshotId = await context.reversionManager.recordSnapshot(context.messageId, taskPath, "modify");
374
- }
328
+ // Build updates object — only include changed fields
375
329
  const updatedFields = [];
376
- const updatedTask = {
377
- ...existingTask,
378
- };
330
+ const updates = {};
379
331
  if (args.subject !== undefined && args.subject !== existingTask.subject) {
380
- updatedTask.subject = args.subject;
332
+ updates.subject = args.subject;
381
333
  updatedFields.push("subject");
382
334
  }
383
335
  if (args.description !== undefined &&
384
336
  args.description !== existingTask.description) {
385
- updatedTask.description = args.description;
337
+ updates.description = args.description;
386
338
  updatedFields.push("description");
387
339
  }
388
- if (args.status !== undefined && args.status !== existingTask.status) {
389
- updatedTask.status = args.status;
390
- updatedFields.push("status");
391
- }
392
340
  if (args.activeForm !== undefined &&
393
341
  args.activeForm !== existingTask.activeForm) {
394
- updatedTask.activeForm = args.activeForm;
342
+ updates.activeForm = args.activeForm;
395
343
  updatedFields.push("activeForm");
396
344
  }
397
345
  if (args.owner !== undefined && args.owner !== existingTask.owner) {
398
- updatedTask.owner = args.owner;
346
+ updates.owner = args.owner;
399
347
  updatedFields.push("owner");
400
348
  }
349
+ if (args.status !== undefined && args.status !== existingTask.status) {
350
+ updates.status = args.status;
351
+ updatedFields.push("status");
352
+ }
353
+ // Merge metadata (null values delete keys)
401
354
  if (args.metadata !== undefined) {
402
- const newMetadata = { ...(existingTask.metadata || {}) };
355
+ const merged = { ...(existingTask.metadata ?? {}) };
403
356
  for (const [key, value] of Object.entries(args.metadata)) {
404
357
  if (value === null) {
405
- delete newMetadata[key];
358
+ delete merged[key];
406
359
  }
407
360
  else {
408
- newMetadata[key] = value;
361
+ merged[key] = value;
409
362
  }
410
363
  }
411
- updatedTask.metadata = newMetadata;
364
+ updates.metadata = merged;
412
365
  updatedFields.push("metadata");
413
366
  }
367
+ // Apply basic field updates
368
+ if (Object.keys(updates).length > 0) {
369
+ await recordSnapshot(context, taskManager, taskId, "modify");
370
+ await taskManager.updateTask({ ...existingTask, ...updates });
371
+ }
372
+ // Add blocks: update this task's blocks + reciprocal blockedBy on targets
414
373
  if (args.addBlocks !== undefined) {
415
- const blocksToAdd = args.addBlocks.filter((id) => !updatedTask.blocks.includes(id));
374
+ const blocksToAdd = args.addBlocks.filter((id) => !existingTask.blocks.includes(id));
416
375
  if (blocksToAdd.length > 0) {
417
- updatedTask.blocks = [...updatedTask.blocks, ...blocksToAdd];
376
+ await recordSnapshot(context, taskManager, taskId, "modify");
377
+ await taskManager.updateTask({
378
+ ...existingTask,
379
+ ...updates,
380
+ blocks: [...existingTask.blocks, ...blocksToAdd],
381
+ });
418
382
  updatedFields.push("blocks");
419
- // Also update the blockedBy of the target tasks
420
383
  for (const targetId of blocksToAdd) {
421
384
  const targetTask = await taskManager.getTask(targetId);
422
385
  if (targetTask && !targetTask.blockedBy.includes(taskId)) {
423
- let targetSnapshotId;
424
- if (context.reversionManager && context.messageId) {
425
- const targetPath = taskManager.getTaskPath(targetId);
426
- targetSnapshotId = await context.reversionManager.recordSnapshot(context.messageId, targetPath, "modify");
427
- }
428
- await taskManager.updateTask({
429
- ...targetTask,
430
- blockedBy: [...targetTask.blockedBy, taskId],
431
- });
432
- if (context.reversionManager && targetSnapshotId) {
433
- await context.reversionManager.commitSnapshot(targetSnapshotId);
434
- }
386
+ await updateTaskField(context, taskManager, targetTask, "blockedBy", [...targetTask.blockedBy, taskId]);
435
387
  }
436
388
  }
437
389
  }
438
390
  }
391
+ // Add blockedBy: update this task's blockedBy + reciprocal blocks on targets
439
392
  if (args.addBlockedBy !== undefined) {
440
- const blockedByToAdd = args.addBlockedBy.filter((id) => !updatedTask.blockedBy.includes(id));
393
+ const blockedByToAdd = args.addBlockedBy.filter((id) => !existingTask.blockedBy.includes(id));
441
394
  if (blockedByToAdd.length > 0) {
442
- updatedTask.blockedBy = [...updatedTask.blockedBy, ...blockedByToAdd];
395
+ await recordSnapshot(context, taskManager, taskId, "modify");
396
+ await taskManager.updateTask({
397
+ ...existingTask,
398
+ ...updates,
399
+ blockedBy: [...existingTask.blockedBy, ...blockedByToAdd],
400
+ });
443
401
  updatedFields.push("blockedBy");
444
- // Also update the blocks of the target tasks
445
402
  for (const targetId of blockedByToAdd) {
446
403
  const targetTask = await taskManager.getTask(targetId);
447
404
  if (targetTask && !targetTask.blocks.includes(taskId)) {
448
- let targetSnapshotId;
449
- if (context.reversionManager && context.messageId) {
450
- const targetPath = taskManager.getTaskPath(targetId);
451
- targetSnapshotId = await context.reversionManager.recordSnapshot(context.messageId, targetPath, "modify");
452
- }
453
- await taskManager.updateTask({
454
- ...targetTask,
455
- blocks: [...targetTask.blocks, taskId],
456
- });
457
- if (context.reversionManager && targetSnapshotId) {
458
- await context.reversionManager.commitSnapshot(targetSnapshotId);
459
- }
405
+ await updateTaskField(context, taskManager, targetTask, "blocks", [
406
+ ...targetTask.blocks,
407
+ taskId,
408
+ ]);
460
409
  }
461
410
  }
462
411
  }
463
412
  }
464
- await taskManager.updateTask(updatedTask);
465
- if (context.reversionManager && snapshotId) {
466
- await context.reversionManager.commitSnapshot(snapshotId);
467
- }
468
413
  let content = `Updated task #${taskId} ${updatedFields.join(", ")}`;
469
- if (updatedTask.status === "completed") {
414
+ if (updates.status === "completed") {
470
415
  content += `\n\nTask completed. Call TaskList now to find your next available task or see if your work unblocked others.`;
471
416
  }
472
417
  return {
@@ -485,13 +430,7 @@ export const taskListTool = {
485
430
  description: "List all tasks in the task list",
486
431
  parameters: {
487
432
  type: "object",
488
- properties: {
489
- status: {
490
- type: "string",
491
- enum: ["pending", "in_progress", "completed", "deleted"],
492
- description: "Optional filter by status.",
493
- },
494
- },
433
+ properties: {},
495
434
  },
496
435
  },
497
436
  },
@@ -515,22 +454,29 @@ Returns a summary of each task:
515
454
  - **blockedBy**: List of open task IDs that must be resolved first (tasks with blockedBy cannot be claimed until dependencies resolve)
516
455
 
517
456
  Use TaskGet with a specific task ID to view full details including description and comments.`,
518
- execute: async (args, context) => {
457
+ execute: async (_args, context) => {
519
458
  const taskManager = context.taskManager;
520
- let tasks = await taskManager.listTasks();
521
- if (args.status) {
522
- tasks = tasks.filter((t) => t.status === args.status);
523
- }
524
- if (tasks.length === 0) {
459
+ // Filter out internal metadata tasks (like Claude Code)
460
+ const allTasks = (await taskManager.listTasks()).filter((t) => !t.metadata?._internal);
461
+ if (allTasks.length === 0) {
525
462
  return {
526
463
  success: true,
527
- content: "No tasks found.",
464
+ content: "No tasks found",
528
465
  };
529
466
  }
530
467
  // Sort by ID numerically
531
- tasks.sort((a, b) => parseInt(a.id, 10) - parseInt(b.id, 10));
532
- const content = tasks
533
- .map((t) => `[${t.id}] ${t.subject} (${t.status})`)
468
+ allTasks.sort((a, b) => parseInt(a.id, 10) - parseInt(b.id, 10));
469
+ // Filter resolved blockers from blockedBy
470
+ const completedIds = new Set(allTasks.filter((t) => t.status === "completed").map((t) => t.id));
471
+ const content = allTasks
472
+ .map((t) => {
473
+ const blockedBy = t.blockedBy.filter((id) => !completedIds.has(id));
474
+ const owner = t.owner ? ` (${t.owner})` : "";
475
+ const blocked = blockedBy.length > 0
476
+ ? ` [blocked by ${blockedBy.map((id) => `#${id}`).join(", ")}]`
477
+ : "";
478
+ return `#${t.id} [${t.status}] ${t.subject}${owner}${blocked}`;
479
+ })
534
480
  .join("\n");
535
481
  return {
536
482
  success: true,
@@ -96,5 +96,7 @@ export interface ToolContext {
96
96
  originalWorkdir?: string;
97
97
  /** Merged environment variables (process.env + agent env) for child processes */
98
98
  env?: Record<string, string>;
99
+ /** Workflow manager instance for workflow orchestration */
100
+ workflowManager?: import("../managers/workflowManager.js").WorkflowManager;
99
101
  }
100
102
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tools/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tools/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"webFetchTool.d.ts","sourceRoot":"","sources":["../../src/tools/webFetchTool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAoGtE,eAAO,MAAM,YAAY,EAAE,UAoL1B,CAAC"}
1
+ {"version":3,"file":"webFetchTool.d.ts","sourceRoot":"","sources":["../../src/tools/webFetchTool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAiGtE,eAAO,MAAM,YAAY,EAAE,UA2K1B,CAAC"}
@@ -73,7 +73,6 @@ function isPermittedRedirect(originalUrl, redirectUrl) {
73
73
  return false;
74
74
  }
75
75
  }
76
- const GITHUB_URL_ERROR = "For GitHub URLs, please use the 'gh' CLI via the Bash tool instead (e.g., 'gh pr view', 'gh issue view', 'gh api').";
77
76
  // --- Tool ---
78
77
  export const webFetchTool = {
79
78
  name: WEB_FETCH_TOOL_NAME,
@@ -142,14 +141,6 @@ Usage notes:
142
141
  error: validation.error,
143
142
  };
144
143
  }
145
- // Check for GitHub URLs
146
- if (url.includes("github.com")) {
147
- return {
148
- success: false,
149
- content: "",
150
- error: GITHUB_URL_ERROR,
151
- };
152
- }
153
144
  try {
154
145
  const cached = cache.get(url);
155
146
  if (cached) {
@@ -0,0 +1,11 @@
1
+ import type { ToolPlugin } from "./types.js";
2
+ /**
3
+ * Workflow tool plugin for executing deterministic multi-subagent orchestration scripts.
4
+ *
5
+ * The AI model calls this tool with a JavaScript script that orchestrates
6
+ * multiple subagents via agent(), parallel(), pipeline(), phase(), log() APIs.
7
+ * Workflows run in the background — the tool returns immediately with a run ID,
8
+ * and a <task-notification> arrives when the workflow completes.
9
+ */
10
+ export declare const workflowTool: ToolPlugin;
11
+ //# sourceMappingURL=workflowTool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflowTool.d.ts","sourceRoot":"","sources":["../../src/tools/workflowTool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAItE;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,EAAE,UAgM1B,CAAC"}
@@ -0,0 +1,190 @@
1
+ import { WORKFLOW_TOOL_NAME } from "../constants/tools.js";
2
+ import { logger } from "../utils/globalLogger.js";
3
+ /**
4
+ * Workflow tool plugin for executing deterministic multi-subagent orchestration scripts.
5
+ *
6
+ * The AI model calls this tool with a JavaScript script that orchestrates
7
+ * multiple subagents via agent(), parallel(), pipeline(), phase(), log() APIs.
8
+ * Workflows run in the background — the tool returns immediately with a run ID,
9
+ * and a <task-notification> arrives when the workflow completes.
10
+ */
11
+ export const workflowTool = {
12
+ name: WORKFLOW_TOOL_NAME,
13
+ config: {
14
+ type: "function",
15
+ function: {
16
+ name: WORKFLOW_TOOL_NAME,
17
+ description: "Execute a workflow script that orchestrates multiple subagents deterministically. Workflows run in the background — this tool returns immediately with a run ID, and a <task-notification> arrives when the workflow completes. Use /workflows to watch live progress.",
18
+ parameters: {
19
+ type: "object",
20
+ properties: {
21
+ script: {
22
+ type: "string",
23
+ description: "Inline workflow script (JavaScript). Must start with 'export const meta = {name, description, phases}'. Pass the script inline — do not Write it to a file first. Every Workflow invocation automatically persists its script.",
24
+ },
25
+ scriptPath: {
26
+ type: "string",
27
+ description: "Path to a saved workflow script file. Use this to re-run or iterate on a previously saved script. One of 'script' or 'scriptPath' is required.",
28
+ },
29
+ args: {
30
+ description: "Arguments passed to the workflow script as the `args` global. Pass arrays/objects as actual JSON values, NOT as a JSON-encoded string.",
31
+ },
32
+ resumeFromRunId: {
33
+ type: "string",
34
+ description: "Resume from a previous run's journal. Cached agent results are replayed instantly; the first edited/new call and everything after it runs live.",
35
+ },
36
+ },
37
+ },
38
+ },
39
+ },
40
+ prompt: () => `Execute a workflow script that orchestrates multiple subagents deterministically. Workflows run in the background — this tool returns immediately with a task ID, and a <task-notification> arrives when the workflow completes. Use /workflows to watch live progress.
41
+
42
+ A workflow structures work across many agents — to be comprehensive (decompose and cover in parallel), to be confident (independent perspectives and adversarial checks before committing), or to take on scale one context can't hold (migrations, audits, broad sweeps). The script is where you encode that structure: what fans out, what verifies, what synthesizes.
43
+
44
+ ONLY call this tool when the user has explicitly opted into multi-agent orchestration. Workflows can spawn dozens of agents and consume a large amount of tokens; the user must request that scale, not have it inferred. Explicit opt-in means one of:
45
+ - The user directly asked you to run a workflow or use multi-agent orchestration in their own words ("use a workflow", "run a workflow", "fan out agents", "orchestrate this with subagents"). The ask must be in the user's words — a task that would merely benefit from a workflow does not count.
46
+ - The user invoked a skill or slash command whose instructions tell you to call Workflow.
47
+ - The user asked you to run a specific named or saved workflow.
48
+
49
+ For any other task — even one that would clearly benefit from parallelism — do NOT call this tool. Use the Agent tool for individual subagents, or briefly describe what a multi-agent workflow could do and how much it would roughly cost, and ask the user whether to run it.
50
+
51
+ When you do call it, the right move is often **hybrid**: scout inline first (list the files, find the channels, scope the diff) to discover the work-list, then call Workflow to pipeline over it.
52
+
53
+ Common single-phase workflows you can chain across turns:
54
+ - **Understand** — parallel readers over relevant subsystems → structured map
55
+ - **Design** — judge panel of N independent approaches → scored synthesis
56
+ - **Review** — dimensions → find → adversarially verify
57
+ - **Research** — multi-modal sweep → deep-read → synthesize
58
+ - **Migrate** — discover sites → transform each (worktree isolation) → verify
59
+
60
+ For larger work, run several in sequence — read each result before deciding the next phase.
61
+
62
+ Every script must begin with \`export const meta = {...}\`:
63
+ export const meta = {
64
+ name: 'find-flaky-tests',
65
+ description: 'Find flaky tests and propose fixes',
66
+ phases: [
67
+ { title: 'Scan', detail: 'grep test logs for retries' },
68
+ { title: 'Fix', detail: 'one agent per flaky test' },
69
+ ],
70
+ }
71
+ // script body starts here — use agent()/parallel()/pipeline()/phase()/log()
72
+
73
+ Script body hooks:
74
+ - **agent(prompt, opts?)**: Promise<any> — spawn a subagent. Without schema, returns its final text as a string. With schema (a JSON Schema), the subagent is forced to call a StructuredOutput tool and agent() returns the validated object — no parsing needed. Returns null if the user skips the agent mid-run or the subagent dies on a terminal API error (filter with .filter(Boolean)). opts.label overrides the display label. opts.phase assigns this agent to a progress group. opts.model overrides the model for this agent call. opts.agentType uses a custom subagent type instead of the default.
75
+ - **pipeline(items, stage1, stage2, ...)**: Promise<any[]> — run each item through all stages independently, NO barrier between stages. Item A can be in stage 3 while item B is still in stage 1. This is the DEFAULT for multi-stage work. Every stage callback receives (prevResult, originalItem, index). In the first stage prevResult is undefined; in later stages it is the return value of the previous stage. A stage that throws drops that item to null. Example single-stage: \`pipeline(files, (prev, file) => agent('Read ' + file))\`. Example two-stage: \`pipeline(files, (prev, file) => agent('Read ' + file), (prev, file) => agent('Summarize: ' + prev))\`.
76
+ - **parallel(thunks: Array<() => Promise<any>>)**: Promise<any[]> — run tasks concurrently. This is a BARRIER: awaits all thunks before returning. A thunk that throws resolves to null in the result array. Use ONLY when you genuinely need all results together.
77
+ - **log(message: string)**: void — emit a progress message
78
+ - **phase(title: string)**: void — start a new phase; subsequent agent() calls are grouped under this title
79
+ - **args**: any — the value passed as Workflow's args input, verbatim
80
+ - **budget**: {total: number|null, spent(): number, remaining(): number} — the turn's token target
81
+
82
+ Scripts are plain JavaScript, NOT TypeScript — type annotations fail to parse. The script body runs in an async context — use await directly. Standard JS built-ins (JSON, Math, Array, etc.) are available — EXCEPT Date.now()/Math.random()/argless new Date(), which throw (they would break resume). No filesystem or Node.js API access.
83
+
84
+ DEFAULT TO pipeline(). Only reach for a barrier (parallel between stages) when you genuinely need ALL prior-stage results together.
85
+
86
+ Concurrent agent() calls are capped at min(16, cpu cores - 2) per workflow. Total agent count is capped at 1000 per run. A single parallel()/pipeline() call accepts at most 4096 items.
87
+
88
+ Quality patterns:
89
+ - **Adversarial verify**: spawn N independent skeptics per finding, each prompted to REFUTE. Kill if >=majority refute.
90
+ - **Judge panel**: generate N independent approaches, score with parallel judges, synthesize from the winner.
91
+ - **Loop-until-dry**: keep spawning finders until K consecutive rounds return nothing new.
92
+ - **Multi-modal sweep**: parallel agents each searching a different way (by-container, by-content, by-entity).
93
+ - **Completeness critic**: a final agent that asks "what's missing?" — findings become next round of work.
94
+ - **No silent caps**: if a workflow bounds coverage, log() what was dropped.
95
+
96
+ ## Resume
97
+
98
+ The tool result includes a runId. To resume after a pause, kill, or script edit, relaunch with Workflow({scriptPath, resumeFromRunId}) — the longest unchanged prefix of agent() calls returns cached results instantly; the first edited/new call and everything after it runs live.
99
+
100
+ Use this tool for multi-step orchestration where control flow should be deterministic (loops, conditionals, fan-out) rather than model-driven.`,
101
+ execute: async (args, context) => {
102
+ const workflowManager = context.workflowManager;
103
+ if (!workflowManager) {
104
+ return {
105
+ success: false,
106
+ content: "",
107
+ error: "Workflow manager not available in tool context",
108
+ shortResult: "Workflow execution failed",
109
+ };
110
+ }
111
+ const script = args.script;
112
+ const scriptPath = args.scriptPath;
113
+ const workflowArgs = args.args;
114
+ const resumeFromRunId = args.resumeFromRunId;
115
+ // Resolve script text
116
+ let scriptText;
117
+ if (script) {
118
+ scriptText = script;
119
+ }
120
+ else if (scriptPath) {
121
+ try {
122
+ const fs = await import("fs/promises");
123
+ scriptText = await fs.readFile(scriptPath, "utf-8");
124
+ }
125
+ catch (error) {
126
+ return {
127
+ success: false,
128
+ content: "",
129
+ error: `Failed to read script file: ${error instanceof Error ? error.message : String(error)}`,
130
+ shortResult: "Workflow script read failed",
131
+ };
132
+ }
133
+ }
134
+ else {
135
+ return {
136
+ success: false,
137
+ content: "",
138
+ error: "Either 'script' or 'scriptPath' parameter is required",
139
+ shortResult: "Workflow execution failed",
140
+ };
141
+ }
142
+ try {
143
+ // Create run
144
+ const run = await workflowManager.createRun(scriptText, workflowArgs, {
145
+ resumeFromRunId,
146
+ });
147
+ // Start execution in background
148
+ await workflowManager.startRun(run.runId);
149
+ return {
150
+ success: true,
151
+ content: [
152
+ `Workflow started with run ID: ${run.runId}`,
153
+ `Name: ${run.meta.name}`,
154
+ `Description: ${run.meta.description}`,
155
+ run.meta.phases?.length
156
+ ? `Phases: ${run.meta.phases.map((p) => p.title).join(" → ")}`
157
+ : "",
158
+ `The workflow is running in the background. You will be notified automatically when it completes.`,
159
+ `Use /workflows to watch live progress.`,
160
+ `Script saved to: ${run.scriptPath}`,
161
+ ]
162
+ .filter(Boolean)
163
+ .join("\n"),
164
+ shortResult: `Workflow started: ${run.meta.name} (${run.runId})`,
165
+ };
166
+ }
167
+ catch (error) {
168
+ const msg = error instanceof Error ? error.message : String(error);
169
+ logger.error(`[Workflow Tool] execution failed: ${msg}`);
170
+ return {
171
+ success: false,
172
+ content: `Workflow failed: ${msg}. Fix the error and try again.`,
173
+ error: `Workflow execution failed: ${msg}`,
174
+ shortResult: "Workflow execution failed",
175
+ };
176
+ }
177
+ },
178
+ formatCompactParams: (params) => {
179
+ if (params.scriptPath) {
180
+ return `scriptPath: ${params.scriptPath}`;
181
+ }
182
+ const script = params.script;
183
+ if (script) {
184
+ // Extract meta.name from the script
185
+ const nameMatch = script.match(/name:\s*['"]([^'"]+)['"]/);
186
+ return nameMatch ? nameMatch[1] : script.slice(0, 50) + "...";
187
+ }
188
+ return "workflow";
189
+ },
190
+ };