pi-subagents 0.24.4 → 0.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 +8 -0
- package/README.md +16 -10
- package/package.json +1 -1
- package/prompts/review-loop.md +1 -1
- package/skills/pi-subagents/SKILL.md +46 -10
- package/src/extension/fanout-child.ts +170 -0
- package/src/extension/index.ts +6 -2
- package/src/intercom/result-intercom.ts +108 -0
- package/src/runs/background/async-execution.ts +101 -4
- package/src/runs/background/async-job-tracker.ts +41 -6
- package/src/runs/background/async-resume.ts +28 -15
- package/src/runs/background/async-status.ts +60 -30
- package/src/runs/background/result-watcher.ts +111 -54
- package/src/runs/background/run-id-resolver.ts +83 -0
- package/src/runs/background/run-status.ts +79 -3
- package/src/runs/background/stale-run-reconciler.ts +46 -1
- package/src/runs/background/subagent-runner.ts +48 -11
- package/src/runs/foreground/chain-execution.ts +6 -0
- package/src/runs/foreground/execution.ts +4 -0
- package/src/runs/foreground/subagent-executor.ts +310 -14
- package/src/runs/shared/nested-events.ts +819 -0
- package/src/runs/shared/nested-path.ts +52 -0
- package/src/runs/shared/nested-render.ts +115 -0
- package/src/runs/shared/pi-args.ts +62 -5
- package/src/runs/shared/subagent-prompt-runtime.ts +25 -5
- package/src/shared/types.ts +95 -0
- package/src/tui/render.ts +107 -10
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
type AsyncParallelGroupStatus,
|
|
15
15
|
type AsyncStatus,
|
|
16
16
|
type ModelAttempt,
|
|
17
|
+
type NestedRouteInfo,
|
|
17
18
|
type ResolvedControlConfig,
|
|
18
19
|
type SubagentRunMode,
|
|
19
20
|
type Usage,
|
|
@@ -40,6 +41,7 @@ import {
|
|
|
40
41
|
MAX_PARALLEL_CONCURRENCY,
|
|
41
42
|
} from "../shared/parallel-utils.ts";
|
|
42
43
|
import { buildPiArgs, cleanupTempDir } from "../shared/pi-args.ts";
|
|
44
|
+
import { nestedSummaryFromAsyncStatus, writeNestedEvent } from "../shared/nested-events.ts";
|
|
43
45
|
import { formatModelAttemptNote, isRetryableModelFailure } from "../shared/model-fallback.ts";
|
|
44
46
|
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
45
47
|
import { detectSubagentError, extractTextFromContent, extractToolArgsPreview, getFinalOutput } from "../../shared/utils.ts";
|
|
@@ -92,6 +94,8 @@ interface SubagentRunConfig {
|
|
|
92
94
|
controlIntercomTarget?: string;
|
|
93
95
|
childIntercomTargets?: Array<string | undefined>;
|
|
94
96
|
resultMode?: SubagentRunMode;
|
|
97
|
+
nestedRoute?: NestedRouteInfo;
|
|
98
|
+
nestedSelf?: { parentRunId: string; parentStepIndex?: number; depth: number; path?: Array<{ runId: string; stepIndex?: number; agent?: string }> };
|
|
95
99
|
}
|
|
96
100
|
|
|
97
101
|
interface StepResult {
|
|
@@ -554,6 +558,7 @@ interface SingleStepContext {
|
|
|
554
558
|
registerInterrupt?: (interrupt: (() => void) | undefined) => void;
|
|
555
559
|
childIntercomTarget?: string;
|
|
556
560
|
orchestratorIntercomTarget?: string;
|
|
561
|
+
nestedRoute?: NestedRouteInfo;
|
|
557
562
|
onAttemptStart?: (attempt: { model?: string; thinking?: string }) => void;
|
|
558
563
|
onChildEvent?: (event: ChildEvent) => void;
|
|
559
564
|
}
|
|
@@ -629,6 +634,10 @@ async function runSingleStep(
|
|
|
629
634
|
runId: ctx.id,
|
|
630
635
|
childAgentName: step.agent,
|
|
631
636
|
childIndex: ctx.flatIndex,
|
|
637
|
+
parentEventSink: ctx.nestedRoute?.eventSink,
|
|
638
|
+
parentControlInbox: ctx.nestedRoute?.controlInbox,
|
|
639
|
+
parentRootRunId: ctx.nestedRoute?.rootRunId,
|
|
640
|
+
parentCapabilityToken: ctx.nestedRoute?.capabilityToken,
|
|
632
641
|
});
|
|
633
642
|
const run = await runPiStreaming(
|
|
634
643
|
args,
|
|
@@ -938,6 +947,32 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
938
947
|
|
|
939
948
|
fs.mkdirSync(asyncDir, { recursive: true });
|
|
940
949
|
writeAtomicJson(statusPath, statusPayload);
|
|
950
|
+
const emitNestedSelfEvent = (type: "subagent.nested.updated" | "subagent.nested.completed"): void => {
|
|
951
|
+
if (!config.nestedRoute || !config.nestedSelf) return;
|
|
952
|
+
try {
|
|
953
|
+
writeNestedEvent(config.nestedRoute, {
|
|
954
|
+
type,
|
|
955
|
+
ts: Date.now(),
|
|
956
|
+
parentRunId: config.nestedSelf.parentRunId,
|
|
957
|
+
parentStepIndex: config.nestedSelf.parentStepIndex,
|
|
958
|
+
child: nestedSummaryFromAsyncStatus(statusPayload, asyncDir, {
|
|
959
|
+
id,
|
|
960
|
+
parentRunId: config.nestedSelf.parentRunId,
|
|
961
|
+
parentStepIndex: config.nestedSelf.parentStepIndex,
|
|
962
|
+
depth: config.nestedSelf.depth,
|
|
963
|
+
path: config.nestedSelf.path,
|
|
964
|
+
mode: statusPayload.mode,
|
|
965
|
+
ts: Date.now(),
|
|
966
|
+
}),
|
|
967
|
+
});
|
|
968
|
+
} catch (error) {
|
|
969
|
+
console.error("Failed to emit nested async status event:", error);
|
|
970
|
+
}
|
|
971
|
+
};
|
|
972
|
+
const writeStatusPayload = (): void => {
|
|
973
|
+
writeAtomicJson(statusPath, statusPayload);
|
|
974
|
+
emitNestedSelfEvent(statusPayload.state === "running" || statusPayload.state === "queued" ? "subagent.nested.updated" : "subagent.nested.completed");
|
|
975
|
+
};
|
|
941
976
|
|
|
942
977
|
const stepOutputActivityAt = (index: number): number => {
|
|
943
978
|
const step = statusPayload.steps[index];
|
|
@@ -1028,7 +1063,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1028
1063
|
step.model = model;
|
|
1029
1064
|
step.thinking = thinking;
|
|
1030
1065
|
statusPayload.lastUpdate = now;
|
|
1031
|
-
|
|
1066
|
+
writeStatusPayload();
|
|
1032
1067
|
};
|
|
1033
1068
|
const updateStepFromChildEvent = (flatIndex: number, event: ChildEvent): void => {
|
|
1034
1069
|
const step = statusPayload.steps[flatIndex];
|
|
@@ -1116,7 +1151,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1116
1151
|
statusPayload.lastActivityAt = now;
|
|
1117
1152
|
statusPayload.lastUpdate = now;
|
|
1118
1153
|
maybeEmitActiveLongRunning(flatIndex, now);
|
|
1119
|
-
|
|
1154
|
+
writeStatusPayload();
|
|
1120
1155
|
};
|
|
1121
1156
|
const updateRunnerActivityState = (now: number): boolean => {
|
|
1122
1157
|
if (!controlConfig.enabled) return false;
|
|
@@ -1171,7 +1206,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1171
1206
|
changed = true;
|
|
1172
1207
|
}
|
|
1173
1208
|
statusPayload.lastUpdate = now;
|
|
1174
|
-
if (changed)
|
|
1209
|
+
if (changed) writeStatusPayload();
|
|
1175
1210
|
return changed;
|
|
1176
1211
|
};
|
|
1177
1212
|
if (controlConfig.enabled) {
|
|
@@ -1200,7 +1235,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1200
1235
|
step.lastActivityAt = now;
|
|
1201
1236
|
}
|
|
1202
1237
|
}
|
|
1203
|
-
|
|
1238
|
+
writeStatusPayload();
|
|
1204
1239
|
appendJsonl(eventsPath, JSON.stringify({
|
|
1205
1240
|
type: "subagent.run.paused",
|
|
1206
1241
|
ts: now,
|
|
@@ -1311,7 +1346,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1311
1346
|
statusPayload.steps[fi].exitCode = -1;
|
|
1312
1347
|
statusPayload.steps[fi].activityState = undefined;
|
|
1313
1348
|
statusPayload.lastUpdate = skippedAt;
|
|
1314
|
-
|
|
1349
|
+
writeStatusPayload();
|
|
1315
1350
|
appendJsonl(eventsPath, JSON.stringify({
|
|
1316
1351
|
type: "subagent.step.failed", ts: skippedAt, runId: id, stepIndex: fi, agent: task.agent, exitCode: -1, durationMs: 0,
|
|
1317
1352
|
}));
|
|
@@ -1331,7 +1366,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1331
1366
|
statusPayload.outputFile = path.join(asyncDir, `output-${fi}.log`);
|
|
1332
1367
|
statusPayload.lastActivityAt = taskStartTime;
|
|
1333
1368
|
statusPayload.lastUpdate = taskStartTime;
|
|
1334
|
-
|
|
1369
|
+
writeStatusPayload();
|
|
1335
1370
|
|
|
1336
1371
|
appendJsonl(eventsPath, JSON.stringify({
|
|
1337
1372
|
type: "subagent.step.started", ts: taskStartTime, runId: id, stepIndex: fi, agent: task.agent,
|
|
@@ -1352,6 +1387,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1352
1387
|
piArgv1: config.piArgv1,
|
|
1353
1388
|
childIntercomTarget: config.childIntercomTargets?.[fi],
|
|
1354
1389
|
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
1390
|
+
nestedRoute: config.nestedRoute,
|
|
1355
1391
|
registerInterrupt: (interrupt) => {
|
|
1356
1392
|
activeChildInterrupt = interrupt;
|
|
1357
1393
|
},
|
|
@@ -1375,7 +1411,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1375
1411
|
statusPayload.steps[fi].modelAttempts = singleResult.modelAttempts;
|
|
1376
1412
|
statusPayload.steps[fi].error = singleResult.error;
|
|
1377
1413
|
statusPayload.lastUpdate = taskEndTime;
|
|
1378
|
-
|
|
1414
|
+
writeStatusPayload();
|
|
1379
1415
|
|
|
1380
1416
|
appendJsonl(eventsPath, JSON.stringify({
|
|
1381
1417
|
type: singleResult.exitCode === 0 ? "subagent.step.completed" : "subagent.step.failed",
|
|
@@ -1419,7 +1455,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1419
1455
|
}
|
|
1420
1456
|
statusPayload.totalTokens = { ...previousCumulativeTokens };
|
|
1421
1457
|
statusPayload.lastUpdate = Date.now();
|
|
1422
|
-
|
|
1458
|
+
writeStatusPayload();
|
|
1423
1459
|
|
|
1424
1460
|
for (const pr of parallelResults) {
|
|
1425
1461
|
results.push({
|
|
@@ -1477,7 +1513,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1477
1513
|
statusPayload.lastActivityAt = stepStartTime;
|
|
1478
1514
|
statusPayload.lastUpdate = stepStartTime;
|
|
1479
1515
|
statusPayload.outputFile = path.join(asyncDir, `output-${flatIndex}.log`);
|
|
1480
|
-
|
|
1516
|
+
writeStatusPayload();
|
|
1481
1517
|
|
|
1482
1518
|
appendJsonl(eventsPath, JSON.stringify({
|
|
1483
1519
|
type: "subagent.step.started",
|
|
@@ -1497,6 +1533,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1497
1533
|
piArgv1: config.piArgv1,
|
|
1498
1534
|
childIntercomTarget: config.childIntercomTargets?.[flatIndex],
|
|
1499
1535
|
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
1536
|
+
nestedRoute: config.nestedRoute,
|
|
1500
1537
|
registerInterrupt: (interrupt) => {
|
|
1501
1538
|
activeChildInterrupt = interrupt;
|
|
1502
1539
|
},
|
|
@@ -1557,7 +1594,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1557
1594
|
statusPayload.totalTokens = { ...previousCumulativeTokens };
|
|
1558
1595
|
}
|
|
1559
1596
|
statusPayload.lastUpdate = stepEndTime;
|
|
1560
|
-
|
|
1597
|
+
writeStatusPayload();
|
|
1561
1598
|
|
|
1562
1599
|
appendJsonl(eventsPath, JSON.stringify({
|
|
1563
1600
|
type: singleResult.exitCode === 0 ? "subagent.step.completed" : "subagent.step.failed",
|
|
@@ -1659,7 +1696,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1659
1696
|
statusPayload.error = `Step failed: ${failedStep.agent}`;
|
|
1660
1697
|
}
|
|
1661
1698
|
}
|
|
1662
|
-
|
|
1699
|
+
writeStatusPayload();
|
|
1663
1700
|
appendJsonl(
|
|
1664
1701
|
eventsPath,
|
|
1665
1702
|
JSON.stringify({
|
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
type ControlEvent,
|
|
52
52
|
type Details,
|
|
53
53
|
type IntercomEventBus,
|
|
54
|
+
type NestedRouteInfo,
|
|
54
55
|
type ResolvedControlConfig,
|
|
55
56
|
type SingleResult,
|
|
56
57
|
MAX_CONCURRENCY,
|
|
@@ -112,6 +113,7 @@ interface ParallelChainRunInput {
|
|
|
112
113
|
totalSteps: number;
|
|
113
114
|
worktreeSetup?: WorktreeSetup;
|
|
114
115
|
maxSubagentDepth: number;
|
|
116
|
+
nestedRoute?: NestedRouteInfo;
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
function buildChainExecutionDetails(input: ChainExecutionDetailsInput): Details {
|
|
@@ -244,6 +246,7 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
244
246
|
onControlEvent: input.onControlEvent,
|
|
245
247
|
intercomSessionName: input.childIntercomTarget?.(task.agent, input.globalTaskIndex + taskIndex),
|
|
246
248
|
orchestratorIntercomTarget: input.orchestratorIntercomTarget,
|
|
249
|
+
nestedRoute: input.nestedRoute,
|
|
247
250
|
modelOverride: effectiveModel,
|
|
248
251
|
availableModels: input.availableModels,
|
|
249
252
|
preferredModelProvider: input.ctx.model?.provider,
|
|
@@ -331,6 +334,7 @@ interface ChainExecutionParams {
|
|
|
331
334
|
chainSkills?: string[];
|
|
332
335
|
chainDir?: string;
|
|
333
336
|
maxSubagentDepth: number;
|
|
337
|
+
nestedRoute?: NestedRouteInfo;
|
|
334
338
|
worktreeSetupHook?: string;
|
|
335
339
|
worktreeSetupHookTimeoutMs?: number;
|
|
336
340
|
}
|
|
@@ -591,6 +595,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
591
595
|
childIntercomTarget,
|
|
592
596
|
orchestratorIntercomTarget,
|
|
593
597
|
foregroundControl,
|
|
598
|
+
nestedRoute: params.nestedRoute,
|
|
594
599
|
worktreeSetup,
|
|
595
600
|
maxSubagentDepth: params.maxSubagentDepth,
|
|
596
601
|
});
|
|
@@ -793,6 +798,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
793
798
|
onControlEvent,
|
|
794
799
|
intercomSessionName: childIntercomTarget?.(seqStep.agent, globalTaskIndex),
|
|
795
800
|
orchestratorIntercomTarget,
|
|
801
|
+
nestedRoute: params.nestedRoute,
|
|
796
802
|
modelOverride: effectiveModel,
|
|
797
803
|
availableModels,
|
|
798
804
|
preferredModelProvider: ctx.model?.provider,
|
|
@@ -158,6 +158,10 @@ async function runSingleAttempt(
|
|
|
158
158
|
runId: options.runId,
|
|
159
159
|
childAgentName: agent.name,
|
|
160
160
|
childIndex: options.index ?? 0,
|
|
161
|
+
parentEventSink: options.nestedRoute?.eventSink,
|
|
162
|
+
parentControlInbox: options.nestedRoute?.controlInbox,
|
|
163
|
+
parentRootRunId: options.nestedRoute?.rootRunId,
|
|
164
|
+
parentCapabilityToken: options.nestedRoute?.capabilityToken,
|
|
161
165
|
});
|
|
162
166
|
|
|
163
167
|
const result: SingleResult = {
|