oira666_pi-subagent 0.2.8 → 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 +40 -16
  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;
@@ -601,6 +621,10 @@ export default function (pi: ExtensionAPI) {
601
621
  // nested subagents that can no longer delegate. Those leaf processes
602
622
  // still need the real model to continue their own work.
603
623
  const restorableModel = getRestorableModel(ctx);
624
+ if (restorableModel) {
625
+ lastRestorableModel = restorableModel;
626
+ resumeModelRegistry = ctx.modelRegistry;
627
+ }
604
628
  if (ctx.model?.provider === RESUME_PROVIDER && restorableModel) {
605
629
  await pi.setModel(restorableModel);
606
630
  }
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.9",
4
4
  "description": "Subagent extension for Pi coding agent. Delegate tasks to specialized agents.",
5
5
  "type": "module",
6
6
  "main": "index.ts",