oira666_pi-subagent 0.2.27 → 0.2.29
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/agents/team-lead.md +1 -1
- package/index.ts +154 -7
- package/package.json +1 -1
- package/runner.ts +1202 -1155
- package/tree.ts +73 -4
- package/types.ts +479 -319
package/agents/team-lead.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: team-lead
|
|
3
|
-
description:
|
|
3
|
+
description: A Team of agents with different specializations, that can take any complex task, split it to parts and implement: arhitecture, generation, review or any other kind of task.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
You are an experienced team lead, focused on tasks management. You don't do any work yourself. You delegate.
|
package/index.ts
CHANGED
|
@@ -42,13 +42,22 @@ import {
|
|
|
42
42
|
type DelegationMode,
|
|
43
43
|
type SingleResult,
|
|
44
44
|
type SubagentDetails,
|
|
45
|
+
type SubagentUsageSummary,
|
|
45
46
|
DEFAULT_DELEGATION_MODE,
|
|
47
|
+
buildLiveSubagentDetails,
|
|
46
48
|
buildSubagentDetails,
|
|
47
49
|
getFinalOutput,
|
|
48
50
|
getNestedSubagentResults,
|
|
49
51
|
isResultError,
|
|
50
52
|
isSubagentDetails,
|
|
53
|
+
emptyUsage,
|
|
54
|
+
addUsage,
|
|
55
|
+
emptyUsageSummary,
|
|
56
|
+
addUsageSummary,
|
|
57
|
+
usageSummaryToUsageStats,
|
|
58
|
+
usageSummaryFromUsage,
|
|
51
59
|
} from "./types.js";
|
|
60
|
+
import { formatCombinedUsageStatusLine } from "./tree.js";
|
|
52
61
|
|
|
53
62
|
// ---------------------------------------------------------------------------
|
|
54
63
|
// Limits
|
|
@@ -294,15 +303,106 @@ function makeDetailsFactory(
|
|
|
294
303
|
projectAgentsDir: string | null,
|
|
295
304
|
delegationMode: DelegationMode,
|
|
296
305
|
) {
|
|
297
|
-
return (mode: "single" | "parallel") =>
|
|
298
|
-
(results: SingleResult[]): SubagentDetails =>
|
|
306
|
+
return (mode: "single" | "parallel") => {
|
|
307
|
+
const makeDetails = (results: SingleResult[]): SubagentDetails =>
|
|
299
308
|
buildSubagentDetails(mode, delegationMode, projectAgentsDir, results);
|
|
309
|
+
makeDetails.live = (results: SingleResult[]): SubagentDetails =>
|
|
310
|
+
buildLiveSubagentDetails(mode, delegationMode, projectAgentsDir, results);
|
|
311
|
+
return makeDetails;
|
|
312
|
+
};
|
|
300
313
|
}
|
|
301
314
|
|
|
302
315
|
function formatAgentNames(agents: AgentConfig[]): string {
|
|
303
316
|
return agents.map((a) => `${a.name} (${a.source})`).join(", ") || "none";
|
|
304
317
|
}
|
|
305
318
|
|
|
319
|
+
function liveDetailsSignature(details: SubagentDetails): string {
|
|
320
|
+
return JSON.stringify(details.results.map((result) => ({
|
|
321
|
+
agent: result.agent,
|
|
322
|
+
task: result.task ?? "",
|
|
323
|
+
})));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function collectLiveUsageSummary(details: SubagentDetails): SubagentUsageSummary {
|
|
327
|
+
const summary = emptyUsageSummary();
|
|
328
|
+
for (const result of details.results) {
|
|
329
|
+
addUsageSummary(summary, usageSummaryFromUsage(result.usage));
|
|
330
|
+
|
|
331
|
+
const completedNested = getNestedSubagentResults(result.messages ?? []);
|
|
332
|
+
const completedNestedIds = new Set<string>();
|
|
333
|
+
const completedNestedSignatureCounts = new Map<string, number>();
|
|
334
|
+
for (const nested of completedNested) {
|
|
335
|
+
if (nested.toolCallId) completedNestedIds.add(nested.toolCallId);
|
|
336
|
+
const signature = liveDetailsSignature(nested.details);
|
|
337
|
+
completedNestedSignatureCounts.set(
|
|
338
|
+
signature,
|
|
339
|
+
(completedNestedSignatureCounts.get(signature) ?? 0) + 1,
|
|
340
|
+
);
|
|
341
|
+
addUsageSummary(
|
|
342
|
+
summary,
|
|
343
|
+
nested.details.usageSummary ?? collectLiveUsageSummary(nested.details),
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
for (const [liveToolCallId, liveNested] of Object.entries(result.liveNestedSubagents ?? {})) {
|
|
348
|
+
if (!isSubagentDetails(liveNested)) continue;
|
|
349
|
+
// Prefer durable completed toolResult messages over matching live progress.
|
|
350
|
+
// Some Pi versions key live progress differently than final tool results,
|
|
351
|
+
// so also de-dupe by requested child agent/task signature as a multiset.
|
|
352
|
+
if (completedNestedIds.has(liveToolCallId)) continue;
|
|
353
|
+
const signature = liveDetailsSignature(liveNested);
|
|
354
|
+
const completedSignatureCount = completedNestedSignatureCounts.get(signature) ?? 0;
|
|
355
|
+
if (completedSignatureCount > 0) {
|
|
356
|
+
completedNestedSignatureCounts.set(signature, completedSignatureCount - 1);
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
addUsageSummary(summary, collectLiveUsageSummary(liveNested));
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return summary;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function collectCombinedUsageStatusLine(
|
|
366
|
+
ctx: any,
|
|
367
|
+
liveSummaries: SubagentUsageSummary[] = [],
|
|
368
|
+
): string | undefined {
|
|
369
|
+
const entries = typeof ctx?.sessionManager?.getEntries === "function" || typeof ctx?.sessionManager?.getBranch === "function"
|
|
370
|
+
? branchEntries(ctx)
|
|
371
|
+
: [];
|
|
372
|
+
if (entries.length === 0 && liveSummaries.length === 0) return undefined;
|
|
373
|
+
|
|
374
|
+
const parentUsage = emptyUsage();
|
|
375
|
+
const subagents = emptyUsageSummary();
|
|
376
|
+
for (const entry of entries as any[]) {
|
|
377
|
+
if (entry?.type !== "message") continue;
|
|
378
|
+
const msg = entry.message;
|
|
379
|
+
if (!msg) continue;
|
|
380
|
+
if (msg.role === "assistant" && msg.provider !== RESUME_PROVIDER) {
|
|
381
|
+
const usage = msg.usage;
|
|
382
|
+
if (usage) {
|
|
383
|
+
parentUsage.input += usage.input || 0;
|
|
384
|
+
parentUsage.output += usage.output || 0;
|
|
385
|
+
parentUsage.cacheRead += usage.cacheRead || 0;
|
|
386
|
+
parentUsage.cacheWrite += usage.cacheWrite || 0;
|
|
387
|
+
parentUsage.cost += typeof usage.cost === "number" ? usage.cost : usage.cost?.total || 0;
|
|
388
|
+
parentUsage.turns += 1;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (msg.role === "toolResult" && msg.toolName === "subagent" && isSubagentDetails(msg.details)) {
|
|
392
|
+
addUsageSummary(
|
|
393
|
+
subagents,
|
|
394
|
+
msg.details.usageSummary ?? usageSummaryFromUsage(msg.details.aggregatedUsage),
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
for (const liveSummary of liveSummaries) {
|
|
399
|
+
addUsageSummary(subagents, liveSummary);
|
|
400
|
+
}
|
|
401
|
+
const subagentUsage = usageSummaryToUsageStats(subagents) ?? emptyUsage();
|
|
402
|
+
addUsage(parentUsage, subagentUsage);
|
|
403
|
+
return formatCombinedUsageStatusLine(parentUsage, subagents.subagentCount);
|
|
404
|
+
}
|
|
405
|
+
|
|
306
406
|
function getCycleViolations(
|
|
307
407
|
requestedNames: Set<string>,
|
|
308
408
|
ancestorAgentStack: string[],
|
|
@@ -644,6 +744,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
644
744
|
let modelToRestoreAfterResume: any | undefined;
|
|
645
745
|
const approvedProjectAgentDirsForSession = new Set<string>();
|
|
646
746
|
const activeSubagents = new Map<number, { agent: string; task: string; handle: RunningSubagentHandle }>();
|
|
747
|
+
const activeSubagentUsageSummaries = new Map<string, SubagentUsageSummary>();
|
|
647
748
|
const latestBroadcastTargets = {
|
|
648
749
|
all: [] as BroadcastTarget[],
|
|
649
750
|
youngest: [] as BroadcastTarget[],
|
|
@@ -815,9 +916,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
815
916
|
}
|
|
816
917
|
|
|
817
918
|
function extractPendingSubagentTaskCounts(result: SingleResult): number[] {
|
|
818
|
-
const
|
|
919
|
+
const messages = Array.isArray((result as any).messages) ? (result as any).messages : [];
|
|
920
|
+
const completedToolCallIds = new Set(getNestedSubagentResults(messages).map((nested) => nested.toolCallId));
|
|
819
921
|
const counts: number[] = [];
|
|
820
|
-
for (const message of
|
|
922
|
+
for (const message of messages) {
|
|
821
923
|
if (message?.role !== "assistant" || !Array.isArray(message.content)) continue;
|
|
822
924
|
for (const part of message.content) {
|
|
823
925
|
if (part?.type !== "toolCall" || part?.name !== "subagent") continue;
|
|
@@ -1006,9 +1108,36 @@ export default function (pi: ExtensionAPI) {
|
|
|
1006
1108
|
}
|
|
1007
1109
|
}
|
|
1008
1110
|
|
|
1111
|
+
// Pi exposes footer/status-line extension text through ctx.ui.setStatus().
|
|
1112
|
+
// Keep a dedicated line updated with parent + active-branch subagent totals.
|
|
1113
|
+
// This is shown by the normal footer underneath Pi's built-in session stats.
|
|
1114
|
+
function formatFooterStatusText(ctx: any, text: string): string {
|
|
1115
|
+
return typeof ctx?.ui?.theme?.fg === "function"
|
|
1116
|
+
? ctx.ui.theme.fg("dim", text)
|
|
1117
|
+
: text;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
function updateCombinedUsageStatus(ctx?: any): void {
|
|
1121
|
+
const targetCtx = ctx ?? latestSessionCtx;
|
|
1122
|
+
if (!targetCtx?.ui || typeof targetCtx.ui.setStatus !== "function") return;
|
|
1123
|
+
try {
|
|
1124
|
+
const line = collectCombinedUsageStatusLine(
|
|
1125
|
+
targetCtx,
|
|
1126
|
+
Array.from(activeSubagentUsageSummaries.values()),
|
|
1127
|
+
);
|
|
1128
|
+
targetCtx.ui.setStatus(
|
|
1129
|
+
"subagent-usage",
|
|
1130
|
+
line ? formatFooterStatusText(targetCtx, `total ${line}`) : undefined,
|
|
1131
|
+
);
|
|
1132
|
+
} catch (err) {
|
|
1133
|
+
console.error("[pi-subagent] Failed to update combined subagent status line:", err);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1009
1137
|
// Auto-discover agents on session start
|
|
1010
1138
|
pi.on("session_start", async (event, ctx) => {
|
|
1011
1139
|
latestSessionCtx = ctx;
|
|
1140
|
+
updateCombinedUsageStatus(ctx);
|
|
1012
1141
|
try {
|
|
1013
1142
|
// Always repair sessions left on the synthetic resume model, even in
|
|
1014
1143
|
// nested subagents that can no longer delegate. Those leaf processes
|
|
@@ -1118,10 +1247,26 @@ export default function (pi: ExtensionAPI) {
|
|
|
1118
1247
|
}
|
|
1119
1248
|
});
|
|
1120
1249
|
|
|
1121
|
-
pi.on("agent_end", async () => {
|
|
1250
|
+
pi.on("agent_end", async (_event, ctx) => {
|
|
1251
|
+
updateCombinedUsageStatus(ctx);
|
|
1122
1252
|
await restoreModelAfterResumeFailure();
|
|
1123
1253
|
});
|
|
1124
1254
|
|
|
1255
|
+
pi.on("message_end", (_event, ctx) => {
|
|
1256
|
+
latestSessionCtx = ctx;
|
|
1257
|
+
updateCombinedUsageStatus(ctx);
|
|
1258
|
+
setTimeout(() => updateCombinedUsageStatus(ctx), 0);
|
|
1259
|
+
});
|
|
1260
|
+
|
|
1261
|
+
pi.on("tool_execution_end", (event, ctx) => {
|
|
1262
|
+
latestSessionCtx = ctx;
|
|
1263
|
+
if (event.toolName === "subagent") {
|
|
1264
|
+
activeSubagentUsageSummaries.delete(event.toolCallId);
|
|
1265
|
+
updateCombinedUsageStatus(ctx);
|
|
1266
|
+
setTimeout(() => updateCombinedUsageStatus(ctx), 0);
|
|
1267
|
+
}
|
|
1268
|
+
});
|
|
1269
|
+
|
|
1125
1270
|
pi.on("resources_discover", (_event, ctx) => {
|
|
1126
1271
|
const prompt = pendingInteractiveResumePrompt;
|
|
1127
1272
|
if (!prompt) return;
|
|
@@ -1251,7 +1396,9 @@ calls one after another. Do NOT put dependent tasks in the same array.
|
|
|
1251
1396
|
nextActiveSubagentId += tasks.length;
|
|
1252
1397
|
const trackedOnUpdate = (partial: any) => {
|
|
1253
1398
|
if (isSubagentDetails(partial?.details)) {
|
|
1399
|
+
activeSubagentUsageSummaries.set(toolCallId, collectLiveUsageSummary(partial.details));
|
|
1254
1400
|
updateLatestBroadcastTargets(partial.details, topLevelBaseId);
|
|
1401
|
+
updateCombinedUsageStatus(ctx);
|
|
1255
1402
|
emitNestedProgressToParent(toolCallId, partial.details);
|
|
1256
1403
|
}
|
|
1257
1404
|
onUpdate?.(partial);
|
|
@@ -1466,7 +1613,7 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
|
|
|
1466
1613
|
const errorMsg =
|
|
1467
1614
|
result.errorMessage ||
|
|
1468
1615
|
result.stderr ||
|
|
1469
|
-
getFinalOutput(result.messages) ||
|
|
1616
|
+
getFinalOutput(result.messages, result.finalOutput) ||
|
|
1470
1617
|
"(no output)";
|
|
1471
1618
|
return {
|
|
1472
1619
|
content: [
|
|
@@ -1483,7 +1630,7 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
|
|
|
1483
1630
|
content: [
|
|
1484
1631
|
{
|
|
1485
1632
|
type: "text" as const,
|
|
1486
|
-
text: getFinalOutput(result.messages) || "(no output)",
|
|
1633
|
+
text: getFinalOutput(result.messages, result.finalOutput) || "(no output)",
|
|
1487
1634
|
},
|
|
1488
1635
|
],
|
|
1489
1636
|
details: makeDetails("single")([result]),
|