@hyav/pi-provider 0.1.1 → 0.1.3

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,6 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
3
- import { homedir } from "node:os";
4
3
  import { dirname, join } from "node:path";
5
4
  import { withDeadline } from "./deadline.ts";
6
5
  import type { ModelQualityScore, ProviderCost, ProviderModel, ProviderModelDraft } from "./types.ts";
@@ -68,11 +67,8 @@ const pricingCache = new Map<string, PricingCacheEntry>();
68
67
  const pricingRequests = new Map<string, Promise<Record<string, OfficialModelMeta>>>();
69
68
 
70
69
  /** Default cache for OpenRouter metadata, not Pi's native model catalog. */
71
- export function getDefaultOpenRouterMetadataCachePath(): string {
72
- const configuredAgentDir = process.env.PI_CODING_AGENT_DIR;
73
- const agentDir =
74
- configuredAgentDir && configuredAgentDir.trim() !== "" ? configuredAgentDir : join(homedir(), ".pi", "agent");
75
- return join(agentDir, "provider-kit", "openrouter-model-metadata.json");
70
+ export function getDefaultOpenRouterMetadataCachePath(agentDir: string): string {
71
+ return join(agentDir, "extensions", "pi-provider", "openrouter-model-metadata.json");
76
72
  }
77
73
 
78
74
  function cloneCost(cost: ProviderCost): ProviderCost {
@@ -1,7 +1,7 @@
1
1
  import type { ExtensionAPI, ProviderConfig } from "@earendil-works/pi-coding-agent";
2
2
  import { applyOfficialModelCosts, findOfficialMeta, type OfficialModelMeta } from "./official-pricing.ts";
3
3
  import { resolvePricingDetails } from "./pricing-adjustments.ts";
4
- import type { ProviderKitDependencies } from "./runtime-config.ts";
4
+ import type { PiProviderDependencies } from "./runtime-config.ts";
5
5
  import type {
6
6
  ProviderAdapter,
7
7
  ProviderCost,
@@ -109,7 +109,7 @@ function selectPricingAdjustment(
109
109
 
110
110
  function resolveModelRegistration(
111
111
  adapter: ProviderAdapter,
112
- runtime: ProviderKitDependencies,
112
+ runtime: PiProviderDependencies,
113
113
  modelDrafts: ProviderModelDraft[],
114
114
  officialPricing: Record<string, OfficialModelMeta>,
115
115
  ): { models: ProviderModel[]; modelMetadata: Record<string, ProviderModelMetadata> } {
@@ -184,7 +184,7 @@ function getErrorCode(error: unknown): string {
184
184
  */
185
185
  export function prepareProviderRegistration(
186
186
  adapter: ProviderAdapter,
187
- runtime: ProviderKitDependencies,
187
+ runtime: PiProviderDependencies,
188
188
  officialPricing: Record<string, OfficialModelMeta> = {},
189
189
  modelDrafts?: ProviderModelDraft[],
190
190
  ): ProviderConfig {
@@ -239,7 +239,7 @@ export function prepareProviderRegistration(
239
239
  export function refreshProviderRegistrations(
240
240
  pi: Pick<ExtensionAPI, "registerProvider">,
241
241
  providers: readonly ProviderAdapter[],
242
- runtime: ProviderKitDependencies,
242
+ runtime: PiProviderDependencies,
243
243
  officialPricing: Record<string, OfficialModelMeta>,
244
244
  providerDrafts?: ReadonlyMap<ProviderAdapter, ProviderModelDraft[]>,
245
245
  ): void {
@@ -251,7 +251,7 @@ export function refreshProviderRegistrations(
251
251
  export function registerProviderAdapter(
252
252
  pi: Pick<ExtensionAPI, "registerProvider">,
253
253
  adapter: ProviderAdapter,
254
- runtime: ProviderKitDependencies,
254
+ runtime: PiProviderDependencies,
255
255
  officialPricing: Record<string, OfficialModelMeta> = {},
256
256
  modelDrafts?: ProviderModelDraft[],
257
257
  ): ProviderConfig {
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Jiti-safe public entrypoint for user adapter files discovered from the
3
+ * adapter roots. `loadPackageAdapterExtensions` aliases `@hyav/pi-provider`
4
+ * to this module so adapter files can import the same helpers and types the
5
+ * built-in adapters use, without resolving Pi's bundled runtime packages.
6
+ */
7
+
8
+ export type {
9
+ AdapterExtensionContext,
10
+ PreflightExtensionDefinition,
11
+ ProviderExtensionDefinition,
12
+ StatusExtensionDefinition,
13
+ TunerExtensionDefinition,
14
+ } from "./adapter-extensions.ts";
15
+ export {
16
+ definePreflightExtension,
17
+ defineProviderExtension,
18
+ defineStatusExtension,
19
+ defineTunerExtension,
20
+ } from "./adapter-extensions.ts";
21
+ export { withDeadline } from "./deadline.ts";
22
+ export { isProviderDataError, ProviderDataError } from "./errors.ts";
23
+ export { createOpenCodeCatalogPreflightAdapter } from "./opencode-preflight.ts";
24
+ export type {
25
+ PreflightAdapter,
26
+ PreflightContextLike,
27
+ PreflightModel,
28
+ PreflightSnapshot,
29
+ } from "./preflight-manager.ts";
30
+ export { normalizeProviderModels } from "./provider-registration.ts";
31
+ export { parseRetryAfter } from "./retry-after.ts";
32
+ export type { StatusContextLike } from "./status-manager.ts";
33
+ export type {
34
+ ActiveModel,
35
+ ModelCatalogStatus,
36
+ ProviderAdapter,
37
+ ProviderModel,
38
+ ProviderModelDraft,
39
+ ProviderRefreshContext,
40
+ StatusAdapter,
41
+ StatusContext,
42
+ StatusEntry,
43
+ StatusSnapshot,
44
+ StoredCredentialLike,
45
+ TunerContext,
46
+ } from "./types.ts";
@@ -1,10 +1,12 @@
1
+ import { homedir } from "node:os";
2
+ import { join } from "node:path";
1
3
  import { isValidTimeoutMs } from "./deadline.ts";
2
- import type { ProviderKitDefinition } from "./definition.ts";
4
+ import type { PiProviderDefinition } from "./definition.ts";
3
5
  import { getDefaultOpenRouterMetadataCachePath, OPENROUTER_MODELS_URL } from "./official-pricing.ts";
4
6
  import { validatePricingPolicy } from "./pricing-adjustments.ts";
5
- import type { ProviderPricingPolicy } from "./types.ts";
7
+ import type { ProviderPricingPolicy, StoredCredentialLike } from "./types.ts";
6
8
 
7
- export interface ProviderKitDependencies {
9
+ export interface PiProviderDependencies {
8
10
  fetch: typeof globalThis.fetch;
9
11
  now: () => number;
10
12
  modelDiscoveryTimeoutMs: number;
@@ -14,16 +16,83 @@ export interface ProviderKitDependencies {
14
16
  officialPricingTimeoutMs: number;
15
17
  officialPricingCacheTtlMs: number;
16
18
  officialPricingMaxStaleMs: number;
19
+ /** Resolved Pi agent directory; empty disables disk persistence of pricing metadata. */
20
+ agentDir: string;
17
21
  /** Persistent cache for OpenRouter metadata used by the pricing fallback. */
18
22
  openRouterMetadataCachePath: string;
23
+ /** Read Pi's stored credential metadata; injected by the Pi entrypoint. */
24
+ readStoredCredential: (providerId: string) => StoredCredentialLike | undefined;
25
+ /** Wrap ANSI-aware text to a render width; injected by the Pi entrypoint. */
26
+ wrapTextWithAnsi: (text: string, width: number) => string[];
19
27
  enableOfficialPricingFallback: boolean;
20
- /** Optional Provider Kit-level price policies keyed by Provider ID. */
28
+ /** Optional Pi Provider-level price policies keyed by Provider ID. */
21
29
  pricingPolicies?: Record<string, ProviderPricingPolicy>;
22
30
  }
23
31
 
24
- export type ProviderKitLoader = (runtime: ProviderKitDependencies) => Promise<ProviderKitDefinition>;
32
+ export type PiProviderLoader = (runtime: PiProviderDependencies) => Promise<PiProviderDefinition>;
25
33
 
26
- const defaultDependencies: ProviderKitDependencies = {
34
+ /**
35
+ * Resolve Pi's agent directory without importing Pi's bundled packages, so the
36
+ * Jiti module graph and programmatic consumers share the same default. Mirrors
37
+ * Pi's `getAgentDir()`: `PI_CODING_AGENT_DIR` wins, `~/` expands to the home
38
+ * directory, and the fallback is `~/.pi/agent`.
39
+ */
40
+ export function resolveDefaultAgentDir(): string {
41
+ const configured = process.env.PI_CODING_AGENT_DIR;
42
+ const raw =
43
+ configured !== undefined && configured.trim() !== "" ? configured.trim() : join(homedir(), ".pi", "agent");
44
+ if (raw === "~") return homedir();
45
+ if (raw.startsWith("~/")) return join(homedir(), raw.slice(2));
46
+ return raw;
47
+ }
48
+
49
+ /** Degraded fallback used only when the Pi entrypoint does not inject the real wrapper. */
50
+ const WIDE_CHAR_RANGES: Array<[number, number]> = [
51
+ [0x1100, 0x115f],
52
+ [0x2329, 0x232a],
53
+ [0x2e80, 0xa4cf],
54
+ [0xac00, 0xd7a3],
55
+ [0xf900, 0xfaff],
56
+ [0xfe30, 0xfe4f],
57
+ [0xff00, 0xff60],
58
+ [0xffe0, 0xffe6],
59
+ [0x1f300, 0x1f64f],
60
+ [0x1f900, 0x1f9ff],
61
+ [0x20000, 0x2fffd],
62
+ [0x30000, 0x3fffd],
63
+ ];
64
+
65
+ function displayWidth(text: string): number {
66
+ let width = 0;
67
+ for (const ch of text) {
68
+ const code = ch.codePointAt(0) ?? 0;
69
+ const wide = WIDE_CHAR_RANGES.some(([start, end]) => code >= start && code <= end);
70
+ width += wide ? 2 : 1;
71
+ }
72
+ return width;
73
+ }
74
+
75
+ function defaultWrapTextWithAnsi(text: string, width: number): string[] {
76
+ if (width <= 0) return [text];
77
+ const plain = text.replace(/\u001b\[[0-9;]*m/g, "");
78
+ if (displayWidth(plain) <= width) return [text];
79
+ const chunks: string[] = [];
80
+ let chunk = "";
81
+ for (const ch of plain) {
82
+ if (chunk !== "" && displayWidth(chunk) + displayWidth(ch) > width) {
83
+ chunks.push(chunk);
84
+ chunk = ch;
85
+ } else {
86
+ chunk += ch;
87
+ }
88
+ }
89
+ if (chunk !== "") chunks.push(chunk);
90
+ return chunks;
91
+ }
92
+
93
+ type DefaultDependencies = Omit<PiProviderDependencies, "agentDir" | "openRouterMetadataCachePath">;
94
+
95
+ const defaultDependencies: DefaultDependencies = {
27
96
  fetch: globalThis.fetch,
28
97
  now: Date.now,
29
98
  modelDiscoveryTimeoutMs: 3_000,
@@ -33,42 +102,57 @@ const defaultDependencies: ProviderKitDependencies = {
33
102
  officialPricingTimeoutMs: 3_000,
34
103
  officialPricingCacheTtlMs: 60 * 60 * 1_000,
35
104
  officialPricingMaxStaleMs: 24 * 60 * 60 * 1_000,
36
- openRouterMetadataCachePath: getDefaultOpenRouterMetadataCachePath(),
105
+ readStoredCredential: () => undefined,
106
+ wrapTextWithAnsi: defaultWrapTextWithAnsi,
37
107
  enableOfficialPricingFallback: true,
38
108
  pricingPolicies: {},
39
109
  };
40
110
 
41
- export function getDefaultProviderKitDependencies(): ProviderKitDependencies {
42
- return { ...defaultDependencies };
111
+ /**
112
+ * Programmatic defaults keep the resolved agent directory and its pricing cache
113
+ * path. The Pi entrypoint overrides `agentDir` with Pi's own resolution.
114
+ */
115
+ export function getDefaultPiProviderDependencies(agentDir = resolveDefaultAgentDir()): PiProviderDependencies {
116
+ return {
117
+ ...defaultDependencies,
118
+ agentDir,
119
+ openRouterMetadataCachePath: getDefaultOpenRouterMetadataCachePath(agentDir),
120
+ };
43
121
  }
44
122
 
45
- export function validateProviderKitDependencies(runtime: ProviderKitDependencies): void {
46
- if (typeof runtime.fetch !== "function") throw new Error("Provider Kit fetch must be a function");
47
- if (typeof runtime.now !== "function") throw new Error("Provider Kit now must be a function");
123
+ export function validatePiProviderDependencies(runtime: PiProviderDependencies): void {
124
+ if (typeof runtime.fetch !== "function") throw new Error("Pi Provider fetch must be a function");
125
+ if (typeof runtime.now !== "function") throw new Error("Pi Provider now must be a function");
48
126
  for (const [name, value] of [
49
127
  ["modelDiscoveryTimeoutMs", runtime.modelDiscoveryTimeoutMs],
50
128
  ["statusRequestTimeoutMs", runtime.statusRequestTimeoutMs],
51
129
  ["liveCheckRequestTimeoutMs", runtime.liveCheckRequestTimeoutMs],
52
130
  ["officialPricingTimeoutMs", runtime.officialPricingTimeoutMs],
53
131
  ] as const) {
54
- if (!isValidTimeoutMs(value)) throw new Error(`Provider Kit ${name} must be a valid timeout`);
132
+ if (!isValidTimeoutMs(value)) throw new Error(`Pi Provider ${name} must be a valid timeout`);
55
133
  }
56
134
  for (const [name, value] of [
57
135
  ["officialPricingCacheTtlMs", runtime.officialPricingCacheTtlMs],
58
136
  ["officialPricingMaxStaleMs", runtime.officialPricingMaxStaleMs],
59
137
  ] as const) {
60
138
  if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
61
- throw new Error(`Provider Kit ${name} must be a finite non-negative number`);
139
+ throw new Error(`Pi Provider ${name} must be a finite non-negative number`);
62
140
  }
63
141
  }
64
142
  if (typeof runtime.officialPricingUrl !== "string" || runtime.officialPricingUrl.trim() === "") {
65
- throw new Error("Provider Kit officialPricingUrl must be a non-empty string");
143
+ throw new Error("Pi Provider officialPricingUrl must be a non-empty string");
144
+ }
145
+ if (typeof runtime.openRouterMetadataCachePath !== "string") {
146
+ throw new Error("Pi Provider openRouterMetadataCachePath must be a string");
147
+ }
148
+ if (typeof runtime.readStoredCredential !== "function") {
149
+ throw new Error("Pi Provider readStoredCredential must be a function");
66
150
  }
67
- if (typeof runtime.openRouterMetadataCachePath !== "string" || runtime.openRouterMetadataCachePath.trim() === "") {
68
- throw new Error("Provider Kit openRouterMetadataCachePath must be a non-empty path");
151
+ if (typeof runtime.wrapTextWithAnsi !== "function") {
152
+ throw new Error("Pi Provider wrapTextWithAnsi must be a function");
69
153
  }
70
154
  if (typeof runtime.enableOfficialPricingFallback !== "boolean") {
71
- throw new Error("Provider Kit enableOfficialPricingFallback must be a boolean");
155
+ throw new Error("Pi Provider enableOfficialPricingFallback must be a boolean");
72
156
  }
73
157
  if (runtime.pricingPolicies !== undefined) {
74
158
  if (
@@ -76,20 +160,20 @@ export function validateProviderKitDependencies(runtime: ProviderKitDependencies
76
160
  typeof runtime.pricingPolicies !== "object" ||
77
161
  Array.isArray(runtime.pricingPolicies)
78
162
  ) {
79
- throw new Error("Provider Kit pricingPolicies must be an object");
163
+ throw new Error("Pi Provider pricingPolicies must be an object");
80
164
  }
81
165
  for (const [providerId, policy] of Object.entries(runtime.pricingPolicies)) {
82
- if (providerId.trim() === "") throw new Error("Provider Kit pricingPolicies has an empty Provider ID");
83
- validatePricingPolicy(policy, `Provider Kit pricingPolicies.${providerId}`);
166
+ if (providerId.trim() === "") throw new Error("Pi Provider pricingPolicies has an empty Provider ID");
167
+ validatePricingPolicy(policy, `Pi Provider pricingPolicies.${providerId}`);
84
168
  }
85
169
  }
86
170
  }
87
171
 
88
- export function resolveProviderKitDependencies(
89
- dependencies: Partial<ProviderKitDependencies> = {},
90
- ): ProviderKitDependencies {
91
- const runtime = { ...defaultDependencies, ...dependencies };
172
+ export function resolvePiProviderDependencies(
173
+ dependencies: Partial<PiProviderDependencies> = {},
174
+ ): PiProviderDependencies {
175
+ const runtime = { ...getDefaultPiProviderDependencies(), ...dependencies };
92
176
  if (runtime.pricingPolicies === undefined) runtime.pricingPolicies = {};
93
- validateProviderKitDependencies(runtime);
177
+ validatePiProviderDependencies(runtime);
94
178
  return runtime;
95
179
  }
@@ -0,0 +1,26 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { loadPackageAdapterExtensions } from "./adapter-loader.ts";
3
+ import { createPiProviderHost } from "./host.ts";
4
+ import type { PiProviderDependencies } from "./runtime-config.ts";
5
+ import type { StoredCredentialLike } from "./types.ts";
6
+
7
+ /** Runtime values resolved by the Pi-loaded entrypoint and injected into the Jiti graph. */
8
+ export interface PiProviderEntry {
9
+ agentDir: string;
10
+ readStoredCredential: (providerId: string) => StoredCredentialLike | undefined;
11
+ wrapTextWithAnsi: (text: string, width: number) => string[];
12
+ adapterRoot?: string;
13
+ dependencies?: Partial<PiProviderDependencies>;
14
+ }
15
+
16
+ /** Runs the Pi Provider host and adapter discovery inside a single Jiti module graph. */
17
+ export async function runPiProviderEntry(pi: ExtensionAPI, entry: PiProviderEntry): Promise<void> {
18
+ const piProviderHost = createPiProviderHost({
19
+ agentDir: entry.agentDir,
20
+ readStoredCredential: entry.readStoredCredential,
21
+ wrapTextWithAnsi: entry.wrapTextWithAnsi,
22
+ ...entry.dependencies,
23
+ });
24
+ piProviderHost(pi);
25
+ await loadPackageAdapterExtensions(pi, { agentDir: entry.agentDir, userRoot: entry.adapterRoot });
26
+ }
package/core/runtime.ts CHANGED
@@ -1,8 +1,6 @@
1
1
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
- import { readStoredCredential } from "@earendil-works/pi-coding-agent";
3
- import { wrapTextWithAnsi } from "@earendil-works/pi-tui";
4
- import type { ProviderKitDefinition } from "./definition.ts";
5
- import { validateProviderKitDefinition } from "./definition.ts";
2
+ import type { PiProviderDefinition } from "./definition.ts";
3
+ import { validatePiProviderDefinition } from "./definition.ts";
6
4
  import { LiveCheckManager, type LiveCheckResult } from "./live-check-manager.ts";
7
5
  import {
8
6
  fetchOfficialPricing,
@@ -14,8 +12,8 @@ import {
14
12
  import type { PreflightContextLike } from "./preflight-manager.ts";
15
13
  import { PreflightManager } from "./preflight-manager.ts";
16
14
  import { refreshProviderRegistrations, registerProviderAdapter } from "./provider-registration.ts";
17
- import type { ProviderKitDependencies, ProviderKitLoader } from "./runtime-config.ts";
18
- import { resolveProviderKitDependencies } from "./runtime-config.ts";
15
+ import type { PiProviderDependencies, PiProviderLoader } from "./runtime-config.ts";
16
+ import { resolvePiProviderDependencies } from "./runtime-config.ts";
19
17
  import type { StatusContextLike } from "./status-manager.ts";
20
18
  import { StatusManager } from "./status-manager.ts";
21
19
  import {
@@ -33,15 +31,19 @@ import type {
33
31
  ProviderCost,
34
32
  ProviderModelDraft,
35
33
  ProviderModelMetadata,
34
+ StoredCredentialLike,
36
35
  } from "./types.ts";
37
36
 
38
37
  type ActiveModel = NonNullable<ExtensionContext["model"]>;
39
38
  type StatusNotificationContext = Pick<ExtensionContext, "modelRegistry" | "ui"> & {
40
39
  mode?: ExtensionContext["mode"];
41
40
  };
42
- const STATUS_WIDGET_KEY = "provider-kit-status";
41
+ const STATUS_WIDGET_KEY = "pi-provider-status";
43
42
 
44
- function readProviderCredentialMetadata(provider: string): unknown {
43
+ function readProviderCredentialMetadata(
44
+ provider: string,
45
+ readStoredCredential: (providerId: string) => StoredCredentialLike | undefined,
46
+ ): unknown {
45
47
  try {
46
48
  const credential = readStoredCredential(provider);
47
49
  if (credential?.type !== "oauth") return undefined;
@@ -59,7 +61,11 @@ function clearTransientStatus(ctx: Pick<ExtensionContext, "ui">): void {
59
61
  ctx.ui.setWidget(STATUS_WIDGET_KEY, undefined);
60
62
  }
61
63
 
62
- function showTransientStatus(message: string, ctx: StatusNotificationContext): boolean {
64
+ function showTransientStatus(
65
+ message: string,
66
+ ctx: StatusNotificationContext,
67
+ wrapTextWithAnsi: (text: string, width: number) => string[],
68
+ ): boolean {
63
69
  if ((ctx.mode !== "tui" && ctx.mode !== "rpc") || typeof ctx.ui.setWidget !== "function") return false;
64
70
  // RPC cannot render component factories, so keep its plain text protocol unchanged.
65
71
  if (ctx.mode === "rpc") {
@@ -103,7 +109,7 @@ export function scheduleModelCatalogRefresh(ctx: Pick<ExtensionContext, "modelRe
103
109
  .catch(() => undefined);
104
110
  }
105
111
 
106
- export interface ProviderKitRuntimeController {
112
+ export interface PiProviderRuntimeController {
107
113
  resetForSession(): void;
108
114
  updateOfficialPricing?(snapshot: Record<string, OfficialModelMeta>): void;
109
115
  shutdown(): void;
@@ -126,7 +132,7 @@ function cloneProviderCost(cost: ProviderCost): ProviderCost {
126
132
 
127
133
  function getOfficialMetadataStatus(
128
134
  snapshot: Record<string, OfficialModelMeta>,
129
- runtime: ProviderKitDependencies,
135
+ runtime: PiProviderDependencies,
130
136
  ): ModelMetadataStatus | undefined {
131
137
  const source = runtime.officialPricingUrl === OPENROUTER_MODELS_URL ? "AA/OpenRouter" : "Official metadata";
132
138
  if (!runtime.enableOfficialPricingFallback && Object.keys(snapshot).length === 0) return undefined;
@@ -185,17 +191,17 @@ function getNativeModelMetadata(
185
191
  };
186
192
  }
187
193
 
188
- export function installProviderKitRuntime(
194
+ export function installPiProviderRuntime(
189
195
  pi: ExtensionAPI,
190
- runtime: ProviderKitDependencies,
191
- definition: ProviderKitDefinition,
196
+ runtime: PiProviderDependencies,
197
+ definition: PiProviderDefinition,
192
198
  officialPricing: Record<string, OfficialModelMeta> = {},
193
199
  options: {
194
200
  registerHandlers?: boolean;
195
201
  providerDrafts?: ReadonlyMap<ProviderAdapter, ProviderModelDraft[]>;
196
202
  } = {},
197
- ): ProviderKitRuntimeController {
198
- validateProviderKitDefinition(definition);
203
+ ): PiProviderRuntimeController {
204
+ validatePiProviderDefinition(definition);
199
205
  const registerHandlers = options.registerHandlers ?? true;
200
206
  let currentOfficialPricing = officialPricing;
201
207
  let currentOfficialMetadataStatus = getOfficialMetadataStatus(officialPricing, runtime);
@@ -301,7 +307,7 @@ export function installProviderKitRuntime(
301
307
  );
302
308
  const message = report.report;
303
309
  if (report.warningLevel !== "hard") {
304
- statusPresentationVisible = showTransientStatus(message, ctx);
310
+ statusPresentationVisible = showTransientStatus(message, ctx, runtime.wrapTextWithAnsi);
305
311
  if (!statusPresentationVisible) ctx.ui.notify(message, "info");
306
312
  return;
307
313
  }
@@ -335,7 +341,7 @@ export function installProviderKitRuntime(
335
341
  model,
336
342
  modelRegistry: ctx.modelRegistry,
337
343
  getCredentialKey: () => ctx.modelRegistry.getApiKeyForProvider(model.provider),
338
- getCredentialMetadata: () => readProviderCredentialMetadata(model.provider),
344
+ getCredentialMetadata: () => readProviderCredentialMetadata(model.provider, runtime.readStoredCredential),
339
345
  };
340
346
  const preflightContext: PreflightContextLike = { model, modelRegistry: ctx.modelRegistry };
341
347
  const refreshChecks: Array<Promise<unknown>> = [];
@@ -387,7 +393,7 @@ export function installProviderKitRuntime(
387
393
  liveCheckManager.clear();
388
394
  };
389
395
 
390
- const controller: ProviderKitRuntimeController = {
396
+ const controller: PiProviderRuntimeController = {
391
397
  resetForSession,
392
398
  updateOfficialPricing(snapshot) {
393
399
  currentOfficialPricing = snapshot;
@@ -425,15 +431,15 @@ export function installProviderKitRuntime(
425
431
  return controller;
426
432
  }
427
433
 
428
- export function createProviderKitRuntime(
429
- loadDefinition: ProviderKitLoader,
430
- dependencies: Partial<ProviderKitDependencies> = {},
434
+ export function createPiProviderRuntime(
435
+ loadDefinition: PiProviderLoader,
436
+ dependencies: Partial<PiProviderDependencies> = {},
431
437
  ): (pi: ExtensionAPI) => Promise<void> {
432
- const runtime = resolveProviderKitDependencies(dependencies);
438
+ const runtime = resolvePiProviderDependencies(dependencies);
433
439
  return async (pi) => {
434
440
  let latestBackgroundPricing: Record<string, OfficialModelMeta> | undefined;
435
- let installedDefinition: ProviderKitDefinition | undefined;
436
- let installedController: ProviderKitRuntimeController | undefined;
441
+ let installedDefinition: PiProviderDefinition | undefined;
442
+ let installedController: PiProviderRuntimeController | undefined;
437
443
  let disposed = false;
438
444
  const onBackgroundRefresh = (snapshot: Record<string, OfficialModelMeta>): void => {
439
445
  latestBackgroundPricing = snapshot;
@@ -462,8 +468,8 @@ export function createProviderKitRuntime(
462
468
  : Promise.resolve({});
463
469
  const definitionPromise = loadDefinition(runtime);
464
470
  const [officialPricing, definition] = await Promise.all([officialPricingPromise, definitionPromise]);
465
- validateProviderKitDefinition(definition);
466
- installedController = installProviderKitRuntime(pi, runtime, definition, officialPricing);
471
+ validatePiProviderDefinition(definition);
472
+ installedController = installPiProviderRuntime(pi, runtime, definition, officialPricing);
467
473
  installedDefinition = definition;
468
474
  if (latestBackgroundPricing !== undefined) onBackgroundRefresh(latestBackgroundPricing);
469
475
  pi.on("session_shutdown", () => {
@@ -264,7 +264,7 @@ function formatCatalog(
264
264
  now: number,
265
265
  ): { lines: string[]; issue: ReportIssue } {
266
266
  if (!adapter) {
267
- if (!nativeLookupAvailable) return { lines: ["Status: not managed by Provider Kit"], issue: { level: "none" } };
267
+ if (!nativeLookupAvailable) return { lines: ["Status: not managed by Pi Provider"], issue: { level: "none" } };
268
268
  if (!nativeProvider) return { lines: ["Status: unavailable in Pi"], issue: { level: "none" } };
269
269
  const count = getNativeModelCount(nativeProvider);
270
270
  return {
@@ -482,7 +482,7 @@ function appendLiveCheckReport(
482
482
  return { level: "none" };
483
483
  }
484
484
  if (options.showScope) {
485
- lines.push("Live check scope: streamSimple() · Provider Kit tuners only (other hooks not replayed)");
485
+ lines.push("Live check scope: streamSimple() · Pi Provider tuners only (other hooks not replayed)");
486
486
  }
487
487
  if (diagnostics?.pending) lines.push("Availability: checking");
488
488
  else if (!diagnostics?.snapshot && !diagnostics?.lastError) lines.push("Availability: not checked");
package/core/types.ts CHANGED
@@ -10,7 +10,7 @@ export type ThinkingLevel = ReturnType<ExtensionAPI["getThinkingLevel"]>;
10
10
  export type ProviderCost = ProviderModelConfig["cost"];
11
11
  export type ProviderModel = ProviderModelConfig;
12
12
  export type PricingSku = "input" | "output" | "cacheRead" | "cacheWrite";
13
- /** Pricing provenance used by Provider Kit sidecars; not added to Pi model objects. */
13
+ /** Pricing provenance used by Pi Provider sidecars; not added to Pi model objects. */
14
14
  export type ProviderPricingSource = "provider" | "fallback" | "official";
15
15
  export type ModelPricingSource = ProviderPricingSource | "native";
16
16
  export type ModelFieldSource = ProviderPricingSource | "native" | "default";
@@ -22,6 +22,16 @@ export interface ModelMetadataStatus {
22
22
  source?: string;
23
23
  }
24
24
 
25
+ /**
26
+ * Narrow credential shape shared across Pi's isolated extension module contexts.
27
+ * The full Credential type comes from Pi's bundled AI package; consumers of
28
+ * injected credential readers must not rely on `instanceof` or extra fields.
29
+ */
30
+ export interface StoredCredentialLike {
31
+ readonly type?: string;
32
+ readonly teamName?: string;
33
+ }
34
+
25
35
  export interface ModelFieldSources {
26
36
  contextWindow?: ModelFieldSource;
27
37
  maxTokens?: ModelFieldSource;
package/index.ts CHANGED
@@ -1,4 +1,8 @@
1
- import { createProviderKitHost } from "./core/host.ts";
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { getAgentDir, readStoredCredential } from "@earendil-works/pi-coding-agent";
3
+ import { wrapTextWithAnsi } from "@earendil-works/pi-tui";
4
+ import { createJiti } from "jiti";
5
+ import type { PiProviderDependencies } from "./core/runtime-config.ts";
2
6
 
3
7
  export type {
4
8
  AdapterExtensionContext,
@@ -13,25 +17,26 @@ export {
13
17
  defineStatusExtension,
14
18
  defineTunerExtension,
15
19
  } from "./core/adapter-extensions.ts";
20
+ export { withDeadline } from "./core/deadline.ts";
16
21
  export type { ProviderDataErrorLike } from "./core/errors.ts";
17
22
  export { isProviderDataError, ProviderDataError } from "./core/errors.ts";
18
23
  export type {
19
- ProviderKitDefinition,
20
- ProviderKitDependencies,
21
- ProviderKitLoader,
22
- ProviderKitRuntimeController,
24
+ PiProviderDefinition,
25
+ PiProviderDependencies,
26
+ PiProviderLoader,
27
+ PiProviderRuntimeController,
23
28
  } from "./core/extension.ts";
24
29
  export {
25
- createProviderKitRuntime,
26
- getDefaultProviderKitDependencies,
27
- installProviderKitRuntime,
30
+ createPiProviderRuntime,
31
+ getDefaultPiProviderDependencies,
32
+ installPiProviderRuntime,
28
33
  prepareProviderRegistration,
29
34
  registerProviderAdapter,
30
- resolveProviderKitDependencies,
31
- validateProviderKitDefinition,
32
- validateProviderKitDependencies,
35
+ resolvePiProviderDependencies,
36
+ validatePiProviderDefinition,
37
+ validatePiProviderDependencies,
33
38
  } from "./core/extension.ts";
34
- export { createProviderKitHost } from "./core/host.ts";
39
+ export { createPiProviderHost } from "./core/host.ts";
35
40
  export type {
36
41
  LiveCheckContextLike,
37
42
  LiveCheckDiagnostics,
@@ -56,6 +61,7 @@ export {
56
61
  parseOpenRouterPricing,
57
62
  setPricingCache,
58
63
  } from "./core/official-pricing.ts";
64
+ export { createOpenCodeCatalogPreflightAdapter } from "./core/opencode-preflight.ts";
59
65
  export type {
60
66
  PreflightAdapter,
61
67
  PreflightContext,
@@ -67,6 +73,8 @@ export type {
67
73
  } from "./core/preflight-manager.ts";
68
74
  export { getPreflightKey, normalizePreflightSnapshot, PreflightManager } from "./core/preflight-manager.ts";
69
75
  export { applyPricingAdjustment, resolvePricingDetails } from "./core/pricing-adjustments.ts";
76
+ export { normalizeProviderModels } from "./core/provider-registration.ts";
77
+ export { parseRetryAfter } from "./core/retry-after.ts";
70
78
  export type { StatusDiagnostics, StatusErrorState } from "./core/status-manager.ts";
71
79
  export { normalizeStatusSnapshot, StatusManager } from "./core/status-manager.ts";
72
80
  export { applyTunerAdapters, sortTunerAdapters } from "./core/tuner-manager.ts";
@@ -105,4 +113,39 @@ export type {
105
113
  TunerContext,
106
114
  } from "./core/types.ts";
107
115
 
108
- export default createProviderKitHost();
116
+ export interface PiProviderExtensionOptions {
117
+ /** User adapter root; replaces the default `<agentDir>/pi-provider` directory. */
118
+ adapterRoot?: string;
119
+ /** Host runtime dependency overrides. */
120
+ dependencies?: Partial<PiProviderDependencies>;
121
+ }
122
+
123
+ /** Create one Pi extension that discovers the current Adapter files when it loads. */
124
+ export function createPiProviderExtension(
125
+ options: PiProviderExtensionOptions = {},
126
+ ): (pi: ExtensionAPI) => Promise<void> {
127
+ return async (pi) => {
128
+ const jiti = createJiti(import.meta.url, { moduleCache: true, tryNative: false });
129
+ const { runPiProviderEntry } = (await jiti.import("./core/runtime-entry.ts")) as {
130
+ runPiProviderEntry: (
131
+ pi: ExtensionAPI,
132
+ entry: {
133
+ agentDir: string;
134
+ readStoredCredential: typeof readStoredCredential;
135
+ wrapTextWithAnsi: typeof wrapTextWithAnsi;
136
+ adapterRoot?: string;
137
+ dependencies?: Partial<PiProviderDependencies>;
138
+ },
139
+ ) => Promise<void>;
140
+ };
141
+ await runPiProviderEntry(pi, {
142
+ agentDir: getAgentDir(),
143
+ readStoredCredential,
144
+ wrapTextWithAnsi,
145
+ adapterRoot: options.adapterRoot,
146
+ dependencies: options.dependencies,
147
+ });
148
+ };
149
+ }
150
+
151
+ export default createPiProviderExtension();