@oh-my-pi/pi-coding-agent 3.21.0 → 3.24.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 +40 -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-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 +41 -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/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 +180 -0
- package/src/core/tools/index.ts +94 -237
- 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 +5 -17
- 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/discovery.ts +0 -6
- package/src/core/tools/task/executor.ts +264 -254
- package/src/core/tools/task/index.ts +45 -220
- package/src/core/tools/task/render.ts +21 -11
- package/src/core/tools/task/types.ts +6 -11
- package/src/core/tools/task/worker-protocol.ts +17 -0
- package/src/core/tools/task/worker.ts +238 -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 +0 -7
- package/src/prompts/tools/output.md +2 -2
- package/src/prompts/tools/task.md +68 -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,6 +16,7 @@
|
|
|
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";
|
|
@@ -27,15 +28,13 @@ import {
|
|
|
27
28
|
MAX_AGENTS_IN_DESCRIPTION,
|
|
28
29
|
MAX_CONCURRENCY,
|
|
29
30
|
MAX_PARALLEL_TASKS,
|
|
30
|
-
OMP_BLOCKED_AGENT_ENV,
|
|
31
|
-
OMP_NO_SUBAGENTS_ENV,
|
|
32
|
-
OMP_SPAWNS_ENV,
|
|
33
31
|
type TaskToolDetails,
|
|
34
32
|
taskSchema,
|
|
35
33
|
} from "./types";
|
|
36
34
|
|
|
37
|
-
// Import review tools for side effects (registers
|
|
35
|
+
// Import review tools for side effects (registers subagent tool handlers)
|
|
38
36
|
import "../review";
|
|
37
|
+
import type { ToolSession } from "..";
|
|
39
38
|
|
|
40
39
|
/** Format byte count for display */
|
|
41
40
|
function formatBytes(bytes: number): string {
|
|
@@ -83,51 +82,6 @@ function addUsageTotals(target: Usage, usage: Partial<Usage>): void {
|
|
|
83
82
|
target.cost.total += cost.total;
|
|
84
83
|
}
|
|
85
84
|
|
|
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
85
|
// Re-export types and utilities
|
|
132
86
|
export { loadBundledAgents as BUNDLED_AGENTS } from "./agents";
|
|
133
87
|
export { discoverCommands, expandCommand, getCommand } from "./commands";
|
|
@@ -141,166 +95,45 @@ export { taskSchema } from "./types";
|
|
|
141
95
|
async function buildDescription(cwd: string): Promise<string> {
|
|
142
96
|
const { agents } = await discoverAgents(cwd);
|
|
143
97
|
|
|
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
|
-
|
|
98
|
+
// Build agents list
|
|
99
|
+
const agentLines: string[] = [];
|
|
154
100
|
for (const agent of agents.slice(0, MAX_AGENTS_IN_DESCRIPTION)) {
|
|
155
101
|
const tools = agent.tools?.join(", ") || "All tools";
|
|
156
|
-
|
|
102
|
+
agentLines.push(`- ${agent.name}: ${agent.description} (Tools: ${tools})`);
|
|
157
103
|
}
|
|
158
104
|
if (agents.length > MAX_AGENTS_IN_DESCRIPTION) {
|
|
159
|
-
|
|
105
|
+
agentLines.push(` ...and ${agents.length - MAX_AGENTS_IN_DESCRIPTION} more agents`);
|
|
160
106
|
}
|
|
161
107
|
|
|
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");
|
|
108
|
+
// Fill template placeholders
|
|
109
|
+
return taskDescriptionTemplate
|
|
110
|
+
.replace("{{AGENTS_LIST}}", agentLines.join("\n"))
|
|
111
|
+
.replace("{{MAX_PARALLEL_TASKS}}", String(MAX_PARALLEL_TASKS))
|
|
112
|
+
.replace("{{MAX_CONCURRENCY}}", String(MAX_CONCURRENCY));
|
|
262
113
|
}
|
|
263
114
|
|
|
264
115
|
/**
|
|
265
|
-
* Create the task tool configured for a specific
|
|
116
|
+
* Create the task tool configured for a specific session.
|
|
266
117
|
*/
|
|
267
118
|
export async function createTaskTool(
|
|
268
|
-
|
|
269
|
-
sessionContext?: SessionContext,
|
|
270
|
-
options?: TaskToolOptions,
|
|
119
|
+
session: ToolSession,
|
|
271
120
|
): 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
121
|
// Check for same-agent blocking (allows other agent types)
|
|
292
|
-
const blockedAgent = process.env
|
|
122
|
+
const blockedAgent = process.env.OMP_BLOCKED_AGENT;
|
|
123
|
+
|
|
124
|
+
// Build description upfront
|
|
125
|
+
const description = await buildDescription(session.cwd);
|
|
293
126
|
|
|
294
127
|
return {
|
|
295
128
|
name: "task",
|
|
296
129
|
label: "Task",
|
|
297
|
-
description
|
|
130
|
+
description,
|
|
298
131
|
parameters: taskSchema,
|
|
299
132
|
renderCall,
|
|
300
133
|
renderResult,
|
|
301
134
|
execute: async (_toolCallId, params, signal, onUpdate) => {
|
|
302
135
|
const startTime = Date.now();
|
|
303
|
-
const { agents, projectAgentsDir } = await discoverAgents(cwd);
|
|
136
|
+
const { agents, projectAgentsDir } = await discoverAgents(session.cwd);
|
|
304
137
|
const context = params.context;
|
|
305
138
|
|
|
306
139
|
// Handle empty or missing tasks
|
|
@@ -339,7 +172,7 @@ export async function createTaskTool(
|
|
|
339
172
|
}
|
|
340
173
|
|
|
341
174
|
// Derive artifacts directory
|
|
342
|
-
const sessionFile =
|
|
175
|
+
const sessionFile = session.getSessionFile();
|
|
343
176
|
const artifactsDir = sessionFile ? getArtifactsDir(sessionFile) : null;
|
|
344
177
|
const tempArtifactsDir = artifactsDir ? null : createTempArtifactsDir();
|
|
345
178
|
const effectiveArtifactsDir = artifactsDir || tempArtifactsDir!;
|
|
@@ -404,13 +237,12 @@ export async function createTaskTool(
|
|
|
404
237
|
}
|
|
405
238
|
|
|
406
239
|
// Check spawn restrictions from parent
|
|
407
|
-
const parentSpawns =
|
|
240
|
+
const parentSpawns = session.getSessionSpawns() ?? "*";
|
|
241
|
+
const allowedSpawns = parentSpawns.split(",").map((s) => s.trim());
|
|
408
242
|
const isSpawnAllowed = (agentName: string): boolean => {
|
|
409
|
-
if (parentSpawns === undefined) return true; // Root = allow all
|
|
410
243
|
if (parentSpawns === "") return false; // Empty = deny all
|
|
411
244
|
if (parentSpawns === "*") return true; // Wildcard = allow all
|
|
412
|
-
|
|
413
|
-
return allowed.has(agentName);
|
|
245
|
+
return allowedSpawns.includes(agentName);
|
|
414
246
|
};
|
|
415
247
|
|
|
416
248
|
for (const task of tasks) {
|
|
@@ -459,7 +291,7 @@ export async function createTaskTool(
|
|
|
459
291
|
const results = await mapWithConcurrencyLimit(tasksWithContext, MAX_CONCURRENCY, async (task, index) => {
|
|
460
292
|
const agent = getAgent(agents, task.agent)!;
|
|
461
293
|
return runSubprocess({
|
|
462
|
-
cwd,
|
|
294
|
+
cwd: session.cwd,
|
|
463
295
|
agent,
|
|
464
296
|
task: task.task,
|
|
465
297
|
description: task.description,
|
|
@@ -470,6 +302,7 @@ export async function createTaskTool(
|
|
|
470
302
|
persistArtifacts: !!artifactsDir,
|
|
471
303
|
artifactsDir: effectiveArtifactsDir,
|
|
472
304
|
signal,
|
|
305
|
+
eventBus: undefined,
|
|
473
306
|
onProgress: (progress) => {
|
|
474
307
|
progressMap.set(index, structuredClone(progress));
|
|
475
308
|
emitProgress();
|
|
@@ -477,55 +310,48 @@ export async function createTaskTool(
|
|
|
477
310
|
});
|
|
478
311
|
});
|
|
479
312
|
|
|
313
|
+
// Aggregate usage from executor results (already accumulated incrementally)
|
|
480
314
|
const aggregatedUsage = createUsageTotals();
|
|
481
315
|
let hasAggregatedUsage = false;
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
addUsageTotals(aggregatedUsage, usage);
|
|
316
|
+
for (const result of results) {
|
|
317
|
+
if (result.usage) {
|
|
318
|
+
addUsageTotals(aggregatedUsage, result.usage);
|
|
486
319
|
hasAggregatedUsage = true;
|
|
487
|
-
return { ...result, usage };
|
|
488
320
|
}
|
|
489
|
-
|
|
490
|
-
});
|
|
321
|
+
}
|
|
491
322
|
|
|
492
323
|
// Collect output paths (artifacts already written by executor in real-time)
|
|
493
324
|
const outputPaths: string[] = [];
|
|
494
|
-
for (const result of
|
|
325
|
+
for (const result of results) {
|
|
495
326
|
if (result.artifactPaths) {
|
|
496
327
|
outputPaths.push(result.artifactPaths.outputPath);
|
|
497
328
|
}
|
|
498
329
|
}
|
|
499
330
|
|
|
500
331
|
// Build final output - match plugin format
|
|
501
|
-
const successCount =
|
|
332
|
+
const successCount = results.filter((r) => r.exitCode === 0).length;
|
|
502
333
|
const totalDuration = Date.now() - startTime;
|
|
503
334
|
|
|
504
|
-
const summaries =
|
|
335
|
+
const summaries = results.map((r) => {
|
|
505
336
|
const status = r.exitCode === 0 ? "completed" : `failed (exit ${r.exitCode})`;
|
|
506
337
|
const output = r.output.trim() || r.stderr.trim() || "(no output)";
|
|
507
338
|
const preview = output.split("\n").slice(0, 5).join("\n");
|
|
508
|
-
// Include output metadata and ID
|
|
339
|
+
// Include output metadata and ID
|
|
509
340
|
const outputId = `${r.agent}_${r.index}`;
|
|
510
341
|
const meta = r.outputMeta
|
|
511
342
|
? ` [${r.outputMeta.lineCount} lines, ${formatBytes(r.outputMeta.charCount)}]`
|
|
512
343
|
: "";
|
|
513
|
-
|
|
514
|
-
return `[${r.agent}] ${status}${meta} ${outputId}${pathInfo}\n${preview}`;
|
|
344
|
+
return `[${r.agent}] ${status}${meta} ${outputId}\n${preview}`;
|
|
515
345
|
});
|
|
516
346
|
|
|
517
347
|
const skippedNote =
|
|
518
348
|
skippedSelfRecursion > 0
|
|
519
|
-
? ` (${skippedSelfRecursion} ${blockedAgent} task${
|
|
520
|
-
skippedSelfRecursion > 1 ? "s" : ""
|
|
521
|
-
} skipped - self-recursion blocked)`
|
|
349
|
+
? ` (${skippedSelfRecursion} ${blockedAgent} task${skippedSelfRecursion > 1 ? "s" : ""} skipped - self-recursion blocked)`
|
|
522
350
|
: "";
|
|
523
|
-
const outputIds =
|
|
351
|
+
const outputIds = results.map((r) => `${r.agent}_${r.index}`);
|
|
524
352
|
const outputHint =
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
: "";
|
|
528
|
-
const summary = `${successCount}/${resultsWithUsage.length} succeeded${skippedNote} [${formatDuration(
|
|
353
|
+
outputIds.length > 0 ? `\n\nUse output tool for full logs: output ids ${outputIds.join(", ")}` : "";
|
|
354
|
+
const summary = `${successCount}/${results.length} succeeded${skippedNote} [${formatDuration(
|
|
529
355
|
totalDuration,
|
|
530
356
|
)}]\n\n${summaries.join("\n\n---\n\n")}${outputHint}`;
|
|
531
357
|
|
|
@@ -538,7 +364,7 @@ export async function createTaskTool(
|
|
|
538
364
|
content: [{ type: "text", text: summary }],
|
|
539
365
|
details: {
|
|
540
366
|
projectAgentsDir,
|
|
541
|
-
results:
|
|
367
|
+
results: results,
|
|
542
368
|
totalDurationMs: totalDuration,
|
|
543
369
|
usage: hasAggregatedUsage ? aggregatedUsage : undefined,
|
|
544
370
|
outputPaths,
|
|
@@ -563,16 +389,15 @@ export async function createTaskTool(
|
|
|
563
389
|
};
|
|
564
390
|
}
|
|
565
391
|
|
|
566
|
-
// Default task tool
|
|
567
|
-
// Real implementations should use createTaskTool()
|
|
392
|
+
// Default task tool - returns a placeholder tool
|
|
393
|
+
// Real implementations should use createTaskTool(session) to initialize the tool
|
|
568
394
|
export const taskTool: AgentTool<typeof taskSchema, TaskToolDetails, Theme> = {
|
|
569
395
|
name: "task",
|
|
570
396
|
label: "Task",
|
|
571
|
-
description:
|
|
572
|
-
"Launch a new agent to handle complex, multi-step tasks autonomously. (Agent discovery pending - use createTaskTool for full functionality)",
|
|
397
|
+
description: "Launch a new agent to handle complex, multi-step tasks autonomously.",
|
|
573
398
|
parameters: taskSchema,
|
|
574
399
|
execute: async () => ({
|
|
575
|
-
content: [{ type: "text", text: "Task tool not properly initialized. Use createTaskTool(
|
|
400
|
+
content: [{ type: "text", text: "Task tool not properly initialized. Use createTaskTool(session) instead." }],
|
|
576
401
|
details: {
|
|
577
402
|
projectAgentsDir: null,
|
|
578
403
|
results: [],
|
|
@@ -181,19 +181,29 @@ function renderAgentProgress(
|
|
|
181
181
|
|
|
182
182
|
lines.push(statusLine);
|
|
183
183
|
|
|
184
|
-
// Current tool (if running)
|
|
185
|
-
if (progress.status === "running"
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
184
|
+
// Current tool (if running) or most recent completed tool
|
|
185
|
+
if (progress.status === "running") {
|
|
186
|
+
if (progress.currentTool) {
|
|
187
|
+
let toolLine = `${continuePrefix}${theme.tree.hook} ${theme.fg("muted", progress.currentTool)}`;
|
|
188
|
+
if (progress.currentToolArgs) {
|
|
189
|
+
toolLine += `: ${theme.fg("dim", truncate(progress.currentToolArgs, 40, theme.format.ellipsis))}`;
|
|
190
|
+
}
|
|
191
|
+
if (progress.currentToolStartMs) {
|
|
192
|
+
const elapsed = Date.now() - progress.currentToolStartMs;
|
|
193
|
+
if (elapsed > 5000) {
|
|
194
|
+
toolLine += `${theme.sep.dot}${theme.fg("warning", formatDuration(elapsed))}`;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
lines.push(toolLine);
|
|
198
|
+
} else if (progress.recentTools.length > 0) {
|
|
199
|
+
// Show most recent completed tool when idle between tools
|
|
200
|
+
const recent = progress.recentTools[0];
|
|
201
|
+
let toolLine = `${continuePrefix}${theme.tree.hook} ${theme.fg("dim", recent.tool)}`;
|
|
202
|
+
if (recent.args) {
|
|
203
|
+
toolLine += `: ${theme.fg("dim", truncate(recent.args, 40, theme.format.ellipsis))}`;
|
|
194
204
|
}
|
|
205
|
+
lines.push(toolLine);
|
|
195
206
|
}
|
|
196
|
-
lines.push(toolLine);
|
|
197
207
|
}
|
|
198
208
|
|
|
199
209
|
// Render extracted tool data inline (e.g., review findings)
|
|
@@ -19,14 +19,11 @@ export const MAX_OUTPUT_LINES = 5000;
|
|
|
19
19
|
/** Maximum agents to show in description */
|
|
20
20
|
export const MAX_AGENTS_IN_DESCRIPTION = 10;
|
|
21
21
|
|
|
22
|
-
/**
|
|
23
|
-
export const
|
|
22
|
+
/** EventBus channel for raw subagent events */
|
|
23
|
+
export const TASK_SUBAGENT_EVENT_CHANNEL = "task:subagent:event";
|
|
24
24
|
|
|
25
|
-
/**
|
|
26
|
-
export const
|
|
27
|
-
|
|
28
|
-
/** Environment variable containing allowed spawn list (propagated to subprocesses) */
|
|
29
|
-
export const OMP_SPAWNS_ENV = "OMP_SPAWNS";
|
|
25
|
+
/** EventBus channel for aggregated subagent progress */
|
|
26
|
+
export const TASK_SUBAGENT_PROGRESS_CHANNEL = "task:subagent:progress";
|
|
30
27
|
|
|
31
28
|
/** Single task item for parallel execution */
|
|
32
29
|
export const taskItemSchema = Type.Object({
|
|
@@ -81,7 +78,6 @@ export interface AgentDefinition {
|
|
|
81
78
|
tools?: string[];
|
|
82
79
|
spawns?: string[] | "*";
|
|
83
80
|
model?: string;
|
|
84
|
-
recursive?: boolean;
|
|
85
81
|
source: AgentSource;
|
|
86
82
|
filePath?: string;
|
|
87
83
|
}
|
|
@@ -123,10 +119,9 @@ export interface SingleResult {
|
|
|
123
119
|
modelOverride?: string;
|
|
124
120
|
error?: string;
|
|
125
121
|
aborted?: boolean;
|
|
126
|
-
|
|
127
|
-
artifactPaths?: { inputPath: string; outputPath: string; jsonlPath?: string };
|
|
128
|
-
/** Aggregated usage from the subprocess, if available. */
|
|
122
|
+
/** Aggregated usage from the subprocess, accumulated incrementally from message_end events. */
|
|
129
123
|
usage?: Usage;
|
|
124
|
+
artifactPaths?: { inputPath: string; outputPath: string; jsonlPath?: string };
|
|
130
125
|
/** Data extracted by registered subprocess tool handlers (keyed by tool name) */
|
|
131
126
|
extractedToolData?: Record<string, unknown[]>;
|
|
132
127
|
/** Output metadata for Output tool integration */
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AgentEvent } from "@oh-my-pi/pi-agent-core";
|
|
2
|
+
|
|
3
|
+
export interface SubagentWorkerStartPayload {
|
|
4
|
+
cwd: string;
|
|
5
|
+
task: string;
|
|
6
|
+
systemPrompt: string;
|
|
7
|
+
model?: string;
|
|
8
|
+
toolNames?: string[];
|
|
9
|
+
sessionFile?: string | null;
|
|
10
|
+
spawnsEnv?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type SubagentWorkerRequest = { type: "start"; payload: SubagentWorkerStartPayload } | { type: "abort" };
|
|
14
|
+
|
|
15
|
+
export type SubagentWorkerResponse =
|
|
16
|
+
| { type: "event"; event: AgentEvent }
|
|
17
|
+
| { type: "done"; exitCode: number; durationMs: number; error?: string; aborted?: boolean };
|