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.
Files changed (2) hide show
  1. package/index.ts +55 -17
  2. 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 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
- });
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: "stop" as const,
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: "done", reason: "stop", message });
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
- pi.sendUserMessage(`Resuming ${plan.tasks.length} subagents...`);
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oira666_pi-subagent",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "description": "Subagent extension for Pi coding agent. Delegate tasks to specialized agents.",
5
5
  "type": "module",
6
6
  "main": "index.ts",