@hyav/pi-provider 0.1.8 → 0.2.0

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.
@@ -1,11 +1,11 @@
1
1
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
2
  import type { LiveCheckDiagnostics } from "./live-check-manager.ts";
3
+ import type { PiCatalogModelMeta } from "./pi-model-metadata.ts";
3
4
  import type { PreflightAdapter, PreflightDiagnostics } from "./preflight-manager.ts";
4
5
  import type { StatusDiagnostics } from "./status-manager.ts";
5
6
  import type {
6
7
  ModelFieldSource,
7
8
  ModelMetadataStatus,
8
- ModelQualityScore,
9
9
  ProviderAdapter,
10
10
  ProviderCost,
11
11
  ProviderModelMetadata,
@@ -39,6 +39,10 @@ export interface StatusReportOptions {
39
39
  showLiveCheckScope?: boolean;
40
40
  modelMetadata?: ProviderModelMetadata;
41
41
  metadataStatus?: ModelMetadataStatus;
42
+ piCatalogMatch?: {
43
+ matchedModel?: PiCatalogModelMeta;
44
+ matchType: string;
45
+ };
42
46
  }
43
47
 
44
48
  interface ReportIssue {
@@ -91,7 +95,10 @@ function formatTokens(count: number | undefined): string {
91
95
 
92
96
  function formatNumber(value: number): string {
93
97
  if (!Number.isFinite(value)) return "unknown";
94
- return value.toFixed(2).replace(/\.?(0+)$/, "");
98
+ const normalized = Object.is(value, -0) ? 0 : value;
99
+ const formatted = normalized.toFixed(2).replace(/\.?(0+)$/, "");
100
+ if (formatted === "-0" || formatted === "") return "0";
101
+ return formatted;
95
102
  }
96
103
 
97
104
  function formatAge(now: number, timestamp: number): string {
@@ -130,91 +137,102 @@ function formatRate(value: number): string {
130
137
  return `$${value.toFixed(decimals).replace(/\.?(0+)$/, "")}`;
131
138
  }
132
139
 
133
- function formatPricing(model: ActiveModel, metadata?: ProviderModelMetadata): string {
140
+ const HEADER_LABEL_WIDTH = 10;
141
+ const CHECK_LABEL_WIDTH = 14;
142
+ const MODEL_LABEL_WIDTH = 14;
143
+
144
+ function formatHeaderRow(label: string, value: string): string {
145
+ return `${label.padEnd(HEADER_LABEL_WIDTH)}${value}`;
146
+ }
147
+
148
+ function formatCheckRow(label: string, value: string): string {
149
+ return ` ${label.padEnd(CHECK_LABEL_WIDTH)}${value}`;
150
+ }
151
+
152
+ function formatCheckDetail(value: string): string {
153
+ return ` ${"".padEnd(CHECK_LABEL_WIDTH)}${value}`;
154
+ }
155
+
156
+ function formatModelRow(label: string, value: string): string {
157
+ return ` ${label.padEnd(MODEL_LABEL_WIDTH)}${value}`;
158
+ }
159
+
160
+ function formatModelContinuation(value: string): string {
161
+ return formatModelRow("", value);
162
+ }
163
+
164
+ function formatCostRateLines(cost: ProviderCost): string[] {
165
+ const primary = [`input ${formatRate(cost.input)}`, `output ${formatRate(cost.output)}`];
166
+ const cache: string[] = [];
167
+ if (cost.cacheRead > 0) cache.push(`cache read ${formatRate(cost.cacheRead)}`);
168
+ if (cost.cacheWrite > 0) cache.push(`cache write ${formatRate(cost.cacheWrite)}`);
169
+ return [primary.join(" · "), ...(cache.length > 0 ? [cache.join(" · ")] : [])];
170
+ }
171
+
172
+ function formatPricingLines(
173
+ model: ActiveModel,
174
+ metadata: ProviderModelMetadata | undefined,
175
+ pricingSource: string,
176
+ ): string[] {
134
177
  const cost = model.cost;
135
178
  const knownFree = metadata?.pricing.known === true && cost !== undefined;
136
179
  if (
137
180
  !cost ||
138
181
  (!knownFree && cost.input === 0 && cost.output === 0 && cost.cacheRead === 0 && cost.cacheWrite === 0)
139
182
  ) {
140
- return "unavailable";
183
+ return [formatModelRow("Pricing", `unavailable${pricingSource}`)];
141
184
  }
142
- const rates = [`${formatRate(cost.input)} input`, `${formatRate(cost.output)} output`];
143
- if (cost.cacheRead > 0) rates.push(`${formatRate(cost.cacheRead)} cache read`);
144
- if (cost.cacheWrite > 0) rates.push(`${formatRate(cost.cacheWrite)} cache write`);
145
- return `${rates.join(" / ")} per 1M tokens`;
146
- }
147
-
148
- function formatPricingTier(tier: NonNullable<ProviderCost["tiers"]>[number]): string {
149
- const rates = [`${formatRate(tier.input)} input`, `${formatRate(tier.output)} output`];
150
- if (tier.cacheRead > 0) rates.push(`${formatRate(tier.cacheRead)} cache read`);
151
- if (tier.cacheWrite > 0) rates.push(`${formatRate(tier.cacheWrite)} cache write`);
152
- return `above ${formatTokens(tier.inputTokensAbove)} · ${rates.join(" / ")} per 1M tokens`;
185
+ const rateLines = formatCostRateLines(cost);
186
+ return [
187
+ formatModelRow("Pricing", rateLines[0] ?? "unavailable"),
188
+ ...rateLines.slice(1).map(formatModelContinuation),
189
+ formatModelContinuation(`per 1M tokens${pricingSource}`),
190
+ ];
153
191
  }
154
192
 
155
- function formatQuality(quality: ModelQualityScore[], status: ModelMetadataStatus | undefined, now: number): string[] {
156
- const scores = quality.filter(
157
- (score) => score.source === "artificial-analysis" && score.benchmark === "Artificial Analysis",
158
- );
159
- if (scores.length === 0) return [];
160
- const statusParts = [`Status: ${status?.state ?? "available"}`];
161
- if (status?.updatedAt !== undefined) statusParts.push(formatAge(now, status.updatedAt));
193
+ function formatPricingTierLines(tier: NonNullable<ProviderCost["tiers"]>[number]): string[] {
194
+ const rateLines = formatCostRateLines(tier);
162
195
  return [
163
- statusParts.join(" · "),
164
- `Source: ${status?.source ?? "AA/OpenRouter"}`,
165
- `Indices: ${scores.map((score) => `${score.category} ${formatNumber(score.value)}`).join(" · ")}`,
196
+ formatModelRow(`Tier >${formatTokens(tier.inputTokensAbove)}`, rateLines[0] ?? "unknown"),
197
+ ...rateLines.slice(1).map(formatModelContinuation),
198
+ formatModelContinuation("per 1M tokens"),
166
199
  ];
167
200
  }
168
201
 
169
202
  function formatModelFieldSource(source: ModelFieldSource | undefined): string {
170
- if (source === undefined) return "";
203
+ if (source === undefined || source === "provider") return "";
171
204
  const label =
172
205
  source === "native"
173
206
  ? "Pi native"
174
- : source === "provider"
175
- ? "Provider catalog"
176
- : source === "official"
177
- ? "OpenRouter"
178
- : source === "fallback"
179
- ? "Provider fallback"
180
- : source === "normalized"
181
- ? "Normalized catalog value"
207
+ : source === "pi"
208
+ ? "Pi catalog"
209
+ : source === "fallback"
210
+ ? "Provider fallback"
211
+ : source === "normalized"
212
+ ? "Normalized catalog value"
213
+ : source === "mixed"
214
+ ? "mixed"
182
215
  : "Pi default";
183
216
  return ` · ${label}`;
184
217
  }
185
218
 
186
219
  function formatPricingSource(metadata: ProviderModelMetadata): string {
187
220
  const source =
188
- metadata.pricing.source === "provider"
189
- ? "Provider catalog"
190
- : metadata.pricing.source === "fallback"
191
- ? "Provider fallback"
192
- : metadata.pricing.source === "official"
193
- ? "OpenRouter"
221
+ metadata.pricing.source === "fallback"
222
+ ? "Provider fallback"
223
+ : metadata.pricing.source === "pi"
224
+ ? "Pi catalog"
225
+ : metadata.pricing.source === "mixed"
226
+ ? "mixed"
194
227
  : metadata.pricing.source === "native"
195
228
  ? "Pi native"
196
229
  : undefined;
197
230
  const parts = source ? [source] : [];
198
231
  if (metadata.pricing.adjustment) parts.push(metadata.pricing.adjustment.label);
199
- if (metadata.pricing.known) parts.push("estimate");
232
+ if (metadata.pricing.known && metadata.pricing.source !== "provider") parts.push("estimate");
200
233
  return parts.length > 0 ? ` · ${parts.join(" · ")}` : "";
201
234
  }
202
235
 
203
- function formatQualitySource(
204
- quality: ModelQualityScore[] | undefined,
205
- status: ModelMetadataStatus | undefined,
206
- now: number,
207
- ): string[] {
208
- const scores = quality ? formatQuality(quality, status, now) : [];
209
- if (scores.length > 0) return scores;
210
- if (!status?.source) return [];
211
- return [
212
- status.source === "AA/OpenRouter"
213
- ? "Status: unavailable · no AA/OpenRouter metric"
214
- : "Status: unavailable · no public score",
215
- ];
216
- }
217
-
218
236
  const REASONING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"] as const;
219
237
 
220
238
  function getSupportedReasoningLevels(model: ActiveModel): string[] {
@@ -226,10 +244,22 @@ function getSupportedReasoningLevels(model: ActiveModel): string[] {
226
244
  });
227
245
  }
228
246
 
229
- function formatReasoning(model: ActiveModel): string {
230
- if (!model.reasoning) return "not supported";
247
+ function formatThinkingLevelsLine(model: ActiveModel, metadata?: ProviderModelMetadata): string {
248
+ const fieldSources = metadata?.fieldSources;
249
+ if (model.reasoning === false) {
250
+ return formatModelRow("Thinking", `not supported${formatModelFieldSource(fieldSources?.reasoning)}`);
251
+ }
231
252
  const levels = getSupportedReasoningLevels(model);
232
- return levels.length > 0 ? `supported (${levels.join(", ")})` : "supported";
253
+ if (levels.length > 0) {
254
+ return formatModelRow(
255
+ "Thinking",
256
+ `${levels.join(", ")}${formatModelFieldSource(fieldSources?.thinkingLevelMap)}`,
257
+ );
258
+ }
259
+ if (model.reasoning === true) {
260
+ return formatModelRow("Thinking", `supported${formatModelFieldSource(fieldSources?.reasoning)}`);
261
+ }
262
+ return formatModelRow("Thinking", "unavailable");
233
263
  }
234
264
 
235
265
  export function resolveNativeProvider(modelRegistry: NativeProviderRegistry, providerId: string): NativeProviderLookup {
@@ -264,32 +294,40 @@ function formatCatalog(
264
294
  nativeProvider: NativeProvider | undefined,
265
295
  nativeLookupAvailable: boolean,
266
296
  now: number,
267
- ): { lines: string[]; issue: ReportIssue } {
297
+ ): { summary: string; detailLines: string[]; issue: ReportIssue } {
268
298
  if (!adapter) {
269
- if (!nativeLookupAvailable) return { lines: ["Status: not managed by Pi Provider"], issue: { level: "none" } };
270
- if (!nativeProvider) return { lines: ["Status: unavailable in Pi"], issue: { level: "none" } };
299
+ if (!nativeLookupAvailable) {
300
+ return { summary: "not managed by Pi Provider", detailLines: [], issue: { level: "none" } };
301
+ }
302
+ if (!nativeProvider) {
303
+ return { summary: "unavailable in Pi", detailLines: [], issue: { level: "none" } };
304
+ }
271
305
  const count = getNativeModelCount(nativeProvider);
306
+ const countStr = count === undefined ? "unknown" : `${count} ${count === 1 ? "model" : "models"}`;
272
307
  return {
273
- lines: ["Status: static · Pi native", `Models: ${count === undefined ? "unknown" : count}`],
308
+ summary: `static · Pi native · ${countStr}`,
309
+ detailLines: [],
274
310
  issue: { level: "none" },
275
311
  };
276
312
  }
277
313
  const catalog = adapter.catalog;
278
314
  const count = catalog?.modelCount ?? adapter.provider.models.length;
315
+ const countStr = `${count} ${count === 1 ? "model" : "models"}`;
279
316
  const source = catalog?.source ?? "static";
280
317
  const freshness = catalog?.lastError ? "stale" : catalog?.updatedAt !== undefined ? "fresh" : undefined;
281
- const statusParts = freshness ? [freshness, source] : [source];
318
+ const statusParts = freshness ? [freshness, source, countStr] : [source, countStr];
282
319
  if (catalog?.updatedAt !== undefined) statusParts.push(formatAge(now, catalog.updatedAt));
283
- const lines = [`Status: ${statusParts.join(" · ")}`, `Models: ${count}`];
320
+ const summary = statusParts.join(" · ");
321
+ const detailLines: string[] = [];
284
322
  const rejectedCount = catalog?.rejectedCount ?? 0;
285
323
  const duplicateCount = catalog?.duplicateCount ?? 0;
286
324
  if (rejectedCount > 0 || duplicateCount > 0) {
287
- lines.push(`Skipped: ${rejectedCount} invalid · ${duplicateCount} duplicate`);
325
+ detailLines.push(`Skipped: ${rejectedCount} invalid · ${duplicateCount} duplicate`);
288
326
  }
289
- if (catalog?.lastError) lines.push(`Error: ${catalog.lastError}`);
327
+ if (catalog?.lastError) detailLines.push(`Error: ${catalog.lastError}`);
290
328
  if (catalog?.lastError && catalog.nextRetryAt !== undefined) {
291
329
  const failures = catalog.consecutiveFailures ?? 1;
292
- lines.push(
330
+ detailLines.push(
293
331
  `Retry: ${formatUntil(now, catalog.nextRetryAt)} · ${failures} consecutive failure${failures === 1 ? "" : "s"}`,
294
332
  );
295
333
  }
@@ -300,7 +338,7 @@ function formatCatalog(
300
338
  key: `catalog:${catalog.lastError}`,
301
339
  }
302
340
  : { level: "none" as const };
303
- return { lines, issue };
341
+ return { summary, detailLines, issue };
304
342
  }
305
343
 
306
344
  function classifyError(code: string, httpStatus: number | undefined): StatusWarningLevel {
@@ -328,8 +366,8 @@ function scopeIssue(issue: ReportIssue, scope: string): ReportIssue {
328
366
  return issue.level === "none" ? issue : { ...issue, key: `${scope}:${issue.key ?? issue.level}` };
329
367
  }
330
368
 
331
- function indentLines(lines: string[]): string[] {
332
- return lines.map((line) => (line === "" ? "" : ` ${line}`));
369
+ function stripSummaryPrefix(value: string, prefix: string): string {
370
+ return value.startsWith(prefix) ? value.slice(prefix.length) : value;
333
371
  }
334
372
 
335
373
  function formatStatusAmount(entry: StatusAmountEntry): string {
@@ -345,116 +383,57 @@ function formatStatusEntry(entry: StatusEntry): string {
345
383
  return `${entry.label}: ${remaining}${entry.resetAt !== undefined ? ` · reset at ${formatDateTime(entry.resetAt)}` : ""}`;
346
384
  }
347
385
 
348
- function appendStatusReport(
349
- lines: string[],
386
+ function formatAccount(
350
387
  status: StatusAdapter | undefined,
351
388
  diagnostics: StatusDiagnostics | undefined,
352
389
  authConfigured: boolean,
353
390
  now: number,
354
- ): ReportIssue {
391
+ ): { summary: string; detailLines: string[]; issue: ReportIssue } {
355
392
  if (!status) {
356
- lines.push("Status: not supported");
357
- return { level: "none" };
393
+ return { summary: "Account: not supported", detailLines: [], issue: { level: "none" } };
358
394
  }
359
395
  if (!authConfigured) {
360
- lines.push("Status: unavailable · auth missing");
361
- return { level: "none" };
396
+ return { summary: "Account: unavailable · auth missing", detailLines: [], issue: { level: "none" } };
362
397
  }
398
+ let summary: string;
399
+ const detailLines: string[] = [];
400
+ let issue: ReportIssue = { level: "none" };
401
+
363
402
  if (diagnostics?.snapshot) {
364
403
  const expired = now - diagnostics.snapshot.updatedAt >= status.cacheTtlMs;
365
404
  const stale = diagnostics.lastError !== undefined || expired;
366
- lines.push(`Status: ${stale ? "stale" : "fresh"} · ${formatAge(now, diagnostics.snapshot.updatedAt)}`);
367
- for (const entry of diagnostics.snapshot.entries) lines.push(formatStatusEntry(entry));
368
- } else if (diagnostics?.pending) {
369
- lines.push("Status: checking");
370
- } else {
371
- lines.push("Status: unavailable");
372
- }
373
- if (!diagnostics?.lastError) return { level: "none" };
374
- const httpStatus =
375
- diagnostics.lastError.httpStatus !== undefined ? ` · ${formatHttpStatus(diagnostics.lastError.httpStatus)}` : "";
376
- lines.push(`Error: ${diagnostics.lastError.code}${httpStatus}`);
377
- if (diagnostics.lastError.retryAt !== undefined && diagnostics.lastError.retryAt > now) {
378
- lines.push(`Retry: ${formatUntil(now, diagnostics.lastError.retryAt)}`);
379
- }
380
- return errorIssue("status", diagnostics.lastError.code, diagnostics.lastError.httpStatus);
381
- }
382
-
383
- function appendPreflightReport(
384
- lines: string[],
385
- preflight: PreflightAdapter | undefined,
386
- diagnostics: PreflightDiagnostics | undefined,
387
- nativePreflight: NativePreflightStatus | undefined,
388
- authConfigured: boolean,
389
- now: number,
390
- ): ReportIssue {
391
- if (!preflight) {
392
- if (!nativePreflight) {
393
- lines.push("Preflight: not configured");
394
- return { level: "none" };
395
- }
396
- if (!nativePreflight.providerAvailable) {
397
- lines.push("Preflight: failed · Pi provider unavailable");
398
- return { level: "hard", key: "preflight:provider-unavailable" };
399
- }
400
- if (!authConfigured) {
401
- lines.push("Preflight: native · provider/catalog · auth missing");
402
- return { level: "none" };
403
- }
404
- if (!nativePreflight.modelMatched) {
405
- lines.push("Preflight: failed · native/provider/auth/catalog");
406
- lines.push("Preflight detail: model not in Pi catalog");
407
- return { level: "hard", key: "preflight:model-not-in-catalog" };
408
- }
409
- lines.push("Preflight: native · provider/auth/catalog");
410
- return { level: "none" };
411
- }
412
- if (!authConfigured) {
413
- lines.push("Preflight: skipped · auth missing");
414
- return { level: "none" };
415
- }
416
- if (!diagnostics?.snapshot) {
417
- if (diagnostics?.pending) {
418
- lines.push("Preflight: checking");
419
- return { level: "none" };
420
- }
421
- if (diagnostics?.lastError) {
422
- const httpStatus =
423
- diagnostics.lastError.httpStatus !== undefined
424
- ? ` · ${formatHttpStatus(diagnostics.lastError.httpStatus)}`
425
- : "";
426
- lines.push(`Preflight: unavailable · error ${diagnostics.lastError.code}${httpStatus}`);
427
- if (diagnostics.lastError.retryAt !== undefined && diagnostics.lastError.retryAt > now) {
428
- lines.push(`Retry: ${formatUntil(now, diagnostics.lastError.retryAt)}`);
405
+ const freshness = stale ? "stale" : "fresh";
406
+ const age = formatAge(now, diagnostics.snapshot.updatedAt);
407
+ const entries = diagnostics.snapshot.entries;
408
+ if (entries.length === 1 && entries[0]?.kind === "amount") {
409
+ summary = `Account: ${freshness} · ${entries[0].label.toLowerCase()} ${formatStatusAmount(entries[0])} · ${age}`;
410
+ } else if (entries.length === 0) {
411
+ summary = `Account: ${freshness} · ${age}`;
412
+ } else {
413
+ summary = `Account: ${freshness} · ${age}`;
414
+ for (const entry of entries) {
415
+ detailLines.push(formatStatusEntry(entry));
429
416
  }
430
- return errorIssue("preflight", diagnostics.lastError.code, diagnostics.lastError.httpStatus);
431
417
  }
432
- lines.push("Preflight: not checked");
433
- return { level: "none" };
418
+ } else if (diagnostics?.pending) {
419
+ summary = "Account: checking";
420
+ } else {
421
+ summary = "Account: unavailable";
434
422
  }
435
423
 
436
- const expired = now - diagnostics.snapshot.updatedAt >= preflight.cacheTtlMs;
437
- const stale = expired || diagnostics.lastError !== undefined;
438
- const state = diagnostics.snapshot.passed ? "passed" : "failed";
439
- const freshness = stale ? "stale" : "fresh";
440
- const checks = diagnostics.snapshot.checks.length > 0 ? ` · ${diagnostics.snapshot.checks.join("/")}` : "";
441
- lines.push(`Preflight: ${state}${checks} · ${freshness} · ${formatAge(now, diagnostics.snapshot.updatedAt)}`);
442
- if (diagnostics.lastError) {
424
+ if (diagnostics?.lastError) {
443
425
  const httpStatus =
444
426
  diagnostics.lastError.httpStatus !== undefined
445
427
  ? ` · ${formatHttpStatus(diagnostics.lastError.httpStatus)}`
446
428
  : "";
447
- lines.push(`Preflight error: ${diagnostics.lastError.code}${httpStatus}`);
429
+ detailLines.push(`Error: ${diagnostics.lastError.code}${httpStatus}`);
448
430
  if (diagnostics.lastError.retryAt !== undefined && diagnostics.lastError.retryAt > now) {
449
- lines.push(`Retry: ${formatUntil(now, diagnostics.lastError.retryAt)}`);
431
+ detailLines.push(`Retry: ${formatUntil(now, diagnostics.lastError.retryAt)}`);
450
432
  }
433
+ issue = errorIssue("status", diagnostics.lastError.code, diagnostics.lastError.httpStatus);
451
434
  }
452
- if (!diagnostics.snapshot.passed) {
453
- return { level: "hard", key: "preflight:failed" };
454
- }
455
- return diagnostics.lastError
456
- ? errorIssue("preflight", diagnostics.lastError.code, diagnostics.lastError.httpStatus)
457
- : { level: "none" };
435
+
436
+ return { summary, detailLines, issue };
458
437
  }
459
438
 
460
439
  function formatLatency(latencyMs: number): string {
@@ -483,54 +462,154 @@ function formatHttpStatus(status: number): string {
483
462
  return `HTTP ${status}${labels[status] ? ` ${labels[status]}` : ""}`;
484
463
  }
485
464
 
486
- function appendLiveCheckReport(
487
- lines: string[],
488
- diagnostics: LiveCheckDiagnostics | undefined,
465
+ function formatHealth(
466
+ preflight: PreflightAdapter | undefined,
467
+ preflightDiagnostics: PreflightDiagnostics | undefined,
468
+ nativePreflight: NativePreflightStatus | undefined,
469
+ liveCheckDiagnostics: LiveCheckDiagnostics | undefined,
489
470
  authConfigured: boolean,
490
471
  now: number,
491
- options: { requested?: boolean; showScope?: boolean } = {},
492
- ): ReportIssue {
493
- if (!authConfigured) {
494
- lines.push("Availability: skipped · auth missing");
495
- return { level: "none" };
496
- }
497
- if (options.showScope) {
498
- lines.push("Live check scope: streamSimple() · Pi Provider tuners only (other hooks not replayed)");
472
+ options: { liveCheckRequested?: boolean; showLiveCheckScope?: boolean } = {},
473
+ ): {
474
+ preflightSummary: string;
475
+ availabilitySummary: string;
476
+ preflightDetailLines: string[];
477
+ availabilityDetailLines: string[];
478
+ preflightIssue: ReportIssue;
479
+ liveCheckIssue: ReportIssue;
480
+ } {
481
+ let preflightSummary: string;
482
+ const preflightDetailLines: string[] = [];
483
+ let preflightIssue: ReportIssue = { level: "none" };
484
+
485
+ if (!preflight) {
486
+ if (!nativePreflight) {
487
+ preflightSummary = authConfigured ? "preflight not configured" : "preflight skipped · auth missing";
488
+ } else if (!nativePreflight.providerAvailable) {
489
+ preflightSummary = "preflight failed · Pi provider unavailable";
490
+ preflightIssue = { level: "hard", key: "preflight:provider-unavailable" };
491
+ } else if (!authConfigured) {
492
+ preflightSummary = "preflight native · provider/catalog · auth missing";
493
+ } else if (!nativePreflight.modelMatched) {
494
+ preflightSummary = "preflight failed · native/provider/auth/catalog";
495
+ preflightDetailLines.push("Preflight detail: model not in Pi catalog");
496
+ preflightIssue = { level: "hard", key: "preflight:model-not-in-catalog" };
497
+ } else {
498
+ preflightSummary = "preflight native · provider/auth/catalog";
499
+ }
500
+ } else if (!authConfigured) {
501
+ preflightSummary = "preflight skipped · auth missing";
502
+ } else if (!preflightDiagnostics?.snapshot) {
503
+ if (preflightDiagnostics?.pending) {
504
+ preflightSummary = "preflight checking";
505
+ } else if (preflightDiagnostics?.lastError) {
506
+ const httpStatus =
507
+ preflightDiagnostics.lastError.httpStatus !== undefined
508
+ ? ` · ${formatHttpStatus(preflightDiagnostics.lastError.httpStatus)}`
509
+ : "";
510
+ preflightSummary = `preflight unavailable · error ${preflightDiagnostics.lastError.code}${httpStatus}`;
511
+ if (preflightDiagnostics.lastError.retryAt !== undefined && preflightDiagnostics.lastError.retryAt > now) {
512
+ preflightDetailLines.push(`Retry: ${formatUntil(now, preflightDiagnostics.lastError.retryAt)}`);
513
+ }
514
+ preflightIssue = errorIssue(
515
+ "preflight",
516
+ preflightDiagnostics.lastError.code,
517
+ preflightDiagnostics.lastError.httpStatus,
518
+ );
519
+ } else {
520
+ preflightSummary = "preflight not checked";
521
+ }
522
+ } else {
523
+ const expired = now - preflightDiagnostics.snapshot.updatedAt >= preflight.cacheTtlMs;
524
+ const stale = expired || preflightDiagnostics.lastError !== undefined;
525
+ const state = preflightDiagnostics.snapshot.passed ? "passed" : "failed";
526
+ const freshness = stale ? "stale" : "fresh";
527
+ const checks =
528
+ preflightDiagnostics.snapshot.checks.length > 0 ? ` · ${preflightDiagnostics.snapshot.checks.join("/")}` : "";
529
+ preflightSummary = `preflight ${state}${checks} · ${freshness} · ${formatAge(now, preflightDiagnostics.snapshot.updatedAt)}`;
530
+ if (preflightDiagnostics.lastError) {
531
+ const httpStatus =
532
+ preflightDiagnostics.lastError.httpStatus !== undefined
533
+ ? ` · ${formatHttpStatus(preflightDiagnostics.lastError.httpStatus)}`
534
+ : "";
535
+ preflightDetailLines.push(`Preflight error: ${preflightDiagnostics.lastError.code}${httpStatus}`);
536
+ if (preflightDiagnostics.lastError.retryAt !== undefined && preflightDiagnostics.lastError.retryAt > now) {
537
+ preflightDetailLines.push(`Retry: ${formatUntil(now, preflightDiagnostics.lastError.retryAt)}`);
538
+ }
539
+ }
540
+ if (!preflightDiagnostics.snapshot.passed) {
541
+ preflightIssue = { level: "hard", key: "preflight:failed" };
542
+ } else if (preflightDiagnostics.lastError) {
543
+ preflightIssue = errorIssue(
544
+ "preflight",
545
+ preflightDiagnostics.lastError.code,
546
+ preflightDiagnostics.lastError.httpStatus,
547
+ );
548
+ }
499
549
  }
500
- if (diagnostics?.pending) lines.push("Availability: checking");
501
- else if (!diagnostics?.snapshot && !diagnostics?.lastError) lines.push("Availability: not checked");
502
- else if (!diagnostics?.snapshot && diagnostics.lastError) {
550
+
551
+ let availabilitySummary: string;
552
+ const liveCheckDetailLines: string[] = [];
553
+ let liveCheckIssue: ReportIssue = { level: "none" };
554
+
555
+ if (!authConfigured) {
556
+ availabilitySummary = "availability skipped · auth missing";
557
+ } else if (liveCheckDiagnostics?.pending) {
558
+ availabilitySummary = "availability checking";
559
+ } else if (!liveCheckDiagnostics?.snapshot && !liveCheckDiagnostics?.lastError) {
560
+ availabilitySummary = "availability not checked";
561
+ } else if (!liveCheckDiagnostics?.snapshot && liveCheckDiagnostics.lastError) {
503
562
  const status =
504
- diagnostics.lastError.httpStatus !== undefined
505
- ? ` · ${formatHttpStatus(diagnostics.lastError.httpStatus)}`
563
+ liveCheckDiagnostics.lastError.httpStatus !== undefined
564
+ ? ` · ${formatHttpStatus(liveCheckDiagnostics.lastError.httpStatus)}`
506
565
  : "";
507
- lines.push(`Availability: failed${status}`);
508
- lines.push(`Live check error: ${diagnostics.lastError.code}`);
509
- if (diagnostics.lastError.retryAt !== undefined && diagnostics.lastError.retryAt > now) {
510
- lines.push(`Retry: ${formatUntil(now, diagnostics.lastError.retryAt)}`);
566
+ availabilitySummary = `availability failed${status}`;
567
+ liveCheckDetailLines.push(`Live check error: ${liveCheckDiagnostics.lastError.code}`);
568
+ if (liveCheckDiagnostics.lastError.retryAt !== undefined && liveCheckDiagnostics.lastError.retryAt > now) {
569
+ liveCheckDetailLines.push(`Retry: ${formatUntil(now, liveCheckDiagnostics.lastError.retryAt)}`);
511
570
  }
512
- return options.requested ? { level: "hard", key: `live-check:${diagnostics.lastError.code}` } : { level: "soft" };
513
- } else if (diagnostics?.snapshot) {
514
- const stale = diagnostics.lastError !== undefined;
515
- lines.push(`Availability: ${stale ? "stale" : "verified"} · ${formatAge(now, diagnostics.snapshot.checkedAt)}`);
571
+ liveCheckIssue = options.liveCheckRequested
572
+ ? { level: "hard", key: `live-check:${liveCheckDiagnostics.lastError.code}` }
573
+ : { level: "soft" };
574
+ } else if (liveCheckDiagnostics?.snapshot) {
575
+ const stale = liveCheckDiagnostics.lastError !== undefined;
576
+ availabilitySummary = `availability ${stale ? "stale" : "verified"} · ${formatAge(now, liveCheckDiagnostics.snapshot.checkedAt)}`;
516
577
  const httpStatus =
517
- diagnostics.snapshot.httpStatus !== undefined
518
- ? formatHttpStatus(diagnostics.snapshot.httpStatus)
578
+ liveCheckDiagnostics.snapshot.httpStatus !== undefined
579
+ ? formatHttpStatus(liveCheckDiagnostics.snapshot.httpStatus)
519
580
  : "HTTP status unknown";
520
- lines.push(
521
- `Live check: ${stale ? "last success" : "success"} · ${httpStatus} · ${formatLatency(diagnostics.snapshot.latencyMs)}`,
581
+ liveCheckDetailLines.push(
582
+ `Live check: ${stale ? "last success" : "success"} · ${httpStatus} · ${formatLatency(liveCheckDiagnostics.snapshot.latencyMs)}`,
522
583
  );
523
- if (diagnostics.lastError) {
524
- lines.push(`Live check error: ${diagnostics.lastError.code}`);
525
- if (diagnostics.lastError.retryAt !== undefined && diagnostics.lastError.retryAt > now) {
526
- lines.push(`Retry: ${formatUntil(now, diagnostics.lastError.retryAt)}`);
584
+ if (liveCheckDiagnostics.lastError) {
585
+ liveCheckDetailLines.push(`Live check error: ${liveCheckDiagnostics.lastError.code}`);
586
+ if (liveCheckDiagnostics.lastError.retryAt !== undefined && liveCheckDiagnostics.lastError.retryAt > now) {
587
+ liveCheckDetailLines.push(`Retry: ${formatUntil(now, liveCheckDiagnostics.lastError.retryAt)}`);
527
588
  }
528
- return options.requested
529
- ? { level: "hard", key: `live-check:${diagnostics.lastError.code}` }
589
+ liveCheckIssue = options.liveCheckRequested
590
+ ? { level: "hard", key: `live-check:${liveCheckDiagnostics.lastError.code}` }
530
591
  : { level: "soft" };
531
592
  }
593
+ } else {
594
+ availabilitySummary = "availability not checked";
595
+ }
596
+
597
+ const availabilityDetailLines: string[] = [];
598
+ if (options.showLiveCheckScope) {
599
+ availabilityDetailLines.push(
600
+ "Live check scope: streamSimple() · Pi Provider tuners only (other hooks not replayed)",
601
+ );
532
602
  }
533
- return { level: "none" };
603
+ availabilityDetailLines.push(...liveCheckDetailLines);
604
+
605
+ return {
606
+ preflightSummary,
607
+ availabilitySummary,
608
+ preflightDetailLines,
609
+ availabilityDetailLines,
610
+ preflightIssue,
611
+ liveCheckIssue,
612
+ };
534
613
  }
535
614
 
536
615
  export function formatProviderStatus(
@@ -550,56 +629,57 @@ export function formatProviderStatus(
550
629
  ): { report: string; warningKey?: string; warningLevel: StatusWarningLevel } {
551
630
  const catalog = formatCatalog(provider, nativeProvider, nativeLookupAvailable, now);
552
631
  const catalogIssue = scopeIssue(catalog.issue, `catalog:${model.provider}`);
553
- const healthLines: string[] = [];
554
- const preflightIssue = scopeIssue(
555
- appendPreflightReport(healthLines, preflight, preflightDiagnostics, nativePreflight, auth.configured, now),
556
- `preflight:${model.provider}/${model.id}`,
557
- );
558
- const liveCheckIssue = scopeIssue(
559
- appendLiveCheckReport(healthLines, liveCheckDiagnostics, auth.configured, now, {
560
- requested: options.liveCheckRequested,
561
- showScope: options.showLiveCheckScope,
562
- }),
563
- `live-check:${model.provider}/${model.id}`,
564
- );
565
- const accountLines: string[] = [];
566
- const statusIssue = scopeIssue(
567
- appendStatusReport(accountLines, status, diagnostics, auth.configured, now),
568
- `status:${model.provider}`,
632
+ const health = formatHealth(
633
+ preflight,
634
+ preflightDiagnostics,
635
+ nativePreflight,
636
+ liveCheckDiagnostics,
637
+ auth.configured,
638
+ now,
639
+ {
640
+ liveCheckRequested: options.liveCheckRequested,
641
+ showLiveCheckScope: options.showLiveCheckScope,
642
+ },
569
643
  );
644
+ const preflightIssue = scopeIssue(health.preflightIssue, `preflight:${model.provider}/${model.id}`);
645
+ const liveCheckIssue = scopeIssue(health.liveCheckIssue, `live-check:${model.provider}/${model.id}`);
646
+ const account = formatAccount(status, diagnostics, auth.configured, now);
647
+ const statusIssue = scopeIssue(account.issue, `status:${model.provider}`);
570
648
  const fieldSources = options.modelMetadata?.fieldSources;
571
- const qualityLines = formatQualitySource(options.modelMetadata?.quality, options.metadataStatus, now);
572
649
  const pricingSource = options.modelMetadata?.pricing ? formatPricingSource(options.modelMetadata) : "";
573
650
  const lines = [
574
- `Provider: ${model.provider}`,
575
- `Model: ${model.id}`,
576
- `Auth: ${auth.configured ? `configured${auth.source ? ` (${auth.source})` : ""}` : "missing"}`,
577
- "",
578
- "Catalog:",
579
- ...indentLines(catalog.lines),
580
- "",
581
- "Health:",
582
- ...indentLines(healthLines),
651
+ formatHeaderRow("Provider:", model.provider),
652
+ formatHeaderRow("Model:", model.id),
653
+ formatHeaderRow("Auth:", auth.configured ? `configured${auth.source ? ` (${auth.source})` : ""}` : "missing"),
583
654
  "",
584
- "Account:",
585
- ...indentLines(accountLines),
655
+ "Checks:",
656
+ formatCheckRow("Catalog", stripSummaryPrefix(catalog.summary, "Catalog: ")),
657
+ ...catalog.detailLines.map(formatCheckDetail),
658
+ formatCheckRow("Preflight", stripSummaryPrefix(health.preflightSummary, "preflight ")),
659
+ ...health.preflightDetailLines.map(formatCheckDetail),
660
+ formatCheckRow("Availability", stripSummaryPrefix(health.availabilitySummary, "availability ")),
661
+ ...health.availabilityDetailLines.map(formatCheckDetail),
662
+ formatCheckRow("Account", stripSummaryPrefix(account.summary, "Account: ")),
663
+ ...account.detailLines.map(formatCheckDetail),
586
664
  "",
587
665
  "Model details:",
588
- ` API: ${model.api ?? provider?.provider.api ?? "managed by Pi"}`,
589
- ` Endpoint: ${model.baseUrl ?? provider?.provider.baseUrl ?? "managed by Pi"}`,
590
- ` Context: ${formatTokens(model.contextWindow)}${formatModelFieldSource(fieldSources?.contextWindow)}`,
591
- ` Max output: ${formatTokens(model.maxTokens)}${formatModelFieldSource(fieldSources?.maxTokens)}`,
592
- ` Input: ${model.input?.join(", ") || "unknown"}${formatModelFieldSource(fieldSources?.input)}`,
593
- ` Reasoning: ${formatReasoning(model)}${formatModelFieldSource(fieldSources?.reasoning)}`,
594
- ...(model.thinkingLevelMap
595
- ? [
596
- ` Thinking levels: ${getSupportedReasoningLevels(model).join(", ") || "none"}${formatModelFieldSource(fieldSources?.thinkingLevelMap)}`,
597
- ]
666
+ formatModelRow("API", model.api ?? provider?.provider.api ?? "managed by Pi"),
667
+ formatModelRow("Endpoint", model.baseUrl ?? provider?.provider.baseUrl ?? "managed by Pi"),
668
+ formatModelRow(
669
+ "Context",
670
+ `${formatTokens(model.contextWindow)}${formatModelFieldSource(fieldSources?.contextWindow)}`,
671
+ ),
672
+ formatModelRow(
673
+ "Max output",
674
+ `${formatTokens(model.maxTokens)}${formatModelFieldSource(fieldSources?.maxTokens)}`,
675
+ ),
676
+ formatModelRow("Input", `${model.input?.join(", ") || "unknown"}${formatModelFieldSource(fieldSources?.input)}`),
677
+ formatThinkingLevelsLine(model, options.modelMetadata),
678
+ ...formatPricingLines(model, options.modelMetadata, pricingSource),
679
+ ...(model.cost?.tiers?.flatMap(formatPricingTierLines) ?? []),
680
+ ...(options.modelMetadata?.pricing?.note
681
+ ? [formatModelRow("Pricing note", options.modelMetadata.pricing.note)]
598
682
  : []),
599
- ` Pricing: ${formatPricing(model, options.modelMetadata)}${pricingSource}`,
600
- ...(model.cost?.tiers?.map((tier) => ` Pricing tier: ${formatPricingTier(tier)}`) ?? []),
601
- ...(options.modelMetadata?.pricing?.note ? [` Pricing note: ${options.modelMetadata.pricing.note}`] : []),
602
- ...(qualityLines.length > 0 ? ["", "Quality:", ...indentLines(qualityLines)] : []),
603
683
  ];
604
684
  const issue = combineIssues(catalogIssue, preflightIssue, liveCheckIssue, statusIssue);
605
685
  return {