@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.
- package/CHANGELOG.md +55 -1
- package/docs/sdk.md +47 -50
- package/examples/custom-tools/README.md +0 -15
- package/examples/hooks/custom-compaction.ts +1 -3
- package/examples/sdk/README.md +6 -10
- package/package.json +5 -5
- package/src/cli/args.ts +9 -6
- package/src/core/agent-session.ts +3 -3
- package/src/core/custom-commands/bundled/wt/index.ts +3 -0
- package/src/core/custom-tools/wrapper.ts +0 -1
- package/src/core/extensions/index.ts +1 -6
- package/src/core/extensions/wrapper.ts +0 -7
- package/src/core/file-mentions.ts +5 -8
- package/src/core/sdk.ts +48 -111
- package/src/core/session-manager.ts +7 -0
- package/src/core/system-prompt.ts +22 -33
- package/src/core/tools/ask.ts +14 -7
- package/src/core/tools/bash-interceptor.ts +4 -4
- package/src/core/tools/bash.ts +19 -9
- package/src/core/tools/complete.ts +131 -0
- package/src/core/tools/context.ts +7 -0
- package/src/core/tools/edit.ts +8 -15
- package/src/core/tools/exa/render.ts +4 -16
- package/src/core/tools/find.ts +7 -18
- package/src/core/tools/git.ts +13 -3
- package/src/core/tools/grep.ts +7 -18
- package/src/core/tools/index.test.ts +188 -0
- package/src/core/tools/index.ts +106 -236
- package/src/core/tools/jtd-to-json-schema.ts +274 -0
- package/src/core/tools/ls.ts +4 -9
- package/src/core/tools/lsp/index.ts +32 -29
- package/src/core/tools/lsp/render.ts +7 -28
- package/src/core/tools/notebook.ts +3 -5
- package/src/core/tools/output.ts +130 -31
- package/src/core/tools/read.ts +8 -19
- package/src/core/tools/review.ts +0 -18
- package/src/core/tools/rulebook.ts +8 -2
- package/src/core/tools/task/agents.ts +28 -7
- package/src/core/tools/task/artifacts.ts +6 -9
- package/src/core/tools/task/discovery.ts +0 -6
- package/src/core/tools/task/executor.ts +306 -257
- package/src/core/tools/task/index.ts +65 -235
- package/src/core/tools/task/name-generator.ts +247 -0
- package/src/core/tools/task/render.ts +158 -19
- package/src/core/tools/task/types.ts +13 -11
- package/src/core/tools/task/worker-protocol.ts +18 -0
- package/src/core/tools/task/worker.ts +270 -0
- package/src/core/tools/web-fetch.ts +4 -36
- package/src/core/tools/web-search/index.ts +2 -1
- package/src/core/tools/web-search/render.ts +1 -4
- package/src/core/tools/write.ts +7 -15
- package/src/discovery/helpers.test.ts +1 -1
- package/src/index.ts +5 -16
- package/src/main.ts +4 -4
- package/src/modes/interactive/theme/theme.ts +4 -4
- package/src/prompts/task.md +14 -57
- package/src/prompts/tools/output.md +4 -3
- package/src/prompts/tools/task.md +70 -0
- package/examples/custom-tools/question/index.ts +0 -84
- package/examples/custom-tools/subagent/README.md +0 -172
- package/examples/custom-tools/subagent/agents/planner.md +0 -37
- package/examples/custom-tools/subagent/agents/scout.md +0 -50
- package/examples/custom-tools/subagent/agents/worker.md +0 -24
- package/examples/custom-tools/subagent/agents.ts +0 -156
- package/examples/custom-tools/subagent/commands/implement-and-review.md +0 -10
- package/examples/custom-tools/subagent/commands/implement.md +0 -10
- package/examples/custom-tools/subagent/commands/scout-and-plan.md +0 -9
- package/examples/custom-tools/subagent/index.ts +0 -1002
- package/examples/sdk/05-tools.ts +0 -94
- package/examples/sdk/12-full-control.ts +0 -95
- 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
|
|
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
|
-
|
|
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
|
-
|
|
103
|
+
agentLines.push(`- ${agent.name}: ${agent.description} (Tools: ${tools})`);
|
|
157
104
|
}
|
|
158
105
|
if (agents.length > MAX_AGENTS_IN_DESCRIPTION) {
|
|
159
|
-
|
|
106
|
+
agentLines.push(` ...and ${agents.length - MAX_AGENTS_IN_DESCRIPTION} more agents`);
|
|
160
107
|
}
|
|
161
108
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
"
|
|
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
|
|
117
|
+
* Create the task tool configured for a specific session.
|
|
266
118
|
*/
|
|
267
119
|
export async function createTaskTool(
|
|
268
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 <
|
|
432
|
-
const
|
|
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
|
-
|
|
279
|
+
taskId: t.taskId,
|
|
280
|
+
agent: t.agent,
|
|
436
281
|
agentSource: agentCfg?.source ?? "user",
|
|
437
282
|
status: "pending",
|
|
438
|
-
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:
|
|
445
|
-
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
|
|
483
|
-
|
|
484
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
339
|
+
const successCount = results.filter((r) => r.exitCode === 0).length;
|
|
502
340
|
const totalDuration = Date.now() - startTime;
|
|
503
341
|
|
|
504
|
-
const summaries =
|
|
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
|
-
|
|
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 =
|
|
356
|
+
const outputIds = results.map((r) => r.taskId);
|
|
524
357
|
const outputHint =
|
|
525
|
-
|
|
526
|
-
|
|
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:
|
|
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
|
|
567
|
-
// Real implementations should use createTaskTool()
|
|
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(
|
|
405
|
+
content: [{ type: "text", text: "Task tool not properly initialized. Use createTaskTool(session) instead." }],
|
|
576
406
|
details: {
|
|
577
407
|
projectAgentsDir: null,
|
|
578
408
|
results: [],
|