oira666_pi-subagent 0.2.6 → 0.2.8
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/index.ts +40 -2
- package/package.json +1 -1
- package/runner.ts +20 -0
package/index.ts
CHANGED
|
@@ -425,6 +425,35 @@ function formatModelFlag(model: any): string | undefined {
|
|
|
425
425
|
return `${model.provider}/${model.id}`;
|
|
426
426
|
}
|
|
427
427
|
|
|
428
|
+
function findLastNonResumeModel(ctx: any): any | undefined {
|
|
429
|
+
const entries = (() => {
|
|
430
|
+
const leafId = ctx.sessionManager?.getLeafId?.();
|
|
431
|
+
if (leafId) {
|
|
432
|
+
const branch = ctx.sessionManager?.getBranch?.(leafId);
|
|
433
|
+
if (Array.isArray(branch)) return branch;
|
|
434
|
+
}
|
|
435
|
+
const all = ctx.sessionManager?.getEntries?.();
|
|
436
|
+
return Array.isArray(all) ? all : [];
|
|
437
|
+
})();
|
|
438
|
+
|
|
439
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
440
|
+
const entry = entries[i];
|
|
441
|
+
if (entry?.type !== "model_change") continue;
|
|
442
|
+
const provider = entry.provider;
|
|
443
|
+
const modelId = entry.modelId;
|
|
444
|
+
if (provider === RESUME_PROVIDER) continue;
|
|
445
|
+
if (typeof provider !== "string" || typeof modelId !== "string") continue;
|
|
446
|
+
const model = ctx.modelRegistry?.find?.(provider, modelId);
|
|
447
|
+
if (model) return model;
|
|
448
|
+
}
|
|
449
|
+
return undefined;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function getRestorableModel(ctx: any): any | undefined {
|
|
453
|
+
if (ctx.model?.provider && ctx.model.provider !== RESUME_PROVIDER) return ctx.model;
|
|
454
|
+
return findLastNonResumeModel(ctx);
|
|
455
|
+
}
|
|
456
|
+
|
|
428
457
|
// ---------------------------------------------------------------------------
|
|
429
458
|
// Extension entry point
|
|
430
459
|
// ---------------------------------------------------------------------------
|
|
@@ -567,8 +596,17 @@ export default function (pi: ExtensionAPI) {
|
|
|
567
596
|
|
|
568
597
|
// Auto-discover agents on session start
|
|
569
598
|
pi.on("session_start", async (event, ctx) => {
|
|
570
|
-
if (!canDelegate) return;
|
|
571
599
|
try {
|
|
600
|
+
// Always repair sessions left on the synthetic resume model, even in
|
|
601
|
+
// nested subagents that can no longer delegate. Those leaf processes
|
|
602
|
+
// still need the real model to continue their own work.
|
|
603
|
+
const restorableModel = getRestorableModel(ctx);
|
|
604
|
+
if (ctx.model?.provider === RESUME_PROVIDER && restorableModel) {
|
|
605
|
+
await pi.setModel(restorableModel);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if (!canDelegate) return;
|
|
609
|
+
|
|
572
610
|
const discovery = discoverAgents(ctx.cwd, "both");
|
|
573
611
|
discoveredAgents = discovery.agents;
|
|
574
612
|
currentSessionId = ctx.sessionManager.getSessionId?.() ?? "ephemeral";
|
|
@@ -605,7 +643,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
605
643
|
resumeState.plan = plan;
|
|
606
644
|
resumeState.phase = "tool";
|
|
607
645
|
resumeState.trigger = hasCliInitialPrompt(process.argv) ? "nextRequest" : "resumePrompt";
|
|
608
|
-
modelToRestoreAfterResume = ctx.model;
|
|
646
|
+
modelToRestoreAfterResume = restorableModel ?? ctx.model;
|
|
609
647
|
resumeModelRegistry = ctx.modelRegistry;
|
|
610
648
|
ensureSubagentToolActive(pi);
|
|
611
649
|
const resumeModel = ctx.modelRegistry.find(RESUME_PROVIDER, RESUME_MODEL_ID);
|
package/package.json
CHANGED
package/runner.ts
CHANGED
|
@@ -48,11 +48,24 @@ const TERMINAL_STOP_REASONS = new Set(["end_turn", "stop", "max_tokens", "error"
|
|
|
48
48
|
function isTerminalStopReason(reason: string | undefined): boolean {
|
|
49
49
|
return reason !== undefined && TERMINAL_STOP_REASONS.has(reason);
|
|
50
50
|
}
|
|
51
|
+
|
|
52
|
+
function endedWithEmptySyntheticResume(messages: Message[]): boolean {
|
|
53
|
+
const lastAssistant = [...messages].reverse().find((message: any) => message?.role === "assistant") as any;
|
|
54
|
+
return (
|
|
55
|
+
lastAssistant?.provider === RESUME_PROVIDER &&
|
|
56
|
+
lastAssistant?.model === RESUME_MODEL_ID &&
|
|
57
|
+
lastAssistant?.stopReason === "stop" &&
|
|
58
|
+
Array.isArray(lastAssistant.content) &&
|
|
59
|
+
lastAssistant.content.length === 0
|
|
60
|
+
);
|
|
61
|
+
}
|
|
51
62
|
const SUBAGENT_DEPTH_ENV = "PI_SUBAGENT_DEPTH";
|
|
52
63
|
const SUBAGENT_MAX_DEPTH_ENV = "PI_SUBAGENT_MAX_DEPTH";
|
|
53
64
|
const SUBAGENT_STACK_ENV = "PI_SUBAGENT_STACK";
|
|
54
65
|
const SUBAGENT_PREVENT_CYCLES_ENV = "PI_SUBAGENT_PREVENT_CYCLES";
|
|
55
66
|
const SUBAGENT_FALLBACK_MODEL_ENV = "PI_SUBAGENT_FALLBACK_MODEL";
|
|
67
|
+
const RESUME_PROVIDER = "pi-subagent-resume";
|
|
68
|
+
const RESUME_MODEL_ID = "synthetic-tool-call";
|
|
56
69
|
// PI_OFFLINE intentionally removed: setting it on child processes blocks all API
|
|
57
70
|
// calls and renders subagents unable to do any LLM work. Children inherit the
|
|
58
71
|
// parent's PI_OFFLINE value via process.env spread if needed.
|
|
@@ -730,6 +743,13 @@ export async function runAgentSubprocess(opts: RunAgentOptions): Promise<SingleR
|
|
|
730
743
|
if (!result.stderr.trim()) result.stderr = "Subagent was aborted.";
|
|
731
744
|
}
|
|
732
745
|
|
|
746
|
+
if (result.exitCode === 0 && endedWithEmptySyntheticResume(result.messages)) {
|
|
747
|
+
result.exitCode = 1;
|
|
748
|
+
result.stopReason = "error";
|
|
749
|
+
result.errorMessage = "Subagent resume failed before the real model continued.";
|
|
750
|
+
if (!result.stderr.trim()) result.stderr = result.errorMessage;
|
|
751
|
+
}
|
|
752
|
+
|
|
733
753
|
if (result.exitCode === 0) {
|
|
734
754
|
const nestedErrorSummary = getNestedSubagentErrorSummary(result.messages);
|
|
735
755
|
if (nestedErrorSummary) {
|