oira666_pi-subagent 0.2.8 → 0.2.10
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 +55 -17
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -372,6 +372,8 @@ function hasCliInitialPrompt(argv: string[]): boolean {
|
|
|
372
372
|
const RESUME_PROVIDER = "pi-subagent-resume";
|
|
373
373
|
const RESUME_MODEL_ID = "synthetic-tool-call";
|
|
374
374
|
const RESUME_STATE_KEY = "__piSubagentResumeState";
|
|
375
|
+
const SUBAGENT_FALLBACK_MODEL_ENV = "PI_SUBAGENT_FALLBACK_MODEL";
|
|
376
|
+
const RESUME_INTERACTIVE_DELAY_MS = 250;
|
|
375
377
|
|
|
376
378
|
type SyntheticResumeState = {
|
|
377
379
|
plan: ResumableSubagentCall | null;
|
|
@@ -449,9 +451,18 @@ function findLastNonResumeModel(ctx: any): any | undefined {
|
|
|
449
451
|
return undefined;
|
|
450
452
|
}
|
|
451
453
|
|
|
454
|
+
function getEnvFallbackModel(ctx: any): any | undefined {
|
|
455
|
+
const raw = process.env[SUBAGENT_FALLBACK_MODEL_ENV];
|
|
456
|
+
if (!raw || !raw.includes("/")) return undefined;
|
|
457
|
+
const [provider, ...idParts] = raw.split("/");
|
|
458
|
+
const id = idParts.join("/");
|
|
459
|
+
if (!provider || !id) return undefined;
|
|
460
|
+
return ctx.modelRegistry?.find?.(provider, id);
|
|
461
|
+
}
|
|
462
|
+
|
|
452
463
|
function getRestorableModel(ctx: any): any | undefined {
|
|
453
464
|
if (ctx.model?.provider && ctx.model.provider !== RESUME_PROVIDER) return ctx.model;
|
|
454
|
-
return findLastNonResumeModel(ctx);
|
|
465
|
+
return findLastNonResumeModel(ctx) ?? getEnvFallbackModel(ctx);
|
|
455
466
|
}
|
|
456
467
|
|
|
457
468
|
// ---------------------------------------------------------------------------
|
|
@@ -460,6 +471,22 @@ function getRestorableModel(ctx: any): any | undefined {
|
|
|
460
471
|
|
|
461
472
|
export default function (pi: ExtensionAPI) {
|
|
462
473
|
let resumeModelRegistry: any | undefined;
|
|
474
|
+
let lastRestorableModel: any | undefined;
|
|
475
|
+
|
|
476
|
+
async function streamWithRealModelFallback(context: any, options: any, fallback: any) {
|
|
477
|
+
if (!fallback) return null;
|
|
478
|
+
const auth = resumeModelRegistry
|
|
479
|
+
? await resumeModelRegistry.getApiKeyAndHeaders(fallback)
|
|
480
|
+
: { ok: true, apiKey: undefined, headers: undefined };
|
|
481
|
+
if (!auth.ok) {
|
|
482
|
+
throw new Error(auth.error);
|
|
483
|
+
}
|
|
484
|
+
return streamModelSimple(fallback, context, {
|
|
485
|
+
...options,
|
|
486
|
+
apiKey: auth.apiKey,
|
|
487
|
+
headers: auth.headers,
|
|
488
|
+
});
|
|
489
|
+
}
|
|
463
490
|
|
|
464
491
|
pi.registerFlag("subagent-max-depth", {
|
|
465
492
|
description: "Maximum allowed subagent delegation depth (default: 3).",
|
|
@@ -512,37 +539,31 @@ export default function (pi: ExtensionAPI) {
|
|
|
512
539
|
}
|
|
513
540
|
|
|
514
541
|
if (phase === "final" && modelToRestoreAfterResume) {
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
? await resumeModelRegistry.getApiKeyAndHeaders(restore)
|
|
518
|
-
: { ok: true, apiKey: undefined, headers: undefined };
|
|
519
|
-
if (!auth.ok) {
|
|
520
|
-
throw new Error(auth.error);
|
|
521
|
-
}
|
|
522
|
-
return streamModelSimple(restore, context, {
|
|
523
|
-
...options,
|
|
524
|
-
apiKey: auth.apiKey,
|
|
525
|
-
headers: auth.headers,
|
|
526
|
-
});
|
|
542
|
+
const delegated = await streamWithRealModelFallback(context, options, modelToRestoreAfterResume);
|
|
543
|
+
if (delegated) return delegated;
|
|
527
544
|
}
|
|
528
545
|
|
|
546
|
+
const fallback = await streamWithRealModelFallback(context, options, lastRestorableModel);
|
|
547
|
+
if (fallback) return fallback;
|
|
548
|
+
|
|
529
549
|
if (!(plan && phase === "tool")) {
|
|
530
550
|
state.plan = null;
|
|
531
551
|
state.phase = "tool";
|
|
532
552
|
}
|
|
533
553
|
const message = {
|
|
534
554
|
role: "assistant" as const,
|
|
535
|
-
content: [],
|
|
555
|
+
content: [{ type: "text" as const, text: "Subagent resume failed: synthetic resume provider was invoked without a valid resume plan." }],
|
|
536
556
|
api: model.api,
|
|
537
557
|
provider: model.provider,
|
|
538
558
|
model: model.id,
|
|
539
559
|
usage: emptyModelUsage(),
|
|
540
|
-
stopReason: "
|
|
560
|
+
stopReason: "error" as const,
|
|
561
|
+
errorMessage: "Subagent resume failed: synthetic resume provider was invoked without a valid resume plan.",
|
|
541
562
|
timestamp: Date.now(),
|
|
542
563
|
};
|
|
543
564
|
queueMicrotask(() => {
|
|
544
565
|
stream.push({ type: "start", partial: message });
|
|
545
|
-
stream.push({ type: "
|
|
566
|
+
stream.push({ type: "error", reason: "error", error: message });
|
|
546
567
|
stream.end(message);
|
|
547
568
|
});
|
|
548
569
|
return stream;
|
|
@@ -601,6 +622,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
601
622
|
// nested subagents that can no longer delegate. Those leaf processes
|
|
602
623
|
// still need the real model to continue their own work.
|
|
603
624
|
const restorableModel = getRestorableModel(ctx);
|
|
625
|
+
if (restorableModel) {
|
|
626
|
+
lastRestorableModel = restorableModel;
|
|
627
|
+
resumeModelRegistry = ctx.modelRegistry;
|
|
628
|
+
}
|
|
604
629
|
if (ctx.model?.provider === RESUME_PROVIDER && restorableModel) {
|
|
605
630
|
await pi.setModel(restorableModel);
|
|
606
631
|
}
|
|
@@ -660,7 +685,20 @@ export default function (pi: ExtensionAPI) {
|
|
|
660
685
|
if (hasCliInitialPrompt(process.argv)) {
|
|
661
686
|
if (ctx.hasUI) ctx.ui.notify(`Resuming ${plan.tasks.length} subagents...`, "info");
|
|
662
687
|
} else {
|
|
663
|
-
|
|
688
|
+
// Do not start the synthetic resume turn synchronously from
|
|
689
|
+
// session_start. In interactive mode Pi may still be rebuilding the
|
|
690
|
+
// resumed chat UI; if the tool starts before that finishes, later
|
|
691
|
+
// renderSessionContext() can clear the live pending tool component and
|
|
692
|
+
// all real-time tool updates disappear. A short macrotask delay lets
|
|
693
|
+
// the normal "Resumed session" render settle first.
|
|
694
|
+
setTimeout(() => {
|
|
695
|
+
try {
|
|
696
|
+
pi.sendUserMessage(`Resuming ${plan.tasks.length} subagents...`);
|
|
697
|
+
} catch (err) {
|
|
698
|
+
console.error("[pi-subagent] Failed to start deferred resume turn:", err);
|
|
699
|
+
void restoreModelAfterResumeFailure(ctx);
|
|
700
|
+
}
|
|
701
|
+
}, RESUME_INTERACTIVE_DELAY_MS);
|
|
664
702
|
}
|
|
665
703
|
} catch (err) {
|
|
666
704
|
console.error("[pi-subagent] Error in session_start:", err);
|