oira666_pi-subagent 0.2.22 → 0.2.25
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/README.md +1 -1
- package/index.ts +147 -49
- package/package.json +76 -74
- package/render.ts +87 -471
- package/resume.ts +39 -7
- package/runner.ts +35 -5
- package/shims.d.ts +78 -0
- package/tree.ts +475 -0
- package/types.ts +9 -1
package/README.md
CHANGED
|
@@ -117,7 +117,7 @@ pi --no-subagent-prevent-cycles # allow cycles (not recommended)
|
|
|
117
117
|
|
|
118
118
|
| Env Var | Default | Description |
|
|
119
119
|
| -------------------------------- | ------- | ---------------------------------------- |
|
|
120
|
-
| `PI_SUBAGENT_MAX_PARALLEL_TASKS` | `
|
|
120
|
+
| `PI_SUBAGENT_MAX_PARALLEL_TASKS` | `30` | Max tasks per single call |
|
|
121
121
|
| `PI_SUBAGENT_MAX_CONCURRENCY` | `8` | Max subagents running simultaneously |
|
|
122
122
|
|
|
123
123
|
## Steering Running Subagents
|
package/index.ts
CHANGED
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
SUBAGENT_RESUME_PROMPT_ENV,
|
|
23
23
|
branchEntries,
|
|
24
24
|
buildSubagentSessionDir,
|
|
25
|
-
|
|
25
|
+
findLatestResumableSubagentCalls,
|
|
26
26
|
getDefaultSubagentSessionRoot,
|
|
27
27
|
isFinishedResult,
|
|
28
28
|
parseBooleanEnv,
|
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
// ---------------------------------------------------------------------------
|
|
56
56
|
|
|
57
57
|
const DEFAULT_MAX_DELEGATION_DEPTH = 3;
|
|
58
|
-
const DEFAULT_PREVENT_CYCLE_DELEGATION =
|
|
58
|
+
const DEFAULT_PREVENT_CYCLE_DELEGATION = true;
|
|
59
59
|
const DEFAULT_PROJECT_AGENT_CONFIRMATION = "ask";
|
|
60
60
|
const SUBAGENT_DEPTH_ENV = "PI_SUBAGENT_DEPTH";
|
|
61
61
|
const SUBAGENT_MAX_DEPTH_ENV = "PI_SUBAGENT_MAX_DEPTH";
|
|
@@ -355,6 +355,15 @@ function ensureSubagentToolActive(pi: ExtensionAPI): void {
|
|
|
355
355
|
}
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
+
function isRpcMode(argv: string[]): boolean {
|
|
359
|
+
for (let i = 2; i < argv.length; i++) {
|
|
360
|
+
const arg = argv[i];
|
|
361
|
+
if (arg === "--mode" && argv[i + 1] === "rpc") return true;
|
|
362
|
+
if (arg === "--mode=rpc") return true;
|
|
363
|
+
}
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
|
|
358
367
|
function hasCliInitialPrompt(argv: string[]): boolean {
|
|
359
368
|
for (let i = 2; i < argv.length; i++) {
|
|
360
369
|
const arg = argv[i];
|
|
@@ -381,14 +390,14 @@ const SUBAGENT_FALLBACK_MODEL_ENV = "PI_SUBAGENT_FALLBACK_MODEL";
|
|
|
381
390
|
const RESUME_INTERACTIVE_DELAY_MS = 50;
|
|
382
391
|
|
|
383
392
|
type SyntheticResumeState = {
|
|
384
|
-
|
|
393
|
+
plans: ResumableSubagentCall[];
|
|
385
394
|
phase: "tool" | "final";
|
|
386
395
|
trigger: "resumePrompt" | "nextRequest";
|
|
387
396
|
};
|
|
388
397
|
|
|
389
398
|
function clearSyntheticResumeState(): void {
|
|
390
399
|
const state = getSyntheticResumeState();
|
|
391
|
-
state.
|
|
400
|
+
state.plans = [];
|
|
392
401
|
state.phase = "tool";
|
|
393
402
|
state.trigger = "resumePrompt";
|
|
394
403
|
}
|
|
@@ -396,7 +405,7 @@ function clearSyntheticResumeState(): void {
|
|
|
396
405
|
function getSyntheticResumeState(): SyntheticResumeState {
|
|
397
406
|
const g = globalThis as any;
|
|
398
407
|
if (!g[RESUME_STATE_KEY]) {
|
|
399
|
-
g[RESUME_STATE_KEY] = {
|
|
408
|
+
g[RESUME_STATE_KEY] = { plans: [], phase: "tool", trigger: "resumePrompt" } satisfies SyntheticResumeState;
|
|
400
409
|
}
|
|
401
410
|
return g[RESUME_STATE_KEY] as SyntheticResumeState;
|
|
402
411
|
}
|
|
@@ -518,27 +527,34 @@ export default function (pi: ExtensionAPI) {
|
|
|
518
527
|
streamSimple: async (model, context, options) => {
|
|
519
528
|
const stream = createAssistantMessageEventStream();
|
|
520
529
|
const state = getSyntheticResumeState();
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
530
|
+
const discoveredPlans = state.plans.length > 0
|
|
531
|
+
? state.plans
|
|
532
|
+
: pendingResumePlans.length > 0
|
|
533
|
+
? pendingResumePlans
|
|
534
|
+
: latestSessionCtx
|
|
535
|
+
? findLatestResumableSubagentCalls(latestSessionCtx)
|
|
536
|
+
: [];
|
|
537
|
+
if (discoveredPlans.length > 0 && state.plans.length === 0) {
|
|
538
|
+
state.plans = [...discoveredPlans];
|
|
539
|
+
pendingResumePlans = [...discoveredPlans];
|
|
525
540
|
}
|
|
526
|
-
const
|
|
541
|
+
const plans = discoveredPlans;
|
|
542
|
+
const totalTaskCount = plans.reduce((sum, plan) => sum + plan.tasks.length, 0);
|
|
527
543
|
const phase = state.phase;
|
|
528
544
|
const triggerMatches =
|
|
529
545
|
state.trigger === "nextRequest" ||
|
|
530
|
-
(
|
|
531
|
-
if (
|
|
546
|
+
(totalTaskCount > 0 ? isSyntheticResumePrompt(context, totalTaskCount) : false);
|
|
547
|
+
if (plans.length > 0 && phase === "tool" && triggerMatches) {
|
|
532
548
|
state.phase = "final";
|
|
533
|
-
const
|
|
549
|
+
const toolCalls = plans.map((plan, index) => ({
|
|
534
550
|
type: "toolCall" as const,
|
|
535
|
-
id: `resume_subagent_${Date.now()}`,
|
|
551
|
+
id: `resume_subagent_${Date.now()}_${index}`,
|
|
536
552
|
name: "subagent",
|
|
537
553
|
arguments: { tasks: plan.tasks },
|
|
538
|
-
};
|
|
554
|
+
}));
|
|
539
555
|
const message = {
|
|
540
556
|
role: "assistant" as const,
|
|
541
|
-
content:
|
|
557
|
+
content: toolCalls,
|
|
542
558
|
api: model.api,
|
|
543
559
|
provider: model.provider,
|
|
544
560
|
model: model.id,
|
|
@@ -548,13 +564,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
548
564
|
};
|
|
549
565
|
queueMicrotask(() => {
|
|
550
566
|
stream.push({ type: "start", partial: message });
|
|
551
|
-
|
|
552
|
-
|
|
567
|
+
toolCalls.forEach((toolCall, contentIndex) => {
|
|
568
|
+
stream.push({ type: "toolcall_start", contentIndex, partial: message });
|
|
569
|
+
stream.push({ type: "toolcall_end", contentIndex, toolCall, partial: message });
|
|
570
|
+
});
|
|
553
571
|
stream.push({ type: "done", reason: "toolUse", message });
|
|
554
572
|
stream.end(message);
|
|
555
573
|
|
|
556
574
|
// The synthetic provider's only job is to inject the resumed subagent
|
|
557
|
-
// tool
|
|
575
|
+
// tool calls. Restore the real model immediately after that handoff so
|
|
558
576
|
// the TUI does not appear stuck on `pi-subagent-resume` while the
|
|
559
577
|
// subagent tool execution is still running.
|
|
560
578
|
void restoreVisibleModelForResume();
|
|
@@ -572,8 +590,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
572
590
|
const fallback = await streamWithRealModelFallback(context, options, restore ?? lastRestorableModel);
|
|
573
591
|
if (fallback) return fallback;
|
|
574
592
|
|
|
575
|
-
if (!(
|
|
576
|
-
state.
|
|
593
|
+
if (!(plans.length > 0 && phase === "tool")) {
|
|
594
|
+
state.plans = [];
|
|
577
595
|
state.phase = "tool";
|
|
578
596
|
}
|
|
579
597
|
const message = {
|
|
@@ -622,7 +640,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
622
640
|
let discoveredAgents: AgentConfig[] = [];
|
|
623
641
|
let currentSessionId = "ephemeral";
|
|
624
642
|
let currentSubagentSessionRoot = "";
|
|
625
|
-
let
|
|
643
|
+
let pendingResumePlans: ResumableSubagentCall[] = [];
|
|
626
644
|
let modelToRestoreAfterResume: any | undefined;
|
|
627
645
|
const approvedProjectAgentDirsForSession = new Set<string>();
|
|
628
646
|
const activeSubagents = new Map<number, { agent: string; task: string; handle: RunningSubagentHandle }>();
|
|
@@ -632,6 +650,73 @@ export default function (pi: ExtensionAPI) {
|
|
|
632
650
|
};
|
|
633
651
|
let nextActiveSubagentId = 1;
|
|
634
652
|
|
|
653
|
+
/**
|
|
654
|
+
* Build a lightweight version of SubagentDetails for live progress bubbling.
|
|
655
|
+
*
|
|
656
|
+
* Full message histories can be very large in long agent trees. For live TUI
|
|
657
|
+
* rendering we only need subagent tool-call structure, nested subagent
|
|
658
|
+
* results, live logs, metadata, and usage counters. Text conversations are
|
|
659
|
+
* intentionally omitted; final durable results still arrive via normal
|
|
660
|
+
* tool_result_end messages.
|
|
661
|
+
*/
|
|
662
|
+
function slimDetailsForProgress(details: SubagentDetails): SubagentDetails {
|
|
663
|
+
const slimResult = (result: SingleResult): SingleResult => {
|
|
664
|
+
const slimMessages = result.messages
|
|
665
|
+
.map((message: any) => {
|
|
666
|
+
if (message?.role === "assistant" && Array.isArray(message.content)) {
|
|
667
|
+
const subagentCalls = message.content.filter(
|
|
668
|
+
(part: any) => part?.type === "toolCall" && part?.name === "subagent",
|
|
669
|
+
);
|
|
670
|
+
return subagentCalls.length > 0
|
|
671
|
+
? { ...message, content: subagentCalls }
|
|
672
|
+
: null;
|
|
673
|
+
}
|
|
674
|
+
if (message?.role === "toolResult" && message.toolName === "subagent") {
|
|
675
|
+
return isSubagentDetails(message.details)
|
|
676
|
+
? { ...message, details: slimDetailsForProgress(message.details) }
|
|
677
|
+
: message;
|
|
678
|
+
}
|
|
679
|
+
return null;
|
|
680
|
+
})
|
|
681
|
+
.filter(Boolean) as SingleResult["messages"];
|
|
682
|
+
|
|
683
|
+
const liveNestedSubagents = result.liveNestedSubagents
|
|
684
|
+
? Object.fromEntries(
|
|
685
|
+
Object.entries(result.liveNestedSubagents).map(([nestedToolCallId, nested]) => [
|
|
686
|
+
nestedToolCallId,
|
|
687
|
+
slimDetailsForProgress(nested),
|
|
688
|
+
]),
|
|
689
|
+
)
|
|
690
|
+
: undefined;
|
|
691
|
+
|
|
692
|
+
return {
|
|
693
|
+
...result,
|
|
694
|
+
messages: slimMessages,
|
|
695
|
+
stderr: result.stderr ? result.stderr.slice(-1000) : "",
|
|
696
|
+
liveLog: [...(result.liveLog ?? [])],
|
|
697
|
+
liveNestedSubagents,
|
|
698
|
+
};
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
return {
|
|
702
|
+
...details,
|
|
703
|
+
results: details.results.map(slimResult),
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
function emitNestedProgressToParent(toolCallId: string, details: SubagentDetails): void {
|
|
708
|
+
if (currentDepth <= 0) return;
|
|
709
|
+
try {
|
|
710
|
+
process.stdout.write(`${JSON.stringify({
|
|
711
|
+
type: "subagent_progress",
|
|
712
|
+
toolCallId,
|
|
713
|
+
details: slimDetailsForProgress(details),
|
|
714
|
+
})}\n`);
|
|
715
|
+
} catch {
|
|
716
|
+
// Best-effort only. Normal final tool_result_end still carries the durable result.
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
635
720
|
const BROADCAST_STEER_PREFIX = "__PI_SUBAGENT_BROADCAST_STEER__";
|
|
636
721
|
|
|
637
722
|
interface BroadcastTarget {
|
|
@@ -784,7 +869,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
784
869
|
return true;
|
|
785
870
|
}
|
|
786
871
|
|
|
787
|
-
function updateLatestBroadcastTargets(details: SubagentDetails | undefined): void {
|
|
872
|
+
function updateLatestBroadcastTargets(details: SubagentDetails | undefined, topLevelBaseId = 1): void {
|
|
788
873
|
latestBroadcastTargets.all = [];
|
|
789
874
|
latestBroadcastTargets.youngest = [];
|
|
790
875
|
if (!details) {
|
|
@@ -796,7 +881,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
796
881
|
return;
|
|
797
882
|
}
|
|
798
883
|
details.results.forEach((result, index) => {
|
|
799
|
-
collectRunningBroadcastTargetsFromResult(result, [
|
|
884
|
+
collectRunningBroadcastTargetsFromResult(result, [topLevelBaseId + index], latestBroadcastTargets);
|
|
800
885
|
});
|
|
801
886
|
const dedupe = (targets: BroadcastTarget[]) =>
|
|
802
887
|
Array.from(new Map(targets.map((target) => [target.display, target])).values())
|
|
@@ -909,7 +994,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
909
994
|
async function restoreModelAfterResumeFailure(ctx?: { ui?: { notify?: (message: string, type?: "info" | "warning" | "error") => void } }) {
|
|
910
995
|
const restore = modelToRestoreAfterResume;
|
|
911
996
|
modelToRestoreAfterResume = undefined;
|
|
912
|
-
|
|
997
|
+
pendingResumePlans = [];
|
|
913
998
|
clearSyntheticResumeState();
|
|
914
999
|
if (!restore) return;
|
|
915
1000
|
try {
|
|
@@ -957,15 +1042,17 @@ export default function (pi: ExtensionAPI) {
|
|
|
957
1042
|
const resumeDisabled = parseBooleanEnv(process.env[SUBAGENT_RESUME_DISABLE_ENV]) === true;
|
|
958
1043
|
if (resumeDisabled || (event.reason !== "resume" && event.reason !== "startup")) return;
|
|
959
1044
|
|
|
960
|
-
const
|
|
961
|
-
if (
|
|
1045
|
+
const plans = findLatestResumableSubagentCalls(ctx);
|
|
1046
|
+
if (plans.length === 0) return;
|
|
1047
|
+
const totalTaskCount = plans.reduce((sum, plan) => sum + plan.tasks.length, 0);
|
|
962
1048
|
|
|
963
1049
|
let shouldResume = true;
|
|
964
1050
|
const shouldPrompt = parseBooleanEnv(process.env[SUBAGENT_RESUME_PROMPT_ENV]) !== false;
|
|
965
|
-
|
|
1051
|
+
const rpcMode = isRpcMode(process.argv);
|
|
1052
|
+
if (ctx.hasUI && !rpcMode && shouldPrompt) {
|
|
966
1053
|
shouldResume = await ctx.ui.confirm(
|
|
967
1054
|
"Resume subagents?",
|
|
968
|
-
`The resumed session has an unfinished subagent call
|
|
1055
|
+
`The resumed session has ${plans.length === 1 ? "an" : String(plans.length)} unfinished subagent call${plans.length === 1 ? "" : "s"} (${totalTaskCount} task${totalTaskCount === 1 ? "" : "s"}). Resume from saved subagent sessions?`,
|
|
969
1056
|
);
|
|
970
1057
|
}
|
|
971
1058
|
if (!shouldResume) {
|
|
@@ -990,11 +1077,17 @@ export default function (pi: ExtensionAPI) {
|
|
|
990
1077
|
return;
|
|
991
1078
|
}
|
|
992
1079
|
|
|
993
|
-
|
|
1080
|
+
pendingResumePlans = [...plans];
|
|
994
1081
|
const resumeState = getSyntheticResumeState();
|
|
995
|
-
resumeState.
|
|
1082
|
+
resumeState.plans = [...plans];
|
|
996
1083
|
resumeState.phase = "tool";
|
|
997
|
-
|
|
1084
|
+
// Headless subprocess/RPC subagents cannot answer a visible resume
|
|
1085
|
+
// prompt. They already receive an initial RPC prompt from the parent
|
|
1086
|
+
// runner, so inject the synthetic resume tool call into that next model
|
|
1087
|
+
// request. Interactive top-level sessions keep using a visible prompt so
|
|
1088
|
+
// the user sees exactly what is happening.
|
|
1089
|
+
const injectOnNextRequest = rpcMode || hasCliInitialPrompt(process.argv) || !ctx.hasUI;
|
|
1090
|
+
resumeState.trigger = injectOnNextRequest ? "nextRequest" : "resumePrompt";
|
|
998
1091
|
modelToRestoreAfterResume = restorableModel ?? ctx.model;
|
|
999
1092
|
resumeModelRegistry = ctx.modelRegistry;
|
|
1000
1093
|
ensureSubagentToolActive(pi);
|
|
@@ -1009,15 +1102,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
1009
1102
|
// to be sent. That prompt will be answered by the synthetic provider with
|
|
1010
1103
|
// a real assistant subagent tool call. In interactive mode, submit a short
|
|
1011
1104
|
// visible prompt that triggers the same synthetic provider path.
|
|
1012
|
-
if (
|
|
1013
|
-
if (ctx.hasUI) ctx.ui.notify(`Resuming ${
|
|
1105
|
+
if (injectOnNextRequest) {
|
|
1106
|
+
if (ctx.hasUI) ctx.ui.notify(`Resuming ${totalTaskCount} subagents...`, "info");
|
|
1014
1107
|
} else {
|
|
1015
1108
|
// Do not start the synthetic resume turn from session_start. Pi renders
|
|
1016
1109
|
// the resumed chat only after session_start/resources_discover complete;
|
|
1017
1110
|
// starting now lets that render wipe out the live tool component, so no
|
|
1018
1111
|
// real-time updates appear. Queue it for resources_discover instead,
|
|
1019
1112
|
// which is the last extension hook before the initial chat render.
|
|
1020
|
-
pendingInteractiveResumePrompt = `Resuming ${
|
|
1113
|
+
pendingInteractiveResumePrompt = `Resuming ${totalTaskCount} subagents...`;
|
|
1021
1114
|
}
|
|
1022
1115
|
} catch (err) {
|
|
1023
1116
|
console.error("[pi-subagent] Error in session_start:", err);
|
|
@@ -1131,9 +1224,7 @@ calls one after another. Do NOT put dependent tasks in the same array.
|
|
|
1131
1224
|
|
|
1132
1225
|
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
1133
1226
|
try {
|
|
1134
|
-
activeSubagents.clear();
|
|
1135
1227
|
updateLatestBroadcastTargets(undefined);
|
|
1136
|
-
nextActiveSubagentId = 1;
|
|
1137
1228
|
const discovery = discoverAgents(ctx.cwd, "both");
|
|
1138
1229
|
const { agents } = discovery;
|
|
1139
1230
|
|
|
@@ -1156,8 +1247,13 @@ calls one after another. Do NOT put dependent tasks in the same array.
|
|
|
1156
1247
|
}
|
|
1157
1248
|
|
|
1158
1249
|
const executionMode = tasks.length === 1 ? "single" : "parallel";
|
|
1250
|
+
const topLevelBaseId = nextActiveSubagentId;
|
|
1251
|
+
nextActiveSubagentId += tasks.length;
|
|
1159
1252
|
const trackedOnUpdate = (partial: any) => {
|
|
1160
|
-
if (isSubagentDetails(partial?.details))
|
|
1253
|
+
if (isSubagentDetails(partial?.details)) {
|
|
1254
|
+
updateLatestBroadcastTargets(partial.details, topLevelBaseId);
|
|
1255
|
+
emitNestedProgressToParent(toolCallId, partial.details);
|
|
1256
|
+
}
|
|
1161
1257
|
onUpdate?.(partial);
|
|
1162
1258
|
};
|
|
1163
1259
|
|
|
@@ -1205,7 +1301,7 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
|
|
|
1205
1301
|
projectAgentConfirmationSetting === "ask" &&
|
|
1206
1302
|
!approvedProjectAgentDirsForSession.has(projectAgentSessionKey);
|
|
1207
1303
|
if (shouldConfirmProjectAgents) {
|
|
1208
|
-
if (ctx.hasUI) {
|
|
1304
|
+
if (ctx.hasUI && !isRpcMode(process.argv)) {
|
|
1209
1305
|
const approval = await confirmProjectAgentsIfNeeded(
|
|
1210
1306
|
requestedProjectAgents,
|
|
1211
1307
|
discovery.projectAgentsDir,
|
|
@@ -1241,12 +1337,10 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
|
|
|
1241
1337
|
}
|
|
1242
1338
|
}
|
|
1243
1339
|
|
|
1244
|
-
const
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
if (resumePlan) {
|
|
1249
|
-
pendingResumePlan = null;
|
|
1340
|
+
const resumePlanIndex = pendingResumePlans.findIndex((plan) => sameTasks(plan.tasks, tasks));
|
|
1341
|
+
const resumePlan = resumePlanIndex >= 0 ? pendingResumePlans[resumePlanIndex] : null;
|
|
1342
|
+
if (resumePlanIndex >= 0) {
|
|
1343
|
+
pendingResumePlans.splice(resumePlanIndex, 1);
|
|
1250
1344
|
}
|
|
1251
1345
|
|
|
1252
1346
|
if (tasks.length === 1) {
|
|
@@ -1267,6 +1361,7 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
|
|
|
1267
1361
|
// defaulting to whatever settings.json says at spawn time, which
|
|
1268
1362
|
// can change while the parent session is long-running.
|
|
1269
1363
|
formatModelFlag(modelToRestoreAfterResume ?? ctx.model ?? lastRestorableModel),
|
|
1364
|
+
topLevelBaseId,
|
|
1270
1365
|
);
|
|
1271
1366
|
}
|
|
1272
1367
|
|
|
@@ -1281,6 +1376,7 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
|
|
|
1281
1376
|
(index) => getSessionDirForTask(resumePlan?.previousToolCallId ?? toolCallId, index),
|
|
1282
1377
|
!!resumePlan,
|
|
1283
1378
|
formatModelFlag(modelToRestoreAfterResume ?? ctx.model ?? lastRestorableModel),
|
|
1379
|
+
topLevelBaseId,
|
|
1284
1380
|
);
|
|
1285
1381
|
} catch (err) {
|
|
1286
1382
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -1322,7 +1418,8 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
|
|
|
1322
1418
|
previousResult: SingleResult | undefined,
|
|
1323
1419
|
sessionDir: string,
|
|
1324
1420
|
resumeExistingSession: boolean,
|
|
1325
|
-
fallbackModel
|
|
1421
|
+
fallbackModel: string | undefined,
|
|
1422
|
+
topLevelBaseId: number,
|
|
1326
1423
|
) {
|
|
1327
1424
|
if (previousResult && isFinishedResult(previousResult)) {
|
|
1328
1425
|
return {
|
|
@@ -1355,7 +1452,7 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
|
|
|
1355
1452
|
initialResult: previousResult,
|
|
1356
1453
|
fallbackModel,
|
|
1357
1454
|
onHandle: (handle) => {
|
|
1358
|
-
activeId =
|
|
1455
|
+
activeId = topLevelBaseId;
|
|
1359
1456
|
activeSubagents.set(activeId, { agent: agentName, task, handle });
|
|
1360
1457
|
updateLatestBroadcastTargets(undefined);
|
|
1361
1458
|
},
|
|
@@ -1403,7 +1500,8 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
|
|
|
1403
1500
|
resumeResults: SingleResult[] | undefined,
|
|
1404
1501
|
getSessionDir: (index: number) => string,
|
|
1405
1502
|
resumeExistingSessions: boolean,
|
|
1406
|
-
fallbackModel
|
|
1503
|
+
fallbackModel: string | undefined,
|
|
1504
|
+
topLevelBaseId: number,
|
|
1407
1505
|
) {
|
|
1408
1506
|
const taskIds = new Map<number, number>();
|
|
1409
1507
|
try {
|
|
@@ -1424,7 +1522,7 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
|
|
|
1424
1522
|
currentSubagentSessionRoot,
|
|
1425
1523
|
fallbackModel,
|
|
1426
1524
|
(index, task, handle) => {
|
|
1427
|
-
const id =
|
|
1525
|
+
const id = topLevelBaseId + index;
|
|
1428
1526
|
taskIds.set(index, id);
|
|
1429
1527
|
activeSubagents.set(id, { agent: task.agent, task: task.task, handle });
|
|
1430
1528
|
updateLatestBroadcastTargets(undefined);
|
package/package.json
CHANGED
|
@@ -1,74 +1,76 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "oira666_pi-subagent",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Subagent extension for Pi coding agent. Delegate tasks to specialized agents.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "index.ts",
|
|
7
|
-
"files": [
|
|
8
|
-
"index.ts",
|
|
9
|
-
"agents.ts",
|
|
10
|
-
"runner.ts",
|
|
11
|
-
"resume.ts",
|
|
12
|
-
"shared.ts",
|
|
13
|
-
"render.ts",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"@mariozechner/pi-
|
|
55
|
-
"@
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "oira666_pi-subagent",
|
|
3
|
+
"version": "0.2.25",
|
|
4
|
+
"description": "Subagent extension for Pi coding agent. Delegate tasks to specialized agents.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"index.ts",
|
|
9
|
+
"agents.ts",
|
|
10
|
+
"runner.ts",
|
|
11
|
+
"resume.ts",
|
|
12
|
+
"shared.ts",
|
|
13
|
+
"render.ts",
|
|
14
|
+
"tree.ts",
|
|
15
|
+
"types.ts",
|
|
16
|
+
"shims.d.ts",
|
|
17
|
+
"agents/*.md",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"pi": {
|
|
22
|
+
"extensions": [
|
|
23
|
+
"./index.ts"
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"pi",
|
|
28
|
+
"subagent",
|
|
29
|
+
"delegation",
|
|
30
|
+
"pi-package"
|
|
31
|
+
],
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/gee666/pi-subagent.git"
|
|
35
|
+
},
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/gee666/pi-subagent/issues"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://github.com/gee666/pi-subagent#readme",
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"scripts": {
|
|
45
|
+
"test": "node --import tsx/esm --test tests/*.test.ts test/*.test.ts",
|
|
46
|
+
"typecheck": "tsc --noEmit"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^25.2.3",
|
|
50
|
+
"tsx": "^4.21.0",
|
|
51
|
+
"typescript": "^5.9.3"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"@mariozechner/pi-agent-core": ">=0.37.0",
|
|
55
|
+
"@mariozechner/pi-ai": ">=0.37.0",
|
|
56
|
+
"@mariozechner/pi-coding-agent": ">=0.37.0",
|
|
57
|
+
"@mariozechner/pi-tui": ">=0.37.0"
|
|
58
|
+
},
|
|
59
|
+
"peerDependenciesMeta": {
|
|
60
|
+
"@mariozechner/pi-agent-core": {
|
|
61
|
+
"optional": true
|
|
62
|
+
},
|
|
63
|
+
"@mariozechner/pi-coding-agent": {
|
|
64
|
+
"optional": true
|
|
65
|
+
},
|
|
66
|
+
"@mariozechner/pi-tui": {
|
|
67
|
+
"optional": true
|
|
68
|
+
},
|
|
69
|
+
"@mariozechner/pi-ai": {
|
|
70
|
+
"optional": true
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"@sinclair/typebox": ">=0.34.0"
|
|
75
|
+
}
|
|
76
|
+
}
|