oira666_pi-subagent 0.2.7 → 0.2.9
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 +50 -22
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -372,6 +372,7 @@ 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";
|
|
375
376
|
|
|
376
377
|
type SyntheticResumeState = {
|
|
377
378
|
plan: ResumableSubagentCall | null;
|
|
@@ -449,9 +450,18 @@ function findLastNonResumeModel(ctx: any): any | undefined {
|
|
|
449
450
|
return undefined;
|
|
450
451
|
}
|
|
451
452
|
|
|
453
|
+
function getEnvFallbackModel(ctx: any): any | undefined {
|
|
454
|
+
const raw = process.env[SUBAGENT_FALLBACK_MODEL_ENV];
|
|
455
|
+
if (!raw || !raw.includes("/")) return undefined;
|
|
456
|
+
const [provider, ...idParts] = raw.split("/");
|
|
457
|
+
const id = idParts.join("/");
|
|
458
|
+
if (!provider || !id) return undefined;
|
|
459
|
+
return ctx.modelRegistry?.find?.(provider, id);
|
|
460
|
+
}
|
|
461
|
+
|
|
452
462
|
function getRestorableModel(ctx: any): any | undefined {
|
|
453
463
|
if (ctx.model?.provider && ctx.model.provider !== RESUME_PROVIDER) return ctx.model;
|
|
454
|
-
return findLastNonResumeModel(ctx);
|
|
464
|
+
return findLastNonResumeModel(ctx) ?? getEnvFallbackModel(ctx);
|
|
455
465
|
}
|
|
456
466
|
|
|
457
467
|
// ---------------------------------------------------------------------------
|
|
@@ -460,6 +470,22 @@ function getRestorableModel(ctx: any): any | undefined {
|
|
|
460
470
|
|
|
461
471
|
export default function (pi: ExtensionAPI) {
|
|
462
472
|
let resumeModelRegistry: any | undefined;
|
|
473
|
+
let lastRestorableModel: any | undefined;
|
|
474
|
+
|
|
475
|
+
async function streamWithRealModelFallback(context: any, options: any, fallback: any) {
|
|
476
|
+
if (!fallback) return null;
|
|
477
|
+
const auth = resumeModelRegistry
|
|
478
|
+
? await resumeModelRegistry.getApiKeyAndHeaders(fallback)
|
|
479
|
+
: { ok: true, apiKey: undefined, headers: undefined };
|
|
480
|
+
if (!auth.ok) {
|
|
481
|
+
throw new Error(auth.error);
|
|
482
|
+
}
|
|
483
|
+
return streamModelSimple(fallback, context, {
|
|
484
|
+
...options,
|
|
485
|
+
apiKey: auth.apiKey,
|
|
486
|
+
headers: auth.headers,
|
|
487
|
+
});
|
|
488
|
+
}
|
|
463
489
|
|
|
464
490
|
pi.registerFlag("subagent-max-depth", {
|
|
465
491
|
description: "Maximum allowed subagent delegation depth (default: 3).",
|
|
@@ -512,37 +538,31 @@ export default function (pi: ExtensionAPI) {
|
|
|
512
538
|
}
|
|
513
539
|
|
|
514
540
|
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
|
-
});
|
|
541
|
+
const delegated = await streamWithRealModelFallback(context, options, modelToRestoreAfterResume);
|
|
542
|
+
if (delegated) return delegated;
|
|
527
543
|
}
|
|
528
544
|
|
|
545
|
+
const fallback = await streamWithRealModelFallback(context, options, lastRestorableModel);
|
|
546
|
+
if (fallback) return fallback;
|
|
547
|
+
|
|
529
548
|
if (!(plan && phase === "tool")) {
|
|
530
549
|
state.plan = null;
|
|
531
550
|
state.phase = "tool";
|
|
532
551
|
}
|
|
533
552
|
const message = {
|
|
534
553
|
role: "assistant" as const,
|
|
535
|
-
content: [],
|
|
554
|
+
content: [{ type: "text" as const, text: "Subagent resume failed: synthetic resume provider was invoked without a valid resume plan." }],
|
|
536
555
|
api: model.api,
|
|
537
556
|
provider: model.provider,
|
|
538
557
|
model: model.id,
|
|
539
558
|
usage: emptyModelUsage(),
|
|
540
|
-
stopReason: "
|
|
559
|
+
stopReason: "error" as const,
|
|
560
|
+
errorMessage: "Subagent resume failed: synthetic resume provider was invoked without a valid resume plan.",
|
|
541
561
|
timestamp: Date.now(),
|
|
542
562
|
};
|
|
543
563
|
queueMicrotask(() => {
|
|
544
564
|
stream.push({ type: "start", partial: message });
|
|
545
|
-
stream.push({ type: "
|
|
565
|
+
stream.push({ type: "error", reason: "error", error: message });
|
|
546
566
|
stream.end(message);
|
|
547
567
|
});
|
|
548
568
|
return stream;
|
|
@@ -596,8 +616,21 @@ export default function (pi: ExtensionAPI) {
|
|
|
596
616
|
|
|
597
617
|
// Auto-discover agents on session start
|
|
598
618
|
pi.on("session_start", async (event, ctx) => {
|
|
599
|
-
if (!canDelegate) return;
|
|
600
619
|
try {
|
|
620
|
+
// Always repair sessions left on the synthetic resume model, even in
|
|
621
|
+
// nested subagents that can no longer delegate. Those leaf processes
|
|
622
|
+
// still need the real model to continue their own work.
|
|
623
|
+
const restorableModel = getRestorableModel(ctx);
|
|
624
|
+
if (restorableModel) {
|
|
625
|
+
lastRestorableModel = restorableModel;
|
|
626
|
+
resumeModelRegistry = ctx.modelRegistry;
|
|
627
|
+
}
|
|
628
|
+
if (ctx.model?.provider === RESUME_PROVIDER && restorableModel) {
|
|
629
|
+
await pi.setModel(restorableModel);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
if (!canDelegate) return;
|
|
633
|
+
|
|
601
634
|
const discovery = discoverAgents(ctx.cwd, "both");
|
|
602
635
|
discoveredAgents = discovery.agents;
|
|
603
636
|
currentSessionId = ctx.sessionManager.getSessionId?.() ?? "ephemeral";
|
|
@@ -613,11 +646,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
613
646
|
);
|
|
614
647
|
}
|
|
615
648
|
|
|
616
|
-
const restorableModel = getRestorableModel(ctx);
|
|
617
|
-
if (ctx.model?.provider === RESUME_PROVIDER && restorableModel) {
|
|
618
|
-
await pi.setModel(restorableModel);
|
|
619
|
-
}
|
|
620
|
-
|
|
621
649
|
const resumeDisabled = parseBooleanEnv(process.env[SUBAGENT_RESUME_DISABLE_ENV]) === true;
|
|
622
650
|
if (resumeDisabled || (event.reason !== "resume" && event.reason !== "startup")) return;
|
|
623
651
|
|