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.
Files changed (2) hide show
  1. package/index.ts +50 -22
  2. 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 restore = modelToRestoreAfterResume;
516
- const auth = resumeModelRegistry
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: "stop" as const,
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: "done", reason: "stop", message });
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oira666_pi-subagent",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "Subagent extension for Pi coding agent. Delegate tasks to specialized agents.",
5
5
  "type": "module",
6
6
  "main": "index.ts",