@oh-my-pi/pi-coding-agent 3.24.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 +15 -0
- package/package.json +4 -4
- package/src/core/custom-commands/bundled/wt/index.ts +3 -0
- package/src/core/sdk.ts +7 -0
- package/src/core/tools/complete.ts +131 -0
- package/src/core/tools/index.test.ts +9 -1
- package/src/core/tools/index.ts +18 -5
- package/src/core/tools/jtd-to-json-schema.ts +274 -0
- package/src/core/tools/output.ts +125 -14
- package/src/core/tools/task/artifacts.ts +6 -9
- package/src/core/tools/task/executor.ts +44 -5
- package/src/core/tools/task/index.ts +23 -18
- package/src/core/tools/task/name-generator.ts +247 -0
- package/src/core/tools/task/render.ts +137 -8
- package/src/core/tools/task/types.ts +7 -0
- package/src/core/tools/task/worker-protocol.ts +1 -0
- package/src/core/tools/task/worker.ts +33 -1
- package/src/prompts/task.md +14 -50
- package/src/prompts/tools/output.md +2 -1
- package/src/prompts/tools/task.md +3 -1
package/src/core/tools/output.ts
CHANGED
|
@@ -36,6 +36,11 @@ const outputSchema = Type.Object({
|
|
|
36
36
|
description: "Output format: raw (default), json (structured), stripped (no ANSI)",
|
|
37
37
|
}),
|
|
38
38
|
),
|
|
39
|
+
query: Type.Optional(
|
|
40
|
+
Type.String({
|
|
41
|
+
description: "jq-like query for JSON outputs (e.g., .result.items[0].name). Requires JSON output.",
|
|
42
|
+
}),
|
|
43
|
+
),
|
|
39
44
|
offset: Type.Optional(
|
|
40
45
|
Type.Number({
|
|
41
46
|
description: "Line number to start reading from (1-indexed)",
|
|
@@ -70,6 +75,7 @@ interface OutputEntry {
|
|
|
70
75
|
provenance?: OutputProvenance;
|
|
71
76
|
previewLines?: string[];
|
|
72
77
|
range?: OutputRange;
|
|
78
|
+
query?: string;
|
|
73
79
|
}
|
|
74
80
|
|
|
75
81
|
export interface OutputToolDetails {
|
|
@@ -83,6 +89,77 @@ function stripAnsi(text: string): string {
|
|
|
83
89
|
return text.replace(/\x1b\[[0-9;]*m/g, "");
|
|
84
90
|
}
|
|
85
91
|
|
|
92
|
+
function parseQuery(query: string): Array<string | number> {
|
|
93
|
+
let input = query.trim();
|
|
94
|
+
if (!input) return [];
|
|
95
|
+
if (input.startsWith(".")) input = input.slice(1);
|
|
96
|
+
if (!input) return [];
|
|
97
|
+
|
|
98
|
+
const tokens: Array<string | number> = [];
|
|
99
|
+
let i = 0;
|
|
100
|
+
|
|
101
|
+
const isIdentChar = (ch: string) => /[A-Za-z0-9_-]/.test(ch);
|
|
102
|
+
|
|
103
|
+
while (i < input.length) {
|
|
104
|
+
const ch = input[i];
|
|
105
|
+
if (ch === ".") {
|
|
106
|
+
i++;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (ch === "[") {
|
|
110
|
+
const closeIndex = input.indexOf("]", i + 1);
|
|
111
|
+
if (closeIndex === -1) {
|
|
112
|
+
throw new Error(`Invalid query: missing ] in ${query}`);
|
|
113
|
+
}
|
|
114
|
+
const raw = input.slice(i + 1, closeIndex).trim();
|
|
115
|
+
if (!raw) {
|
|
116
|
+
throw new Error(`Invalid query: empty [] in ${query}`);
|
|
117
|
+
}
|
|
118
|
+
const quote = raw[0];
|
|
119
|
+
if ((quote === '"' || quote === "'") && raw.endsWith(quote)) {
|
|
120
|
+
let inner = raw.slice(1, -1);
|
|
121
|
+
inner = inner.replace(/\\(["'\\])/g, "$1");
|
|
122
|
+
tokens.push(inner);
|
|
123
|
+
} else if (/^\d+$/.test(raw)) {
|
|
124
|
+
tokens.push(Number(raw));
|
|
125
|
+
} else {
|
|
126
|
+
tokens.push(raw);
|
|
127
|
+
}
|
|
128
|
+
i = closeIndex + 1;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const start = i;
|
|
133
|
+
while (i < input.length && isIdentChar(input[i])) {
|
|
134
|
+
i++;
|
|
135
|
+
}
|
|
136
|
+
if (start === i) {
|
|
137
|
+
throw new Error(`Invalid query: unexpected token '${input[i]}' in ${query}`);
|
|
138
|
+
}
|
|
139
|
+
const ident = input.slice(start, i);
|
|
140
|
+
tokens.push(ident);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return tokens;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function applyQuery(data: unknown, query: string): unknown {
|
|
147
|
+
const tokens = parseQuery(query);
|
|
148
|
+
let current: unknown = data;
|
|
149
|
+
for (const token of tokens) {
|
|
150
|
+
if (current === null || current === undefined) return undefined;
|
|
151
|
+
if (typeof token === "number") {
|
|
152
|
+
if (!Array.isArray(current)) return undefined;
|
|
153
|
+
current = current[token];
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (typeof current !== "object") return undefined;
|
|
157
|
+
const record = current as Record<string, unknown>;
|
|
158
|
+
current = record[token];
|
|
159
|
+
}
|
|
160
|
+
return current;
|
|
161
|
+
}
|
|
162
|
+
|
|
86
163
|
/** List available output IDs in artifacts directory */
|
|
87
164
|
function listAvailableOutputs(artifactsDir: string): string[] {
|
|
88
165
|
try {
|
|
@@ -128,7 +205,13 @@ export function createOutputTool(session: ToolSession): AgentTool<typeof outputS
|
|
|
128
205
|
parameters: outputSchema,
|
|
129
206
|
execute: async (
|
|
130
207
|
_toolCallId: string,
|
|
131
|
-
params: {
|
|
208
|
+
params: {
|
|
209
|
+
ids: string[];
|
|
210
|
+
format?: "raw" | "json" | "stripped";
|
|
211
|
+
query?: string;
|
|
212
|
+
offset?: number;
|
|
213
|
+
limit?: number;
|
|
214
|
+
},
|
|
132
215
|
): Promise<{ content: TextContent[]; details: OutputToolDetails }> => {
|
|
133
216
|
const sessionFile = session.getSessionFile();
|
|
134
217
|
|
|
@@ -150,7 +233,15 @@ export function createOutputTool(session: ToolSession): AgentTool<typeof outputS
|
|
|
150
233
|
const outputs: OutputEntry[] = [];
|
|
151
234
|
const notFound: string[] = [];
|
|
152
235
|
const outputContentById = new Map<string, string>();
|
|
153
|
-
const
|
|
236
|
+
const query = params.query?.trim();
|
|
237
|
+
const wantsQuery = query !== undefined && query.length > 0;
|
|
238
|
+
const format = params.format ?? (wantsQuery ? "json" : "raw");
|
|
239
|
+
|
|
240
|
+
if (wantsQuery && (params.offset !== undefined || params.limit !== undefined)) {
|
|
241
|
+
throw new Error("query cannot be combined with offset/limit");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const queryResults: Array<{ id: string; value: unknown }> = [];
|
|
154
245
|
|
|
155
246
|
for (const id of params.ids) {
|
|
156
247
|
const outputPath = path.join(artifactsDir, `${id}.out.md`);
|
|
@@ -168,7 +259,22 @@ export function createOutputTool(session: ToolSession): AgentTool<typeof outputS
|
|
|
168
259
|
let selectedContent = rawContent;
|
|
169
260
|
let range: OutputRange | undefined;
|
|
170
261
|
|
|
171
|
-
if (
|
|
262
|
+
if (wantsQuery && query) {
|
|
263
|
+
let jsonValue: unknown;
|
|
264
|
+
try {
|
|
265
|
+
jsonValue = JSON.parse(rawContent);
|
|
266
|
+
} catch (err) {
|
|
267
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
268
|
+
throw new Error(`Output ${id} is not valid JSON: ${message}`);
|
|
269
|
+
}
|
|
270
|
+
const value = applyQuery(jsonValue, query);
|
|
271
|
+
queryResults.push({ id, value });
|
|
272
|
+
try {
|
|
273
|
+
selectedContent = JSON.stringify(value, null, 2) ?? "null";
|
|
274
|
+
} catch {
|
|
275
|
+
selectedContent = String(value);
|
|
276
|
+
}
|
|
277
|
+
} else if (params.offset !== undefined || params.limit !== undefined) {
|
|
172
278
|
const startLine = Math.max(1, params.offset ?? 1);
|
|
173
279
|
if (startLine > totalLines) {
|
|
174
280
|
throw new Error(
|
|
@@ -186,11 +292,12 @@ export function createOutputTool(session: ToolSession): AgentTool<typeof outputS
|
|
|
186
292
|
outputs.push({
|
|
187
293
|
id,
|
|
188
294
|
path: outputPath,
|
|
189
|
-
lineCount: totalLines,
|
|
190
|
-
charCount: totalChars,
|
|
295
|
+
lineCount: wantsQuery ? selectedContent.split("\n").length : totalLines,
|
|
296
|
+
charCount: wantsQuery ? selectedContent.length : totalChars,
|
|
191
297
|
provenance: parseOutputProvenance(id),
|
|
192
298
|
previewLines: extractPreviewLines(selectedContent, 4),
|
|
193
299
|
range,
|
|
300
|
+
query: query,
|
|
194
301
|
});
|
|
195
302
|
}
|
|
196
303
|
|
|
@@ -212,15 +319,17 @@ export function createOutputTool(session: ToolSession): AgentTool<typeof outputS
|
|
|
212
319
|
let contentText: string;
|
|
213
320
|
|
|
214
321
|
if (format === "json") {
|
|
215
|
-
const jsonData =
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
322
|
+
const jsonData = wantsQuery
|
|
323
|
+
? queryResults
|
|
324
|
+
: outputs.map((o) => ({
|
|
325
|
+
id: o.id,
|
|
326
|
+
lineCount: o.lineCount,
|
|
327
|
+
charCount: o.charCount,
|
|
328
|
+
provenance: o.provenance,
|
|
329
|
+
previewLines: o.previewLines,
|
|
330
|
+
range: o.range,
|
|
331
|
+
content: outputContentById.get(o.id) ?? "",
|
|
332
|
+
}));
|
|
224
333
|
contentText = JSON.stringify(jsonData, null, 2);
|
|
225
334
|
} else {
|
|
226
335
|
// raw or stripped
|
|
@@ -257,6 +366,7 @@ export function createOutputTool(session: ToolSession): AgentTool<typeof outputS
|
|
|
257
366
|
interface OutputRenderArgs {
|
|
258
367
|
ids: string[];
|
|
259
368
|
format?: "raw" | "json" | "stripped";
|
|
369
|
+
query?: string;
|
|
260
370
|
offset?: number;
|
|
261
371
|
limit?: number;
|
|
262
372
|
}
|
|
@@ -285,6 +395,7 @@ export const outputToolRenderer = {
|
|
|
285
395
|
|
|
286
396
|
const meta: string[] = [];
|
|
287
397
|
if (args.format && args.format !== "raw") meta.push(`format:${args.format}`);
|
|
398
|
+
if (args.query) meta.push(`query:${args.query}`);
|
|
288
399
|
if (args.offset !== undefined) meta.push(`offset:${args.offset}`);
|
|
289
400
|
if (args.limit !== undefined) meta.push(`limit:${args.limit}`);
|
|
290
401
|
text += formatMeta(meta, uiTheme);
|
|
@@ -38,14 +38,12 @@ export function ensureArtifactsDir(dir: string): void {
|
|
|
38
38
|
*/
|
|
39
39
|
export function getArtifactPaths(
|
|
40
40
|
dir: string,
|
|
41
|
-
|
|
42
|
-
index: number,
|
|
41
|
+
taskId: string,
|
|
43
42
|
): { inputPath: string; outputPath: string; jsonlPath: string } {
|
|
44
|
-
const base = `${agentName}_${index}`;
|
|
45
43
|
return {
|
|
46
|
-
inputPath: path.join(dir, `${
|
|
47
|
-
outputPath: path.join(dir, `${
|
|
48
|
-
jsonlPath: path.join(dir, `${
|
|
44
|
+
inputPath: path.join(dir, `${taskId}.in.md`),
|
|
45
|
+
outputPath: path.join(dir, `${taskId}.out.md`),
|
|
46
|
+
jsonlPath: path.join(dir, `${taskId}.jsonl`),
|
|
49
47
|
};
|
|
50
48
|
}
|
|
51
49
|
|
|
@@ -54,15 +52,14 @@ export function getArtifactPaths(
|
|
|
54
52
|
*/
|
|
55
53
|
export async function writeArtifacts(
|
|
56
54
|
dir: string,
|
|
57
|
-
|
|
58
|
-
index: number,
|
|
55
|
+
taskId: string,
|
|
59
56
|
input: string,
|
|
60
57
|
output: string,
|
|
61
58
|
jsonlEvents?: string[],
|
|
62
59
|
): Promise<{ inputPath: string; outputPath: string; jsonlPath?: string }> {
|
|
63
60
|
ensureArtifactsDir(dir);
|
|
64
61
|
|
|
65
|
-
const paths = getArtifactPaths(dir,
|
|
62
|
+
const paths = getArtifactPaths(dir, taskId);
|
|
66
63
|
|
|
67
64
|
// Write input
|
|
68
65
|
await fs.promises.writeFile(paths.inputPath, input, "utf-8");
|
|
@@ -28,8 +28,10 @@ export interface ExecutorOptions {
|
|
|
28
28
|
task: string;
|
|
29
29
|
description?: string;
|
|
30
30
|
index: number;
|
|
31
|
+
taskId: string;
|
|
31
32
|
context?: string;
|
|
32
33
|
modelOverride?: string;
|
|
34
|
+
outputSchema?: unknown;
|
|
33
35
|
signal?: AbortSignal;
|
|
34
36
|
onProgress?: (progress: AgentProgress) => void;
|
|
35
37
|
sessionFile?: string | null;
|
|
@@ -130,12 +132,13 @@ function getUsageTokens(usage: unknown): number {
|
|
|
130
132
|
* Run a single agent in a worker.
|
|
131
133
|
*/
|
|
132
134
|
export async function runSubprocess(options: ExecutorOptions): Promise<SingleResult> {
|
|
133
|
-
const { cwd, agent, task, index, context, modelOverride, signal, onProgress } = options;
|
|
135
|
+
const { cwd, agent, task, index, taskId, context, modelOverride, outputSchema, signal, onProgress } = options;
|
|
134
136
|
const startTime = Date.now();
|
|
135
137
|
|
|
136
138
|
// Initialize progress
|
|
137
139
|
const progress: AgentProgress = {
|
|
138
140
|
index,
|
|
141
|
+
taskId,
|
|
139
142
|
agent: agent.name,
|
|
140
143
|
agentSource: agent.source,
|
|
141
144
|
status: "running",
|
|
@@ -153,6 +156,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
153
156
|
if (signal?.aborted) {
|
|
154
157
|
return {
|
|
155
158
|
index,
|
|
159
|
+
taskId,
|
|
156
160
|
agent: agent.name,
|
|
157
161
|
agentSource: agent.source,
|
|
158
162
|
task,
|
|
@@ -177,7 +181,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
177
181
|
|
|
178
182
|
if (options.artifactsDir) {
|
|
179
183
|
ensureArtifactsDir(options.artifactsDir);
|
|
180
|
-
artifactPaths = getArtifactPaths(options.artifactsDir,
|
|
184
|
+
artifactPaths = getArtifactPaths(options.artifactsDir, taskId);
|
|
181
185
|
subtaskSessionFile = artifactPaths.jsonlPath;
|
|
182
186
|
|
|
183
187
|
// Write input file immediately (real-time visibility)
|
|
@@ -451,6 +455,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
451
455
|
systemPrompt: agent.systemPrompt,
|
|
452
456
|
model: resolvedModel,
|
|
453
457
|
toolNames,
|
|
458
|
+
outputSchema,
|
|
454
459
|
sessionFile,
|
|
455
460
|
spawnsEnv,
|
|
456
461
|
},
|
|
@@ -497,13 +502,46 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
497
502
|
}
|
|
498
503
|
worker.terminate();
|
|
499
504
|
|
|
500
|
-
|
|
505
|
+
let exitCode = done.exitCode;
|
|
501
506
|
if (done.error) {
|
|
502
507
|
stderr = done.error;
|
|
503
508
|
}
|
|
504
509
|
|
|
505
510
|
// Use final output if available, otherwise accumulated output
|
|
506
|
-
|
|
511
|
+
let rawOutput = finalOutput || output;
|
|
512
|
+
let abortedViaComplete = false;
|
|
513
|
+
const completeItems = progress.extractedToolData?.complete as
|
|
514
|
+
| Array<{ data?: unknown; status?: "success" | "aborted"; error?: string }>
|
|
515
|
+
| undefined;
|
|
516
|
+
const hasComplete = Array.isArray(completeItems) && completeItems.length > 0;
|
|
517
|
+
if (hasComplete) {
|
|
518
|
+
const lastComplete = completeItems[completeItems.length - 1];
|
|
519
|
+
if (lastComplete?.status === "aborted") {
|
|
520
|
+
// Agent explicitly aborted via complete tool - clean exit with error info
|
|
521
|
+
abortedViaComplete = true;
|
|
522
|
+
exitCode = 0;
|
|
523
|
+
stderr = lastComplete.error || "Subagent aborted task";
|
|
524
|
+
try {
|
|
525
|
+
rawOutput = JSON.stringify({ aborted: true, error: lastComplete.error }, null, 2);
|
|
526
|
+
} catch {
|
|
527
|
+
rawOutput = `{"aborted":true,"error":"${lastComplete.error || "Unknown error"}"}`;
|
|
528
|
+
}
|
|
529
|
+
} else {
|
|
530
|
+
// Normal successful completion
|
|
531
|
+
const completeData = lastComplete?.data ?? null;
|
|
532
|
+
try {
|
|
533
|
+
rawOutput = JSON.stringify(completeData, null, 2) ?? "null";
|
|
534
|
+
} catch (err) {
|
|
535
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
536
|
+
rawOutput = `{"error":"Failed to serialize complete data: ${errorMessage}"}`;
|
|
537
|
+
}
|
|
538
|
+
exitCode = 0;
|
|
539
|
+
stderr = "";
|
|
540
|
+
}
|
|
541
|
+
} else {
|
|
542
|
+
const warning = "SYSTEM WARNING: Subagent exited without calling complete tool after 3 reminders.";
|
|
543
|
+
rawOutput = rawOutput ? `${warning}\n\n${rawOutput}` : warning;
|
|
544
|
+
}
|
|
507
545
|
const { text: truncatedOutput, truncated } = truncateOutput(rawOutput);
|
|
508
546
|
|
|
509
547
|
// Write output artifact (input and jsonl already written in real-time)
|
|
@@ -522,12 +560,13 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
522
560
|
}
|
|
523
561
|
|
|
524
562
|
// Update final progress
|
|
525
|
-
const wasAborted = done.aborted || signal?.aborted || false;
|
|
563
|
+
const wasAborted = abortedViaComplete || (!hasComplete && (done.aborted || signal?.aborted || false));
|
|
526
564
|
progress.status = wasAborted ? "aborted" : exitCode === 0 ? "completed" : "failed";
|
|
527
565
|
emitProgress();
|
|
528
566
|
|
|
529
567
|
return {
|
|
530
568
|
index,
|
|
569
|
+
taskId,
|
|
531
570
|
agent: agent.name,
|
|
532
571
|
agentSource: agent.source,
|
|
533
572
|
task,
|
|
@@ -21,6 +21,7 @@ import { formatDuration } from "../render-utils";
|
|
|
21
21
|
import { cleanupTempDir, createTempArtifactsDir, getArtifactsDir } from "./artifacts";
|
|
22
22
|
import { discoverAgents, getAgent } from "./discovery";
|
|
23
23
|
import { runSubprocess } from "./executor";
|
|
24
|
+
import { generateTaskName } from "./name-generator";
|
|
24
25
|
import { mapWithConcurrencyLimit } from "./parallel";
|
|
25
26
|
import { renderCall, renderResult } from "./render";
|
|
26
27
|
import {
|
|
@@ -135,6 +136,7 @@ export async function createTaskTool(
|
|
|
135
136
|
const startTime = Date.now();
|
|
136
137
|
const { agents, projectAgentsDir } = await discoverAgents(session.cwd);
|
|
137
138
|
const context = params.context;
|
|
139
|
+
const outputSchema = params.output_schema;
|
|
138
140
|
|
|
139
141
|
// Handle empty or missing tasks
|
|
140
142
|
if (!params.tasks || params.tasks.length === 0) {
|
|
@@ -259,34 +261,37 @@ export async function createTaskTool(
|
|
|
259
261
|
}
|
|
260
262
|
}
|
|
261
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
|
+
|
|
262
273
|
// Initialize progress for all tasks
|
|
263
|
-
for (let i = 0; i <
|
|
264
|
-
const
|
|
274
|
+
for (let i = 0; i < tasksWithContext.length; i++) {
|
|
275
|
+
const t = tasksWithContext[i];
|
|
276
|
+
const agentCfg = getAgent(agents, t.agent);
|
|
265
277
|
progressMap.set(i, {
|
|
266
278
|
index: i,
|
|
267
|
-
|
|
279
|
+
taskId: t.taskId,
|
|
280
|
+
agent: t.agent,
|
|
268
281
|
agentSource: agentCfg?.source ?? "user",
|
|
269
282
|
status: "pending",
|
|
270
|
-
task:
|
|
283
|
+
task: t.task,
|
|
271
284
|
recentTools: [],
|
|
272
285
|
recentOutput: [],
|
|
273
286
|
toolCount: 0,
|
|
274
287
|
tokens: 0,
|
|
275
288
|
durationMs: 0,
|
|
276
|
-
modelOverride:
|
|
277
|
-
description:
|
|
289
|
+
modelOverride: t.model,
|
|
290
|
+
description: t.description,
|
|
278
291
|
});
|
|
279
292
|
}
|
|
280
293
|
emitProgress();
|
|
281
294
|
|
|
282
|
-
// Build full prompts with context prepended
|
|
283
|
-
const tasksWithContext = tasks.map((t) => ({
|
|
284
|
-
agent: t.agent,
|
|
285
|
-
task: context ? `${context}\n\n${t.task}` : t.task,
|
|
286
|
-
model: t.model,
|
|
287
|
-
description: t.description,
|
|
288
|
-
}));
|
|
289
|
-
|
|
290
295
|
// Execute in parallel with concurrency limit
|
|
291
296
|
const results = await mapWithConcurrencyLimit(tasksWithContext, MAX_CONCURRENCY, async (task, index) => {
|
|
292
297
|
const agent = getAgent(agents, task.agent)!;
|
|
@@ -296,8 +301,10 @@ export async function createTaskTool(
|
|
|
296
301
|
task: task.task,
|
|
297
302
|
description: task.description,
|
|
298
303
|
index,
|
|
304
|
+
taskId: task.taskId,
|
|
299
305
|
context: undefined, // Already prepended above
|
|
300
306
|
modelOverride: task.model,
|
|
307
|
+
outputSchema,
|
|
301
308
|
sessionFile,
|
|
302
309
|
persistArtifacts: !!artifactsDir,
|
|
303
310
|
artifactsDir: effectiveArtifactsDir,
|
|
@@ -336,19 +343,17 @@ export async function createTaskTool(
|
|
|
336
343
|
const status = r.exitCode === 0 ? "completed" : `failed (exit ${r.exitCode})`;
|
|
337
344
|
const output = r.output.trim() || r.stderr.trim() || "(no output)";
|
|
338
345
|
const preview = output.split("\n").slice(0, 5).join("\n");
|
|
339
|
-
// Include output metadata and ID
|
|
340
|
-
const outputId = `${r.agent}_${r.index}`;
|
|
341
346
|
const meta = r.outputMeta
|
|
342
347
|
? ` [${r.outputMeta.lineCount} lines, ${formatBytes(r.outputMeta.charCount)}]`
|
|
343
348
|
: "";
|
|
344
|
-
return `[${r.agent}] ${status}${meta} ${
|
|
349
|
+
return `[${r.agent}] ${status}${meta} ${r.taskId}\n${preview}`;
|
|
345
350
|
});
|
|
346
351
|
|
|
347
352
|
const skippedNote =
|
|
348
353
|
skippedSelfRecursion > 0
|
|
349
354
|
? ` (${skippedSelfRecursion} ${blockedAgent} task${skippedSelfRecursion > 1 ? "s" : ""} skipped - self-recursion blocked)`
|
|
350
355
|
: "";
|
|
351
|
-
const outputIds = results.map((r) =>
|
|
356
|
+
const outputIds = results.map((r) => r.taskId);
|
|
352
357
|
const outputHint =
|
|
353
358
|
outputIds.length > 0 ? `\n\nUse output tool for full logs: output ids ${outputIds.join(", ")}` : "";
|
|
354
359
|
const summary = `${successCount}/${results.length} succeeded${skippedNote} [${formatDuration(
|