@oh-my-pi/pi-coding-agent 3.21.0 → 3.25.0

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 (71) hide show
  1. package/CHANGELOG.md +55 -1
  2. package/docs/sdk.md +47 -50
  3. package/examples/custom-tools/README.md +0 -15
  4. package/examples/hooks/custom-compaction.ts +1 -3
  5. package/examples/sdk/README.md +6 -10
  6. package/package.json +5 -5
  7. package/src/cli/args.ts +9 -6
  8. package/src/core/agent-session.ts +3 -3
  9. package/src/core/custom-commands/bundled/wt/index.ts +3 -0
  10. package/src/core/custom-tools/wrapper.ts +0 -1
  11. package/src/core/extensions/index.ts +1 -6
  12. package/src/core/extensions/wrapper.ts +0 -7
  13. package/src/core/file-mentions.ts +5 -8
  14. package/src/core/sdk.ts +48 -111
  15. package/src/core/session-manager.ts +7 -0
  16. package/src/core/system-prompt.ts +22 -33
  17. package/src/core/tools/ask.ts +14 -7
  18. package/src/core/tools/bash-interceptor.ts +4 -4
  19. package/src/core/tools/bash.ts +19 -9
  20. package/src/core/tools/complete.ts +131 -0
  21. package/src/core/tools/context.ts +7 -0
  22. package/src/core/tools/edit.ts +8 -15
  23. package/src/core/tools/exa/render.ts +4 -16
  24. package/src/core/tools/find.ts +7 -18
  25. package/src/core/tools/git.ts +13 -3
  26. package/src/core/tools/grep.ts +7 -18
  27. package/src/core/tools/index.test.ts +188 -0
  28. package/src/core/tools/index.ts +106 -236
  29. package/src/core/tools/jtd-to-json-schema.ts +274 -0
  30. package/src/core/tools/ls.ts +4 -9
  31. package/src/core/tools/lsp/index.ts +32 -29
  32. package/src/core/tools/lsp/render.ts +7 -28
  33. package/src/core/tools/notebook.ts +3 -5
  34. package/src/core/tools/output.ts +130 -31
  35. package/src/core/tools/read.ts +8 -19
  36. package/src/core/tools/review.ts +0 -18
  37. package/src/core/tools/rulebook.ts +8 -2
  38. package/src/core/tools/task/agents.ts +28 -7
  39. package/src/core/tools/task/artifacts.ts +6 -9
  40. package/src/core/tools/task/discovery.ts +0 -6
  41. package/src/core/tools/task/executor.ts +306 -257
  42. package/src/core/tools/task/index.ts +65 -235
  43. package/src/core/tools/task/name-generator.ts +247 -0
  44. package/src/core/tools/task/render.ts +158 -19
  45. package/src/core/tools/task/types.ts +13 -11
  46. package/src/core/tools/task/worker-protocol.ts +18 -0
  47. package/src/core/tools/task/worker.ts +270 -0
  48. package/src/core/tools/web-fetch.ts +4 -36
  49. package/src/core/tools/web-search/index.ts +2 -1
  50. package/src/core/tools/web-search/render.ts +1 -4
  51. package/src/core/tools/write.ts +7 -15
  52. package/src/discovery/helpers.test.ts +1 -1
  53. package/src/index.ts +5 -16
  54. package/src/main.ts +4 -4
  55. package/src/modes/interactive/theme/theme.ts +4 -4
  56. package/src/prompts/task.md +14 -57
  57. package/src/prompts/tools/output.md +4 -3
  58. package/src/prompts/tools/task.md +70 -0
  59. package/examples/custom-tools/question/index.ts +0 -84
  60. package/examples/custom-tools/subagent/README.md +0 -172
  61. package/examples/custom-tools/subagent/agents/planner.md +0 -37
  62. package/examples/custom-tools/subagent/agents/scout.md +0 -50
  63. package/examples/custom-tools/subagent/agents/worker.md +0 -24
  64. package/examples/custom-tools/subagent/agents.ts +0 -156
  65. package/examples/custom-tools/subagent/commands/implement-and-review.md +0 -10
  66. package/examples/custom-tools/subagent/commands/implement.md +0 -10
  67. package/examples/custom-tools/subagent/commands/scout-and-plan.md +0 -9
  68. package/examples/custom-tools/subagent/index.ts +0 -1002
  69. package/examples/sdk/05-tools.ts +0 -94
  70. package/examples/sdk/12-full-control.ts +0 -95
  71. package/src/prompts/browser.md +0 -71
@@ -16,10 +16,12 @@
16
16
  import type { Usage } from "@mariozechner/pi-ai";
17
17
  import type { AgentTool } from "@oh-my-pi/pi-agent-core";
18
18
  import type { Theme } from "../../../modes/interactive/theme/theme";
19
+ import taskDescriptionTemplate from "../../../prompts/tools/task.md" with { type: "text" };
19
20
  import { formatDuration } from "../render-utils";
20
21
  import { cleanupTempDir, createTempArtifactsDir, getArtifactsDir } from "./artifacts";
21
22
  import { discoverAgents, getAgent } from "./discovery";
22
23
  import { runSubprocess } from "./executor";
24
+ import { generateTaskName } from "./name-generator";
23
25
  import { mapWithConcurrencyLimit } from "./parallel";
24
26
  import { renderCall, renderResult } from "./render";
25
27
  import {
@@ -27,15 +29,13 @@ import {
27
29
  MAX_AGENTS_IN_DESCRIPTION,
28
30
  MAX_CONCURRENCY,
29
31
  MAX_PARALLEL_TASKS,
30
- OMP_BLOCKED_AGENT_ENV,
31
- OMP_NO_SUBAGENTS_ENV,
32
- OMP_SPAWNS_ENV,
33
32
  type TaskToolDetails,
34
33
  taskSchema,
35
34
  } from "./types";
36
35
 
37
- // Import review tools for side effects (registers subprocess tool handlers)
36
+ // Import review tools for side effects (registers subagent tool handlers)
38
37
  import "../review";
38
+ import type { ToolSession } from "..";
39
39
 
40
40
  /** Format byte count for display */
41
41
  function formatBytes(bytes: number): string {
@@ -83,51 +83,6 @@ function addUsageTotals(target: Usage, usage: Partial<Usage>): void {
83
83
  target.cost.total += cost.total;
84
84
  }
85
85
 
86
- function parseSubagentUsage(events: string[] | undefined): Usage | undefined {
87
- if (!events || events.length === 0) return undefined;
88
-
89
- const totals = createUsageTotals();
90
- let hasUsage = false;
91
-
92
- for (const line of events) {
93
- let event: unknown;
94
- try {
95
- event = JSON.parse(line);
96
- } catch {
97
- continue;
98
- }
99
-
100
- if (!event || typeof event !== "object") continue;
101
- const record = event as Record<string, unknown>;
102
- if (record.type !== "message_end") continue;
103
-
104
- const message = record.message;
105
- if (!message || typeof message !== "object") continue;
106
- const msgRecord = message as Record<string, unknown>;
107
- if (msgRecord.role !== "assistant") continue;
108
- if (msgRecord.stopReason === "aborted" || msgRecord.stopReason === "error") continue;
109
-
110
- const usage = msgRecord.usage;
111
- if (!usage || typeof usage !== "object") continue;
112
-
113
- addUsageTotals(totals, usage as Partial<Usage>);
114
- hasUsage = true;
115
- }
116
-
117
- return hasUsage ? totals : undefined;
118
- }
119
-
120
- /** Session context interface */
121
- interface SessionContext {
122
- getSessionFile: () => string | null;
123
- }
124
-
125
- /** Task tool options */
126
- interface TaskToolOptions {
127
- /** Set of available tool names (for cross-tool awareness) */
128
- availableTools?: Set<string>;
129
- }
130
-
131
86
  // Re-export types and utilities
132
87
  export { loadBundledAgents as BUNDLED_AGENTS } from "./agents";
133
88
  export { discoverCommands, expandCommand, getCommand } from "./commands";
@@ -141,167 +96,47 @@ export { taskSchema } from "./types";
141
96
  async function buildDescription(cwd: string): Promise<string> {
142
97
  const { agents } = await discoverAgents(cwd);
143
98
 
144
- const lines: string[] = [];
145
-
146
- lines.push("Launch a new agent to handle complex, multi-step tasks autonomously.");
147
- lines.push("");
148
- lines.push(
149
- "The Task tool launches specialized agents (subprocesses) that autonomously handle complex tasks. Each agent type has specific capabilities and tools available to it.",
150
- );
151
- lines.push("");
152
- lines.push("Available agent types and the tools they have access to:");
153
-
99
+ // Build agents list
100
+ const agentLines: string[] = [];
154
101
  for (const agent of agents.slice(0, MAX_AGENTS_IN_DESCRIPTION)) {
155
102
  const tools = agent.tools?.join(", ") || "All tools";
156
- lines.push(`- ${agent.name}: ${agent.description} (Tools: ${tools})`);
103
+ agentLines.push(`- ${agent.name}: ${agent.description} (Tools: ${tools})`);
157
104
  }
158
105
  if (agents.length > MAX_AGENTS_IN_DESCRIPTION) {
159
- lines.push(` ...and ${agents.length - MAX_AGENTS_IN_DESCRIPTION} more agents`);
106
+ agentLines.push(` ...and ${agents.length - MAX_AGENTS_IN_DESCRIPTION} more agents`);
160
107
  }
161
108
 
162
- lines.push("");
163
- lines.push("When NOT to use the Task tool:");
164
- lines.push(
165
- "- If you want to read a specific file path, use the Read or Glob tool instead of the Task tool, to find the match more quickly",
166
- );
167
- lines.push(
168
- '- If you are searching for a specific class definition like "class Foo", use the Glob tool instead, to find the match more quickly',
169
- );
170
- lines.push(
171
- "- If you are searching for code within a specific file or set of 2-3 files, use the Read tool instead of the Task tool, to find the match more quickly",
172
- );
173
- lines.push("- Other tasks that are not related to the agent descriptions above");
174
- lines.push("");
175
- lines.push("");
176
- lines.push("Usage notes:");
177
- lines.push("- Always include a short description of the task in the task parameter");
178
- lines.push(
179
- "- Prefer plan-then-execute: put shared constraints in context, keep each task focused, and specify output format and acceptance criteria",
180
- );
181
- lines.push(
182
- "- Minimize tool chatter: avoid repeating large context and use the Output tool with output ids for full logs",
183
- );
184
- lines.push("- Launch multiple agents concurrently whenever possible, to maximize performance");
185
- lines.push(
186
- "- When the agent is done, it will return a single message back to you. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result.",
187
- );
188
- lines.push(
189
- "- Each agent invocation is stateless. You will not be able to send additional messages to the agent, nor will the agent be able to communicate with you outside of its final report. Therefore, your task should contain a highly detailed task description for the agent to perform autonomously and you should specify exactly what information the agent should return back to you in its final and only message to you.",
190
- );
191
- lines.push(
192
- "- IMPORTANT: Agent results are intermediate data, not task completions. Use the agent's findings to continue executing the user's request. Do not treat agent reports as 'task complete' signals - they provide context for you to perform the actual work.",
193
- );
194
- lines.push("- The agent's outputs should generally be trusted");
195
- lines.push(
196
- "- Clearly tell the agent whether you expect it to write code or just to do research (search, file reads, web fetches, etc.), since it is not aware of the user's intent",
197
- );
198
- lines.push(
199
- "- If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first. Use your judgement.",
200
- );
201
- lines.push("");
202
- lines.push("Parameters:");
203
- lines.push(
204
- `- tasks: Array of {agent, task, description?, model?} - tasks to run in parallel (max ${MAX_PARALLEL_TASKS}, ${MAX_CONCURRENCY} concurrent)`,
205
- );
206
- lines.push(
207
- ' - model: (optional) Override the agent\'s default model with fuzzy matching (e.g., "sonnet", "codex", "5.2"). Supports comma-separated fallbacks: "gpt, opus" tries gpt first, then opus. Use "default" for omp\'s default model',
208
- );
209
- lines.push(
210
- "- context: (optional) Shared context string prepended to all task prompts - use this to avoid repeating instructions",
211
- );
212
- lines.push("");
213
- lines.push("Results are always written to {tempdir}/omp-task-{runId}/task_{agent}_{index}.md");
214
- lines.push("");
215
- lines.push("Example usage:");
216
- lines.push("");
217
- lines.push("<example_agent_descriptions>");
218
- lines.push('"code-reviewer": use this agent after you are done writing a significant piece of code');
219
- lines.push('"explore": use this agent for fast codebase exploration and research');
220
- lines.push("</example_agent_descriptions>");
221
- lines.push("");
222
- lines.push("<example>");
223
- lines.push('user: "Please write a function that checks if a number is prime"');
224
- lines.push("assistant: Sure let me write a function that checks if a number is prime");
225
- lines.push("assistant: I'm going to use the Write tool to write the following code:");
226
- lines.push("<code>");
227
- lines.push("function isPrime(n) {");
228
- lines.push(" if (n <= 1) return false");
229
- lines.push(" for (let i = 2; i * i <= n; i++) {");
230
- lines.push(" if (n % i === 0) return false");
231
- lines.push(" }");
232
- lines.push(" return true");
233
- lines.push("}");
234
- lines.push("</code>");
235
- lines.push("<commentary>");
236
- lines.push(
237
- "Since a significant piece of code was written and the task was completed, now use the code-reviewer agent to review the code",
238
- );
239
- lines.push("</commentary>");
240
- lines.push("assistant: Now let me use the code-reviewer agent to review the code");
241
- lines.push(
242
- 'assistant: Uses the Task tool: { tasks: [{ agent: "code-reviewer", task: "Review the isPrime function" }] }',
243
- );
244
- lines.push("</example>");
245
- lines.push("");
246
- lines.push("<example>");
247
- lines.push('user: "Find all TODO comments in the codebase"');
248
- lines.push("assistant: I'll use multiple explore agents to search different directories in parallel");
249
- lines.push("assistant: Uses the Task tool:");
250
- lines.push("{");
251
- lines.push(' "context": "Find all TODO comments. Return file:line:content format.",');
252
- lines.push(' "tasks": [');
253
- lines.push(' { "agent": "explore", "task": "Search in src/" },');
254
- lines.push(' { "agent": "explore", "task": "Search in lib/" },');
255
- lines.push(' { "agent": "explore", "task": "Search in tests/" }');
256
- lines.push(" ]");
257
- lines.push("}");
258
- lines.push("Results → {tempdir}/omp-task-{runId}/task_explore_*.md");
259
- lines.push("</example>");
260
-
261
- return lines.join("\n");
109
+ // Fill template placeholders
110
+ return taskDescriptionTemplate
111
+ .replace("{{AGENTS_LIST}}", agentLines.join("\n"))
112
+ .replace("{{MAX_PARALLEL_TASKS}}", String(MAX_PARALLEL_TASKS))
113
+ .replace("{{MAX_CONCURRENCY}}", String(MAX_CONCURRENCY));
262
114
  }
263
115
 
264
116
  /**
265
- * Create the task tool configured for a specific working directory.
117
+ * Create the task tool configured for a specific session.
266
118
  */
267
119
  export async function createTaskTool(
268
- cwd: string,
269
- sessionContext?: SessionContext,
270
- options?: TaskToolOptions,
120
+ session: ToolSession,
271
121
  ): Promise<AgentTool<typeof taskSchema, TaskToolDetails, Theme>> {
272
- const hasOutputTool = options?.availableTools?.has("output") ?? false;
273
- // Check if subagents are completely inhibited (legacy recursion prevention)
274
- if (process.env[OMP_NO_SUBAGENTS_ENV]) {
275
- return {
276
- name: "task",
277
- label: "Task",
278
- description: "Sub-agents disabled (recursion prevention)",
279
- parameters: taskSchema,
280
- execute: async () => ({
281
- content: [{ type: "text", text: "Sub-agents are disabled for this agent (recursion prevention)." }],
282
- details: {
283
- projectAgentsDir: null,
284
- results: [],
285
- totalDurationMs: 0,
286
- },
287
- }),
288
- };
289
- }
290
-
291
122
  // Check for same-agent blocking (allows other agent types)
292
- const blockedAgent = process.env[OMP_BLOCKED_AGENT_ENV];
123
+ const blockedAgent = process.env.OMP_BLOCKED_AGENT;
124
+
125
+ // Build description upfront
126
+ const description = await buildDescription(session.cwd);
293
127
 
294
128
  return {
295
129
  name: "task",
296
130
  label: "Task",
297
- description: await buildDescription(cwd),
131
+ description,
298
132
  parameters: taskSchema,
299
133
  renderCall,
300
134
  renderResult,
301
135
  execute: async (_toolCallId, params, signal, onUpdate) => {
302
136
  const startTime = Date.now();
303
- const { agents, projectAgentsDir } = await discoverAgents(cwd);
137
+ const { agents, projectAgentsDir } = await discoverAgents(session.cwd);
304
138
  const context = params.context;
139
+ const outputSchema = params.output_schema;
305
140
 
306
141
  // Handle empty or missing tasks
307
142
  if (!params.tasks || params.tasks.length === 0) {
@@ -339,7 +174,7 @@ export async function createTaskTool(
339
174
  }
340
175
 
341
176
  // Derive artifacts directory
342
- const sessionFile = sessionContext?.getSessionFile() ?? null;
177
+ const sessionFile = session.getSessionFile();
343
178
  const artifactsDir = sessionFile ? getArtifactsDir(sessionFile) : null;
344
179
  const tempArtifactsDir = artifactsDir ? null : createTempArtifactsDir();
345
180
  const effectiveArtifactsDir = artifactsDir || tempArtifactsDir!;
@@ -404,13 +239,12 @@ export async function createTaskTool(
404
239
  }
405
240
 
406
241
  // Check spawn restrictions from parent
407
- const parentSpawns = process.env[OMP_SPAWNS_ENV];
242
+ const parentSpawns = session.getSessionSpawns() ?? "*";
243
+ const allowedSpawns = parentSpawns.split(",").map((s) => s.trim());
408
244
  const isSpawnAllowed = (agentName: string): boolean => {
409
- if (parentSpawns === undefined) return true; // Root = allow all
410
245
  if (parentSpawns === "") return false; // Empty = deny all
411
246
  if (parentSpawns === "*") return true; // Wildcard = allow all
412
- const allowed = new Set(parentSpawns.split(",").map((s) => s.trim()));
413
- return allowed.has(agentName);
247
+ return allowedSpawns.includes(agentName);
414
248
  };
415
249
 
416
250
  for (const task of tasks) {
@@ -427,49 +261,55 @@ export async function createTaskTool(
427
261
  }
428
262
  }
429
263
 
264
+ // Build full prompts with context prepended and generate task IDs
265
+ const tasksWithContext = tasks.map((t) => ({
266
+ agent: t.agent,
267
+ task: context ? `${context}\n\n${t.task}` : t.task,
268
+ model: t.model,
269
+ description: t.description,
270
+ taskId: generateTaskName(),
271
+ }));
272
+
430
273
  // Initialize progress for all tasks
431
- for (let i = 0; i < tasks.length; i++) {
432
- const agentCfg = getAgent(agents, tasks[i].agent);
274
+ for (let i = 0; i < tasksWithContext.length; i++) {
275
+ const t = tasksWithContext[i];
276
+ const agentCfg = getAgent(agents, t.agent);
433
277
  progressMap.set(i, {
434
278
  index: i,
435
- agent: tasks[i].agent,
279
+ taskId: t.taskId,
280
+ agent: t.agent,
436
281
  agentSource: agentCfg?.source ?? "user",
437
282
  status: "pending",
438
- task: tasks[i].task,
283
+ task: t.task,
439
284
  recentTools: [],
440
285
  recentOutput: [],
441
286
  toolCount: 0,
442
287
  tokens: 0,
443
288
  durationMs: 0,
444
- modelOverride: tasks[i].model,
445
- description: tasks[i].description,
289
+ modelOverride: t.model,
290
+ description: t.description,
446
291
  });
447
292
  }
448
293
  emitProgress();
449
294
 
450
- // Build full prompts with context prepended
451
- const tasksWithContext = tasks.map((t) => ({
452
- agent: t.agent,
453
- task: context ? `${context}\n\n${t.task}` : t.task,
454
- model: t.model,
455
- description: t.description,
456
- }));
457
-
458
295
  // Execute in parallel with concurrency limit
459
296
  const results = await mapWithConcurrencyLimit(tasksWithContext, MAX_CONCURRENCY, async (task, index) => {
460
297
  const agent = getAgent(agents, task.agent)!;
461
298
  return runSubprocess({
462
- cwd,
299
+ cwd: session.cwd,
463
300
  agent,
464
301
  task: task.task,
465
302
  description: task.description,
466
303
  index,
304
+ taskId: task.taskId,
467
305
  context: undefined, // Already prepended above
468
306
  modelOverride: task.model,
307
+ outputSchema,
469
308
  sessionFile,
470
309
  persistArtifacts: !!artifactsDir,
471
310
  artifactsDir: effectiveArtifactsDir,
472
311
  signal,
312
+ eventBus: undefined,
473
313
  onProgress: (progress) => {
474
314
  progressMap.set(index, structuredClone(progress));
475
315
  emitProgress();
@@ -477,55 +317,46 @@ export async function createTaskTool(
477
317
  });
478
318
  });
479
319
 
320
+ // Aggregate usage from executor results (already accumulated incrementally)
480
321
  const aggregatedUsage = createUsageTotals();
481
322
  let hasAggregatedUsage = false;
482
- const resultsWithUsage = results.map((result) => {
483
- const usage = parseSubagentUsage(result.jsonlEvents);
484
- if (usage) {
485
- addUsageTotals(aggregatedUsage, usage);
323
+ for (const result of results) {
324
+ if (result.usage) {
325
+ addUsageTotals(aggregatedUsage, result.usage);
486
326
  hasAggregatedUsage = true;
487
- return { ...result, usage };
488
327
  }
489
- return result;
490
- });
328
+ }
491
329
 
492
330
  // Collect output paths (artifacts already written by executor in real-time)
493
331
  const outputPaths: string[] = [];
494
- for (const result of resultsWithUsage) {
332
+ for (const result of results) {
495
333
  if (result.artifactPaths) {
496
334
  outputPaths.push(result.artifactPaths.outputPath);
497
335
  }
498
336
  }
499
337
 
500
338
  // Build final output - match plugin format
501
- const successCount = resultsWithUsage.filter((r) => r.exitCode === 0).length;
339
+ const successCount = results.filter((r) => r.exitCode === 0).length;
502
340
  const totalDuration = Date.now() - startTime;
503
341
 
504
- const summaries = resultsWithUsage.map((r) => {
342
+ const summaries = results.map((r) => {
505
343
  const status = r.exitCode === 0 ? "completed" : `failed (exit ${r.exitCode})`;
506
344
  const output = r.output.trim() || r.stderr.trim() || "(no output)";
507
345
  const preview = output.split("\n").slice(0, 5).join("\n");
508
- // Include output metadata and ID; include path only if Output tool unavailable (for Read fallback)
509
- const outputId = `${r.agent}_${r.index}`;
510
346
  const meta = r.outputMeta
511
347
  ? ` [${r.outputMeta.lineCount} lines, ${formatBytes(r.outputMeta.charCount)}]`
512
348
  : "";
513
- const pathInfo = !hasOutputTool && r.artifactPaths?.outputPath ? ` (${r.artifactPaths.outputPath})` : "";
514
- return `[${r.agent}] ${status}${meta} ${outputId}${pathInfo}\n${preview}`;
349
+ return `[${r.agent}] ${status}${meta} ${r.taskId}\n${preview}`;
515
350
  });
516
351
 
517
352
  const skippedNote =
518
353
  skippedSelfRecursion > 0
519
- ? ` (${skippedSelfRecursion} ${blockedAgent} task${
520
- skippedSelfRecursion > 1 ? "s" : ""
521
- } skipped - self-recursion blocked)`
354
+ ? ` (${skippedSelfRecursion} ${blockedAgent} task${skippedSelfRecursion > 1 ? "s" : ""} skipped - self-recursion blocked)`
522
355
  : "";
523
- const outputIds = resultsWithUsage.map((r) => `${r.agent}_${r.index}`);
356
+ const outputIds = results.map((r) => r.taskId);
524
357
  const outputHint =
525
- hasOutputTool && outputIds.length > 0
526
- ? `\n\nUse output tool for full logs: output ids ${outputIds.join(", ")}`
527
- : "";
528
- const summary = `${successCount}/${resultsWithUsage.length} succeeded${skippedNote} [${formatDuration(
358
+ outputIds.length > 0 ? `\n\nUse output tool for full logs: output ids ${outputIds.join(", ")}` : "";
359
+ const summary = `${successCount}/${results.length} succeeded${skippedNote} [${formatDuration(
529
360
  totalDuration,
530
361
  )}]\n\n${summaries.join("\n\n---\n\n")}${outputHint}`;
531
362
 
@@ -538,7 +369,7 @@ export async function createTaskTool(
538
369
  content: [{ type: "text", text: summary }],
539
370
  details: {
540
371
  projectAgentsDir,
541
- results: resultsWithUsage,
372
+ results: results,
542
373
  totalDurationMs: totalDuration,
543
374
  usage: hasAggregatedUsage ? aggregatedUsage : undefined,
544
375
  outputPaths,
@@ -563,16 +394,15 @@ export async function createTaskTool(
563
394
  };
564
395
  }
565
396
 
566
- // Default task tool using process.cwd() - returns a placeholder sync tool
567
- // Real implementations should use createTaskTool() which properly initializes the tool
397
+ // Default task tool - returns a placeholder tool
398
+ // Real implementations should use createTaskTool(session) to initialize the tool
568
399
  export const taskTool: AgentTool<typeof taskSchema, TaskToolDetails, Theme> = {
569
400
  name: "task",
570
401
  label: "Task",
571
- description:
572
- "Launch a new agent to handle complex, multi-step tasks autonomously. (Agent discovery pending - use createTaskTool for full functionality)",
402
+ description: "Launch a new agent to handle complex, multi-step tasks autonomously.",
573
403
  parameters: taskSchema,
574
404
  execute: async () => ({
575
- content: [{ type: "text", text: "Task tool not properly initialized. Use createTaskTool(cwd) instead." }],
405
+ content: [{ type: "text", text: "Task tool not properly initialized. Use createTaskTool(session) instead." }],
576
406
  details: {
577
407
  projectAgentsDir: null,
578
408
  results: [],