oira666_pi-subagent 0.2.28 → 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 +108 -18
- package/package.json +1 -1
- package/tree.ts +7 -1
- package/types.ts +29 -24
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,6 +42,7 @@ import {
|
|
|
42
42
|
type DelegationMode,
|
|
43
43
|
type SingleResult,
|
|
44
44
|
type SubagentDetails,
|
|
45
|
+
type SubagentUsageSummary,
|
|
45
46
|
DEFAULT_DELEGATION_MODE,
|
|
46
47
|
buildLiveSubagentDetails,
|
|
47
48
|
buildSubagentDetails,
|
|
@@ -54,6 +55,7 @@ import {
|
|
|
54
55
|
emptyUsageSummary,
|
|
55
56
|
addUsageSummary,
|
|
56
57
|
usageSummaryToUsageStats,
|
|
58
|
+
usageSummaryFromUsage,
|
|
57
59
|
} from "./types.js";
|
|
58
60
|
import { formatCombinedUsageStatusLine } from "./tree.js";
|
|
59
61
|
|
|
@@ -314,11 +316,60 @@ function formatAgentNames(agents: AgentConfig[]): string {
|
|
|
314
316
|
return agents.map((a) => `${a.name} (${a.source})`).join(", ") || "none";
|
|
315
317
|
}
|
|
316
318
|
|
|
317
|
-
function
|
|
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 {
|
|
318
369
|
const entries = typeof ctx?.sessionManager?.getEntries === "function" || typeof ctx?.sessionManager?.getBranch === "function"
|
|
319
370
|
? branchEntries(ctx)
|
|
320
371
|
: [];
|
|
321
|
-
if (entries.length === 0) return undefined;
|
|
372
|
+
if (entries.length === 0 && liveSummaries.length === 0) return undefined;
|
|
322
373
|
|
|
323
374
|
const parentUsage = emptyUsage();
|
|
324
375
|
const subagents = emptyUsageSummary();
|
|
@@ -333,14 +384,20 @@ function collectCombinedUsageStatusLine(ctx: any): string | undefined {
|
|
|
333
384
|
parentUsage.output += usage.output || 0;
|
|
334
385
|
parentUsage.cacheRead += usage.cacheRead || 0;
|
|
335
386
|
parentUsage.cacheWrite += usage.cacheWrite || 0;
|
|
336
|
-
parentUsage.cost += usage.cost?.total || 0;
|
|
387
|
+
parentUsage.cost += typeof usage.cost === "number" ? usage.cost : usage.cost?.total || 0;
|
|
337
388
|
parentUsage.turns += 1;
|
|
338
389
|
}
|
|
339
390
|
}
|
|
340
391
|
if (msg.role === "toolResult" && msg.toolName === "subagent" && isSubagentDetails(msg.details)) {
|
|
341
|
-
addUsageSummary(
|
|
392
|
+
addUsageSummary(
|
|
393
|
+
subagents,
|
|
394
|
+
msg.details.usageSummary ?? usageSummaryFromUsage(msg.details.aggregatedUsage),
|
|
395
|
+
);
|
|
342
396
|
}
|
|
343
397
|
}
|
|
398
|
+
for (const liveSummary of liveSummaries) {
|
|
399
|
+
addUsageSummary(subagents, liveSummary);
|
|
400
|
+
}
|
|
344
401
|
const subagentUsage = usageSummaryToUsageStats(subagents) ?? emptyUsage();
|
|
345
402
|
addUsage(parentUsage, subagentUsage);
|
|
346
403
|
return formatCombinedUsageStatusLine(parentUsage, subagents.subagentCount);
|
|
@@ -687,6 +744,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
687
744
|
let modelToRestoreAfterResume: any | undefined;
|
|
688
745
|
const approvedProjectAgentDirsForSession = new Set<string>();
|
|
689
746
|
const activeSubagents = new Map<number, { agent: string; task: string; handle: RunningSubagentHandle }>();
|
|
747
|
+
const activeSubagentUsageSummaries = new Map<string, SubagentUsageSummary>();
|
|
690
748
|
const latestBroadcastTargets = {
|
|
691
749
|
all: [] as BroadcastTarget[],
|
|
692
750
|
youngest: [] as BroadcastTarget[],
|
|
@@ -858,9 +916,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
858
916
|
}
|
|
859
917
|
|
|
860
918
|
function extractPendingSubagentTaskCounts(result: SingleResult): number[] {
|
|
861
|
-
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));
|
|
862
921
|
const counts: number[] = [];
|
|
863
|
-
for (const message of
|
|
922
|
+
for (const message of messages) {
|
|
864
923
|
if (message?.role !== "assistant" || !Array.isArray(message.content)) continue;
|
|
865
924
|
for (const part of message.content) {
|
|
866
925
|
if (part?.type !== "toolCall" || part?.name !== "subagent") continue;
|
|
@@ -1049,23 +1108,36 @@ export default function (pi: ExtensionAPI) {
|
|
|
1049
1108
|
}
|
|
1050
1109
|
}
|
|
1051
1110
|
|
|
1052
|
-
//
|
|
1053
|
-
//
|
|
1054
|
-
//
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
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);
|
|
1061
1134
|
}
|
|
1062
|
-
} catch (err) {
|
|
1063
|
-
console.error("[pi-subagent] Failed to register combined subagent status line:", err);
|
|
1064
1135
|
}
|
|
1065
1136
|
|
|
1066
1137
|
// Auto-discover agents on session start
|
|
1067
1138
|
pi.on("session_start", async (event, ctx) => {
|
|
1068
1139
|
latestSessionCtx = ctx;
|
|
1140
|
+
updateCombinedUsageStatus(ctx);
|
|
1069
1141
|
try {
|
|
1070
1142
|
// Always repair sessions left on the synthetic resume model, even in
|
|
1071
1143
|
// nested subagents that can no longer delegate. Those leaf processes
|
|
@@ -1175,10 +1247,26 @@ export default function (pi: ExtensionAPI) {
|
|
|
1175
1247
|
}
|
|
1176
1248
|
});
|
|
1177
1249
|
|
|
1178
|
-
pi.on("agent_end", async () => {
|
|
1250
|
+
pi.on("agent_end", async (_event, ctx) => {
|
|
1251
|
+
updateCombinedUsageStatus(ctx);
|
|
1179
1252
|
await restoreModelAfterResumeFailure();
|
|
1180
1253
|
});
|
|
1181
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
|
+
|
|
1182
1270
|
pi.on("resources_discover", (_event, ctx) => {
|
|
1183
1271
|
const prompt = pendingInteractiveResumePrompt;
|
|
1184
1272
|
if (!prompt) return;
|
|
@@ -1308,7 +1396,9 @@ calls one after another. Do NOT put dependent tasks in the same array.
|
|
|
1308
1396
|
nextActiveSubagentId += tasks.length;
|
|
1309
1397
|
const trackedOnUpdate = (partial: any) => {
|
|
1310
1398
|
if (isSubagentDetails(partial?.details)) {
|
|
1399
|
+
activeSubagentUsageSummaries.set(toolCallId, collectLiveUsageSummary(partial.details));
|
|
1311
1400
|
updateLatestBroadcastTargets(partial.details, topLevelBaseId);
|
|
1401
|
+
updateCombinedUsageStatus(ctx);
|
|
1312
1402
|
emitNestedProgressToParent(toolCallId, partial.details);
|
|
1313
1403
|
}
|
|
1314
1404
|
onUpdate?.(partial);
|
package/package.json
CHANGED
package/tree.ts
CHANGED
|
@@ -76,8 +76,14 @@ export function formatCombinedUsageStatusLine(
|
|
|
76
76
|
const input = usage.input ?? 0;
|
|
77
77
|
const output = usage.output ?? 0;
|
|
78
78
|
const cacheRead = usage.cacheRead ?? 0;
|
|
79
|
+
const cacheWrite = usage.cacheWrite ?? 0;
|
|
80
|
+
const turns = usage.turns ?? 0;
|
|
79
81
|
const cost = usage.cost ?? 0;
|
|
80
|
-
|
|
82
|
+
const total = input + output + cacheRead + cacheWrite;
|
|
83
|
+
const cache = cacheRead || cacheWrite
|
|
84
|
+
? ` R${formatTokens(cacheRead)} W${formatTokens(cacheWrite)}`
|
|
85
|
+
: "";
|
|
86
|
+
return `Σ ↑${formatTokens(input)} ↓${formatTokens(output)}${cache} T${formatTokens(total)} • ${turns} turn${turns === 1 ? "" : "s"} • $${cost.toFixed(4)} • ${subagentCount} subagent${subagentCount === 1 ? "" : "s"}`;
|
|
81
87
|
}
|
|
82
88
|
|
|
83
89
|
export function formatUsage(usage: Partial<UsageStats>, model?: string): string {
|
package/types.ts
CHANGED
|
@@ -173,12 +173,16 @@ export function mergeToolCalls(target: ToolCallCounts, source: ToolCallCounts):
|
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
+
function asMessageArray(messages: unknown): Message[] {
|
|
177
|
+
return Array.isArray(messages) ? (messages as Message[]) : [];
|
|
178
|
+
}
|
|
179
|
+
|
|
176
180
|
/** Extract all tool calls made by assistant turns in a message list */
|
|
177
|
-
export function extractToolCalls(messages:
|
|
181
|
+
export function extractToolCalls(messages: unknown): ToolCallCounts {
|
|
178
182
|
const counts: ToolCallCounts = {};
|
|
179
|
-
for (const msg of messages) {
|
|
183
|
+
for (const msg of asMessageArray(messages)) {
|
|
180
184
|
if (msg.role !== "assistant") continue;
|
|
181
|
-
for (const part of (msg.content as any
|
|
185
|
+
for (const part of (Array.isArray((msg as any).content) ? (msg as any).content : [])) {
|
|
182
186
|
if ((part as any)?.type !== "toolCall") continue;
|
|
183
187
|
const name: string = typeof (part as any).name === "string" ? (part as any).name : "unknown";
|
|
184
188
|
counts[name] = (counts[name] ?? 0) + 1;
|
|
@@ -393,13 +397,14 @@ export function isSubagentDetails(value: unknown): value is SubagentDetails {
|
|
|
393
397
|
}
|
|
394
398
|
|
|
395
399
|
/** Extract the last assistant text from a message history. */
|
|
396
|
-
export function getFinalOutput(messages:
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
400
|
+
export function getFinalOutput(messages: unknown, fallback?: string): string {
|
|
401
|
+
const history = asMessageArray(messages);
|
|
402
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
403
|
+
const msg = history[i];
|
|
404
|
+
if (msg.role === "assistant" && Array.isArray((msg as any).content)) {
|
|
405
|
+
for (let j = (msg as any).content.length - 1; j >= 0; j--) {
|
|
406
|
+
const part = (msg as any).content[j];
|
|
407
|
+
if (part?.type === "text") return part.text;
|
|
403
408
|
}
|
|
404
409
|
}
|
|
405
410
|
}
|
|
@@ -407,14 +412,14 @@ export function getFinalOutput(messages: Message[], fallback?: string): string {
|
|
|
407
412
|
}
|
|
408
413
|
|
|
409
414
|
/** Extract all display-worthy items from a message history. */
|
|
410
|
-
export function getDisplayItems(messages:
|
|
415
|
+
export function getDisplayItems(messages: unknown): DisplayItem[] {
|
|
411
416
|
const items: DisplayItem[] = [];
|
|
412
|
-
for (const msg of messages) {
|
|
413
|
-
if (msg.role === "assistant") {
|
|
414
|
-
for (const part of msg.content) {
|
|
415
|
-
if (part
|
|
417
|
+
for (const msg of asMessageArray(messages)) {
|
|
418
|
+
if (msg.role === "assistant" && Array.isArray((msg as any).content)) {
|
|
419
|
+
for (const part of (msg as any).content) {
|
|
420
|
+
if (part?.type === "text") {
|
|
416
421
|
items.push({ type: "text", text: part.text });
|
|
417
|
-
} else if (part
|
|
422
|
+
} else if (part?.type === "toolCall") {
|
|
418
423
|
items.push({ type: "toolCall", name: part.name, args: part.arguments });
|
|
419
424
|
}
|
|
420
425
|
}
|
|
@@ -424,16 +429,16 @@ export function getDisplayItems(messages: Message[]): DisplayItem[] {
|
|
|
424
429
|
}
|
|
425
430
|
|
|
426
431
|
/** Extract nested subagent tool results from a message history. */
|
|
427
|
-
export function getNestedSubagentResults(messages:
|
|
432
|
+
export function getNestedSubagentResults(messages: unknown): NestedSubagentResult[] {
|
|
428
433
|
const results: NestedSubagentResult[] = [];
|
|
429
|
-
for (const msg of messages) {
|
|
430
|
-
if (msg
|
|
431
|
-
if (msg.toolName !== "subagent") continue;
|
|
432
|
-
if (!isSubagentDetails(msg.details)) continue;
|
|
434
|
+
for (const msg of asMessageArray(messages)) {
|
|
435
|
+
if ((msg as any)?.role !== "toolResult") continue;
|
|
436
|
+
if ((msg as any).toolName !== "subagent") continue;
|
|
437
|
+
if (!isSubagentDetails((msg as any).details)) continue;
|
|
433
438
|
results.push({
|
|
434
|
-
details: msg.details,
|
|
435
|
-
isError: msg.isError,
|
|
436
|
-
toolCallId: msg.toolCallId,
|
|
439
|
+
details: (msg as any).details,
|
|
440
|
+
isError: Boolean((msg as any).isError),
|
|
441
|
+
toolCallId: typeof (msg as any).toolCallId === "string" ? (msg as any).toolCallId : "",
|
|
437
442
|
});
|
|
438
443
|
}
|
|
439
444
|
return results;
|