@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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  This file is the authoritative user-facing release history for `@hyav/pi-provider`.
4
4
 
5
+ ## 0.1.3 - 2026-08-17
6
+
7
+ - Discover user Adapter files under `<agent-dir>/extensions/pi-provider/` in addition to built-ins; user files load last and override same-ID built-ins.
8
+ - Run the provider runtime inside a single Jiti module graph behind a thin Pi entrypoint; inject the agent directory, stored credentials, and ANSI text wrapping.
9
+ - Export the Adapter API through a Jiti-safe public entrypoint aliased as `@hyav/pi-provider`, aligned with the built-in Adapters as reference templates.
10
+ - Resolve duplicate Adapter IDs and bindings to the latest registration with a warning instead of excluding all colliding entries.
11
+ - Re-read modified Adapter files on `/reload` by clearing the Adapter module cache.
12
+ - Restore the programmatic default OpenRouter metadata cache path under the resolved agent directory.
13
+
14
+ ## 0.1.2 - 2026-08-17
15
+
16
+ - Expose one Pi package entrypoint while preserving file-level Adapter discovery when `/reload` runs.
17
+ - Isolate invalid Adapter modules behind capability-relative diagnostics and declare `jiti` as the runtime loader.
18
+ - Rename the public APIs to Pi Provider equivalents and remove legacy aliases; programmatic consumers must update those imported names.
19
+ - Store generated metadata under `<agent-dir>/pi-provider/`, following Pi's resolved agent directory.
20
+
5
21
  ## 0.1.1 - 2026-08-16
6
22
 
7
23
  - Hardened zero-coupling contract tests and isolated temporary directories for manifest drop-in validation.
@@ -10,7 +26,7 @@ This file is the authoritative user-facing release history for `@hyav/pi-provide
10
26
  ## 0.1.0 - 2026-08-16
11
27
 
12
28
  - Initial public release of `@hyav/pi-provider`.
13
- - Provider Kit host for dynamic providers, status, preflight, live checks, and request tuners.
29
+ - Pi Provider host for dynamic providers, status, preflight, live checks, and request tuners.
14
30
  - Manifest-discovered adapter extensions with deterministic ordering, reload isolation, validation, and conflict handling.
15
31
  - Cached model catalogs, bounded background refresh, pricing metadata, and explicit free-versus-billable diagnostics.
16
32
  - Built-in Charm Hyper, DeepSeek, Google Gemini, OpenAI Codex, OpenCode Zen, and OpenCode Go integrations.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @hyav/pi-provider
1
+ # pi-provider
2
2
 
3
3
  [简体中文](README.zh-CN.md)
4
4
 
@@ -8,8 +8,8 @@ A provider extension toolkit for [Pi](https://pi.dev). It registers LLM provider
8
8
 
9
9
  ## Highlights
10
10
 
11
- - One Provider Kit Host for registration, status, preflight checks, live checks, and request tuners
12
- - Manifest-discovered provider, status, preflight, and tuner adapters reloaded with `/reload`
11
+ - One Pi Provider Host for registration, status, preflight checks, live checks, and request tuners
12
+ - Provider, status, preflight, and tuner Adapter files discovered by one Pi entrypoint on `/reload`
13
13
  - Resilient model catalogs with cached fallback, bounded background refresh, and failure retention
14
14
  - Provider-first pricing metadata with optional OpenRouter completion and quality indicators
15
15
  - Explicit diagnostics: cached `/status`, free `/status refresh`, and potentially billable `/status check`
@@ -46,11 +46,32 @@ Use `/status refresh` for free endpoint, authentication, catalog, and account ch
46
46
  | Name | Required | Default | Effect |
47
47
  |---|---:|---|---|
48
48
  | `HYPER_API_KEY` | For Charm Hyper API-key auth | None | Supplies the built-in `charm-hyper` provider credential; OAuth users may use `/login` |
49
- | `PI_CODING_AGENT_DIR` | No | `~/.pi/agent` | Changes the base directory for public OpenRouter metadata cache |
49
+ | `PI_CODING_AGENT_DIR` | No | `~/.pi/agent` | Changes Pi's agent directory; public OpenRouter metadata is cached under `<agent-dir>/extensions/pi-provider/` |
50
50
 
51
- Programmatic integrations can configure pricing fallback, pricing policies, request timeouts, metadata URLs, and cache paths through `createProviderKitRuntime()` or `createProviderKitHost()`. The source definition [`ProviderKitDependencies`](core/runtime-config.ts) is authoritative.
51
+ Programmatic integrations can configure pricing fallback, pricing policies, request timeouts, metadata URLs, and cache paths through `createPiProviderRuntime()` or `createPiProviderHost()`. Programmatic defaults resolve the agent directory from `PI_CODING_AGENT_DIR` (falling back to `~/.pi/agent`) and keep the OpenRouter metadata cache on disk under `<agent-dir>/extensions/pi-provider/`, matching the table above; the Pi entrypoint overrides it with Pi's own resolution. Host packages with a custom capability root can call `createPiProviderExtension({ adapterRoot, dependencies })`. The source definition [`PiProviderDependencies`](core/runtime-config.ts) is authoritative.
52
52
 
53
- Trusted packages can add adapters through manifest entries under `providers/`, `status/`, `preflight/`, and `tuners/`. See the [adapter extension contract](https://github.com/hyav/pi-provider/blob/main/docs/adapter-extensions.md) for helpers, validation, conflicts, reload behavior, and lifecycle boundaries. The root [`index.ts`](index.ts) defines the public TypeScript exports.
53
+ ## Adapter discovery (file-level plug and play)
54
+
55
+ Built-in Adapters ship inside the package and are always discovered. User Adapters live under Pi's resolved agent directory and are discovered too:
56
+
57
+ ```text
58
+ <agent-dir>/extensions/pi-provider/
59
+ providers/ # provider Adapter files
60
+ status/ # status Adapter files
61
+ preflight/ # preflight Adapter files
62
+ tuners/ # tuner Adapter files
63
+ ```
64
+
65
+
66
+ Add, remove, or modify files there, then run `/reload` to rediscover them without touching the package; edits to existing files are re-read from disk. User Adapters load after built-ins, so a same-ID file overrides the built-in Adapter (the Host keeps the latest registration and warns). `createPiProviderExtension({ adapterRoot })` replaces the default user directory with a custom root; built-ins are always scanned. The built-in Adapters under the package's `providers/`, `status/`, and `preflight/` are reference templates with this exact shape — copy one and customize it (Charm Hyper and `preflight/openai-codex.ts` also use package-private helpers).
67
+
68
+ Adapter files import helpers and types from `@hyav/pi-provider` (aliased inside the loader):
69
+
70
+ ```ts
71
+ import { defineProviderExtension } from "@hyav/pi-provider";
72
+ ```
73
+
74
+ Adapter files must not runtime-import Pi's bundled packages (`@earendil-works/pi-coding-agent`, `@earendil-works/pi-tui`, `@earendil-works/pi-ai`); type-only imports are fine. Runtime values such as the agent directory, stored credentials, and the ANSI text wrapper are injected by the Pi entrypoint. See the [adapter extension contract](https://github.com/hyav/pi-provider/blob/main/docs/adapter-extensions.md) for helpers, validation, conflicts, reload behavior, and lifecycle boundaries. The root [`index.ts`](index.ts) defines the public TypeScript exports.
54
75
 
55
76
  ## Before you use it
56
77
 
package/README.zh-CN.md CHANGED
@@ -1,4 +1,4 @@
1
- # @hyav/pi-provider
1
+ # pi-provider
2
2
 
3
3
  [English](README.md)
4
4
 
@@ -8,8 +8,8 @@
8
8
 
9
9
  ## 核心能力
10
10
 
11
- - 由一个 Provider Kit Host 统一负责注册、Status、Preflight、实时检查和请求 Tuner
12
- - 通过 manifest 发现 Provider、Status、Preflight 和 Tuner Adapter,并在 `/reload` 后重新加载
11
+ - 由一个 Pi Provider Host 统一负责注册、Status、Preflight、实时检查和请求 Tuner
12
+ - 由单一 Pi 入口在 `/reload` 时发现 Provider、Status、Preflight 和 Tuner Adapter 文件
13
13
  - 通过缓存回退、有界后台刷新和失败保留提供可靠的模型目录
14
14
  - 优先采用 Provider 价格元数据,并可由 OpenRouter 补全价格和质量指标
15
15
  - 显式诊断:缓存 `/status`、免费 `/status refresh` 和可能计费的 `/status check`
@@ -46,11 +46,32 @@ pi install npm:@hyav/pi-provider
46
46
  | 名称 | 必需 | 默认值 | 作用 |
47
47
  |---|---:|---|---|
48
48
  | `HYPER_API_KEY` | Charm Hyper API Key 鉴权需要 | 无 | 为内置 `charm-hyper` Provider 提供凭据;OAuth 用户可以使用 `/login` |
49
- | `PI_CODING_AGENT_DIR` | 否 | `~/.pi/agent` | 修改公开 OpenRouter 元数据缓存的基础目录 |
49
+ | `PI_CODING_AGENT_DIR` | 否 | `~/.pi/agent` | 修改 Pi agent 目录;公开 OpenRouter 元数据缓存在 `<agent-dir>/extensions/pi-provider/` 下 |
50
50
 
51
- 程序化集成可以通过 `createProviderKitRuntime()` 或 `createProviderKitHost()` 配置价格回退、价格策略、请求超时、元数据 URL 和缓存路径。源码定义 [`ProviderKitDependencies`](core/runtime-config.ts) 是权威依据。
51
+ 程序化集成可以通过 `createPiProviderRuntime()` 或 `createPiProviderHost()` 配置价格回退、价格策略、请求超时、元数据 URL 和缓存路径。程序化默认值会从 `PI_CODING_AGENT_DIR`(回退到 `~/.pi/agent`)解析 agent 目录,并保持 OpenRouter 元数据缓存落盘到 `<agent-dir>/extensions/pi-provider/`,与上表一致;Pi 入口会用 Pi 自身的解析覆盖它。使用自定义 capability 根目录的 Host 包可以调用 `createPiProviderExtension({ adapterRoot, dependencies })`。源码定义 [`PiProviderDependencies`](core/runtime-config.ts) 是权威依据。
52
52
 
53
- 可信包可以通过 manifest 中的 `providers/`、`status/`、`preflight/` 和 `tuners/` 条目添加 Adapter。Helper、校验、冲突、reload 行为和生命周期边界见 [Adapter Extension 契约](https://github.com/hyav/pi-provider/blob/main/docs/adapter-extensions.zh-CN.md)。根目录 [`index.ts`](index.ts) 定义公开 TypeScript 导出。
53
+ ## Adapter 发现(文件级即插即用)
54
+
55
+ 内置 Adapter 随包发布,始终被扫描。用户 Adapter 放在 Pi 解析出的 agent 目录下,同样会被发现:
56
+
57
+ ```text
58
+ <agent-dir>/extensions/pi-provider/
59
+ providers/ # Provider Adapter 文件
60
+ status/ # Status Adapter 文件
61
+ preflight/ # Preflight Adapter 文件
62
+ tuners/ # Tuner Adapter 文件
63
+ ```
64
+
65
+
66
+ 在目录中增删或修改文件后执行 `/reload` 即可重新发现,无需改动包;对现有文件的修改会重新从磁盘读取。用户 Adapter 在内置之后加载,因此同 ID 的用户文件会覆盖内置 Adapter(Host 保留最新注册并发出警告)。`createPiProviderExtension({ adapterRoot })` 用自定义根替换默认用户目录;内置目录始终被扫描。包内 `providers/`、`status/`、`preflight/` 下的内置 Adapter 就是采用这种写法的参考模板——复制一份改改即可(Charm Hyper 与 `preflight/openai-codex.ts` 还依赖包内私有辅助文件)。
67
+
68
+ Adapter 文件从 `@hyav/pi-provider` 导入 helper 和类型(加载器内部做了别名映射):
69
+
70
+ ```ts
71
+ import { defineProviderExtension } from "@hyav/pi-provider";
72
+ ```
73
+
74
+ Adapter 文件不得运行时导入 Pi 的内置包(`@earendil-works/pi-coding-agent`、`@earendil-works/pi-tui`、`@earendil-works/pi-ai`),仅 type-only 导入可以;agent 目录、存储凭据和 ANSI 文本包装器等运行时值由 Pi 入口注入。Helper、校验、冲突、reload 行为和生命周期边界见 [Adapter Extension 契约](https://github.com/hyav/pi-provider/blob/main/docs/adapter-extensions.zh-CN.md)。根目录 [`index.ts`](index.ts) 定义公开 TypeScript 导出。
54
75
 
55
76
  ## 使用须知
56
77
 
package/SECURITY.md CHANGED
@@ -30,7 +30,7 @@ Reports are handled on a best-effort basis; no acknowledgement, remediation, or
30
30
 
31
31
  ## Scope and dependency boundary
32
32
 
33
- This policy covers the source repository, the published `@hyav/pi-provider` npm artifact, its Pi manifest, built-in adapters, credential handling, remote response validation, deadlines, and the Provider Kit runtime. The published artifact has no bundled runtime dependencies; its Pi core imports are host-supplied peers. CI runs both `npm run audit:runtime` for the published boundary and `npm run audit:all` for development and tested-host dependencies. Scanner findings are triaged rather than force-fixed across an untested host version.
33
+ This policy covers the source repository, the published `@hyav/pi-provider` npm artifact, its Pi manifest, built-in adapters, credential handling, remote response validation, deadlines, and the Pi Provider runtime. The published artifact has no bundled runtime dependencies; its Pi core imports are host-supplied peers. CI runs both `npm run audit:runtime` for the published boundary and `npm run audit:all` for development and tested-host dependencies. Scanner findings are triaged rather than force-fixed across an untested host version.
34
34
 
35
35
  Vulnerabilities in Pi, a Provider service, npm, GitHub, or another dependency should also be reported to the relevant upstream maintainer. For ordinary defects and usage questions, use [SUPPORT.md](SUPPORT.md) and the [public issue tracker](https://github.com/hyav/pi-provider/issues).
36
36
 
@@ -2,21 +2,21 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
2
  import {
3
3
  type AdapterKind,
4
4
  type AdapterRegistrationEnvelope,
5
- PROVIDER_KIT_ADAPTER_EVENT,
6
- PROVIDER_KIT_ADAPTER_PROTOCOL_VERSION,
7
- PROVIDER_KIT_STARTUP_BRIDGE_EVENT,
5
+ PI_PROVIDER_ADAPTER_EVENT,
6
+ PI_PROVIDER_ADAPTER_PROTOCOL_VERSION,
7
+ PI_PROVIDER_STARTUP_BRIDGE_EVENT,
8
8
  type StartupBridge,
9
9
  type StartupBridgeRequest,
10
10
  } from "./adapter-protocol.ts";
11
11
  import { isStableAdapterId, validateAdapter, validateAdapterIdentity } from "./adapter-validation.ts";
12
12
  import type { PreflightAdapter } from "./preflight-manager.ts";
13
13
  import { registerProviderAdapter } from "./provider-registration.ts";
14
- import type { ProviderKitDependencies } from "./runtime-config.ts";
15
- import { getDefaultProviderKitDependencies } from "./runtime-config.ts";
14
+ import type { PiProviderDependencies } from "./runtime-config.ts";
15
+ import { getDefaultPiProviderDependencies } from "./runtime-config.ts";
16
16
  import type { ProviderAdapter, StatusAdapter, TunerAdapter } from "./types.ts";
17
17
 
18
18
  /** Context supplied to an Adapter Extension factory. */
19
- export interface AdapterExtensionContext extends ProviderKitDependencies {
19
+ export interface AdapterExtensionContext extends PiProviderDependencies {
20
20
  /** The Pi API that owns this extension factory. */
21
21
  pi: ExtensionAPI;
22
22
  }
@@ -52,19 +52,19 @@ function validateStaticIdentity(kind: AdapterKind, id: string, providerId?: stri
52
52
 
53
53
  function getStartupBridge(pi: ExtensionAPI): StartupBridge {
54
54
  const request: StartupBridgeRequest = {};
55
- pi.events.emit(PROVIDER_KIT_STARTUP_BRIDGE_EVENT, request);
55
+ pi.events.emit(PI_PROVIDER_STARTUP_BRIDGE_EVENT, request);
56
56
  return (
57
57
  request.bridge ?? {
58
- dependencies: getDefaultProviderKitDependencies(),
58
+ dependencies: getDefaultPiProviderDependencies(),
59
59
  officialPricing: Promise.resolve({}),
60
60
  }
61
61
  );
62
62
  }
63
63
 
64
64
  function emitRegistration(pi: ExtensionAPI, envelope: AdapterRegistrationEnvelope): void {
65
- pi.events.emit(PROVIDER_KIT_ADAPTER_EVENT, envelope);
65
+ pi.events.emit(PI_PROVIDER_ADAPTER_EVENT, envelope);
66
66
  pi.on("session_start", () => {
67
- pi.events.emit(PROVIDER_KIT_ADAPTER_EVENT, envelope);
67
+ pi.events.emit(PI_PROVIDER_ADAPTER_EVENT, envelope);
68
68
  });
69
69
  }
70
70
 
@@ -94,7 +94,7 @@ function createAdapterExtension<TAdapter extends ProviderAdapter | StatusAdapter
94
94
  // loaded later can recreate the adapter with its configured runtime.
95
95
  registerProviderAdapter(pi, providerAdapter, bridge.dependencies, {}, modelDrafts);
96
96
  emitRegistration(pi, {
97
- version: PROVIDER_KIT_ADAPTER_PROTOCOL_VERSION,
97
+ version: PI_PROVIDER_ADAPTER_PROTOCOL_VERSION,
98
98
  kind: "provider",
99
99
  id,
100
100
  token,
@@ -110,7 +110,7 @@ function createAdapterExtension<TAdapter extends ProviderAdapter | StatusAdapter
110
110
  validateAdapter("status", statusAdapter);
111
111
  validateAdapterIdentity("status", id, providerId, statusAdapter);
112
112
  emitRegistration(pi, {
113
- version: PROVIDER_KIT_ADAPTER_PROTOCOL_VERSION,
113
+ version: PI_PROVIDER_ADAPTER_PROTOCOL_VERSION,
114
114
  kind: "status",
115
115
  id,
116
116
  providerId: providerId!,
@@ -126,7 +126,7 @@ function createAdapterExtension<TAdapter extends ProviderAdapter | StatusAdapter
126
126
  validateAdapter("preflight", preflightAdapter);
127
127
  validateAdapterIdentity("preflight", id, providerId, preflightAdapter);
128
128
  emitRegistration(pi, {
129
- version: PROVIDER_KIT_ADAPTER_PROTOCOL_VERSION,
129
+ version: PI_PROVIDER_ADAPTER_PROTOCOL_VERSION,
130
130
  kind: "preflight",
131
131
  id,
132
132
  providerId: providerId!,
@@ -141,7 +141,7 @@ function createAdapterExtension<TAdapter extends ProviderAdapter | StatusAdapter
141
141
  validateAdapter("tuner", tunerAdapter);
142
142
  validateAdapterIdentity("tuner", id, tunerAdapter);
143
143
  emitRegistration(pi, {
144
- version: PROVIDER_KIT_ADAPTER_PROTOCOL_VERSION,
144
+ version: PI_PROVIDER_ADAPTER_PROTOCOL_VERSION,
145
145
  kind: "tuner",
146
146
  id,
147
147
  token,
@@ -0,0 +1,99 @@
1
+ import type { Dirent } from "node:fs";
2
+ import { readdir } from "node:fs/promises";
3
+ import { createRequire } from "node:module";
4
+ import { dirname, extname, join, relative, sep } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
7
+ import { createJiti } from "jiti";
8
+
9
+ const adapterDirectories = ["providers", "status", "preflight", "tuners"] as const;
10
+ const defaultPackageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
11
+ /** Jiti-safe module that adapter files reach through the `@hyav/pi-provider` alias. */
12
+ const publicAdaptersPath = join(defaultPackageRoot, "core", "public-adapters.ts");
13
+
14
+ type AdapterExtensionFactory = (pi: ExtensionAPI) => void | Promise<void>;
15
+
16
+ export interface AdapterRootOptions {
17
+ /** Package root containing built-in adapters; defaults to this package. */
18
+ packageRoot?: string;
19
+ /** Pi's resolved agent directory; user adapters are discovered under `<agentDir>/extensions/pi-provider`. */
20
+ agentDir?: string;
21
+ /** Explicit user adapter root; replaces the agentDir-based default when provided. */
22
+ userRoot?: string;
23
+ }
24
+
25
+ /** Built-in adapters always load first; user adapters load last so they can override. */
26
+ export function resolveAdapterRoots(options: AdapterRootOptions): string[] {
27
+ const { packageRoot, agentDir, userRoot } = options;
28
+ const defaultUserRoot =
29
+ agentDir !== undefined && agentDir !== "" ? join(agentDir, "extensions", "pi-provider") : undefined;
30
+ const resolvedUserRoot = userRoot ?? defaultUserRoot;
31
+ const roots = [packageRoot ?? defaultPackageRoot];
32
+ if (resolvedUserRoot !== undefined) roots.push(resolvedUserRoot);
33
+ return roots;
34
+ }
35
+
36
+ /**
37
+ * Jiti's module cache is Node's global `require.cache`, so a reload would
38
+ * otherwise reuse previously loaded adapter modules without reading the disk.
39
+ * Drop every cached module under the adapter roots before each load so edits
40
+ * to existing adapter files take effect on `/reload`.
41
+ */
42
+ function clearAdapterModuleCache(roots: string[]): void {
43
+ const require = createRequire(import.meta.url);
44
+ for (const key of Object.keys(require.cache)) {
45
+ if (roots.some((root) => key.startsWith(root.endsWith(sep) ? root : `${root}${sep}`))) {
46
+ delete require.cache[key];
47
+ }
48
+ }
49
+ }
50
+
51
+ function isAdapterFile(name: string): boolean {
52
+ if (name.endsWith(".d.ts")) return false;
53
+ return extname(name) === ".ts" || extname(name) === ".js";
54
+ }
55
+
56
+ function warnAdapterLoadIssue(path: string, error: unknown): void {
57
+ const message = error instanceof Error ? error.message : String(error);
58
+ console.warn(`[pi-provider] failed to load adapter extension ${JSON.stringify(path)}: ${message}`);
59
+ }
60
+
61
+ async function discoverAdapterPaths(root: string): Promise<string[]> {
62
+ const paths: string[] = [];
63
+ for (const directory of adapterDirectories) {
64
+ let entries: Dirent[];
65
+ try {
66
+ entries = await readdir(join(root, directory), { withFileTypes: true });
67
+ } catch (error) {
68
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") continue;
69
+ throw error;
70
+ }
71
+ for (const entry of entries) {
72
+ if (entry.isFile() && isAdapterFile(entry.name)) paths.push(join(root, directory, entry.name));
73
+ }
74
+ }
75
+ return paths.sort();
76
+ }
77
+
78
+ export async function loadPackageAdapterExtensions(pi: ExtensionAPI, options: AdapterRootOptions = {}): Promise<void> {
79
+ const roots = resolveAdapterRoots(options);
80
+ clearAdapterModuleCache(roots);
81
+ const jiti = createJiti(import.meta.url, {
82
+ moduleCache: true,
83
+ tryNative: false,
84
+ alias: { "@hyav/pi-provider": publicAdaptersPath },
85
+ });
86
+ for (const root of roots) {
87
+ for (const path of await discoverAdapterPaths(root)) {
88
+ try {
89
+ const factory = (await jiti.import(path, { default: true })) as unknown;
90
+ if (typeof factory !== "function") {
91
+ throw new TypeError("default export must be a Pi extension factory");
92
+ }
93
+ await (factory as AdapterExtensionFactory)(pi);
94
+ } catch (error) {
95
+ warnAdapterLoadIssue(relative(root, path).replaceAll("\\", "/"), error);
96
+ }
97
+ }
98
+ }
99
+ }
@@ -1,24 +1,24 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
2
  import type { OfficialModelMeta } from "./official-pricing.ts";
3
3
  import type { PreflightAdapter } from "./preflight-manager.ts";
4
- import type { ProviderKitDependencies } from "./runtime-config.ts";
4
+ import type { PiProviderDependencies } from "./runtime-config.ts";
5
5
  import type { ProviderAdapter, ProviderModelDraft, StatusAdapter, TunerAdapter } from "./types.ts";
6
6
 
7
7
  /** Registration protocol version shared by Host and Adapter Extensions. */
8
- export const PROVIDER_KIT_ADAPTER_PROTOCOL_VERSION = 2 as const;
8
+ export const PI_PROVIDER_ADAPTER_PROTOCOL_VERSION = 2 as const;
9
9
 
10
10
  /** Event-bus channel used for Adapter Extension registration envelopes. */
11
- export const PROVIDER_KIT_ADAPTER_EVENT = "provider-kit:adapter";
11
+ export const PI_PROVIDER_ADAPTER_EVENT = "pi-provider:adapter";
12
12
 
13
13
  /** Event-bus channel used by the Host to expose the factory-stage startup bridge. */
14
- export const PROVIDER_KIT_STARTUP_BRIDGE_EVENT = "provider-kit:startup-bridge";
14
+ export const PI_PROVIDER_STARTUP_BRIDGE_EVENT = "pi-provider:startup-bridge";
15
15
 
16
- /** Event-bus channel used to detect more than one Provider Kit Host. */
17
- export const PROVIDER_KIT_HOST_CLAIM_EVENT = "provider-kit:host-claim";
16
+ /** Event-bus channel used to detect more than one Pi Provider Host. */
17
+ export const PI_PROVIDER_HOST_CLAIM_EVENT = "pi-provider:host-claim";
18
18
 
19
19
  export type AdapterKind = "provider" | "status" | "preflight" | "tuner";
20
20
 
21
- export interface AdapterFactoryContext extends ProviderKitDependencies {
21
+ export interface AdapterFactoryContext extends PiProviderDependencies {
22
22
  pi: ExtensionAPI;
23
23
  }
24
24
 
@@ -32,14 +32,14 @@ export type AdapterFactory = (
32
32
  | Promise<ProviderAdapter | StatusAdapter | PreflightAdapter | TunerAdapter>;
33
33
 
34
34
  export interface AdapterRegistrationEnvelopeBase {
35
- version: typeof PROVIDER_KIT_ADAPTER_PROTOCOL_VERSION;
35
+ version: typeof PI_PROVIDER_ADAPTER_PROTOCOL_VERSION;
36
36
  kind: AdapterKind;
37
37
  id: string;
38
38
  token: object;
39
39
  /** Factory retained for Host-side rehydration when Host loaded after Adapter. */
40
40
  factory: AdapterFactory;
41
41
  /** Exact dependency object used by the initial factory invocation. */
42
- startupDependencies: ProviderKitDependencies;
42
+ startupDependencies: PiProviderDependencies;
43
43
  }
44
44
 
45
45
  export interface ProviderAdapterRegistrationEnvelope extends AdapterRegistrationEnvelopeBase {
@@ -73,7 +73,7 @@ export type AdapterRegistrationEnvelope =
73
73
  | TunerAdapterRegistrationEnvelope;
74
74
 
75
75
  export interface StartupBridge {
76
- dependencies: ProviderKitDependencies;
76
+ dependencies: PiProviderDependencies;
77
77
  officialPricing: Promise<Record<string, OfficialModelMeta>>;
78
78
  }
79
79
 
@@ -102,7 +102,7 @@ export function isAdapterRegistrationEnvelope(value: unknown): value is AdapterR
102
102
  if (value === null || typeof value !== "object") return false;
103
103
  const candidate = value as Partial<AdapterRegistrationEnvelope>;
104
104
  return (
105
- candidate.version === PROVIDER_KIT_ADAPTER_PROTOCOL_VERSION &&
105
+ candidate.version === PI_PROVIDER_ADAPTER_PROTOCOL_VERSION &&
106
106
  (candidate.kind === "provider" ||
107
107
  candidate.kind === "status" ||
108
108
  candidate.kind === "preflight" ||
@@ -2,7 +2,7 @@ import { validateAdapter } from "./adapter-validation.ts";
2
2
  import type { PreflightAdapter } from "./preflight-manager.ts";
3
3
  import type { ProviderAdapter, StatusAdapter, TunerAdapter } from "./types.ts";
4
4
 
5
- export interface ProviderKitDefinition {
5
+ export interface PiProviderDefinition {
6
6
  providers: ProviderAdapter[];
7
7
  statuses?: StatusAdapter[];
8
8
  preflights?: PreflightAdapter[];
@@ -24,15 +24,15 @@ function rejectDuplicate(ids: Set<string>, id: string, label: string): void {
24
24
  * Dynamic Hosts use the same adapter validators while resolving conflicts, but
25
25
  * keep invalid entries isolated instead of failing the whole registry.
26
26
  */
27
- export function validateProviderKitDefinition(value: unknown): asserts value is ProviderKitDefinition {
27
+ export function validatePiProviderDefinition(value: unknown): asserts value is PiProviderDefinition {
28
28
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
29
- throw new Error("Provider kit definition must be an object");
29
+ throw new Error("Pi Provider definition must be an object");
30
30
  }
31
- const definition = value as Partial<ProviderKitDefinition>;
32
- assertArray(definition.providers, "Provider kit definition providers");
33
- if (definition.statuses !== undefined) assertArray(definition.statuses, "Provider kit definition statuses");
34
- if (definition.preflights !== undefined) assertArray(definition.preflights, "Provider kit definition preflights");
35
- if (definition.tuners !== undefined) assertArray(definition.tuners, "Provider kit definition tuners");
31
+ const definition = value as Partial<PiProviderDefinition>;
32
+ assertArray(definition.providers, "Pi Provider definition providers");
33
+ if (definition.statuses !== undefined) assertArray(definition.statuses, "Pi Provider definition statuses");
34
+ if (definition.preflights !== undefined) assertArray(definition.preflights, "Pi Provider definition preflights");
35
+ if (definition.tuners !== undefined) assertArray(definition.tuners, "Pi Provider definition tuners");
36
36
 
37
37
  const providerIds = new Set<string>();
38
38
  for (const adapter of definition.providers) {
package/core/extension.ts CHANGED
@@ -1,5 +1,5 @@
1
- export type { ProviderKitDefinition } from "./definition.ts";
2
- export { validateProviderKitDefinition } from "./definition.ts";
1
+ export type { PiProviderDefinition } from "./definition.ts";
2
+ export { validatePiProviderDefinition } from "./definition.ts";
3
3
  export {
4
4
  normalizeProviderModel,
5
5
  normalizeProviderModels,
@@ -7,14 +7,14 @@ export {
7
7
  registerProviderAdapter,
8
8
  } from "./provider-registration.ts";
9
9
  export {
10
- createProviderKitRuntime,
11
- installProviderKitRuntime,
12
- type ProviderKitRuntimeController,
10
+ createPiProviderRuntime,
11
+ installPiProviderRuntime,
12
+ type PiProviderRuntimeController,
13
13
  } from "./runtime.ts";
14
- export type { ProviderKitDependencies, ProviderKitLoader } from "./runtime-config.ts";
14
+ export type { PiProviderDependencies, PiProviderLoader } from "./runtime-config.ts";
15
15
  export {
16
- getDefaultProviderKitDependencies,
17
- resolveProviderKitDependencies,
18
- validateProviderKitDependencies,
16
+ getDefaultPiProviderDependencies,
17
+ resolvePiProviderDependencies,
18
+ validatePiProviderDependencies,
19
19
  } from "./runtime-config.ts";
20
20
  export { getStatusModeCompletions } from "./status-report.ts";
package/core/host.ts CHANGED
@@ -3,26 +3,26 @@ import {
3
3
  type AdapterRegistrationEnvelope,
4
4
  isAdapterRegistrationEnvelope,
5
5
  isHostClaimRequest,
6
- PROVIDER_KIT_ADAPTER_EVENT,
7
- PROVIDER_KIT_HOST_CLAIM_EVENT,
8
- PROVIDER_KIT_STARTUP_BRIDGE_EVENT,
6
+ PI_PROVIDER_ADAPTER_EVENT,
7
+ PI_PROVIDER_HOST_CLAIM_EVENT,
8
+ PI_PROVIDER_STARTUP_BRIDGE_EVENT,
9
9
  type StartupBridge,
10
10
  type StartupBridgeRequest,
11
11
  } from "./adapter-protocol.ts";
12
12
  import { validateAdapter, validateAdapterIdentity, validateProviderAdapter } from "./adapter-validation.ts";
13
- import { type ProviderKitDefinition, validateProviderKitDefinition } from "./definition.ts";
13
+ import { type PiProviderDefinition, validatePiProviderDefinition } from "./definition.ts";
14
14
  import {
15
15
  getStatusModeCompletions,
16
- installProviderKitRuntime,
17
- type ProviderKitRuntimeController,
16
+ installPiProviderRuntime,
17
+ type PiProviderRuntimeController,
18
18
  prepareProviderRegistration,
19
19
  } from "./extension.ts";
20
20
  import { fetchOfficialPricing, type OfficialModelMeta, OPENROUTER_MODELS_URL } from "./official-pricing.ts";
21
21
  import type { PreflightAdapter } from "./preflight-manager.ts";
22
22
  import { refreshProviderRegistrations } from "./provider-registration.ts";
23
23
  import { scheduleModelCatalogRefresh } from "./runtime.ts";
24
- import type { ProviderKitDependencies } from "./runtime-config.ts";
25
- import { resolveProviderKitDependencies } from "./runtime-config.ts";
24
+ import type { PiProviderDependencies } from "./runtime-config.ts";
25
+ import { resolvePiProviderDependencies } from "./runtime-config.ts";
26
26
  import type { ProviderAdapter, ProviderModelDraft, StatusAdapter, TunerAdapter } from "./types.ts";
27
27
 
28
28
  function compareAdapterIds(left: { id: string }, right: { id: string }): number {
@@ -34,43 +34,43 @@ function adapterKindLabel(kind: AdapterRegistrationEnvelope["kind"]): string {
34
34
  }
35
35
 
36
36
  function warnAdapterIssue(message: string): void {
37
- console.warn(`[provider-kit] ${message}`);
37
+ console.warn(`[pi-provider] ${message}`);
38
38
  }
39
39
 
40
40
  /**
41
- * Create the single Provider Kit Host used by the published Pi entrypoint.
41
+ * Create the single Pi Provider Host used by the published Pi entrypoint.
42
42
  *
43
43
  * The Host deliberately does not assemble adapters during extension factory
44
44
  * loading. Adapter factories can run before or after this factory, so the
45
45
  * event-bus envelopes are collected and assembled at the first session-level
46
46
  * operation after Pi's session_start registration barrier.
47
47
  */
48
- export function createProviderKitHost(dependencies: Partial<ProviderKitDependencies> = {}): (pi: ExtensionAPI) => void {
49
- const runtime = resolveProviderKitDependencies(dependencies);
48
+ export function createPiProviderHost(dependencies: Partial<PiProviderDependencies> = {}): (pi: ExtensionAPI) => void {
49
+ const runtime = resolvePiProviderDependencies(dependencies);
50
50
  return (pi) => {
51
51
  const hostToken = {};
52
52
  const hostClaim = { token: hostToken, occupied: false };
53
- const unsubscribeHostClaim = pi.events.on(PROVIDER_KIT_HOST_CLAIM_EVENT, (value) => {
53
+ const unsubscribeHostClaim = pi.events.on(PI_PROVIDER_HOST_CLAIM_EVENT, (value) => {
54
54
  if (!isHostClaimRequest(value) || value.token === hostToken) return;
55
55
  value.occupied = true;
56
56
  });
57
- pi.events.emit(PROVIDER_KIT_HOST_CLAIM_EVENT, hostClaim);
57
+ pi.events.emit(PI_PROVIDER_HOST_CLAIM_EVENT, hostClaim);
58
58
  if (hostClaim.occupied) {
59
59
  unsubscribeHostClaim();
60
- warnAdapterIssue("ignored a second Provider Kit Host; only one Host is supported per Pi runtime");
60
+ warnAdapterIssue("ignored a second Pi Provider Host; only one Host is supported per Pi runtime");
61
61
  return;
62
62
  }
63
63
 
64
64
  const registrations = new Map<object, AdapterRegistrationEnvelope>();
65
- let active: ProviderKitRuntimeController | undefined;
66
- let readyPromise: Promise<ProviderKitRuntimeController | undefined> | undefined;
65
+ let active: PiProviderRuntimeController | undefined;
66
+ let readyPromise: Promise<PiProviderRuntimeController | undefined> | undefined;
67
67
  let disposed = false;
68
68
  let lifecycleGeneration = 0;
69
69
  let latestBackgroundPricing: Record<string, OfficialModelMeta> | undefined;
70
70
  let installedDefinition:
71
71
  | {
72
72
  generation: number;
73
- definition: ProviderKitDefinition;
73
+ definition: PiProviderDefinition;
74
74
  providerDrafts: Map<ProviderAdapter, ProviderModelDraft[]>;
75
75
  }
76
76
  | undefined;
@@ -97,7 +97,7 @@ export function createProviderKitHost(dependencies: Partial<ProviderKitDependenc
97
97
  readyPromise = undefined;
98
98
  };
99
99
 
100
- const unsubscribeBridge = pi.events.on(PROVIDER_KIT_STARTUP_BRIDGE_EVENT, (value) => {
100
+ const unsubscribeBridge = pi.events.on(PI_PROVIDER_STARTUP_BRIDGE_EVENT, (value) => {
101
101
  if (value === null || typeof value !== "object") return;
102
102
  const request = value as StartupBridgeRequest;
103
103
  request.bridge ??= bridge;
@@ -136,7 +136,7 @@ export function createProviderKitHost(dependencies: Partial<ProviderKitDependenc
136
136
  if (previous === undefined) invalidateRuntime();
137
137
  };
138
138
 
139
- const unsubscribeRegistrations = pi.events.on(PROVIDER_KIT_ADAPTER_EVENT, receiveRegistration);
139
+ const unsubscribeRegistrations = pi.events.on(PI_PROVIDER_ADAPTER_EVENT, receiveRegistration);
140
140
 
141
141
  const groupedWithoutConflicts = <T>(items: T[], key: (item: T) => string, label: string): T[] => {
142
142
  const groups = new Map<string, T[]>();
@@ -148,10 +148,11 @@ export function createProviderKitHost(dependencies: Partial<ProviderKitDependenc
148
148
  const result: T[] = [];
149
149
  for (const [id, group] of groups) {
150
150
  if (group.length > 1) {
151
- warnAdapterIssue(`excluded ${group.length} colliding ${label} entries for ${JSON.stringify(id)}`);
152
- continue;
151
+ warnAdapterIssue(
152
+ `resolved ${group.length} colliding ${label} entries for ${JSON.stringify(id)} to the latest registration`,
153
+ );
153
154
  }
154
- result.push(group[0]!);
155
+ result.push(group[group.length - 1]!);
155
156
  }
156
157
  return result;
157
158
  };
@@ -173,7 +174,7 @@ export function createProviderKitHost(dependencies: Partial<ProviderKitDependenc
173
174
  const buildDefinition = async (
174
175
  ctx?: ExtensionContext,
175
176
  ): Promise<{
176
- definition: ProviderKitDefinition;
177
+ definition: PiProviderDefinition;
177
178
  providerDrafts: Map<ProviderAdapter, ProviderModelDraft[]>;
178
179
  pricing: Record<string, OfficialModelMeta>;
179
180
  }> => {
@@ -358,15 +359,15 @@ export function createProviderKitHost(dependencies: Partial<ProviderKitDependenc
358
359
  preflights: preflightBindings,
359
360
  tuners: tuners.sort(compareAdapterIds),
360
361
  };
361
- validateProviderKitDefinition(definition);
362
+ validatePiProviderDefinition(definition);
362
363
  return { definition, providerDrafts, pricing };
363
364
  };
364
365
 
365
- const ensureReady = (ctx?: ExtensionContext): Promise<ProviderKitRuntimeController | undefined> => {
366
+ const ensureReady = (ctx?: ExtensionContext): Promise<PiProviderRuntimeController | undefined> => {
366
367
  if (active) return Promise.resolve(active);
367
368
  if (readyPromise) return readyPromise;
368
369
  const generation = lifecycleGeneration;
369
- const pending = (async (): Promise<ProviderKitRuntimeController | undefined> => {
370
+ const pending = (async (): Promise<PiProviderRuntimeController | undefined> => {
370
371
  if (disposed || generation !== lifecycleGeneration) return undefined;
371
372
  const { definition, providerDrafts, pricing } = await buildDefinition(ctx);
372
373
  if (disposed || generation !== lifecycleGeneration) return undefined;
@@ -382,7 +383,7 @@ export function createProviderKitHost(dependencies: Partial<ProviderKitDependenc
382
383
  for (const providerId of providerIds) pi.unregisterProvider(providerId);
383
384
  }
384
385
  if (disposed || generation !== lifecycleGeneration) return undefined;
385
- const controller = installProviderKitRuntime(pi, runtime, definition, pricing, {
386
+ const controller = installPiProviderRuntime(pi, runtime, definition, pricing, {
386
387
  registerHandlers: false,
387
388
  providerDrafts,
388
389
  });
@@ -442,7 +443,7 @@ export function createProviderKitHost(dependencies: Partial<ProviderKitDependenc
442
443
  }
443
444
 
444
445
  function fetchOfficialPricingForHost(
445
- runtime: ProviderKitDependencies,
446
+ runtime: PiProviderDependencies,
446
447
  onBackgroundRefresh?: (snapshot: Record<string, OfficialModelMeta>) => void,
447
448
  ) {
448
449
  return fetchOfficialPricing(