@oh-my-pi/pi-coding-agent 3.20.1 → 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 +107 -8
- package/docs/custom-tools.md +3 -3
- package/docs/extensions.md +226 -220
- package/docs/hooks.md +2 -2
- package/docs/sdk.md +50 -53
- package/examples/custom-tools/README.md +2 -17
- package/examples/extensions/README.md +76 -74
- package/examples/extensions/todo.ts +2 -5
- package/examples/hooks/custom-compaction.ts +2 -4
- package/examples/hooks/handoff.ts +1 -1
- package/examples/hooks/qna.ts +1 -1
- package/examples/sdk/02-custom-model.ts +1 -1
- package/examples/sdk/README.md +7 -11
- package/package.json +6 -6
- package/src/cli/args.ts +9 -6
- package/src/cli/file-processor.ts +1 -1
- package/src/cli/list-models.ts +1 -1
- package/src/core/agent-session.ts +16 -5
- package/src/core/auth-storage.ts +1 -1
- package/src/core/compaction/branch-summarization.ts +2 -2
- package/src/core/compaction/compaction.ts +2 -2
- package/src/core/compaction/utils.ts +1 -1
- package/src/core/custom-tools/types.ts +1 -1
- package/src/core/custom-tools/wrapper.ts +0 -1
- package/src/core/extensions/index.ts +1 -6
- package/src/core/extensions/runner.ts +1 -1
- package/src/core/extensions/types.ts +1 -1
- package/src/core/extensions/wrapper.ts +1 -8
- package/src/core/file-mentions.ts +5 -8
- package/src/core/hooks/runner.ts +2 -2
- package/src/core/hooks/types.ts +1 -1
- package/src/core/messages.ts +1 -1
- package/src/core/model-registry.ts +1 -1
- package/src/core/model-resolver.ts +1 -1
- package/src/core/sdk.ts +64 -105
- package/src/core/session-manager.ts +18 -22
- package/src/core/settings-manager.ts +66 -1
- package/src/core/slash-commands.ts +12 -5
- package/src/core/system-prompt.ts +49 -36
- package/src/core/title-generator.ts +2 -2
- package/src/core/tools/ask.ts +98 -4
- package/src/core/tools/bash-interceptor.ts +11 -4
- package/src/core/tools/bash.ts +121 -5
- package/src/core/tools/context.ts +7 -0
- package/src/core/tools/edit-diff.ts +73 -24
- package/src/core/tools/edit.ts +221 -34
- package/src/core/tools/exa/render.ts +4 -16
- package/src/core/tools/find.ts +149 -5
- package/src/core/tools/gemini-image.ts +279 -56
- package/src/core/tools/git.ts +17 -3
- package/src/core/tools/grep.ts +185 -5
- package/src/core/tools/index.test.ts +180 -0
- package/src/core/tools/index.ts +96 -242
- package/src/core/tools/ls.ts +133 -5
- package/src/core/tools/lsp/index.ts +32 -29
- package/src/core/tools/lsp/render.ts +21 -22
- package/src/core/tools/notebook.ts +112 -4
- package/src/core/tools/output.ts +175 -15
- package/src/core/tools/read.ts +127 -25
- package/src/core/tools/render-utils.ts +241 -0
- package/src/core/tools/renderers.ts +40 -828
- package/src/core/tools/review.ts +26 -25
- package/src/core/tools/rulebook.ts +11 -3
- 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 +48 -208
- package/src/core/tools/task/render.ts +26 -11
- package/src/core/tools/task/types.ts +7 -12
- package/src/core/tools/task/worker-protocol.ts +17 -0
- package/src/core/tools/task/worker.ts +238 -0
- package/src/core/tools/truncate.ts +27 -1
- package/src/core/tools/web-fetch.ts +25 -49
- package/src/core/tools/web-search/index.ts +132 -46
- package/src/core/tools/web-search/providers/anthropic.ts +7 -2
- package/src/core/tools/web-search/providers/exa.ts +2 -1
- package/src/core/tools/web-search/providers/perplexity.ts +6 -1
- package/src/core/tools/web-search/render.ts +6 -4
- package/src/core/tools/web-search/types.ts +13 -0
- package/src/core/tools/write.ts +96 -14
- package/src/core/voice.ts +1 -1
- package/src/discovery/helpers.test.ts +1 -1
- package/src/index.ts +5 -16
- package/src/main.ts +5 -5
- package/src/modes/interactive/components/assistant-message.ts +1 -1
- package/src/modes/interactive/components/custom-message.ts +1 -1
- package/src/modes/interactive/components/extensions/inspector-panel.ts +25 -22
- package/src/modes/interactive/components/extensions/state-manager.ts +12 -0
- package/src/modes/interactive/components/footer.ts +1 -1
- package/src/modes/interactive/components/hook-message.ts +1 -1
- package/src/modes/interactive/components/model-selector.ts +1 -1
- package/src/modes/interactive/components/oauth-selector.ts +1 -1
- package/src/modes/interactive/components/settings-defs.ts +49 -0
- package/src/modes/interactive/components/status-line.ts +1 -1
- package/src/modes/interactive/components/tool-execution.ts +93 -538
- package/src/modes/interactive/interactive-mode.ts +19 -7
- package/src/modes/interactive/theme/theme.ts +4 -4
- package/src/modes/print-mode.ts +1 -1
- package/src/modes/rpc/rpc-client.ts +1 -1
- package/src/modes/rpc/rpc-types.ts +1 -1
- package/src/prompts/system-prompt.md +4 -0
- package/src/prompts/task.md +0 -7
- package/src/prompts/tools/gemini-image.md +5 -1
- package/src/prompts/tools/output.md +6 -2
- package/src/prompts/tools/task.md +68 -0
- package/src/prompts/tools/web-fetch.md +1 -0
- package/src/prompts/tools/web-search.md +2 -0
- package/src/utils/image-convert.ts +8 -2
- package/src/utils/image-magick.ts +247 -0
- package/src/utils/image-resize.ts +53 -13
- 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
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
* - Session artifacts for debugging
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
import type { Usage } from "@mariozechner/pi-ai";
|
|
16
17
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
17
|
-
import type { Usage } from "@oh-my-pi/pi-ai";
|
|
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,160 +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("- Launch multiple agents concurrently whenever possible, to maximize performance");
|
|
179
|
-
lines.push(
|
|
180
|
-
"- 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.",
|
|
181
|
-
);
|
|
182
|
-
lines.push(
|
|
183
|
-
"- 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.",
|
|
184
|
-
);
|
|
185
|
-
lines.push(
|
|
186
|
-
"- 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.",
|
|
187
|
-
);
|
|
188
|
-
lines.push("- The agent's outputs should generally be trusted");
|
|
189
|
-
lines.push(
|
|
190
|
-
"- 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",
|
|
191
|
-
);
|
|
192
|
-
lines.push(
|
|
193
|
-
"- 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.",
|
|
194
|
-
);
|
|
195
|
-
lines.push("");
|
|
196
|
-
lines.push("Parameters:");
|
|
197
|
-
lines.push(
|
|
198
|
-
`- tasks: Array of {agent, task, description?, model?} - tasks to run in parallel (max ${MAX_PARALLEL_TASKS}, ${MAX_CONCURRENCY} concurrent)`,
|
|
199
|
-
);
|
|
200
|
-
lines.push(
|
|
201
|
-
' - 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',
|
|
202
|
-
);
|
|
203
|
-
lines.push(
|
|
204
|
-
"- context: (optional) Shared context string prepended to all task prompts - use this to avoid repeating instructions",
|
|
205
|
-
);
|
|
206
|
-
lines.push("");
|
|
207
|
-
lines.push("Results are always written to {tempdir}/omp-task-{runId}/task_{agent}_{index}.md");
|
|
208
|
-
lines.push("");
|
|
209
|
-
lines.push("Example usage:");
|
|
210
|
-
lines.push("");
|
|
211
|
-
lines.push("<example_agent_descriptions>");
|
|
212
|
-
lines.push('"code-reviewer": use this agent after you are done writing a significant piece of code');
|
|
213
|
-
lines.push('"explore": use this agent for fast codebase exploration and research');
|
|
214
|
-
lines.push("</example_agent_descriptions>");
|
|
215
|
-
lines.push("");
|
|
216
|
-
lines.push("<example>");
|
|
217
|
-
lines.push('user: "Please write a function that checks if a number is prime"');
|
|
218
|
-
lines.push("assistant: Sure let me write a function that checks if a number is prime");
|
|
219
|
-
lines.push("assistant: I'm going to use the Write tool to write the following code:");
|
|
220
|
-
lines.push("<code>");
|
|
221
|
-
lines.push("function isPrime(n) {");
|
|
222
|
-
lines.push(" if (n <= 1) return false");
|
|
223
|
-
lines.push(" for (let i = 2; i * i <= n; i++) {");
|
|
224
|
-
lines.push(" if (n % i === 0) return false");
|
|
225
|
-
lines.push(" }");
|
|
226
|
-
lines.push(" return true");
|
|
227
|
-
lines.push("}");
|
|
228
|
-
lines.push("</code>");
|
|
229
|
-
lines.push("<commentary>");
|
|
230
|
-
lines.push(
|
|
231
|
-
"Since a significant piece of code was written and the task was completed, now use the code-reviewer agent to review the code",
|
|
232
|
-
);
|
|
233
|
-
lines.push("</commentary>");
|
|
234
|
-
lines.push("assistant: Now let me use the code-reviewer agent to review the code");
|
|
235
|
-
lines.push(
|
|
236
|
-
'assistant: Uses the Task tool: { tasks: [{ agent: "code-reviewer", task: "Review the isPrime function" }] }',
|
|
237
|
-
);
|
|
238
|
-
lines.push("</example>");
|
|
239
|
-
lines.push("");
|
|
240
|
-
lines.push("<example>");
|
|
241
|
-
lines.push('user: "Find all TODO comments in the codebase"');
|
|
242
|
-
lines.push("assistant: I'll use multiple explore agents to search different directories in parallel");
|
|
243
|
-
lines.push("assistant: Uses the Task tool:");
|
|
244
|
-
lines.push("{");
|
|
245
|
-
lines.push(' "context": "Find all TODO comments. Return file:line:content format.",');
|
|
246
|
-
lines.push(' "tasks": [');
|
|
247
|
-
lines.push(' { "agent": "explore", "task": "Search in src/" },');
|
|
248
|
-
lines.push(' { "agent": "explore", "task": "Search in lib/" },');
|
|
249
|
-
lines.push(' { "agent": "explore", "task": "Search in tests/" }');
|
|
250
|
-
lines.push(" ]");
|
|
251
|
-
lines.push("}");
|
|
252
|
-
lines.push("Results → {tempdir}/omp-task-{runId}/task_explore_*.md");
|
|
253
|
-
lines.push("</example>");
|
|
254
|
-
|
|
255
|
-
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));
|
|
256
113
|
}
|
|
257
114
|
|
|
258
115
|
/**
|
|
259
|
-
* Create the task tool configured for a specific
|
|
116
|
+
* Create the task tool configured for a specific session.
|
|
260
117
|
*/
|
|
261
118
|
export async function createTaskTool(
|
|
262
|
-
|
|
263
|
-
sessionContext?: SessionContext,
|
|
264
|
-
options?: TaskToolOptions,
|
|
119
|
+
session: ToolSession,
|
|
265
120
|
): Promise<AgentTool<typeof taskSchema, TaskToolDetails, Theme>> {
|
|
266
|
-
const hasOutputTool = options?.availableTools?.has("output") ?? false;
|
|
267
|
-
// Check if subagents are completely inhibited (legacy recursion prevention)
|
|
268
|
-
if (process.env[OMP_NO_SUBAGENTS_ENV]) {
|
|
269
|
-
return {
|
|
270
|
-
name: "task",
|
|
271
|
-
label: "Task",
|
|
272
|
-
description: "Sub-agents disabled (recursion prevention)",
|
|
273
|
-
parameters: taskSchema,
|
|
274
|
-
execute: async () => ({
|
|
275
|
-
content: [{ type: "text", text: "Sub-agents are disabled for this agent (recursion prevention)." }],
|
|
276
|
-
details: {
|
|
277
|
-
projectAgentsDir: null,
|
|
278
|
-
results: [],
|
|
279
|
-
totalDurationMs: 0,
|
|
280
|
-
},
|
|
281
|
-
}),
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
|
|
285
121
|
// Check for same-agent blocking (allows other agent types)
|
|
286
|
-
const blockedAgent = process.env
|
|
122
|
+
const blockedAgent = process.env.OMP_BLOCKED_AGENT;
|
|
123
|
+
|
|
124
|
+
// Build description upfront
|
|
125
|
+
const description = await buildDescription(session.cwd);
|
|
287
126
|
|
|
288
127
|
return {
|
|
289
128
|
name: "task",
|
|
290
129
|
label: "Task",
|
|
291
|
-
description
|
|
130
|
+
description,
|
|
292
131
|
parameters: taskSchema,
|
|
293
132
|
renderCall,
|
|
294
133
|
renderResult,
|
|
295
134
|
execute: async (_toolCallId, params, signal, onUpdate) => {
|
|
296
135
|
const startTime = Date.now();
|
|
297
|
-
const { agents, projectAgentsDir } = await discoverAgents(cwd);
|
|
136
|
+
const { agents, projectAgentsDir } = await discoverAgents(session.cwd);
|
|
298
137
|
const context = params.context;
|
|
299
138
|
|
|
300
139
|
// Handle empty or missing tasks
|
|
@@ -333,7 +172,7 @@ export async function createTaskTool(
|
|
|
333
172
|
}
|
|
334
173
|
|
|
335
174
|
// Derive artifacts directory
|
|
336
|
-
const sessionFile =
|
|
175
|
+
const sessionFile = session.getSessionFile();
|
|
337
176
|
const artifactsDir = sessionFile ? getArtifactsDir(sessionFile) : null;
|
|
338
177
|
const tempArtifactsDir = artifactsDir ? null : createTempArtifactsDir();
|
|
339
178
|
const effectiveArtifactsDir = artifactsDir || tempArtifactsDir!;
|
|
@@ -398,13 +237,12 @@ export async function createTaskTool(
|
|
|
398
237
|
}
|
|
399
238
|
|
|
400
239
|
// Check spawn restrictions from parent
|
|
401
|
-
const parentSpawns =
|
|
240
|
+
const parentSpawns = session.getSessionSpawns() ?? "*";
|
|
241
|
+
const allowedSpawns = parentSpawns.split(",").map((s) => s.trim());
|
|
402
242
|
const isSpawnAllowed = (agentName: string): boolean => {
|
|
403
|
-
if (parentSpawns === undefined) return true; // Root = allow all
|
|
404
243
|
if (parentSpawns === "") return false; // Empty = deny all
|
|
405
244
|
if (parentSpawns === "*") return true; // Wildcard = allow all
|
|
406
|
-
|
|
407
|
-
return allowed.has(agentName);
|
|
245
|
+
return allowedSpawns.includes(agentName);
|
|
408
246
|
};
|
|
409
247
|
|
|
410
248
|
for (const task of tasks) {
|
|
@@ -453,7 +291,7 @@ export async function createTaskTool(
|
|
|
453
291
|
const results = await mapWithConcurrencyLimit(tasksWithContext, MAX_CONCURRENCY, async (task, index) => {
|
|
454
292
|
const agent = getAgent(agents, task.agent)!;
|
|
455
293
|
return runSubprocess({
|
|
456
|
-
cwd,
|
|
294
|
+
cwd: session.cwd,
|
|
457
295
|
agent,
|
|
458
296
|
task: task.task,
|
|
459
297
|
description: task.description,
|
|
@@ -464,6 +302,7 @@ export async function createTaskTool(
|
|
|
464
302
|
persistArtifacts: !!artifactsDir,
|
|
465
303
|
artifactsDir: effectiveArtifactsDir,
|
|
466
304
|
signal,
|
|
305
|
+
eventBus: undefined,
|
|
467
306
|
onProgress: (progress) => {
|
|
468
307
|
progressMap.set(index, structuredClone(progress));
|
|
469
308
|
emitProgress();
|
|
@@ -471,48 +310,50 @@ export async function createTaskTool(
|
|
|
471
310
|
});
|
|
472
311
|
});
|
|
473
312
|
|
|
313
|
+
// Aggregate usage from executor results (already accumulated incrementally)
|
|
474
314
|
const aggregatedUsage = createUsageTotals();
|
|
475
315
|
let hasAggregatedUsage = false;
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
addUsageTotals(aggregatedUsage, usage);
|
|
316
|
+
for (const result of results) {
|
|
317
|
+
if (result.usage) {
|
|
318
|
+
addUsageTotals(aggregatedUsage, result.usage);
|
|
480
319
|
hasAggregatedUsage = true;
|
|
481
|
-
return { ...result, usage };
|
|
482
320
|
}
|
|
483
|
-
|
|
484
|
-
});
|
|
321
|
+
}
|
|
485
322
|
|
|
486
323
|
// Collect output paths (artifacts already written by executor in real-time)
|
|
487
324
|
const outputPaths: string[] = [];
|
|
488
|
-
for (const result of
|
|
325
|
+
for (const result of results) {
|
|
489
326
|
if (result.artifactPaths) {
|
|
490
327
|
outputPaths.push(result.artifactPaths.outputPath);
|
|
491
328
|
}
|
|
492
329
|
}
|
|
493
330
|
|
|
494
331
|
// Build final output - match plugin format
|
|
495
|
-
const successCount =
|
|
332
|
+
const successCount = results.filter((r) => r.exitCode === 0).length;
|
|
496
333
|
const totalDuration = Date.now() - startTime;
|
|
497
334
|
|
|
498
|
-
const summaries =
|
|
335
|
+
const summaries = results.map((r) => {
|
|
499
336
|
const status = r.exitCode === 0 ? "completed" : `failed (exit ${r.exitCode})`;
|
|
500
337
|
const output = r.output.trim() || r.stderr.trim() || "(no output)";
|
|
501
338
|
const preview = output.split("\n").slice(0, 5).join("\n");
|
|
502
|
-
// Include output metadata and ID
|
|
339
|
+
// Include output metadata and ID
|
|
503
340
|
const outputId = `${r.agent}_${r.index}`;
|
|
504
341
|
const meta = r.outputMeta
|
|
505
342
|
? ` [${r.outputMeta.lineCount} lines, ${formatBytes(r.outputMeta.charCount)}]`
|
|
506
343
|
: "";
|
|
507
|
-
|
|
508
|
-
return `[${r.agent}] ${status}${meta} ${outputId}${pathInfo}\n${preview}`;
|
|
344
|
+
return `[${r.agent}] ${status}${meta} ${outputId}\n${preview}`;
|
|
509
345
|
});
|
|
510
346
|
|
|
511
347
|
const skippedNote =
|
|
512
348
|
skippedSelfRecursion > 0
|
|
513
349
|
? ` (${skippedSelfRecursion} ${blockedAgent} task${skippedSelfRecursion > 1 ? "s" : ""} skipped - self-recursion blocked)`
|
|
514
350
|
: "";
|
|
515
|
-
const
|
|
351
|
+
const outputIds = results.map((r) => `${r.agent}_${r.index}`);
|
|
352
|
+
const outputHint =
|
|
353
|
+
outputIds.length > 0 ? `\n\nUse output tool for full logs: output ids ${outputIds.join(", ")}` : "";
|
|
354
|
+
const summary = `${successCount}/${results.length} succeeded${skippedNote} [${formatDuration(
|
|
355
|
+
totalDuration,
|
|
356
|
+
)}]\n\n${summaries.join("\n\n---\n\n")}${outputHint}`;
|
|
516
357
|
|
|
517
358
|
// Cleanup temp directory if used
|
|
518
359
|
if (tempArtifactsDir) {
|
|
@@ -523,7 +364,7 @@ export async function createTaskTool(
|
|
|
523
364
|
content: [{ type: "text", text: summary }],
|
|
524
365
|
details: {
|
|
525
366
|
projectAgentsDir,
|
|
526
|
-
results:
|
|
367
|
+
results: results,
|
|
527
368
|
totalDurationMs: totalDuration,
|
|
528
369
|
usage: hasAggregatedUsage ? aggregatedUsage : undefined,
|
|
529
370
|
outputPaths,
|
|
@@ -548,16 +389,15 @@ export async function createTaskTool(
|
|
|
548
389
|
};
|
|
549
390
|
}
|
|
550
391
|
|
|
551
|
-
// Default task tool
|
|
552
|
-
// Real implementations should use createTaskTool()
|
|
392
|
+
// Default task tool - returns a placeholder tool
|
|
393
|
+
// Real implementations should use createTaskTool(session) to initialize the tool
|
|
553
394
|
export const taskTool: AgentTool<typeof taskSchema, TaskToolDetails, Theme> = {
|
|
554
395
|
name: "task",
|
|
555
396
|
label: "Task",
|
|
556
|
-
description:
|
|
557
|
-
"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.",
|
|
558
398
|
parameters: taskSchema,
|
|
559
399
|
execute: async () => ({
|
|
560
|
-
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." }],
|
|
561
401
|
details: {
|
|
562
402
|
projectAgentsDir: null,
|
|
563
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
|
+
}
|
|
194
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))}`;
|
|
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)
|
|
@@ -475,3 +485,8 @@ export function renderResult(
|
|
|
475
485
|
|
|
476
486
|
return new Text(lines.join("\n"), 0, 0);
|
|
477
487
|
}
|
|
488
|
+
|
|
489
|
+
export const taskToolRenderer = {
|
|
490
|
+
renderCall,
|
|
491
|
+
renderResult,
|
|
492
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Usage } from "@
|
|
1
|
+
import type { Usage } from "@mariozechner/pi-ai";
|
|
2
2
|
import { type Static, Type } from "@sinclair/typebox";
|
|
3
3
|
|
|
4
4
|
/** Source of an agent definition */
|
|
@@ -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 };
|