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.
@@ -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
- writeAtomicJson(statusPath, statusPayload);
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
- writeAtomicJson(statusPath, statusPayload);
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) writeAtomicJson(statusPath, statusPayload);
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
- writeAtomicJson(statusPath, statusPayload);
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
- writeAtomicJson(statusPath, statusPayload);
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
- writeAtomicJson(statusPath, statusPayload);
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
- writeAtomicJson(statusPath, statusPayload);
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
- writeAtomicJson(statusPath, statusPayload);
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
- writeAtomicJson(statusPath, statusPayload);
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
- writeAtomicJson(statusPath, statusPayload);
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
- writeAtomicJson(statusPath, statusPayload);
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 = {