shariq-pi-extensions 0.2.3 → 0.2.5

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.
@@ -6,7 +6,7 @@ Last verified: 2026-08-20
6
6
 
7
7
  ### [Antigravity Provider](../extensions/antigravity-provider/README.md)
8
8
 
9
- Registers the `antigravity` provider and `/login antigravity` flow for supported Google Antigravity models. Repeating login adds or updates accounts in the secure `<agent-dir>/antigravity/accounts.json` pool. Requests use quota-aware least-recently-used balancing, rotate before streaming on account-specific auth/rate/quota/capacity failures, honor known reset times, and refresh stored OAuth tokens. `/antigravity` shows account state and live model quotas and can enable or disable accounts; `/antigravity.doctor` reports sanitized provider and rotation diagnostics.
9
+ Registers the `antigravity` provider and `/login antigravity` flow for the latest Gemini Flash, Gemini Pro, Claude Sonnet, and Claude Opus families. Repeating login adds or updates accounts in the secure `<agent-dir>/antigravity/accounts.json` pool. Requests use quota-aware least-recently-used balancing, rotate before streaming on account-specific auth/rate/quota/capacity failures, honor known reset times, and refresh stored OAuth tokens. `/antigravity` shows account state plus the shared five-hour and weekly limits for the Gemini and Claude pools and can enable or disable accounts; `/antigravity.doctor` reports sanitized provider and rotation diagnostics.
10
10
 
11
11
  ### [Cursor provider](../extensions/cursor-provider/README.md)
12
12
 
@@ -8,19 +8,18 @@ Local persistent Pi provider for Google Antigravity-compatible models.
8
8
  - Account and quota dashboard: `/antigravity`
9
9
  - Doctor command: `/antigravity.doctor`
10
10
 
11
- The provider includes an IPv4 OAuth token-exchange fallback for Node environments where the default request fails. Its deterministic catalog follows Antigravity CLI 1.1.13 model identifiers and runtime behavior.
11
+ The provider includes an IPv4 OAuth token-exchange fallback for Node environments where the default request fails. Its curated catalog follows current Antigravity model identifiers and runtime behavior.
12
12
 
13
13
  Successful logins are added to `<agent-dir>/antigravity/accounts.json`, written with owner-only permissions. Existing Pi OAuth credentials are migrated into that pool without exposing token values. Requests select the least recently used eligible account, skip disabled/cooling/exhausted accounts, refresh expiring OAuth tokens, and rotate to another account when an auth, rate, quota, or capacity failure occurs before response streaming begins. Cached per-model remaining quota and reset times guide selection; `/antigravity` refreshes the authoritative catalog and can reversibly enable or disable accounts.
14
14
 
15
15
  Current public model IDs:
16
16
  - `antigravity/gemini-3.7-flash`
17
- - `antigravity/gemini-3.6-flash`
18
- - `antigravity/gemini-3.5-flash`
19
17
  - `antigravity/gemini-3.1-pro`
20
18
  - `antigravity/claude-sonnet-4-6`
21
19
  - `antigravity/claude-opus-4-6`
22
- - `antigravity/gpt-oss-120b`
23
20
 
24
- Pi's thinking selector is normalized to Antigravity's runtime IDs: Flash models clamp `off`/`minimal` to `low` and `xhigh`/`max` to `high`; Gemini 3.1 Pro clamps to its `low`/`high` pair; Claude and GPT-OSS use their single fixed-thinking runtime regardless of the selected Pi level.
21
+ The provider intentionally exposes only the latest Gemini Flash and Pro generations and the latest Claude Sonnet and Opus family. `/antigravity` shows the shared five-hour and weekly limits for the Gemini and Claude pools instead of listing every upstream runtime variant.
22
+
23
+ Pi's thinking selector is normalized to Antigravity's runtime IDs: Gemini 3.7 Flash clamps `off`/`minimal` to `low` and `xhigh`/`max` to `high`; Gemini 3.1 Pro clamps to its `low`/`high` pair; Claude models use their fixed-thinking runtime regardless of the selected Pi level.
25
24
 
26
25
  The provider id intentionally remains `antigravity`; existing `~/.pi/agent/auth.json` credentials continue to work without moving secret values. After updating `agy`, compare `agy models` with `antigravity/models.ts` before changing this deterministic catalog.
@@ -14,11 +14,13 @@ const CAPACITY_COOLDOWN_MS = 60 * 1_000;
14
14
  const REFRESH_SKEW_MS = 60 * 1_000;
15
15
 
16
16
  export type AntigravityQuotaGroup = "gemini" | "non-gemini";
17
+ export type AntigravityQuotaWindow = "five-hour" | "weekly";
17
18
 
18
19
  export interface AntigravityQuotaEntry {
19
20
  modelId: string;
20
21
  displayName?: string;
21
22
  group: AntigravityQuotaGroup;
23
+ window?: AntigravityQuotaWindow;
22
24
  remainingFraction: number;
23
25
  resetTime?: string;
24
26
  }
@@ -78,6 +80,7 @@ function normalizeAccount(value: unknown): AntigravityAccount | undefined {
78
80
  modelId: entry.modelId,
79
81
  displayName: typeof entry.displayName === "string" ? entry.displayName : undefined,
80
82
  group: entry.group,
83
+ window: entry.window === "five-hour" || entry.window === "weekly" ? entry.window : undefined,
81
84
  remainingFraction: Math.max(0, Math.min(1, remainingFraction)),
82
85
  resetTime: typeof entry.resetTime === "string" ? entry.resetTime : undefined,
83
86
  }];
@@ -281,7 +284,74 @@ export function updateAntigravityAccountQuota(id: string, quota: AntigravityQuot
281
284
  }
282
285
 
283
286
  export function parseAntigravityQuota(value: unknown): AntigravityQuotaEntry[] {
284
- const entries: AntigravityQuotaEntry[] = [];
287
+ const findGroups = (node: unknown): unknown[] | undefined => {
288
+ if (!node || typeof node !== "object") return undefined;
289
+ if (Array.isArray(node)) {
290
+ for (const item of node) {
291
+ const found = findGroups(item);
292
+ if (found) return found;
293
+ }
294
+ return undefined;
295
+ }
296
+ const raw = node as Record<string, unknown>;
297
+ if (Array.isArray(raw.groups)) return raw.groups;
298
+ for (const child of Object.values(raw)) {
299
+ const found = findGroups(child);
300
+ if (found) return found;
301
+ }
302
+ return undefined;
303
+ };
304
+
305
+ const summaryEntries: AntigravityQuotaEntry[] = [];
306
+ for (const groupValue of findGroups(value) ?? []) {
307
+ if (!groupValue || typeof groupValue !== "object") continue;
308
+ const group = groupValue as Record<string, unknown>;
309
+ const groupLabel = [group.displayName, group.name, group.id].find((item): item is string => typeof item === "string") ?? "";
310
+ const quotaGroup: AntigravityQuotaGroup | undefined = /gemini/i.test(groupLabel)
311
+ ? "gemini"
312
+ : /claude|third.party|3p|gpt/i.test(groupLabel)
313
+ ? "non-gemini"
314
+ : undefined;
315
+ if (!quotaGroup || !Array.isArray(group.buckets)) continue;
316
+ for (const bucketValue of group.buckets) {
317
+ if (!bucketValue || typeof bucketValue !== "object") continue;
318
+ const bucket = bucketValue as Record<string, unknown>;
319
+ const remaining = bucket.remaining && typeof bucket.remaining === "object"
320
+ ? bucket.remaining as Record<string, unknown>
321
+ : undefined;
322
+ const remainingFraction = finiteNumber(bucket.remainingFraction) ?? finiteNumber(remaining?.remainingFraction);
323
+ const windowLabel = [bucket.window, bucket.bucketId, bucket.displayName].find((item): item is string => typeof item === "string") ?? "";
324
+ const window: AntigravityQuotaWindow | undefined = /week/i.test(windowLabel)
325
+ ? "weekly"
326
+ : /5h|five.?hour/i.test(windowLabel)
327
+ ? "five-hour"
328
+ : undefined;
329
+ if (remainingFraction === undefined || !window) continue;
330
+ summaryEntries.push({
331
+ modelId: typeof bucket.bucketId === "string" ? bucket.bucketId : `${quotaGroup}-${window}`,
332
+ displayName: window === "weekly" ? "Weekly" : "5-Hour",
333
+ group: quotaGroup,
334
+ window,
335
+ remainingFraction: Math.max(0, Math.min(1, remainingFraction)),
336
+ resetTime: typeof bucket.resetTime === "string"
337
+ ? bucket.resetTime
338
+ : typeof remaining?.resetTime === "string"
339
+ ? remaining.resetTime
340
+ : undefined,
341
+ });
342
+ }
343
+ }
344
+ if (summaryEntries.length) return summaryEntries;
345
+
346
+ const supportedRuntimeIds = new Set(
347
+ Object.entries(ANTIGRAVITY_ROUTING).flatMap(([modelId, routing]) => [
348
+ modelId,
349
+ routing.off,
350
+ routing.defaultRequestId,
351
+ ...Object.values(routing.routing ?? {}),
352
+ ]).filter((item): item is string => typeof item === "string"),
353
+ );
354
+ const modelEntries: AntigravityQuotaEntry[] = [];
285
355
  const seen = new Set<string>();
286
356
  const visit = (node: unknown, keyHint?: string) => {
287
357
  if (!node || typeof node !== "object") return;
@@ -291,17 +361,17 @@ export function parseAntigravityQuota(value: unknown): AntigravityQuotaEntry[] {
291
361
  }
292
362
  const raw = node as Record<string, unknown>;
293
363
  const quota = raw.quotaInfo && typeof raw.quotaInfo === "object" ? raw.quotaInfo as Record<string, unknown> : raw;
294
- const remaining = finiteNumber(quota.remainingFraction);
295
- const modelId = [raw.modelId, raw.id, raw.name, raw.model, keyHint].find((item): item is string => typeof item === "string" && /gemini|claude|gpt-oss/i.test(item));
296
- if (modelId && remaining !== undefined) {
364
+ const remainingFraction = finiteNumber(quota.remainingFraction);
365
+ const modelId = [raw.modelId, raw.id, raw.name, raw.model, keyHint].find((item): item is string => typeof item === "string");
366
+ if (modelId && remainingFraction !== undefined) {
297
367
  const normalized = modelId.replace(/^models\//, "");
298
- if (!seen.has(normalized)) {
368
+ if (supportedRuntimeIds.has(normalized) && !seen.has(normalized)) {
299
369
  seen.add(normalized);
300
- entries.push({
370
+ modelEntries.push({
301
371
  modelId: normalized,
302
- displayName: typeof raw.displayName === "string" ? raw.displayName : typeof raw.label === "string" ? raw.label : undefined,
303
372
  group: normalized.startsWith("gemini-") ? "gemini" : "non-gemini",
304
- remainingFraction: Math.max(0, Math.min(1, remaining)),
373
+ window: "five-hour",
374
+ remainingFraction: Math.max(0, Math.min(1, remainingFraction)),
305
375
  resetTime: typeof quota.resetTime === "string" ? quota.resetTime : undefined,
306
376
  });
307
377
  }
@@ -311,7 +381,16 @@ export function parseAntigravityQuota(value: unknown): AntigravityQuotaEntry[] {
311
381
  }
312
382
  };
313
383
  visit(value);
314
- return entries;
384
+
385
+ return (["gemini", "non-gemini"] as const).flatMap((group) => {
386
+ const entries = modelEntries.filter((entry) => entry.group === group).sort((left, right) => left.remainingFraction - right.remainingFraction);
387
+ const mostConstrained = entries[0];
388
+ return mostConstrained ? [{
389
+ ...mostConstrained,
390
+ modelId: group === "gemini" ? "gemini-5h" : "claude-5h",
391
+ displayName: "5-Hour",
392
+ }] : [];
393
+ });
315
394
  }
316
395
 
317
396
  const refreshes = new Map<string, Promise<AntigravityAccount>>();
@@ -29,7 +29,22 @@ function until(timestamp: number | string | undefined) {
29
29
  }
30
30
 
31
31
  function groupQuota(quota: AntigravityQuotaEntry[] | undefined, group: "gemini" | "non-gemini") {
32
- return (quota ?? []).filter((entry) => entry.group === group).sort((left, right) => left.remainingFraction - right.remainingFraction);
32
+ const byWindow = new Map<"five-hour" | "weekly", AntigravityQuotaEntry>();
33
+ for (const entry of (quota ?? []).filter((candidate) => candidate.group === group)) {
34
+ const window = entry.window ?? "five-hour";
35
+ const existing = byWindow.get(window);
36
+ if (!existing || entry.remainingFraction < existing.remainingFraction) {
37
+ byWindow.set(window, {
38
+ ...entry,
39
+ window,
40
+ displayName: window === "weekly" ? "Weekly" : "5-Hour",
41
+ });
42
+ }
43
+ }
44
+ return (["five-hour", "weekly"] as const).flatMap((window) => {
45
+ const entry = byWindow.get(window);
46
+ return entry ? [entry] : [];
47
+ });
33
48
  }
34
49
 
35
50
  function accountState(account: AntigravityAccountStatus) {
@@ -50,8 +65,7 @@ function stateColor(state: string) {
50
65
 
51
66
  function compactGroup(label: string, entries: AntigravityQuotaEntry[]) {
52
67
  if (!entries.length) return `${label}: unavailable`;
53
- const worst = entries[0]!;
54
- return `${label}: ${remaining(worst.remainingFraction)} remaining${worst.resetTime ? ` · resets ${until(worst.resetTime)}` : ""}`;
68
+ return `${label}: ${entries.map((entry) => `${entry.displayName} ${remaining(entry.remainingFraction)}${entry.resetTime ? ` · resets ${until(entry.resetTime)}` : ""}`).join(" · ")}`;
55
69
  }
56
70
 
57
71
  export class AntigravityDashboard implements Component {
@@ -159,7 +173,7 @@ export class AntigravityDashboard implements Component {
159
173
  const lines = width >= 64
160
174
  ? [joinSides(title, `${right} `, width)]
161
175
  : [truncateToWidth(title, width, ""), truncateToWidth(` ${right}`, width, "")];
162
- lines.push(frameTop(this.theme, width, `ACCOUNTS ${accounts.length} · QUOTA REMAINING`));
176
+ lines.push(frameTop(this.theme, width, `ACCOUNTS ${accounts.length} · 5-HOUR + WEEKLY LIMITS REMAINING`));
163
177
  const inner = Math.max(1, width - 2);
164
178
  if (width >= 104 && selected) {
165
179
  const leftWidth = Math.max(46, Math.floor((inner - 1) * 0.45));
@@ -195,7 +209,7 @@ export class AntigravityDashboard implements Component {
195
209
  lines.push(selected ? this.theme.bg("selectedBg", padLine(first, width)) : first);
196
210
  if (!compact) {
197
211
  lines.push(` ${this.theme.fg("muted", compactGroup("Gemini", groupQuota(account.quota, "gemini")))}`);
198
- lines.push(` ${this.theme.fg("muted", compactGroup("Claude / GPT", groupQuota(account.quota, "non-gemini")))}`);
212
+ lines.push(` ${this.theme.fg("muted", compactGroup("Claude", groupQuota(account.quota, "non-gemini")))}`);
199
213
  lines.push("");
200
214
  }
201
215
  }
@@ -211,8 +225,8 @@ export class AntigravityDashboard implements Component {
211
225
  if (account.cooldownUntil && account.cooldownUntil > Date.now()) lines.push(` ${this.theme.fg("warning", `${account.cooldownReason || "rotation"} cooldown · ${until(account.cooldownUntil)}`)}`);
212
226
  if (account.quotaError) lines.push(` ${this.theme.fg("error", oneLine(account.quotaError))}`);
213
227
  if (account.lastError) lines.push(` ${this.theme.fg("warning", oneLine(account.lastError))}`);
214
- lines.push("", ...this.renderQuotaGroup("Gemini", groupQuota(account.quota, "gemini"), width));
215
- lines.push("", ...this.renderQuotaGroup("Claude / GPT-OSS", groupQuota(account.quota, "non-gemini"), width));
228
+ lines.push("", ...this.renderQuotaGroup("Gemini models", groupQuota(account.quota, "gemini"), width));
229
+ lines.push("", ...this.renderQuotaGroup("Claude models", groupQuota(account.quota, "non-gemini"), width));
216
230
  if (account.lastUsedAt) lines.push("", ` ${this.theme.fg("muted", "LAST USED")} ${this.theme.fg("text", new Date(account.lastUsedAt).toLocaleString())}`);
217
231
  return lines.slice(0, height);
218
232
  }
@@ -221,7 +235,7 @@ export class AntigravityDashboard implements Component {
221
235
  const lines = [` ${this.theme.fg("accent", this.theme.bold(`${name.toUpperCase()} · REMAINING`))}`];
222
236
  if (!entries.length) return [...lines, ` ${this.theme.fg("muted", "Unavailable")}`];
223
237
  const meterWidth = Math.max(10, width - 29);
224
- for (const entry of entries.slice(0, 8)) {
238
+ for (const entry of entries) {
225
239
  const percent = entry.remainingFraction * 100;
226
240
  const color = percent <= 0 ? "error" : percent <= 15 ? "warning" : "active";
227
241
  const label = oneLine(entry.displayName || entry.modelId).slice(0, 16).padEnd(16);
@@ -59,42 +59,6 @@ export const ANTIGRAVITY_ROUTING: Record<string, AntigravityRouting> = {
59
59
  },
60
60
  defaultRequestId: "gemini-3.7-flash-low",
61
61
  },
62
- "gemini-3.6-flash": {
63
- off: "gemini-3.6-flash-low",
64
- routing: {
65
- minimal: "gemini-3.6-flash-low",
66
- low: "gemini-3.6-flash-low",
67
- medium: "gemini-3.6-flash-medium",
68
- high: "gemini-3.6-flash-high",
69
- xhigh: "gemini-3.6-flash-high",
70
- max: "gemini-3.6-flash-high",
71
- },
72
- defaultRequestId: "gemini-3.6-flash-low",
73
- },
74
- "gemini-3.5-flash": {
75
- off: "gemini-3.5-flash-extra-low",
76
- routing: {
77
- minimal: "gemini-3.5-flash-extra-low",
78
- low: "gemini-3.5-flash-extra-low",
79
- medium: "gemini-3.5-flash-low",
80
- high: "gemini-3-flash-agent",
81
- xhigh: "gemini-3-flash-agent",
82
- max: "gemini-3-flash-agent",
83
- },
84
- defaultRequestId: "gemini-3.5-flash-low",
85
- },
86
- "gpt-oss-120b": {
87
- off: "gpt-oss-120b-medium",
88
- routing: {
89
- minimal: "gpt-oss-120b-medium",
90
- low: "gpt-oss-120b-medium",
91
- medium: "gpt-oss-120b-medium",
92
- high: "gpt-oss-120b-medium",
93
- xhigh: "gpt-oss-120b-medium",
94
- max: "gpt-oss-120b-medium",
95
- },
96
- defaultRequestId: "gpt-oss-120b-medium",
97
- },
98
62
  };
99
63
 
100
64
  export const ANTIGRAVITY_MODELS: ProviderModelConfig[] = [
@@ -138,36 +102,6 @@ export const ANTIGRAVITY_MODELS: ProviderModelConfig[] = [
138
102
  contextWindow: 1048576,
139
103
  maxTokens: 65536,
140
104
  },
141
- {
142
- id: "gemini-3.6-flash",
143
- name: "Gemini 3.6 Flash (Antigravity)",
144
- reasoning: true,
145
- thinkingLevelMap: { off: null, minimal: "LOW", low: "LOW", medium: "MEDIUM", high: "HIGH", xhigh: "HIGH", max: "HIGH" } as any,
146
- input: ["text", "image"],
147
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
148
- contextWindow: 1048576,
149
- maxTokens: 65536,
150
- },
151
- {
152
- id: "gemini-3.5-flash",
153
- name: "Gemini 3.5 Flash (Antigravity)",
154
- reasoning: true,
155
- thinkingLevelMap: { off: null, minimal: "LOW", low: "LOW", medium: "MEDIUM", high: "HIGH", xhigh: "HIGH", max: "HIGH" } as any,
156
- input: ["text", "image"],
157
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
158
- contextWindow: 1048576,
159
- maxTokens: 65536,
160
- },
161
- {
162
- id: "gpt-oss-120b",
163
- name: "GPT-OSS 120B (Antigravity)",
164
- reasoning: true,
165
- thinkingLevelMap: { off: null, minimal: "MEDIUM", low: "MEDIUM", medium: "MEDIUM", high: "MEDIUM", xhigh: "MEDIUM", max: "MEDIUM" } as any,
166
- input: ["text"],
167
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
168
- contextWindow: 131072,
169
- maxTokens: 32768,
170
- },
171
105
  ];
172
106
 
173
107
  export function getAntigravityRequestModelId(modelId: string, effort: string | undefined): string {
@@ -322,8 +322,28 @@ function findDynamicModel(value: any, requestedId: string): DynamicModelInfo | u
322
322
 
323
323
  export async function fetchAntigravityQuotaCatalog(token: string, projectId: string, signal?: AbortSignal): Promise<unknown> {
324
324
  let lastFailure = "Antigravity quota catalog is unavailable.";
325
- const bodies = [{}, { cloudaicompanionProject: projectId }, { project: projectId }];
326
325
  for (const endpoint of endpointCandidates()) {
326
+ try {
327
+ const res = await fetch(`${endpoint}/v1internal:retrieveUserQuotaSummary`, {
328
+ method: "POST",
329
+ headers: antigravityHeaders(token),
330
+ body: "{}",
331
+ signal: boundedFetchSignal(signal),
332
+ });
333
+ lastStatus = res.status;
334
+ lastEndpoint = endpoint;
335
+ if (res.ok) {
336
+ const summary = await res.json() as { groups?: unknown[] };
337
+ if (Array.isArray(summary.groups) && summary.groups.length) return summary;
338
+ }
339
+ else lastFailure = `HTTP ${res.status}: ${jsonOrTextError(await res.text())}`;
340
+ } catch (error) {
341
+ if (signal?.aborted) throw error;
342
+ lastFailure = safeError(error);
343
+ lastError = lastFailure;
344
+ }
345
+
346
+ const bodies = [{}, { cloudaicompanionProject: projectId }, { project: projectId }];
327
347
  for (const candidateBody of bodies) {
328
348
  try {
329
349
  const res = await fetch(`${endpoint}/v1internal:fetchAvailableModels`, {
@@ -20,7 +20,9 @@ export function refreshAntigravityQuotas(options: { force?: boolean; signal?: Ab
20
20
  const accounts = antigravityAccountStatuses(now);
21
21
  for (const account of accounts) {
22
22
  if (account.disabled) continue;
23
- if (!options.force && account.quotaUpdatedAt && now - account.quotaUpdatedAt < ANTIGRAVITY_QUOTA_TTL_MS) continue;
23
+ const hasBothWindows = account.quota?.some((entry) => entry.window === "five-hour") &&
24
+ account.quota.some((entry) => entry.window === "weekly");
25
+ if (!options.force && hasBothWindows && account.quotaUpdatedAt && now - account.quotaUpdatedAt < ANTIGRAVITY_QUOTA_TTL_MS) continue;
24
26
  try {
25
27
  const resolved = await resolveAntigravityAccount(account, refreshAntigravityToken, now);
26
28
  const catalog = await fetchAntigravityQuotaCatalog(resolved.access, resolved.projectId, options.signal);
@@ -480,7 +480,7 @@ export default function (pi: ExtensionAPI) {
480
480
  childCwd: cwd,
481
481
  parentTrusted: ctx.isProjectTrusted(),
482
482
  }),
483
- inheritedModel: ctx.model ? { provider: ctx.model.provider, id: ctx.model.id } : undefined,
483
+ inheritedModel: ctx.model ?? undefined,
484
484
  inheritedThinkingLevel: pi.getThinkingLevel(),
485
485
  inheritedMessages,
486
486
  parentSessionFile: ctx.sessionManager.getSessionFile(),
@@ -186,14 +186,17 @@ function createParentBridgeTools(task: SpawnTask): ToolDefinition[] {
186
186
  * then must be unambiguous across providers. No hint inherits the parent
187
187
  * model; with nothing to inherit, the SDK default applies.
188
188
  */
189
- function resolvePiModel(
189
+ export function resolvePiModel(
190
190
  registry: ModelRegistry,
191
191
  hint: string | undefined,
192
- inherited: { provider: string; id: string } | undefined,
192
+ inherited: Model<any> | undefined,
193
193
  ): Model<any> | undefined {
194
194
  if (!hint) {
195
195
  if (!inherited) return undefined;
196
- return registry.find(inherited.provider, inherited.id) ?? undefined;
196
+ return registry.find(inherited.provider, inherited.id) ?? inherited;
197
+ }
198
+ if (inherited?.id === hint) {
199
+ return registry.find(inherited.provider, hint) ?? inherited;
197
200
  }
198
201
  const slash = hint.indexOf("/");
199
202
  if (slash > 0) {
@@ -201,11 +204,13 @@ function resolvePiModel(
201
204
  const id = hint.slice(slash + 1);
202
205
  const found = registry.find(provider, id);
203
206
  if (found) return found;
207
+ if (inherited?.provider === provider && inherited.id === id) return inherited;
204
208
  throw new Error(`Unknown model "${hint}".`);
205
209
  }
206
210
  if (inherited) {
207
211
  const found = registry.find(inherited.provider, hint);
208
212
  if (found) return found;
213
+ if (inherited.id === hint) return inherited;
209
214
  }
210
215
  const matches = registry.getAll().filter((m) => m.id === hint);
211
216
  if (matches.length === 1) return matches[0];
@@ -6,7 +6,7 @@
6
6
  * `SubagentEvent` union.
7
7
  */
8
8
 
9
- import type { Message } from "@earendil-works/pi-ai";
9
+ import type { Message, Model } from "@earendil-works/pi-ai";
10
10
  import type { ModelRegistry } from "@earendil-works/pi-coding-agent";
11
11
  import { Data } from "effect";
12
12
  import type { CapabilityMode, IsolationMode } from "./config.ts";
@@ -50,7 +50,7 @@ export interface ParentContext {
50
50
  /** In-process bridge used by child-only message_parent/ask_parent tools. */
51
51
  readonly bridge?: ParentBridge;
52
52
  /** Parent pi model, for the pi backend's "inherit" default. */
53
- readonly inheritedModel?: { readonly provider: string; readonly id: string };
53
+ readonly inheritedModel?: Model<any>;
54
54
  readonly inheritedThinkingLevel?: string;
55
55
  /** Parent model registry; required by the pi backend to resolve models. */
56
56
  readonly modelRegistry?: ModelRegistry;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shariq-pi-extensions",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Cross-platform extension suite for the Pi coding agent.",
5
5
  "license": "MIT",
6
6
  "author": "Shariq Riaz",