From 8a0903ebf2db9d708b3d733119638f7407cdef49 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Wed, 10 Jun 2026 21:17:12 +0200 Subject: [PATCH] feat(ai): compat entrypoint, core-only root barrel (phase 5) The root barrel is now core-only and side-effect free: types, createModels/createProvider, auth substrate, lazyStream/lazyApi, faux, utils. Generated catalogs, api-registry, env-api-keys, images, global stream functions, and per-API lazy wrappers leave the root. New @earendil-works/pi-ai/compat preserves the old surface verbatim as a strict superset of the root: api-dispatch stream/complete with env key injection, the builtin registration side effect (skip-if-present so it cannot clobber earlier overrides), deprecated getModel/getModels/ getProviders aliases of the new getBuiltin* reads in providers/all, lazy api wrappers + setBedrockProviderModule, and image generation. Compat dies with the coding-agent ModelManager migration. Packaging: exports map gains ./compat, ./providers/*, ./api/*; sideEffects array lists only the effectful modules. Old-global imports across agent/coding-agent/examples and pi-ai tests switch to /compat (path-only; compat is a superset). The coding-agent extension loader resolves the pi-ai ROOT specifier to compat, so existing user extensions using the old global API keep working at runtime until compat is removed. vitest configs alias /compat to src; browser smoke imports old globals from /compat. --- packages/agent/docs/models.md | 26 ++++++----- packages/agent/src/agent-loop.ts | 2 +- packages/agent/src/agent.ts | 2 +- packages/agent/src/harness/agent-harness.ts | 2 +- .../compaction/branch-summarization.ts | 4 +- .../src/harness/compaction/compaction.ts | 4 +- packages/agent/src/types.ts | 2 +- packages/agent/test/agent.test.ts | 2 +- .../agent/test/harness/agent-harness.test.ts | 2 +- packages/agent/test/scratch/simple.ts | 2 +- packages/agent/vitest.config.ts | 10 +++++ packages/agent/vitest.harness.config.ts | 10 +++++ packages/ai/package.json | 17 +++++++ packages/ai/src/{stream.ts => compat.ts} | 45 ++++++++++++++++++- packages/ai/src/index.ts | 21 +++------ packages/ai/src/models.ts | 37 --------------- packages/ai/src/providers/all.ts | 33 +++++++++++--- packages/ai/src/utils/oauth/github-copilot.ts | 4 +- packages/ai/test/abort.test.ts | 3 +- ...anthropic-adaptive-thinking-models.test.ts | 2 +- .../anthropic-eager-tool-input-e2e.test.ts | 3 +- ...ic-empty-thinking-signature-compat.test.ts | 2 +- .../anthropic-force-adaptive-thinking.test.ts | 3 +- ...anthropic-long-cache-retention-e2e.test.ts | 3 +- packages/ai/test/anthropic-oauth.test.ts | 37 ++++++++++++++- .../ai/test/anthropic-opus-4-8-smoke.test.ts | 3 +- .../ai/test/anthropic-sse-parsing.test.ts | 2 +- .../test/anthropic-temperature-compat.test.ts | 3 +- .../test/anthropic-thinking-disable.test.ts | 3 +- .../anthropic-tool-name-normalization.test.ts | 3 +- .../ai/test/azure-openai-base-url.test.ts | 2 +- .../ai/test/bedrock-convert-messages.test.ts | 2 +- .../ai/test/bedrock-custom-headers.test.ts | 2 +- .../test/bedrock-endpoint-resolution.test.ts | 2 +- packages/ai/test/bedrock-models.test.ts | 3 +- .../ai/test/bedrock-thinking-payload.test.ts | 2 +- packages/ai/test/cache-retention.test.ts | 3 +- .../ai/test/codex-websocket-cached-probe.ts | 2 +- packages/ai/test/context-overflow.test.ts | 3 +- .../ai/test/cross-provider-handoff.test.ts | 3 +- packages/ai/test/empty.test.ts | 3 +- packages/ai/test/faux-provider.test.ts | 2 +- packages/ai/test/fireworks-models.test.ts | 2 +- .../ai/test/github-copilot-anthropic.test.ts | 2 +- .../ai/test/google-thinking-disable.test.ts | 3 +- .../google-vertex-api-key-resolution.test.ts | 2 +- packages/ai/test/image-tool-result.test.ts | 4 +- packages/ai/test/interleaved-thinking.test.ts | 3 +- packages/ai/test/lazy-module-load.test.ts | 16 +++++-- .../ai/test/mistral-reasoning-mode.test.ts | 3 +- packages/ai/test/mistral-tool-schema.test.ts | 3 +- packages/ai/test/oauth-auth.test.ts | 35 --------------- .../openai-codex-cache-affinity-e2e.test.ts | 3 +- ...i-completions-cache-control-format.test.ts | 2 +- .../openai-completions-empty-tools.test.ts | 3 +- .../openai-completions-prompt-cache.test.ts | 2 +- .../openai-completions-response-model.test.ts | 2 +- .../openai-completions-tool-choice.test.ts | 3 +- ...nai-completions-tool-result-images.test.ts | 2 +- ...penai-responses-cache-affinity-e2e.test.ts | 3 +- .../openai-responses-copilot-provider.test.ts | 2 +- ...enai-responses-foreign-toolcall-id.test.ts | 2 +- .../test/openai-responses-message-id.test.ts | 2 +- ...nai-responses-reasoning-replay-e2e.test.ts | 3 +- ...penai-responses-tool-result-images.test.ts | 4 +- .../test/openrouter-cache-write-repro.test.ts | 3 +- packages/ai/test/responseid.test.ts | 3 +- packages/ai/test/stream.test.ts | 3 +- packages/ai/test/supports-xhigh.test.ts | 2 +- packages/ai/test/together-models.test.ts | 2 +- packages/ai/test/tokens.test.ts | 3 +- .../test/tool-call-id-normalization.test.ts | 3 +- .../ai/test/tool-call-without-result.test.ts | 3 +- packages/ai/test/total-tokens.test.ts | 3 +- packages/ai/test/unicode-surrogate.test.ts | 3 +- packages/ai/test/xhigh.test.ts | 3 +- packages/ai/test/xiaomi-models.test.ts | 2 +- ...ms-anthropic-empty-signature-smoke.test.ts | 2 +- packages/ai/test/zen.test.ts | 2 +- .../examples/extensions/custom-compaction.ts | 2 +- .../custom-provider-gitlab-duo/index.ts | 2 +- .../custom-provider-gitlab-duo/test.ts | 2 +- .../examples/extensions/handoff.ts | 2 +- .../coding-agent/examples/extensions/qna.ts | 2 +- .../examples/extensions/summarize.ts | 2 +- .../examples/sdk/02-custom-model.ts | 2 +- .../examples/sdk/12-full-control.ts | 2 +- .../coding-agent/src/bun/register-bedrock.ts | 2 +- .../coding-agent/src/core/agent-session.ts | 4 +- .../coding-agent/src/core/auth-storage.ts | 2 +- .../core/compaction/branch-summarization.ts | 4 +- .../src/core/compaction/compaction.ts | 4 +- .../src/core/extensions/loader.ts | 22 ++++++--- .../coding-agent/src/core/model-registry.ts | 2 +- packages/coding-agent/src/core/sdk.ts | 2 +- .../src/modes/interactive/interactive-mode.ts | 2 +- ...gent-session-auto-compaction-queue.test.ts | 2 +- .../test/agent-session-branching.test.ts | 2 +- .../test/agent-session-compaction.test.ts | 2 +- .../test/agent-session-concurrent.test.ts | 2 +- .../agent-session-dynamic-provider.test.ts | 2 +- .../test/agent-session-dynamic-tools.test.ts | 2 +- .../test/agent-session-retry.test.ts | 2 +- .../test/agent-session-stats.test.ts | 2 +- .../test/compaction-extensions.test.ts | 2 +- .../test/compaction-summary-reasoning.test.ts | 4 +- packages/coding-agent/test/compaction.test.ts | 4 +- .../coding-agent/test/model-registry.test.ts | 10 ++++- .../rpc-prompt-response-semantics.test.ts | 2 +- .../test/sdk-codex-cache-probe-tool-loop.ts | 2 +- .../test/sdk-session-manager.test.ts | 2 +- ...-allowlist-filters-extension-tools.test.ts | 2 +- ...uiltin-tools-keeps-extension-tools.test.ts | 2 +- packages/coding-agent/test/utilities.ts | 2 +- packages/coding-agent/vitest.config.ts | 2 + scripts/browser-smoke-entry.ts | 3 +- 116 files changed, 316 insertions(+), 261 deletions(-) rename packages/ai/src/{stream.ts => compat.ts} (62%) diff --git a/packages/agent/docs/models.md b/packages/agent/docs/models.md index 0925072e1..cffcecd70 100644 --- a/packages/agent/docs/models.md +++ b/packages/agent/docs/models.md @@ -686,16 +686,17 @@ Built-in provider factories use `createProvider()` internally. models.json custo `@earendil-works/pi-ai/compat` preserves the old global API surface until the coding-agent migration deletes it. New code never imports it. -Old semantics being preserved: global `stream()` dispatched purely on `model.api` via the api-registry, with env API key injection. The compat module reproduces this: +Old semantics being preserved: global `stream()` dispatched purely on `model.api` via the api-registry, with env API key injection. The compat module reproduces this exactly — it does not route through a `Models` collection, so compat consumers get zero behavioral drift (a `Models`-routed variant was considered and dropped: a model with a known provider id but a different api would dispatch wrong, and auth semantics would shift mid-migration). The harness `Models` instance (Phase 6/7) is where new-path streaming happens. -- Lazily creates a default `Models` singleton from `builtinModels()` on first use. -- `stream/complete/streamSimple/completeSimple(model, ctx, opts)`: look up `getProvider(model.provider)`; if found, route through the singleton (auth resolution included). If not found (custom models.json/extension models), fall back to api-dispatch through a hidden `createProvider()` map containing all builtin API implementations plus anything registered via compat `registerApiProvider()`. -- `registerApiProvider()/unregisterApiProviders()` feed that fallback dispatch map. `api-registry.ts` dies as a real mechanism. -- Sync `getModel/getModels/getProviders` become deprecated aliases of `getBuiltinModel/getBuiltinModels/getBuiltinProviders` (they were always pure generated-catalog reads — verified: nothing ever mutated the old `modelRegistry`). -- Re-exports `setBedrockProviderModule` from the bedrock lazy wrapper. -- `getEnvApiKey`/`env-api-keys.ts` stays available from compat only; provider auth methods own env lookup in the new design. +- `stream/complete/streamSimple/completeSimple(model, ctx, opts)`: api-dispatch via the api-registry plus `getEnvApiKey` injection, verbatim old behavior. +- The builtin api registration side effect moves from the root barrel into compat. It skips api ids that already have a registration, since compat may load after a test or extension registered an override. `registerApiProvider()/unregisterApiProviders()` keep feeding the registry; `resetApiProviders()` clears and re-registers builtins. +- Sync `getModel/getModels/getProviders` are deprecated aliases of `getBuiltinModel/getBuiltinModels/getBuiltinProviders` from `providers/all` (they were always pure generated-catalog reads — verified: nothing ever mutated the old `modelRegistry`). +- Re-exports the per-API lazy stream wrappers (incl. `setBedrockProviderModule`), `env-api-keys.ts`, and the image-generation registry/catalogs; none of these stay on the root barrel. +- `export * from "./index.ts"`: compat is a strict superset of the core entrypoint, so consumers switch a file's import path wholesale without symbol surgery. -coding-agent switches imports of these symbols from `@earendil-works/pi-ai` to `@earendil-works/pi-ai/compat` (import-path-only change) and is otherwise untouched until the ModelManager migration. +coding-agent (and the interim agent package) switch imports of these symbols from `@earendil-works/pi-ai` to `@earendil-works/pi-ai/compat` (import-path-only change) and are otherwise untouched until the ModelManager migration. + +Extension grace period: the coding-agent extension loader (jiti aliases + Bun `virtualModules`) resolves the `@earendil-works/pi-ai` ROOT specifier to the compat entrypoint. Existing user extensions using the old global API (`complete`, `getModel`, `registerApiProvider`, ...) keep working at runtime without changes; they break only when compat is removed at the ModelManager migration, with a migration guide in the changelog. Typechecking is the nudge: editors resolve the root to the slim core types, so extension sources that typecheck must import old globals from `/compat` — which is what the repo example extensions demonstrate. ## Builtin static helpers @@ -812,10 +813,10 @@ Check items off as they land. Keep this list current; it is the working state fo ### Phase 5 — packaging -- [ ] `index.ts` core-only (no catalogs, no provider factories, no OAuth, no compat). -- [ ] `compat.ts`: default builtin singleton, `stream/complete/streamSimple/completeSimple` with api-dispatch fallback, `registerApiProvider`/`unregisterApiProviders`, deprecated `getModel/getModels/getProviders` aliases, `setBedrockProviderModule` re-export, `getEnvApiKey`. -- [ ] Subpath exports map; `sideEffects: false`. -- [ ] Browser smoke + shrinkwrap checks green. +- [x] `index.ts` core-only and side-effect free (no catalogs, no provider factories, no api-registry, no env-api-keys, no images, no OAuth, no compat). Typed catalog reads (`getBuiltin*`) implemented in `providers/all.ts`; `models.ts` no longer imports `models.generated.ts`. +- [x] `compat.ts`: superset of index + old api-dispatch globals, deprecated `getModel/getModels/getProviders` aliases, lazy api wrappers + `setBedrockProviderModule`, `getEnvApiKey`, images. Registration side effect lives here (skip-if-present). +- [x] Subpath exports map (`./compat`, `./providers/*`, `./api/*`); `sideEffects` array listing the effectful modules (`compat`, images registration) instead of `false`. +- [x] Browser smoke (entry now imports old globals from `/compat`) + shrinkwrap checks green. Internal old-global imports switched to `/compat` already (42 files in agent/coding-agent/examples; vitest configs alias `/compat` to src; spawn-CLI tests resolve workspace dist, so `packages/ai` + `packages/agent` dists were rebuilt). ### Phase 6 — AgentHarness @@ -828,6 +829,7 @@ Check items off as they land. Keep this list current; it is the working state fo - [ ] Construct `Models` for the harness (builtins + legacy api-dispatch fallback for ModelRegistry custom providers). - [ ] Switch old-global imports to `@earendil-works/pi-ai/compat`. - [ ] Login dialog adapter for `prompt()/notify()` callbacks. +- [ ] Cloudflare cleanup (only after builtin streaming goes through `Models.getAuth`): the cloudflare provider factories' `ApiKeyAuth.resolve` reads key + `CLOUDFLARE_ACCOUNT_ID` (+ `CLOUDFLARE_GATEWAY_ID`) from credential metadata/env, substitutes the `{...}` placeholders in `model.baseUrl`, and returns it as `ModelAuth.baseUrl` (Copilot pattern); unconfigured ids report "not configured" instead of throwing mid-request. Then `resolveCloudflareBaseUrl`/`isCloudflareProvider` drop out of `api/anthropic-messages.ts`, `api/openai-completions.ts`, and `api/openai-responses.ts`; `api/cloudflare.ts` shrinks to the generator's baseUrl constants. The full AuthStorage deletion (`FileCredentialStore` + decorators, see "Replacing AuthStorage") happens in the later ModelManager migration, not this pass. diff --git a/packages/agent/src/agent-loop.ts b/packages/agent/src/agent-loop.ts index 28f037f5d..a3d270df5 100644 --- a/packages/agent/src/agent-loop.ts +++ b/packages/agent/src/agent-loop.ts @@ -10,7 +10,7 @@ import { streamSimple, type ToolResultMessage, validateToolArguments, -} from "@earendil-works/pi-ai"; +} from "@earendil-works/pi-ai/compat"; import type { AgentContext, AgentEvent, diff --git a/packages/agent/src/agent.ts b/packages/agent/src/agent.ts index db6684a8a..54020435e 100644 --- a/packages/agent/src/agent.ts +++ b/packages/agent/src/agent.ts @@ -7,7 +7,7 @@ import { type TextContent, type ThinkingBudgets, type Transport, -} from "@earendil-works/pi-ai"; +} from "@earendil-works/pi-ai/compat"; import { runAgentLoop, runAgentLoopContinue } from "./agent-loop.ts"; import type { AfterToolCallContext, diff --git a/packages/agent/src/harness/agent-harness.ts b/packages/agent/src/harness/agent-harness.ts index 965634653..8d6350a5a 100644 --- a/packages/agent/src/harness/agent-harness.ts +++ b/packages/agent/src/harness/agent-harness.ts @@ -4,7 +4,7 @@ import { type Model, streamSimple, type UserMessage, -} from "@earendil-works/pi-ai"; +} from "@earendil-works/pi-ai/compat"; import { runAgentLoop } from "../agent-loop.ts"; import type { AgentContext, diff --git a/packages/agent/src/harness/compaction/branch-summarization.ts b/packages/agent/src/harness/compaction/branch-summarization.ts index c1824ebf8..a45638894 100644 --- a/packages/agent/src/harness/compaction/branch-summarization.ts +++ b/packages/agent/src/harness/compaction/branch-summarization.ts @@ -1,5 +1,5 @@ -import type { Model } from "@earendil-works/pi-ai"; -import { completeSimple } from "@earendil-works/pi-ai"; +import type { Model } from "@earendil-works/pi-ai/compat"; +import { completeSimple } from "@earendil-works/pi-ai/compat"; import type { AgentMessage } from "../../types.ts"; import { convertToLlm, diff --git a/packages/agent/src/harness/compaction/compaction.ts b/packages/agent/src/harness/compaction/compaction.ts index dba753d78..93c01e2f6 100644 --- a/packages/agent/src/harness/compaction/compaction.ts +++ b/packages/agent/src/harness/compaction/compaction.ts @@ -1,5 +1,5 @@ -import type { AssistantMessage, ImageContent, Model, TextContent, Usage } from "@earendil-works/pi-ai"; -import { completeSimple } from "@earendil-works/pi-ai"; +import type { AssistantMessage, ImageContent, Model, TextContent, Usage } from "@earendil-works/pi-ai/compat"; +import { completeSimple } from "@earendil-works/pi-ai/compat"; import type { AgentMessage, ThinkingLevel } from "../../types.ts"; import { convertToLlm, diff --git a/packages/agent/src/types.ts b/packages/agent/src/types.ts index f24d24963..3365b3046 100644 --- a/packages/agent/src/types.ts +++ b/packages/agent/src/types.ts @@ -9,7 +9,7 @@ import type { TextContent, Tool, ToolResultMessage, -} from "@earendil-works/pi-ai"; +} from "@earendil-works/pi-ai/compat"; import type { Static, TSchema } from "typebox"; /** diff --git a/packages/agent/test/agent.test.ts b/packages/agent/test/agent.test.ts index 82cc58dee..6fa00dd88 100644 --- a/packages/agent/test/agent.test.ts +++ b/packages/agent/test/agent.test.ts @@ -1,4 +1,4 @@ -import { type AssistantMessage, type AssistantMessageEvent, EventStream, getModel } from "@earendil-works/pi-ai"; +import { type AssistantMessage, type AssistantMessageEvent, EventStream, getModel } from "@earendil-works/pi-ai/compat"; import { describe, expect, it } from "vitest"; import { Agent } from "../src/index.ts"; diff --git a/packages/agent/test/harness/agent-harness.test.ts b/packages/agent/test/harness/agent-harness.test.ts index 1d24eb4c2..d54040a8f 100644 --- a/packages/agent/test/harness/agent-harness.test.ts +++ b/packages/agent/test/harness/agent-harness.test.ts @@ -1,4 +1,4 @@ -import { fauxAssistantMessage, fauxToolCall, getModel, registerFauxProvider } from "@earendil-works/pi-ai"; +import { fauxAssistantMessage, fauxToolCall, getModel, registerFauxProvider } from "@earendil-works/pi-ai/compat"; import { afterEach, describe, expect, it } from "vitest"; import { AgentHarness } from "../../src/harness/agent-harness.ts"; import { NodeExecutionEnv } from "../../src/harness/env/nodejs.ts"; diff --git a/packages/agent/test/scratch/simple.ts b/packages/agent/test/scratch/simple.ts index de6ba3a26..6d7bc038a 100644 --- a/packages/agent/test/scratch/simple.ts +++ b/packages/agent/test/scratch/simple.ts @@ -1,6 +1,6 @@ import { homedir } from "node:os"; import { join } from "node:path"; -import { getModel } from "@earendil-works/pi-ai"; +import { getModel } from "@earendil-works/pi-ai/compat"; import { NodeExecutionEnv } from "../../src/harness/env/nodejs.ts"; import { InMemorySessionStorage } from "../../src/harness/session/memory-storage.ts"; import { diff --git a/packages/agent/vitest.config.ts b/packages/agent/vitest.config.ts index bcc497faa..b0f0bb430 100644 --- a/packages/agent/vitest.config.ts +++ b/packages/agent/vitest.config.ts @@ -1,9 +1,19 @@ +import { fileURLToPath } from "node:url"; import { defineConfig } from "vitest/config"; +const aiSrcIndex = fileURLToPath(new URL("../ai/src/index.ts", import.meta.url)); +const aiSrcCompat = fileURLToPath(new URL("../ai/src/compat.ts", import.meta.url)); + export default defineConfig({ test: { globals: true, environment: "node", testTimeout: 30000, // 30 seconds for API calls }, + resolve: { + alias: [ + { find: /^@earendil-works\/pi-ai$/, replacement: aiSrcIndex }, + { find: /^@earendil-works\/pi-ai\/compat$/, replacement: aiSrcCompat }, + ], + }, }); diff --git a/packages/agent/vitest.harness.config.ts b/packages/agent/vitest.harness.config.ts index 9421e5a99..91c0d471b 100644 --- a/packages/agent/vitest.harness.config.ts +++ b/packages/agent/vitest.harness.config.ts @@ -1,5 +1,9 @@ +import { fileURLToPath } from "node:url"; import { defineConfig } from "vitest/config"; +const aiSrcIndex = fileURLToPath(new URL("../ai/src/index.ts", import.meta.url)); +const aiSrcCompat = fileURLToPath(new URL("../ai/src/compat.ts", import.meta.url)); + export default defineConfig({ test: { globals: true, @@ -15,4 +19,10 @@ export default defineConfig({ reportsDirectory: "coverage/harness", }, }, + resolve: { + alias: [ + { find: /^@earendil-works\/pi-ai$/, replacement: aiSrcIndex }, + { find: /^@earendil-works\/pi-ai\/compat$/, replacement: aiSrcCompat }, + ], + }, }); diff --git a/packages/ai/package.json b/packages/ai/package.json index 7b361b9a4..b4112894a 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -5,11 +5,28 @@ "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", + "sideEffects": [ + "./dist/compat.js", + "./dist/images.js", + "./dist/providers/images/register-builtins.js" + ], "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" }, + "./compat": { + "types": "./dist/compat.d.ts", + "import": "./dist/compat.js" + }, + "./providers/*": { + "types": "./dist/providers/*.d.ts", + "import": "./dist/providers/*.js" + }, + "./api/*": { + "types": "./dist/api/*.d.ts", + "import": "./dist/api/*.js" + }, "./anthropic": { "types": "./dist/api/anthropic-messages.d.ts", "import": "./dist/api/anthropic-messages.js" diff --git a/packages/ai/src/stream.ts b/packages/ai/src/compat.ts similarity index 62% rename from packages/ai/src/stream.ts rename to packages/ai/src/compat.ts index f874f03d3..7ddbbaaf5 100644 --- a/packages/ai/src/stream.ts +++ b/packages/ai/src/compat.ts @@ -1,3 +1,32 @@ +/** + * Temporary compatibility entrypoint preserving the old global pi-ai API + * surface: api-dispatch `stream()`/`complete()` with env API key injection, + * the api-registry, generated catalog reads (`getModel`/`getModels`/ + * `getProviders`), per-API lazy stream wrappers, and image generation. + * + * Existing apps switch imports from "@earendil-works/pi-ai" to + * "@earendil-works/pi-ai/compat" unchanged; new code uses `createModels()` + * and the provider factories. This module is deleted with the coding-agent + * ModelManager migration. + */ + +export * from "./api/anthropic-messages.lazy.ts"; +export * from "./api/azure-openai-responses.lazy.ts"; +export * from "./api/bedrock-converse-stream.lazy.ts"; +export * from "./api/google-generative-ai.lazy.ts"; +export * from "./api/google-vertex.lazy.ts"; +export * from "./api/mistral-conversations.lazy.ts"; +export * from "./api/openai-codex-responses.lazy.ts"; +export * from "./api/openai-completions.lazy.ts"; +export * from "./api/openai-responses.lazy.ts"; +export * from "./api-registry.ts"; +export * from "./env-api-keys.ts"; +export * from "./image-models.ts"; +export * from "./images.ts"; +export * from "./images-api-registry.ts"; +export * from "./index.ts"; +export * from "./providers/images/register-builtins.ts"; + import { anthropicMessagesApi } from "./api/anthropic-messages.lazy.ts"; import { azureOpenAIResponsesApi } from "./api/azure-openai-responses.lazy.ts"; import { bedrockConverseStreamApi } from "./api/bedrock-converse-stream.lazy.ts"; @@ -9,6 +38,7 @@ import { openAICompletionsApi } from "./api/openai-completions.lazy.ts"; import { openAIResponsesApi } from "./api/openai-responses.lazy.ts"; import { clearApiProviders, getApiProvider, registerApiProvider } from "./api-registry.ts"; import { getEnvApiKey } from "./env-api-keys.ts"; +import { getBuiltinModel, getBuiltinModels, getBuiltinProviders } from "./providers/all.ts"; import type { Api, AssistantMessage, @@ -21,7 +51,14 @@ import type { StreamOptions, } from "./types.ts"; -export { getEnvApiKey } from "./env-api-keys.ts"; +/** @deprecated Static catalog read. Use `getBuiltinModel` from "@earendil-works/pi-ai/providers/all" or `Models.getModel()`. */ +export const getModel = getBuiltinModel; + +/** @deprecated Static catalog read. Use `getBuiltinModels` from "@earendil-works/pi-ai/providers/all" or `Models.getModels()`. */ +export const getModels = getBuiltinModels; + +/** @deprecated Static catalog read. Use `getBuiltinProviders` from "@earendil-works/pi-ai/providers/all" or `Models.getProviders()`. */ +export const getProviders = getBuiltinProviders; const BUILTIN_APIS: [Api, ProviderStreams][] = [ ["anthropic-messages", anthropicMessagesApi()], @@ -35,8 +72,14 @@ const BUILTIN_APIS: [Api, ProviderStreams][] = [ ["bedrock-converse-stream", bedrockConverseStreamApi()], ]; +/** + * Registers the builtin API implementations into the api-registry without + * clobbering existing entries: compat may load after a test or extension has + * already registered an override for a builtin api id. + */ export function registerBuiltInApiProviders(): void { for (const [api, streams] of BUILTIN_APIS) { + if (getApiProvider(api)) continue; registerApiProvider({ api, stream: streams.stream, streamSimple: streams.streamSimple }); } } diff --git a/packages/ai/src/index.ts b/packages/ai/src/index.ts index cd531d5b6..53a6f7191 100644 --- a/packages/ai/src/index.ts +++ b/packages/ai/src/index.ts @@ -1,40 +1,29 @@ export type { Static, TSchema } from "typebox"; export { Type } from "typebox"; -export * from "./api/anthropic-messages.lazy.ts"; +// Core only, side-effect free: no generated catalogs, no provider factories, +// no api-registry, no OAuth implementations, no compat. Provider factories +// live under "@earendil-works/pi-ai/providers/*", API implementations under +// "@earendil-works/pi-ai/api/*", the old global API under +// "@earendil-works/pi-ai/compat". export type { AnthropicEffort, AnthropicOptions, AnthropicThinkingDisplay } from "./api/anthropic-messages.ts"; -export * from "./api/azure-openai-responses.lazy.ts"; export type { AzureOpenAIResponsesOptions } from "./api/azure-openai-responses.ts"; -export * from "./api/bedrock-converse-stream.lazy.ts"; export type { BedrockOptions, BedrockThinkingDisplay } from "./api/bedrock-converse-stream.ts"; -export * from "./api/google-generative-ai.lazy.ts"; export type { GoogleOptions } from "./api/google-generative-ai.ts"; export type { GoogleThinkingLevel } from "./api/google-shared.ts"; -export * from "./api/google-vertex.lazy.ts"; export type { GoogleVertexOptions } from "./api/google-vertex.ts"; export * from "./api/lazy.ts"; -export * from "./api/mistral-conversations.lazy.ts"; export type { MistralOptions } from "./api/mistral-conversations.ts"; -export * from "./api/openai-codex-responses.lazy.ts"; export type { OpenAICodexResponsesOptions, OpenAICodexWebSocketDebugStats } from "./api/openai-codex-responses.ts"; -export * from "./api/openai-completions.lazy.ts"; export type { OpenAICompletionsOptions } from "./api/openai-completions.ts"; -export * from "./api/openai-responses.lazy.ts"; export type { OpenAIResponsesOptions } from "./api/openai-responses.ts"; -export * from "./api-registry.ts"; export * from "./auth/context.ts"; export * from "./auth/credential-store.ts"; export * from "./auth/helpers.ts"; export * from "./auth/types.ts"; -export * from "./env-api-keys.ts"; -export * from "./image-models.ts"; -export * from "./images.ts"; -export * from "./images-api-registry.ts"; export * from "./models.ts"; export * from "./providers/faux.ts"; -export * from "./providers/images/register-builtins.ts"; export * from "./session-resources.ts"; -export * from "./stream.ts"; export * from "./types.ts"; export * from "./utils/diagnostics.ts"; export * from "./utils/event-stream.ts"; diff --git a/packages/ai/src/models.ts b/packages/ai/src/models.ts index dd698831c..6cb7a87d0 100644 --- a/packages/ai/src/models.ts +++ b/packages/ai/src/models.ts @@ -12,14 +12,12 @@ import type { OAuthCredential, ProviderAuth, } from "./auth/types.ts"; -import { MODELS } from "./models.generated.ts"; import type { Api, ApiStreamOptions, AssistantMessage, AssistantMessageEventStream, Context, - KnownProvider, Model, ModelThinkingLevel, ProviderStreams, @@ -421,41 +419,6 @@ export function hasApi(model: Model, api: TApi): model is return model.api === api; } -const modelRegistry: Map>> = new Map(); - -// Initialize registry from MODELS on module load -for (const [provider, models] of Object.entries(MODELS)) { - const providerModels = new Map>(); - for (const [id, model] of Object.entries(models)) { - providerModels.set(id, model as Model); - } - modelRegistry.set(provider, providerModels); -} - -type ModelApi< - TProvider extends KnownProvider, - TModelId extends keyof (typeof MODELS)[TProvider], -> = (typeof MODELS)[TProvider][TModelId] extends { api: infer TApi } ? (TApi extends Api ? TApi : never) : never; - -export function getModel( - provider: TProvider, - modelId: TModelId, -): Model> { - const providerModels = modelRegistry.get(provider); - return providerModels?.get(modelId as string) as Model>; -} - -export function getProviders(): KnownProvider[] { - return Array.from(modelRegistry.keys()) as KnownProvider[]; -} - -export function getModels( - provider: TProvider, -): Model>[] { - const models = modelRegistry.get(provider); - return models ? (Array.from(models.values()) as Model>[]) : []; -} - export function calculateCost(model: Model, usage: Usage): Usage["cost"] { usage.cost.input = (model.cost.input / 1000000) * usage.input; usage.cost.output = (model.cost.output / 1000000) * usage.output; diff --git a/packages/ai/src/providers/all.ts b/packages/ai/src/providers/all.ts index 7097fcdd7..1c4c70b1c 100644 --- a/packages/ai/src/providers/all.ts +++ b/packages/ai/src/providers/all.ts @@ -1,4 +1,6 @@ +import { MODELS } from "../models.generated.ts"; import { type CreateModelsOptions, createModels, type MutableModels, type Provider } from "../models.ts"; +import type { Api, KnownProvider, Model } from "../types.ts"; import { amazonBedrockProvider } from "./amazon-bedrock.ts"; import { antLingProvider } from "./ant-ling.ts"; import { anthropicProvider } from "./anthropic.ts"; @@ -35,11 +37,32 @@ import { xiaomiTokenPlanSgpProvider } from "./xiaomi-token-plan-sgp.ts"; import { zaiProvider } from "./zai.ts"; import { zaiCodingCnProvider } from "./zai-coding-cn.ts"; -export { - getModel as getBuiltinModel, - getModels as getBuiltinModels, - getProviders as getBuiltinProviders, -} from "../models.ts"; +type BuiltinModelApi< + TProvider extends KnownProvider, + TModelId extends keyof (typeof MODELS)[TProvider], +> = (typeof MODELS)[TProvider][TModelId] extends { api: infer TApi } ? (TApi extends Api ? TApi : never) : never; + +/** Typed read of the generated built-in catalog. */ +export function getBuiltinModel( + provider: TProvider, + modelId: TModelId, +): Model> { + const models = MODELS[provider] as Record> | undefined; + return models?.[modelId as string] as Model>; +} + +export function getBuiltinProviders(): KnownProvider[] { + return Object.keys(MODELS) as KnownProvider[]; +} + +export function getBuiltinModels( + provider: TProvider, +): Model>[] { + const models = MODELS[provider] as Record> | undefined; + return models + ? (Object.values(models) as Model>[]) + : []; +} /** All built-in providers, freshly constructed. */ export function builtinProviders(): Provider[] { diff --git a/packages/ai/src/utils/oauth/github-copilot.ts b/packages/ai/src/utils/oauth/github-copilot.ts index cf7e293bc..e63741a85 100644 --- a/packages/ai/src/utils/oauth/github-copilot.ts +++ b/packages/ai/src/utils/oauth/github-copilot.ts @@ -3,7 +3,7 @@ */ import type { OAuthAuth, OAuthCredential } from "../../auth/types.ts"; -import { getModels } from "../../models.ts"; +import { GITHUB_COPILOT_MODELS } from "../../providers/github-copilot.models.ts"; import type { Api, Model } from "../../types.ts"; import { pollOAuthDeviceCodeFlow } from "./device-code.ts"; import type { OAuthCredentials, OAuthDeviceCodeInfo, OAuthLoginCallbacks, OAuthProviderInterface } from "./types.ts"; @@ -274,7 +274,7 @@ async function enableAllGitHubCopilotModels( enterpriseDomain?: string, onProgress?: (model: string, success: boolean) => void, ): Promise { - const models = getModels("github-copilot"); + const models = Object.values(GITHUB_COPILOT_MODELS); await Promise.all( models.map(async (model) => { const success = await enableGitHubCopilotModel(token, model.id, enterpriseDomain); diff --git a/packages/ai/test/abort.test.ts b/packages/ai/test/abort.test.ts index 27c274aac..e4424c5e1 100644 --- a/packages/ai/test/abort.test.ts +++ b/packages/ai/test/abort.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { complete, stream } from "../src/stream.ts"; +import { complete, getModel, stream } from "../src/compat.ts"; import type { Api, Context, Model, StreamOptions } from "../src/types.ts"; type StreamOptionsWithExtras = StreamOptions & Record; diff --git a/packages/ai/test/anthropic-adaptive-thinking-models.test.ts b/packages/ai/test/anthropic-adaptive-thinking-models.test.ts index da042e2f4..e3cf0601b 100644 --- a/packages/ai/test/anthropic-adaptive-thinking-models.test.ts +++ b/packages/ai/test/anthropic-adaptive-thinking-models.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModels, getProviders } from "../src/models.ts"; +import { getModels, getProviders } from "../src/compat.ts"; import type { Api, Model } from "../src/types.ts"; const EXPECTED_CURRENT_ADAPTIVE_THINKING_MODELS = [ diff --git a/packages/ai/test/anthropic-eager-tool-input-e2e.test.ts b/packages/ai/test/anthropic-eager-tool-input-e2e.test.ts index 2483e1a4b..680f0648f 100644 --- a/packages/ai/test/anthropic-eager-tool-input-e2e.test.ts +++ b/packages/ai/test/anthropic-eager-tool-input-e2e.test.ts @@ -1,8 +1,7 @@ import { Type } from "typebox"; import { describe, expect, it } from "vitest"; +import { complete, getModels, getProviders } from "../src/compat.ts"; import { getEnvApiKey } from "../src/env-api-keys.ts"; -import { getModels, getProviders } from "../src/models.ts"; -import { complete } from "../src/stream.ts"; import type { Api, KnownProvider, Model, ProviderStreamOptions, Tool } from "../src/types.ts"; import { resolveApiKey } from "./oauth.ts"; diff --git a/packages/ai/test/anthropic-empty-thinking-signature-compat.test.ts b/packages/ai/test/anthropic-empty-thinking-signature-compat.test.ts index 69e58e27b..f5c883681 100644 --- a/packages/ai/test/anthropic-empty-thinking-signature-compat.test.ts +++ b/packages/ai/test/anthropic-empty-thinking-signature-compat.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { streamSimple } from "../src/stream.ts"; +import { streamSimple } from "../src/compat.ts"; import type { AssistantMessage, Context, Model } from "../src/types.ts"; interface AnthropicPayload { diff --git a/packages/ai/test/anthropic-force-adaptive-thinking.test.ts b/packages/ai/test/anthropic-force-adaptive-thinking.test.ts index e797c3a94..6629782f2 100644 --- a/packages/ai/test/anthropic-force-adaptive-thinking.test.ts +++ b/packages/ai/test/anthropic-force-adaptive-thinking.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { streamSimple } from "../src/stream.ts"; +import { getModel, streamSimple } from "../src/compat.ts"; import type { Context, Model, SimpleStreamOptions } from "../src/types.ts"; interface AnthropicThinkingPayload { diff --git a/packages/ai/test/anthropic-long-cache-retention-e2e.test.ts b/packages/ai/test/anthropic-long-cache-retention-e2e.test.ts index 2b7667d7e..042d3536a 100644 --- a/packages/ai/test/anthropic-long-cache-retention-e2e.test.ts +++ b/packages/ai/test/anthropic-long-cache-retention-e2e.test.ts @@ -1,7 +1,6 @@ import { describe, expect, it } from "vitest"; +import { complete, getModels, getProviders } from "../src/compat.ts"; import { getEnvApiKey } from "../src/env-api-keys.ts"; -import { getModels, getProviders } from "../src/models.ts"; -import { complete } from "../src/stream.ts"; import type { Api, KnownProvider, Model, ProviderStreamOptions } from "../src/types.ts"; import { resolveApiKey } from "./oauth.ts"; diff --git a/packages/ai/test/anthropic-oauth.test.ts b/packages/ai/test/anthropic-oauth.test.ts index 36585a5fd..ae3ae0938 100644 --- a/packages/ai/test/anthropic-oauth.test.ts +++ b/packages/ai/test/anthropic-oauth.test.ts @@ -1,5 +1,6 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { loginAnthropic, refreshAnthropicToken } from "../src/utils/oauth/anthropic.ts"; +import type { AuthEvent, AuthPrompt } from "../src/auth/types.ts"; +import { anthropicOAuth, loginAnthropic, refreshAnthropicToken } from "../src/utils/oauth/anthropic.ts"; function jsonResponse(body: unknown, status: number = 200): Response { return new Response(JSON.stringify(body), { @@ -96,4 +97,38 @@ describe.sequential("Anthropic OAuth", () => { expect(credentials.refresh).toBe("new-refresh-token"); expect(fetchMock).toHaveBeenCalledOnce(); }); + + it("anthropicOAuth.login resolves through the manual_code prompt and aborts it after settling", async () => { + const fetchMock = vi.fn(async (input: unknown): Promise => { + const url = typeof input === "string" ? input : String(input); + if (url.includes("/oauth/token")) { + return jsonResponse({ access_token: "access", refresh_token: "refresh", expires_in: 3600 }); + } + throw new Error(`Unexpected fetch: ${url}`); + }); + vi.stubGlobal("fetch", fetchMock); + + const events: AuthEvent[] = []; + const prompts: AuthPrompt[] = []; + let manualSignal: AbortSignal | undefined; + + const credential = await anthropicOAuth.login({ + notify: (event) => events.push(event), + prompt: async (prompt) => { + prompts.push(prompt); + if (prompt.type === "manual_code") { + manualSignal = prompt.signal; + return "the-code"; + } + throw new Error(`Unexpected prompt: ${prompt.type}`); + }, + }); + + expect(credential.type).toBe("oauth"); + expect(credential.access).toBe("access"); + expect(events.some((e) => e.type === "auth_url")).toBe(true); + expect(prompts.some((p) => p.type === "manual_code")).toBe(true); + // the prompt's signal is aborted once login settles, so UIs can dismiss it + expect(manualSignal?.aborted).toBe(true); + }); }); diff --git a/packages/ai/test/anthropic-opus-4-8-smoke.test.ts b/packages/ai/test/anthropic-opus-4-8-smoke.test.ts index bb4b739bb..44fa5aebb 100644 --- a/packages/ai/test/anthropic-opus-4-8-smoke.test.ts +++ b/packages/ai/test/anthropic-opus-4-8-smoke.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { streamSimple } from "../src/stream.ts"; +import { getModel, streamSimple } from "../src/compat.ts"; import type { Context } from "../src/types.ts"; interface AnthropicThinkingPayload { diff --git a/packages/ai/test/anthropic-sse-parsing.test.ts b/packages/ai/test/anthropic-sse-parsing.test.ts index 889435821..0ed58b815 100644 --- a/packages/ai/test/anthropic-sse-parsing.test.ts +++ b/packages/ai/test/anthropic-sse-parsing.test.ts @@ -2,7 +2,7 @@ import type Anthropic from "@anthropic-ai/sdk"; import { Type } from "typebox"; import { describe, expect, it } from "vitest"; import { stream as streamAnthropic } from "../src/api/anthropic-messages.ts"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import type { Context, ToolCall } from "../src/types.ts"; function createSseResponse(events: Array<{ event: string; data: string }>): Response { diff --git a/packages/ai/test/anthropic-temperature-compat.test.ts b/packages/ai/test/anthropic-temperature-compat.test.ts index 00161ab84..4b0592377 100644 --- a/packages/ai/test/anthropic-temperature-compat.test.ts +++ b/packages/ai/test/anthropic-temperature-compat.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { streamSimple } from "../src/stream.ts"; +import { getModel, streamSimple } from "../src/compat.ts"; import type { Context, Model, SimpleStreamOptions } from "../src/types.ts"; interface AnthropicTemperaturePayload { diff --git a/packages/ai/test/anthropic-thinking-disable.test.ts b/packages/ai/test/anthropic-thinking-disable.test.ts index 13d333e57..9ecfeb4f4 100644 --- a/packages/ai/test/anthropic-thinking-disable.test.ts +++ b/packages/ai/test/anthropic-thinking-disable.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { streamSimple } from "../src/stream.ts"; +import { getModel, streamSimple } from "../src/compat.ts"; import type { Context, Model, SimpleStreamOptions } from "../src/types.ts"; interface AnthropicThinkingPayload { diff --git a/packages/ai/test/anthropic-tool-name-normalization.test.ts b/packages/ai/test/anthropic-tool-name-normalization.test.ts index bb4540811..b8c45049b 100644 --- a/packages/ai/test/anthropic-tool-name-normalization.test.ts +++ b/packages/ai/test/anthropic-tool-name-normalization.test.ts @@ -1,7 +1,6 @@ import { Type } from "typebox"; import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { stream } from "../src/stream.ts"; +import { getModel, stream } from "../src/compat.ts"; import type { Context, Tool } from "../src/types.ts"; import { resolveApiKey } from "./oauth.ts"; diff --git a/packages/ai/test/azure-openai-base-url.test.ts b/packages/ai/test/azure-openai-base-url.test.ts index b80bae585..e372168ba 100644 --- a/packages/ai/test/azure-openai-base-url.test.ts +++ b/packages/ai/test/azure-openai-base-url.test.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { stream as streamAzureOpenAIResponses } from "../src/api/azure-openai-responses.ts"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import type { Context } from "../src/types.ts"; interface CapturedAzureClientOptions { diff --git a/packages/ai/test/bedrock-convert-messages.test.ts b/packages/ai/test/bedrock-convert-messages.test.ts index ba3ff1374..c43f7978d 100644 --- a/packages/ai/test/bedrock-convert-messages.test.ts +++ b/packages/ai/test/bedrock-convert-messages.test.ts @@ -45,7 +45,7 @@ vi.mock("@aws-sdk/client-bedrock-runtime", () => { }); import { stream as streamBedrock } from "../src/api/bedrock-converse-stream.ts"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import type { Context, Message } from "../src/types.ts"; const baseModel = getModel("amazon-bedrock", "us.anthropic.claude-sonnet-4-5-20250929-v1:0"); diff --git a/packages/ai/test/bedrock-custom-headers.test.ts b/packages/ai/test/bedrock-custom-headers.test.ts index e0899068d..43ee692ce 100644 --- a/packages/ai/test/bedrock-custom-headers.test.ts +++ b/packages/ai/test/bedrock-custom-headers.test.ts @@ -53,7 +53,7 @@ vi.mock("@aws-sdk/client-bedrock-runtime", () => { import type { BedrockOptions } from "../src/api/bedrock-converse-stream.ts"; import { stream as streamBedrock, streamSimple as streamSimpleBedrock } from "../src/api/bedrock-converse-stream.ts"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import type { Context, Model } from "../src/types.ts"; const context: Context = { diff --git a/packages/ai/test/bedrock-endpoint-resolution.test.ts b/packages/ai/test/bedrock-endpoint-resolution.test.ts index 28221d5c2..db2ae04bd 100644 --- a/packages/ai/test/bedrock-endpoint-resolution.test.ts +++ b/packages/ai/test/bedrock-endpoint-resolution.test.ts @@ -45,7 +45,7 @@ vi.mock("@aws-sdk/client-bedrock-runtime", () => { }); import { stream as streamBedrock } from "../src/api/bedrock-converse-stream.ts"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import type { Context, Model } from "../src/types.ts"; const context: Context = { diff --git a/packages/ai/test/bedrock-models.test.ts b/packages/ai/test/bedrock-models.test.ts index 2cfd8fb99..08f95e7ab 100644 --- a/packages/ai/test/bedrock-models.test.ts +++ b/packages/ai/test/bedrock-models.test.ts @@ -17,8 +17,7 @@ */ import { describe, expect, it } from "vitest"; -import { getModels } from "../src/models.ts"; -import { complete } from "../src/stream.ts"; +import { complete, getModels } from "../src/compat.ts"; import type { Context } from "../src/types.ts"; import { hasBedrockCredentials } from "./bedrock-utils.ts"; diff --git a/packages/ai/test/bedrock-thinking-payload.test.ts b/packages/ai/test/bedrock-thinking-payload.test.ts index ff4509cba..d2de49131 100644 --- a/packages/ai/test/bedrock-thinking-payload.test.ts +++ b/packages/ai/test/bedrock-thinking-payload.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; import { type BedrockOptions, stream as streamBedrock } from "../src/api/bedrock-converse-stream.ts"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import type { Context, Model } from "../src/types.ts"; import { hasBedrockCredentials } from "./bedrock-utils.ts"; diff --git a/packages/ai/test/cache-retention.test.ts b/packages/ai/test/cache-retention.test.ts index b37451989..1296cebde 100644 --- a/packages/ai/test/cache-retention.test.ts +++ b/packages/ai/test/cache-retention.test.ts @@ -2,8 +2,7 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { stream as streamAnthropic } from "../src/api/anthropic-messages.ts"; import { stream as streamOpenAICompletions } from "../src/api/openai-completions.ts"; import { stream as streamOpenAIResponses } from "../src/api/openai-responses.ts"; -import { getModel } from "../src/models.ts"; -import { stream } from "../src/stream.ts"; +import { getModel, stream } from "../src/compat.ts"; import type { Context, Model } from "../src/types.ts"; class PayloadCaptured extends Error { diff --git a/packages/ai/test/codex-websocket-cached-probe.ts b/packages/ai/test/codex-websocket-cached-probe.ts index ca08c6c75..d8154ac15 100644 --- a/packages/ai/test/codex-websocket-cached-probe.ts +++ b/packages/ai/test/codex-websocket-cached-probe.ts @@ -16,7 +16,7 @@ import { resetOpenAICodexWebSocketDebugStats, stream as streamOpenAICodexResponses, } from "../src/api/openai-codex-responses.ts"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import type { AssistantMessage, Context, Message, Model, Tool, ToolResultMessage, Transport } from "../src/types.ts"; type ThinkingLevel = "minimal" | "low" | "medium" | "high" | "xhigh"; diff --git a/packages/ai/test/context-overflow.test.ts b/packages/ai/test/context-overflow.test.ts index 9f021a413..683059494 100644 --- a/packages/ai/test/context-overflow.test.ts +++ b/packages/ai/test/context-overflow.test.ts @@ -14,8 +14,7 @@ import type { ChildProcess } from "child_process"; import { execSync, spawn } from "child_process"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { getModel, getModels } from "../src/models.ts"; -import { complete } from "../src/stream.ts"; +import { complete, getModel, getModels } from "../src/compat.ts"; import type { AssistantMessage, Context, Model, Usage } from "../src/types.ts"; import { isContextOverflow } from "../src/utils/overflow.ts"; import { hasAzureOpenAICredentials } from "./azure-utils.ts"; diff --git a/packages/ai/test/cross-provider-handoff.test.ts b/packages/ai/test/cross-provider-handoff.test.ts index 23593394f..57a43b0aa 100644 --- a/packages/ai/test/cross-provider-handoff.test.ts +++ b/packages/ai/test/cross-provider-handoff.test.ts @@ -25,8 +25,7 @@ import { writeFileSync } from "fs"; import { Type } from "typebox"; import { beforeAll, describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { completeSimple, getEnvApiKey } from "../src/stream.ts"; +import { completeSimple, getEnvApiKey, getModel } from "../src/compat.ts"; import type { Api, AssistantMessage, Message, Model, Tool, ToolResultMessage } from "../src/types.ts"; import { hasAzureOpenAICredentials } from "./azure-utils.ts"; import { hasCloudflareAiGatewayCredentials, hasCloudflareWorkersAICredentials } from "./cloudflare-utils.ts"; diff --git a/packages/ai/test/empty.test.ts b/packages/ai/test/empty.test.ts index a8453dcc3..86c25aa48 100644 --- a/packages/ai/test/empty.test.ts +++ b/packages/ai/test/empty.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { complete } from "../src/stream.ts"; +import { complete, getModel } from "../src/compat.ts"; import type { Api, AssistantMessage, Context, Model, StreamOptions, UserMessage } from "../src/types.ts"; type StreamOptionsWithExtras = StreamOptions & Record; diff --git a/packages/ai/test/faux-provider.test.ts b/packages/ai/test/faux-provider.test.ts index 4f110c542..3d8a190f6 100644 --- a/packages/ai/test/faux-provider.test.ts +++ b/packages/ai/test/faux-provider.test.ts @@ -8,7 +8,7 @@ import { registerFauxProvider, stream, Type, -} from "../src/index.ts"; +} from "../src/compat.ts"; import type { AssistantMessageEvent, Context } from "../src/types.ts"; async function collectEvents(streamResult: ReturnType): Promise { diff --git a/packages/ai/test/fireworks-models.test.ts b/packages/ai/test/fireworks-models.test.ts index 0f7ac4c19..6980f3188 100644 --- a/packages/ai/test/fireworks-models.test.ts +++ b/packages/ai/test/fireworks-models.test.ts @@ -3,8 +3,8 @@ import type { AddressInfo } from "node:net"; import { Type } from "typebox"; import { afterEach, describe, expect, it } from "vitest"; import { stream as streamAnthropic } from "../src/api/anthropic-messages.ts"; +import { getModel, getModels } from "../src/compat.ts"; import { findEnvKeys, getEnvApiKey } from "../src/env-api-keys.ts"; -import { getModel, getModels } from "../src/models.ts"; import type { Context, Model, Tool } from "../src/types.ts"; const originalFireworksApiKey = process.env.FIREWORKS_API_KEY; diff --git a/packages/ai/test/github-copilot-anthropic.test.ts b/packages/ai/test/github-copilot-anthropic.test.ts index c1c1a30f2..7900d22ea 100644 --- a/packages/ai/test/github-copilot-anthropic.test.ts +++ b/packages/ai/test/github-copilot-anthropic.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from "vitest"; import { stream as streamAnthropic } from "../src/api/anthropic-messages.ts"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import type { Context } from "../src/types.ts"; const mockState = vi.hoisted(() => ({ diff --git a/packages/ai/test/google-thinking-disable.test.ts b/packages/ai/test/google-thinking-disable.test.ts index 30df3638b..3f6e36c71 100644 --- a/packages/ai/test/google-thinking-disable.test.ts +++ b/packages/ai/test/google-thinking-disable.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { streamSimple } from "../src/stream.ts"; +import { getModel, streamSimple } from "../src/compat.ts"; import type { Api, Context, Model, SimpleStreamOptions } from "../src/types.ts"; type SimpleOptionsWithExtras = SimpleStreamOptions & Record; diff --git a/packages/ai/test/google-vertex-api-key-resolution.test.ts b/packages/ai/test/google-vertex-api-key-resolution.test.ts index 7fa182f9d..46f24a773 100644 --- a/packages/ai/test/google-vertex-api-key-resolution.test.ts +++ b/packages/ai/test/google-vertex-api-key-resolution.test.ts @@ -46,7 +46,7 @@ vi.mock("@google/genai", () => { }); import { stream as streamGoogleVertex } from "../src/api/google-vertex.ts"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import type { Context, Model } from "../src/types.ts"; const model = getModel("google-vertex", "gemini-3-flash-preview"); diff --git a/packages/ai/test/image-tool-result.test.ts b/packages/ai/test/image-tool-result.test.ts index a752b7ceb..946a443ad 100644 --- a/packages/ai/test/image-tool-result.test.ts +++ b/packages/ai/test/image-tool-result.test.ts @@ -2,8 +2,8 @@ import { readFileSync } from "node:fs"; import { join } from "node:path"; import { Type } from "typebox"; import { describe, expect, it } from "vitest"; -import type { Api, Context, Model, Tool, ToolResultMessage } from "../src/index.ts"; -import { complete, getModel } from "../src/index.ts"; +import type { Api, Context, Model, Tool, ToolResultMessage } from "../src/compat.ts"; +import { complete, getModel } from "../src/compat.ts"; import type { StreamOptions } from "../src/types.ts"; type StreamOptionsWithExtras = StreamOptions & Record; diff --git a/packages/ai/test/interleaved-thinking.test.ts b/packages/ai/test/interleaved-thinking.test.ts index 4cf387ce0..b4da12835 100644 --- a/packages/ai/test/interleaved-thinking.test.ts +++ b/packages/ai/test/interleaved-thinking.test.ts @@ -1,8 +1,7 @@ import { Type } from "typebox"; import { describe, expect, it } from "vitest"; +import { completeSimple, getModel } from "../src/compat.ts"; import { getEnvApiKey } from "../src/env-api-keys.ts"; -import { getModel } from "../src/models.ts"; -import { completeSimple } from "../src/stream.ts"; import type { Api, Context, Model, StopReason, Tool, ToolCall, ToolResultMessage } from "../src/types.ts"; import { StringEnum } from "../src/utils/typebox-helpers.ts"; import { hasBedrockCredentials } from "./bedrock-utils.ts"; diff --git a/packages/ai/test/lazy-module-load.test.ts b/packages/ai/test/lazy-module-load.test.ts index aa25d9a12..6f5464ff9 100644 --- a/packages/ai/test/lazy-module-load.test.ts +++ b/packages/ai/test/lazy-module-load.test.ts @@ -5,6 +5,7 @@ import { describe, expect, it } from "vitest"; const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const aiEntryUrl = new URL("../src/index.ts", import.meta.url).href; +const compatEntryUrl = new URL("../src/compat.ts", import.meta.url).href; const providersAllUrl = new URL("../src/providers/all.ts", import.meta.url).href; const SDK_SPECIFIERS = [ @@ -76,8 +77,16 @@ describe("lazy provider module loading", () => { expect(result.loadedSpecifiers).toEqual([]); }); + it("does not load provider SDKs when importing the compat entrypoint", () => { + const result = runProbe(` + await import(${JSON.stringify(compatEntryUrl)}); + `); + expect(result.loadedSpecifiers).toEqual([]); + }); + it("loads only the Anthropic SDK when streaming through the lazy API wrapper", () => { const result = runProbe(` + const compat = await import(${JSON.stringify(compatEntryUrl)}); const model = { id: "claude-sonnet-4-6", name: "Claude Sonnet 4", @@ -91,7 +100,7 @@ describe("lazy provider module loading", () => { maxTokens: 8192, }; const context = { messages: [{ role: "user", content: "hi" }] }; - await mod.anthropicMessagesApi().streamSimple(model, context).result(); + await compat.anthropicMessagesApi().streamSimple(model, context).result(); `); expect(result.loadedSpecifiers).toEqual(["@anthropic-ai/sdk"]); @@ -99,9 +108,10 @@ describe("lazy provider module loading", () => { it("loads only the Anthropic SDK when dispatching through streamSimple", () => { const result = runProbe(` - const model = mod.getModel("anthropic", "claude-sonnet-4-6"); + const compat = await import(${JSON.stringify(compatEntryUrl)}); + const model = compat.getModel("anthropic", "claude-sonnet-4-6"); const context = { messages: [{ role: "user", content: "hi" }] }; - await mod.streamSimple(model, context).result(); + await compat.streamSimple(model, context).result(); `); expect(result.loadedSpecifiers).toEqual(["@anthropic-ai/sdk"]); diff --git a/packages/ai/test/mistral-reasoning-mode.test.ts b/packages/ai/test/mistral-reasoning-mode.test.ts index 35a5b96be..4bd24f795 100644 --- a/packages/ai/test/mistral-reasoning-mode.test.ts +++ b/packages/ai/test/mistral-reasoning-mode.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { streamSimple } from "../src/stream.ts"; +import { getModel, streamSimple } from "../src/compat.ts"; import type { Context, Model, SimpleStreamOptions } from "../src/types.ts"; interface MistralPayload { diff --git a/packages/ai/test/mistral-tool-schema.test.ts b/packages/ai/test/mistral-tool-schema.test.ts index c6898fc64..7691775a9 100644 --- a/packages/ai/test/mistral-tool-schema.test.ts +++ b/packages/ai/test/mistral-tool-schema.test.ts @@ -1,7 +1,6 @@ import { Type } from "typebox"; import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { complete } from "../src/stream.ts"; +import { complete, getModel } from "../src/compat.ts"; import type { Context, Model } from "../src/types.ts"; interface MistralToolPayload { diff --git a/packages/ai/test/oauth-auth.test.ts b/packages/ai/test/oauth-auth.test.ts index 689fc2cf0..579fdb37d 100644 --- a/packages/ai/test/oauth-auth.test.ts +++ b/packages/ai/test/oauth-auth.test.ts @@ -1,6 +1,5 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import { InMemoryCredentialStore } from "../src/auth/credential-store.ts"; -import type { AuthEvent, AuthPrompt } from "../src/auth/types.ts"; import { createModels } from "../src/models.ts"; import { anthropicProvider } from "../src/providers/anthropic.ts"; import { githubCopilotProvider } from "../src/providers/github-copilot.ts"; @@ -86,40 +85,6 @@ describe.sequential("OAuthAuth adapters", () => { expect(refreshed.enterpriseUrl).toBe("company.ghe.com"); expect(fetchedUrls[0]).toContain("api.company.ghe.com"); }); - - it("anthropic login resolves through the manual_code prompt and aborts it after settling", async () => { - const fetchMock = vi.fn(async (input: unknown) => { - const url = typeof input === "string" ? input : String(input); - if (url.includes("/oauth/token")) { - return jsonResponse({ access_token: "access", refresh_token: "refresh", expires_in: 3600 }); - } - throw new Error(`Unexpected fetch: ${url}`); - }); - vi.stubGlobal("fetch", fetchMock); - - const events: AuthEvent[] = []; - const prompts: AuthPrompt[] = []; - let manualSignal: AbortSignal | undefined; - - const credential = await anthropicOAuth.login({ - notify: (event) => events.push(event), - prompt: async (prompt) => { - prompts.push(prompt); - if (prompt.type === "manual_code") { - manualSignal = prompt.signal; - return "the-code"; - } - throw new Error(`Unexpected prompt: ${prompt.type}`); - }, - }); - - expect(credential.type).toBe("oauth"); - expect(credential.access).toBe("access"); - expect(events.some((e) => e.type === "auth_url")).toBe(true); - expect(prompts.some((p) => p.type === "manual_code")).toBe(true); - // the prompt's signal is aborted once login settles, so UIs can dismiss it - expect(manualSignal?.aborted).toBe(true); - }); }); describe("OAuth through Models.getAuth (lazy load chain)", () => { diff --git a/packages/ai/test/openai-codex-cache-affinity-e2e.test.ts b/packages/ai/test/openai-codex-cache-affinity-e2e.test.ts index 0abb46f63..19bf857ba 100644 --- a/packages/ai/test/openai-codex-cache-affinity-e2e.test.ts +++ b/packages/ai/test/openai-codex-cache-affinity-e2e.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { complete } from "../src/stream.ts"; +import { complete, getModel } from "../src/compat.ts"; import type { Context } from "../src/types.ts"; import { resolveApiKey } from "./oauth.ts"; diff --git a/packages/ai/test/openai-completions-cache-control-format.test.ts b/packages/ai/test/openai-completions-cache-control-format.test.ts index 04fd2361f..d87a95eb4 100644 --- a/packages/ai/test/openai-completions-cache-control-format.test.ts +++ b/packages/ai/test/openai-completions-cache-control-format.test.ts @@ -1,7 +1,7 @@ import { Type } from "typebox"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { stream as streamOpenAICompletions } from "../src/api/openai-completions.ts"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import type { Model } from "../src/types.ts"; interface CacheControl { diff --git a/packages/ai/test/openai-completions-empty-tools.test.ts b/packages/ai/test/openai-completions-empty-tools.test.ts index a743351c0..71297a231 100644 --- a/packages/ai/test/openai-completions-empty-tools.test.ts +++ b/packages/ai/test/openai-completions-empty-tools.test.ts @@ -1,6 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { getModel } from "../src/models.ts"; -import { streamSimple } from "../src/stream.ts"; +import { getModel, streamSimple } from "../src/compat.ts"; // Empty tools arrays must NOT be serialized as `tools: []` — some OpenAI-compatible // backends (e.g. DashScope / Aliyun Qwen via compatible-mode) reject the request with diff --git a/packages/ai/test/openai-completions-prompt-cache.test.ts b/packages/ai/test/openai-completions-prompt-cache.test.ts index 0b25e8572..5e9057124 100644 --- a/packages/ai/test/openai-completions-prompt-cache.test.ts +++ b/packages/ai/test/openai-completions-prompt-cache.test.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { stream as streamOpenAICompletions } from "../src/api/openai-completions.ts"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import type { Model } from "../src/types.ts"; interface FakeOpenAIClientOptions { diff --git a/packages/ai/test/openai-completions-response-model.test.ts b/packages/ai/test/openai-completions-response-model.test.ts index d8e4d2f9a..5139bd54d 100644 --- a/packages/ai/test/openai-completions-response-model.test.ts +++ b/packages/ai/test/openai-completions-response-model.test.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { complete } from "../src/stream.ts"; +import { complete } from "../src/compat.ts"; import type { Model } from "../src/types.ts"; // Router/virtual ids (e.g. OpenRouter `auto`) keep `model` pinned to the diff --git a/packages/ai/test/openai-completions-tool-choice.test.ts b/packages/ai/test/openai-completions-tool-choice.test.ts index 61054aad6..610835723 100644 --- a/packages/ai/test/openai-completions-tool-choice.test.ts +++ b/packages/ai/test/openai-completions-tool-choice.test.ts @@ -1,8 +1,7 @@ import { Type } from "typebox"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { convertMessages } from "../src/api/openai-completions.ts"; -import { getModel } from "../src/models.ts"; -import { stream, streamSimple } from "../src/stream.ts"; +import { getModel, stream, streamSimple } from "../src/compat.ts"; import type { AssistantMessage, Model, Tool, ToolResultMessage } from "../src/types.ts"; const mockState = vi.hoisted(() => ({ diff --git a/packages/ai/test/openai-completions-tool-result-images.test.ts b/packages/ai/test/openai-completions-tool-result-images.test.ts index 4fa0fd681..758b8767f 100644 --- a/packages/ai/test/openai-completions-tool-result-images.test.ts +++ b/packages/ai/test/openai-completions-tool-result-images.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; import { convertMessages } from "../src/api/openai-completions.ts"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import type { AssistantMessage, Context, diff --git a/packages/ai/test/openai-responses-cache-affinity-e2e.test.ts b/packages/ai/test/openai-responses-cache-affinity-e2e.test.ts index d173694f9..a2f1197e0 100644 --- a/packages/ai/test/openai-responses-cache-affinity-e2e.test.ts +++ b/packages/ai/test/openai-responses-cache-affinity-e2e.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { complete } from "../src/stream.ts"; +import { complete, getModel } from "../src/compat.ts"; import type { Context } from "../src/types.ts"; describe.skipIf(!process.env.OPENAI_API_KEY)("openai responses cache affinity e2e", () => { diff --git a/packages/ai/test/openai-responses-copilot-provider.test.ts b/packages/ai/test/openai-responses-copilot-provider.test.ts index 92b3d7409..57c319f2e 100644 --- a/packages/ai/test/openai-responses-copilot-provider.test.ts +++ b/packages/ai/test/openai-responses-copilot-provider.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import { stream as streamOpenAIResponses } from "../src/api/openai-responses.ts"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import type { Model } from "../src/types.ts"; type CapturedHeaders = Headers | string[][] | Record | undefined; diff --git a/packages/ai/test/openai-responses-foreign-toolcall-id.test.ts b/packages/ai/test/openai-responses-foreign-toolcall-id.test.ts index 7eea6821f..b82316582 100644 --- a/packages/ai/test/openai-responses-foreign-toolcall-id.test.ts +++ b/packages/ai/test/openai-responses-foreign-toolcall-id.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; import { convertResponsesMessages } from "../src/api/openai-responses-shared.ts"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import type { AssistantMessage, Context, ToolResultMessage, Usage } from "../src/types.ts"; import { shortHash } from "../src/utils/hash.ts"; diff --git a/packages/ai/test/openai-responses-message-id.test.ts b/packages/ai/test/openai-responses-message-id.test.ts index 06687fa1d..cb2fd0c04 100644 --- a/packages/ai/test/openai-responses-message-id.test.ts +++ b/packages/ai/test/openai-responses-message-id.test.ts @@ -1,7 +1,7 @@ import type { ResponseOutputMessage } from "openai/resources/responses/responses.js"; import { describe, expect, it } from "vitest"; import { convertResponsesMessages } from "../src/api/openai-responses-shared.ts"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import type { AssistantMessage, Context, Usage } from "../src/types.ts"; const usage: Usage = { diff --git a/packages/ai/test/openai-responses-reasoning-replay-e2e.test.ts b/packages/ai/test/openai-responses-reasoning-replay-e2e.test.ts index aa6f2e239..85753c039 100644 --- a/packages/ai/test/openai-responses-reasoning-replay-e2e.test.ts +++ b/packages/ai/test/openai-responses-reasoning-replay-e2e.test.ts @@ -1,7 +1,6 @@ import { Type } from "typebox"; import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { complete, getEnvApiKey } from "../src/stream.ts"; +import { complete, getEnvApiKey, getModel } from "../src/compat.ts"; import type { AssistantMessage, Context, Message, Tool, ToolCall } from "../src/types.ts"; const testToolSchema = Type.Object({ diff --git a/packages/ai/test/openai-responses-tool-result-images.test.ts b/packages/ai/test/openai-responses-tool-result-images.test.ts index c6131d912..da33ab861 100644 --- a/packages/ai/test/openai-responses-tool-result-images.test.ts +++ b/packages/ai/test/openai-responses-tool-result-images.test.ts @@ -4,8 +4,8 @@ import { fileURLToPath } from "node:url"; import type { ResponseFunctionCallOutputItemList } from "openai/resources/responses/responses.js"; import { Type } from "typebox"; import { describe, expect, it } from "vitest"; -import type { Api, Context, Model, StreamOptions, Tool, ToolResultMessage } from "../src/index.ts"; -import { complete, getModel } from "../src/index.ts"; +import type { Api, Context, Model, StreamOptions, Tool, ToolResultMessage } from "../src/compat.ts"; +import { complete, getModel } from "../src/compat.ts"; import { hasAzureOpenAICredentials, resolveAzureDeploymentName } from "./azure-utils.ts"; import { resolveApiKey } from "./oauth.ts"; diff --git a/packages/ai/test/openrouter-cache-write-repro.test.ts b/packages/ai/test/openrouter-cache-write-repro.test.ts index 4bdeb2868..2292ec91e 100644 --- a/packages/ai/test/openrouter-cache-write-repro.test.ts +++ b/packages/ai/test/openrouter-cache-write-repro.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { completeSimple } from "../src/stream.ts"; +import { completeSimple, getModel } from "../src/compat.ts"; function createLongSystemPrompt(): string { const nonce = `${Date.now()}-${Math.random()}`; diff --git a/packages/ai/test/responseid.test.ts b/packages/ai/test/responseid.test.ts index d250f5f41..cd5660575 100644 --- a/packages/ai/test/responseid.test.ts +++ b/packages/ai/test/responseid.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { complete } from "../src/stream.ts"; +import { complete, getModel } from "../src/compat.ts"; import type { Api, Context, Model, StreamOptions } from "../src/types.ts"; import { hasAzureOpenAICredentials, resolveAzureDeploymentName } from "./azure-utils.ts"; import { resolveApiKey } from "./oauth.ts"; diff --git a/packages/ai/test/stream.test.ts b/packages/ai/test/stream.test.ts index 9a70b6c28..fbc95f50a 100644 --- a/packages/ai/test/stream.test.ts +++ b/packages/ai/test/stream.test.ts @@ -4,8 +4,7 @@ import { dirname, join } from "path"; import { Type } from "typebox"; import { fileURLToPath } from "url"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { complete, stream } from "../src/stream.ts"; +import { complete, getModel, stream } from "../src/compat.ts"; import type { Api, Context, ImageContent, Model, StreamOptions, Tool, ToolResultMessage } from "../src/types.ts"; type StreamOptionsWithExtras = StreamOptions & Record; diff --git a/packages/ai/test/supports-xhigh.test.ts b/packages/ai/test/supports-xhigh.test.ts index ec6dc87b5..ada30669e 100644 --- a/packages/ai/test/supports-xhigh.test.ts +++ b/packages/ai/test/supports-xhigh.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel, getSupportedThinkingLevels } from "../src/models.ts"; +import { getModel, getSupportedThinkingLevels } from "../src/compat.ts"; describe("getSupportedThinkingLevels", () => { it("includes xhigh for Anthropic Opus 4.6 on anthropic-messages API", () => { diff --git a/packages/ai/test/together-models.test.ts b/packages/ai/test/together-models.test.ts index cb5ea9433..0d766d654 100644 --- a/packages/ai/test/together-models.test.ts +++ b/packages/ai/test/together-models.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it } from "vitest"; +import { getModel } from "../src/compat.ts"; import { findEnvKeys, getEnvApiKey } from "../src/env-api-keys.ts"; -import { getModel } from "../src/models.ts"; const originalTogetherApiKey = process.env.TOGETHER_API_KEY; diff --git a/packages/ai/test/tokens.test.ts b/packages/ai/test/tokens.test.ts index 676f5a29c..e99c71340 100644 --- a/packages/ai/test/tokens.test.ts +++ b/packages/ai/test/tokens.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel, getModels } from "../src/models.ts"; -import { stream } from "../src/stream.ts"; +import { getModel, getModels, stream } from "../src/compat.ts"; import type { Api, Context, Model, StreamOptions } from "../src/types.ts"; type StreamOptionsWithExtras = StreamOptions & Record; diff --git a/packages/ai/test/tool-call-id-normalization.test.ts b/packages/ai/test/tool-call-id-normalization.test.ts index 0672181b2..fd59d9e6f 100644 --- a/packages/ai/test/tool-call-id-normalization.test.ts +++ b/packages/ai/test/tool-call-id-normalization.test.ts @@ -12,8 +12,7 @@ import { Type } from "typebox"; import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { completeSimple, getEnvApiKey } from "../src/stream.ts"; +import { completeSimple, getEnvApiKey, getModel } from "../src/compat.ts"; import type { AssistantMessage, Message, Tool, ToolResultMessage } from "../src/types.ts"; import { resolveApiKey } from "./oauth.ts"; diff --git a/packages/ai/test/tool-call-without-result.test.ts b/packages/ai/test/tool-call-without-result.test.ts index 11b832f3b..198c48c51 100644 --- a/packages/ai/test/tool-call-without-result.test.ts +++ b/packages/ai/test/tool-call-without-result.test.ts @@ -1,7 +1,6 @@ import { Type } from "typebox"; import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { complete } from "../src/stream.ts"; +import { complete, getModel } from "../src/compat.ts"; import type { Api, Context, Model, StreamOptions, Tool } from "../src/types.ts"; type StreamOptionsWithExtras = StreamOptions & Record; diff --git a/packages/ai/test/total-tokens.test.ts b/packages/ai/test/total-tokens.test.ts index 972913a7c..d07fc5ad8 100644 --- a/packages/ai/test/total-tokens.test.ts +++ b/packages/ai/test/total-tokens.test.ts @@ -13,8 +13,7 @@ */ import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { complete } from "../src/stream.ts"; +import { complete, getModel } from "../src/compat.ts"; import type { Api, Context, Model, StreamOptions, Usage } from "../src/types.ts"; type StreamOptionsWithExtras = StreamOptions & Record; diff --git a/packages/ai/test/unicode-surrogate.test.ts b/packages/ai/test/unicode-surrogate.test.ts index 9cdddefa8..f4ea74500 100644 --- a/packages/ai/test/unicode-surrogate.test.ts +++ b/packages/ai/test/unicode-surrogate.test.ts @@ -1,7 +1,6 @@ import { Type } from "typebox"; import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { complete } from "../src/stream.ts"; +import { complete, getModel } from "../src/compat.ts"; import type { Api, Context, Model, StreamOptions, ToolResultMessage } from "../src/types.ts"; type StreamOptionsWithExtras = StreamOptions & Record; diff --git a/packages/ai/test/xhigh.test.ts b/packages/ai/test/xhigh.test.ts index 3c2798635..f1722b9a7 100644 --- a/packages/ai/test/xhigh.test.ts +++ b/packages/ai/test/xhigh.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; -import { stream } from "../src/stream.ts"; +import { getModel, stream } from "../src/compat.ts"; import type { Context, Model } from "../src/types.ts"; function makeContext(): Context { diff --git a/packages/ai/test/xiaomi-models.test.ts b/packages/ai/test/xiaomi-models.test.ts index 6fb1f4b8e..277b84957 100644 --- a/packages/ai/test/xiaomi-models.test.ts +++ b/packages/ai/test/xiaomi-models.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel, getModels } from "../src/models.ts"; +import { getModel, getModels } from "../src/compat.ts"; describe("Xiaomi MiMo models", () => { it("keeps mimo-v2-flash on the API billing provider", () => { diff --git a/packages/ai/test/xiaomi-token-plan-ams-anthropic-empty-signature-smoke.test.ts b/packages/ai/test/xiaomi-token-plan-ams-anthropic-empty-signature-smoke.test.ts index ab2fec30a..74d270898 100644 --- a/packages/ai/test/xiaomi-token-plan-ams-anthropic-empty-signature-smoke.test.ts +++ b/packages/ai/test/xiaomi-token-plan-ams-anthropic-empty-signature-smoke.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { completeSimple, getEnvApiKey, streamSimple } from "../src/stream.ts"; +import { completeSimple, getEnvApiKey, streamSimple } from "../src/compat.ts"; import type { AssistantMessage, Context, Model } from "../src/types.ts"; const provider = "xiaomi-token-plan-ams"; diff --git a/packages/ai/test/zen.test.ts b/packages/ai/test/zen.test.ts index 8ca80014e..0f731b469 100644 --- a/packages/ai/test/zen.test.ts +++ b/packages/ai/test/zen.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; +import { complete } from "../src/compat.ts"; import { MODELS } from "../src/models.generated.ts"; -import { complete } from "../src/stream.ts"; import type { Model } from "../src/types.ts"; describe.skipIf(!process.env.OPENCODE_API_KEY)("OpenCode Models Smoke Test", () => { diff --git a/packages/coding-agent/examples/extensions/custom-compaction.ts b/packages/coding-agent/examples/extensions/custom-compaction.ts index 02c6ceb3a..310e81d8f 100644 --- a/packages/coding-agent/examples/extensions/custom-compaction.ts +++ b/packages/coding-agent/examples/extensions/custom-compaction.ts @@ -13,7 +13,7 @@ * pi --extension examples/extensions/custom-compaction.ts */ -import { complete } from "@earendil-works/pi-ai"; +import { complete } from "@earendil-works/pi-ai/compat"; import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { convertToLlm, serializeConversation } from "@earendil-works/pi-coding-agent"; diff --git a/packages/coding-agent/examples/extensions/custom-provider-gitlab-duo/index.ts b/packages/coding-agent/examples/extensions/custom-provider-gitlab-duo/index.ts index 2efe64acc..60e5a279a 100644 --- a/packages/coding-agent/examples/extensions/custom-provider-gitlab-duo/index.ts +++ b/packages/coding-agent/examples/extensions/custom-provider-gitlab-duo/index.ts @@ -21,7 +21,7 @@ import { openAIResponsesApi, type SimpleStreamOptions, type ThinkingLevelMap, -} from "@earendil-works/pi-ai"; +} from "@earendil-works/pi-ai/compat"; import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; // ============================================================================= diff --git a/packages/coding-agent/examples/extensions/custom-provider-gitlab-duo/test.ts b/packages/coding-agent/examples/extensions/custom-provider-gitlab-duo/test.ts index 0077aca83..79ba3d718 100644 --- a/packages/coding-agent/examples/extensions/custom-provider-gitlab-duo/test.ts +++ b/packages/coding-agent/examples/extensions/custom-provider-gitlab-duo/test.ts @@ -8,7 +8,7 @@ * npx tsx test.ts claude-sonnet-4-5-20250929 --thinking */ -import { type Api, type Context, type Model, registerApiProvider, streamSimple } from "@earendil-works/pi-ai"; +import { type Api, type Context, type Model, registerApiProvider, streamSimple } from "@earendil-works/pi-ai/compat"; import { readFileSync } from "fs"; import { getAgentDir } from "packages/coding-agent/src/config.js"; import { join } from "path"; diff --git a/packages/coding-agent/examples/extensions/handoff.ts b/packages/coding-agent/examples/extensions/handoff.ts index 4af6661f5..a161fad66 100644 --- a/packages/coding-agent/examples/extensions/handoff.ts +++ b/packages/coding-agent/examples/extensions/handoff.ts @@ -13,7 +13,7 @@ */ import type { AgentMessage } from "@earendil-works/pi-agent-core"; -import { complete, type Message } from "@earendil-works/pi-ai"; +import { complete, type Message } from "@earendil-works/pi-ai/compat"; import type { ExtensionAPI, SessionEntry } from "@earendil-works/pi-coding-agent"; import { BorderedLoader, convertToLlm, serializeConversation } from "@earendil-works/pi-coding-agent"; diff --git a/packages/coding-agent/examples/extensions/qna.ts b/packages/coding-agent/examples/extensions/qna.ts index 70dbef7b1..524c27857 100644 --- a/packages/coding-agent/examples/extensions/qna.ts +++ b/packages/coding-agent/examples/extensions/qna.ts @@ -7,7 +7,7 @@ * 3. Loads the result into the editor for user to fill in answers */ -import { complete, type UserMessage } from "@earendil-works/pi-ai"; +import { complete, type UserMessage } from "@earendil-works/pi-ai/compat"; import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { BorderedLoader } from "@earendil-works/pi-coding-agent"; diff --git a/packages/coding-agent/examples/extensions/summarize.ts b/packages/coding-agent/examples/extensions/summarize.ts index e6480974a..66ce5340c 100644 --- a/packages/coding-agent/examples/extensions/summarize.ts +++ b/packages/coding-agent/examples/extensions/summarize.ts @@ -1,4 +1,4 @@ -import { complete, getModel } from "@earendil-works/pi-ai"; +import { complete, getModel } from "@earendil-works/pi-ai/compat"; import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent"; import { DynamicBorder, getMarkdownTheme } from "@earendil-works/pi-coding-agent"; import { Container, Markdown, matchesKey, Text } from "@earendil-works/pi-tui"; diff --git a/packages/coding-agent/examples/sdk/02-custom-model.ts b/packages/coding-agent/examples/sdk/02-custom-model.ts index 531a51c80..641d553e8 100644 --- a/packages/coding-agent/examples/sdk/02-custom-model.ts +++ b/packages/coding-agent/examples/sdk/02-custom-model.ts @@ -4,7 +4,7 @@ * Shows how to select a specific model and thinking level. */ -import { getModel } from "@earendil-works/pi-ai"; +import { getModel } from "@earendil-works/pi-ai/compat"; import { AuthStorage, createAgentSession, ModelRegistry } from "@earendil-works/pi-coding-agent"; // Set up auth storage and model registry diff --git a/packages/coding-agent/examples/sdk/12-full-control.ts b/packages/coding-agent/examples/sdk/12-full-control.ts index cd8ba3432..12b7607dd 100644 --- a/packages/coding-agent/examples/sdk/12-full-control.ts +++ b/packages/coding-agent/examples/sdk/12-full-control.ts @@ -4,7 +4,7 @@ * Replace everything - no discovery, explicit configuration. */ -import { getModel } from "@earendil-works/pi-ai"; +import { getModel } from "@earendil-works/pi-ai/compat"; import { AuthStorage, createAgentSession, diff --git a/packages/coding-agent/src/bun/register-bedrock.ts b/packages/coding-agent/src/bun/register-bedrock.ts index 92af0dde2..18e80dcc0 100644 --- a/packages/coding-agent/src/bun/register-bedrock.ts +++ b/packages/coding-agent/src/bun/register-bedrock.ts @@ -1,4 +1,4 @@ -import { setBedrockProviderModule } from "@earendil-works/pi-ai"; import { bedrockProviderModule } from "@earendil-works/pi-ai/bedrock-provider"; +import { setBedrockProviderModule } from "@earendil-works/pi-ai/compat"; setBedrockProviderModule(bedrockProviderModule); diff --git a/packages/coding-agent/src/core/agent-session.ts b/packages/coding-agent/src/core/agent-session.ts index 201b8084b..1b5f1c7d7 100644 --- a/packages/coding-agent/src/core/agent-session.ts +++ b/packages/coding-agent/src/core/agent-session.ts @@ -23,7 +23,7 @@ import type { AgentTool, ThinkingLevel, } from "@earendil-works/pi-agent-core"; -import type { AssistantMessage, ImageContent, Message, Model, TextContent } from "@earendil-works/pi-ai"; +import type { AssistantMessage, ImageContent, Message, Model, TextContent } from "@earendil-works/pi-ai/compat"; import { clampThinkingLevel, cleanupSessionResources, @@ -32,7 +32,7 @@ import { modelsAreEqual, resetApiProviders, streamSimple, -} from "@earendil-works/pi-ai"; +} from "@earendil-works/pi-ai/compat"; import { theme } from "../modes/interactive/theme/theme.ts"; import { stripFrontmatter } from "../utils/frontmatter.ts"; import { resolvePath } from "../utils/paths.ts"; diff --git a/packages/coding-agent/src/core/auth-storage.ts b/packages/coding-agent/src/core/auth-storage.ts index 9a0564ec1..6b9f2f3a4 100644 --- a/packages/coding-agent/src/core/auth-storage.ts +++ b/packages/coding-agent/src/core/auth-storage.ts @@ -12,7 +12,7 @@ import { type OAuthCredentials, type OAuthLoginCallbacks, type OAuthProviderId, -} from "@earendil-works/pi-ai"; +} from "@earendil-works/pi-ai/compat"; import { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from "@earendil-works/pi-ai/oauth"; import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs"; import { dirname, join } from "path"; diff --git a/packages/coding-agent/src/core/compaction/branch-summarization.ts b/packages/coding-agent/src/core/compaction/branch-summarization.ts index f6ce3d677..caa05bb35 100644 --- a/packages/coding-agent/src/core/compaction/branch-summarization.ts +++ b/packages/coding-agent/src/core/compaction/branch-summarization.ts @@ -6,8 +6,8 @@ */ import type { AgentMessage, StreamFn } from "@earendil-works/pi-agent-core"; -import type { Model, SimpleStreamOptions } from "@earendil-works/pi-ai"; -import { completeSimple } from "@earendil-works/pi-ai"; +import type { Model, SimpleStreamOptions } from "@earendil-works/pi-ai/compat"; +import { completeSimple } from "@earendil-works/pi-ai/compat"; import { convertToLlm, createBranchSummaryMessage, diff --git a/packages/coding-agent/src/core/compaction/compaction.ts b/packages/coding-agent/src/core/compaction/compaction.ts index eae19794e..43ee79ef1 100644 --- a/packages/coding-agent/src/core/compaction/compaction.ts +++ b/packages/coding-agent/src/core/compaction/compaction.ts @@ -6,8 +6,8 @@ */ import type { AgentMessage, StreamFn, ThinkingLevel } from "@earendil-works/pi-agent-core"; -import type { AssistantMessage, Context, Model, SimpleStreamOptions, Usage } from "@earendil-works/pi-ai"; -import { completeSimple } from "@earendil-works/pi-ai"; +import type { AssistantMessage, Context, Model, SimpleStreamOptions, Usage } from "@earendil-works/pi-ai/compat"; +import { completeSimple } from "@earendil-works/pi-ai/compat"; import { convertToLlm, createBranchSummaryMessage, diff --git a/packages/coding-agent/src/core/extensions/loader.ts b/packages/coding-agent/src/core/extensions/loader.ts index 081d2d11a..37f661640 100644 --- a/packages/coding-agent/src/core/extensions/loader.ts +++ b/packages/coding-agent/src/core/extensions/loader.ts @@ -8,7 +8,7 @@ import { createRequire } from "node:module"; import * as path from "node:path"; import { fileURLToPath } from "node:url"; import * as _bundledPiAgentCore from "@earendil-works/pi-agent-core"; -import * as _bundledPiAi from "@earendil-works/pi-ai"; +import * as _bundledPiAiCompat from "@earendil-works/pi-ai/compat"; import * as _bundledPiAiOauth from "@earendil-works/pi-ai/oauth"; import type { KeyId } from "@earendil-works/pi-tui"; import * as _bundledPiTui from "@earendil-works/pi-tui"; @@ -50,12 +50,17 @@ const VIRTUAL_MODULES: Record = { "@sinclair/typebox/value": _bundledTypeboxValue, "@earendil-works/pi-agent-core": _bundledPiAgentCore, "@earendil-works/pi-tui": _bundledPiTui, - "@earendil-works/pi-ai": _bundledPiAi, + // Extensions resolve the pi-ai root to the compat entrypoint (a strict + // superset of the core entrypoint): existing extensions using the old + // global API keep working at runtime until compat is removed. + "@earendil-works/pi-ai": _bundledPiAiCompat, + "@earendil-works/pi-ai/compat": _bundledPiAiCompat, "@earendil-works/pi-ai/oauth": _bundledPiAiOauth, "@earendil-works/pi-coding-agent": _bundledPiCodingAgent, "@mariozechner/pi-agent-core": _bundledPiAgentCore, "@mariozechner/pi-tui": _bundledPiTui, - "@mariozechner/pi-ai": _bundledPiAi, + "@mariozechner/pi-ai": _bundledPiAiCompat, + "@mariozechner/pi-ai/compat": _bundledPiAiCompat, "@mariozechner/pi-ai/oauth": _bundledPiAiOauth, "@mariozechner/pi-coding-agent": _bundledPiCodingAgent, }; @@ -90,19 +95,24 @@ function getAliases(): Record { const piCodingAgentEntry = packageIndex; const piAgentCoreEntry = resolveWorkspaceOrImport("agent/dist/index.js", "@earendil-works/pi-agent-core"); const piTuiEntry = resolveWorkspaceOrImport("tui/dist/index.js", "@earendil-works/pi-tui"); - const piAiEntry = resolveWorkspaceOrImport("ai/dist/index.js", "@earendil-works/pi-ai"); + // Extensions resolve the pi-ai root to the compat entrypoint (a strict + // superset of the core entrypoint): existing extensions using the old + // global API keep working at runtime until compat is removed. + const piAiCompatEntry = resolveWorkspaceOrImport("ai/dist/compat.js", "@earendil-works/pi-ai/compat"); const piAiOauthEntry = resolveWorkspaceOrImport("ai/dist/oauth.js", "@earendil-works/pi-ai/oauth"); _aliases = { "@earendil-works/pi-coding-agent": piCodingAgentEntry, "@earendil-works/pi-agent-core": piAgentCoreEntry, "@earendil-works/pi-tui": piTuiEntry, - "@earendil-works/pi-ai": piAiEntry, + "@earendil-works/pi-ai": piAiCompatEntry, + "@earendil-works/pi-ai/compat": piAiCompatEntry, "@earendil-works/pi-ai/oauth": piAiOauthEntry, "@mariozechner/pi-coding-agent": piCodingAgentEntry, "@mariozechner/pi-agent-core": piAgentCoreEntry, "@mariozechner/pi-tui": piTuiEntry, - "@mariozechner/pi-ai": piAiEntry, + "@mariozechner/pi-ai": piAiCompatEntry, + "@mariozechner/pi-ai/compat": piAiCompatEntry, "@mariozechner/pi-ai/oauth": piAiOauthEntry, typebox: typeboxEntry, "typebox/compile": typeboxCompileEntry, diff --git a/packages/coding-agent/src/core/model-registry.ts b/packages/coding-agent/src/core/model-registry.ts index 00c815761..114cbb004 100644 --- a/packages/coding-agent/src/core/model-registry.ts +++ b/packages/coding-agent/src/core/model-registry.ts @@ -17,7 +17,7 @@ import { registerApiProvider, resetApiProviders, type SimpleStreamOptions, -} from "@earendil-works/pi-ai"; +} from "@earendil-works/pi-ai/compat"; import { registerOAuthProvider, resetOAuthProviders } from "@earendil-works/pi-ai/oauth"; import { existsSync, readFileSync } from "fs"; import { join } from "path"; diff --git a/packages/coding-agent/src/core/sdk.ts b/packages/coding-agent/src/core/sdk.ts index e6db747b7..49d135355 100644 --- a/packages/coding-agent/src/core/sdk.ts +++ b/packages/coding-agent/src/core/sdk.ts @@ -1,6 +1,6 @@ import { join } from "node:path"; import { Agent, type AgentMessage, type ThinkingLevel } from "@earendil-works/pi-agent-core"; -import { clampThinkingLevel, type Message, type Model, streamSimple } from "@earendil-works/pi-ai"; +import { clampThinkingLevel, type Message, type Model, streamSimple } from "@earendil-works/pi-ai/compat"; import { getAgentDir } from "../config.ts"; import { resolvePath } from "../utils/paths.ts"; import { AgentSession } from "./agent-session.ts"; diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index d50611afa..12e7e8190 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -16,7 +16,7 @@ import { type Model, type OAuthProviderId, type OAuthSelectPrompt, -} from "@earendil-works/pi-ai"; +} from "@earendil-works/pi-ai/compat"; import type { AutocompleteItem, AutocompleteProvider, diff --git a/packages/coding-agent/test/agent-session-auto-compaction-queue.test.ts b/packages/coding-agent/test/agent-session-auto-compaction-queue.test.ts index 1abe3e288..cc0f97140 100644 --- a/packages/coding-agent/test/agent-session-auto-compaction-queue.test.ts +++ b/packages/coding-agent/test/agent-session-auto-compaction-queue.test.ts @@ -2,7 +2,7 @@ import { existsSync, mkdirSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { Agent } from "@earendil-works/pi-agent-core"; -import { type AssistantMessage, getModel } from "@earendil-works/pi-ai"; +import { type AssistantMessage, getModel } from "@earendil-works/pi-ai/compat"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { AgentSession } from "../src/core/agent-session.ts"; import { AuthStorage } from "../src/core/auth-storage.ts"; diff --git a/packages/coding-agent/test/agent-session-branching.test.ts b/packages/coding-agent/test/agent-session-branching.test.ts index f516e6d65..76d355e16 100644 --- a/packages/coding-agent/test/agent-session-branching.test.ts +++ b/packages/coding-agent/test/agent-session-branching.test.ts @@ -10,7 +10,7 @@ import { existsSync, mkdirSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { getModel } from "@earendil-works/pi-ai"; +import { getModel } from "@earendil-works/pi-ai/compat"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import type { AgentSession } from "../src/core/agent-session.ts"; import { diff --git a/packages/coding-agent/test/agent-session-compaction.test.ts b/packages/coding-agent/test/agent-session-compaction.test.ts index 5724d6842..516c3b45d 100644 --- a/packages/coding-agent/test/agent-session-compaction.test.ts +++ b/packages/coding-agent/test/agent-session-compaction.test.ts @@ -11,7 +11,7 @@ import { existsSync, mkdirSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { Agent } from "@earendil-works/pi-agent-core"; -import { getModel } from "@earendil-works/pi-ai"; +import { getModel } from "@earendil-works/pi-ai/compat"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { AgentSession, type AgentSessionEvent } from "../src/core/agent-session.ts"; import { AuthStorage } from "../src/core/auth-storage.ts"; diff --git a/packages/coding-agent/test/agent-session-concurrent.test.ts b/packages/coding-agent/test/agent-session-concurrent.test.ts index dcd2ac9b5..46599068b 100644 --- a/packages/coding-agent/test/agent-session-concurrent.test.ts +++ b/packages/coding-agent/test/agent-session-concurrent.test.ts @@ -13,7 +13,7 @@ import { getModel, type ImageContent, type TextContent, -} from "@earendil-works/pi-ai"; +} from "@earendil-works/pi-ai/compat"; import { Type } from "typebox"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { AgentSession } from "../src/core/agent-session.ts"; diff --git a/packages/coding-agent/test/agent-session-dynamic-provider.test.ts b/packages/coding-agent/test/agent-session-dynamic-provider.test.ts index a6da8c9ed..eee5583d2 100644 --- a/packages/coding-agent/test/agent-session-dynamic-provider.test.ts +++ b/packages/coding-agent/test/agent-session-dynamic-provider.test.ts @@ -1,7 +1,7 @@ import { existsSync, mkdirSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { getModel } from "@earendil-works/pi-ai"; +import { getModel } from "@earendil-works/pi-ai/compat"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { AuthStorage } from "../src/core/auth-storage.ts"; import { DefaultResourceLoader } from "../src/core/resource-loader.ts"; diff --git a/packages/coding-agent/test/agent-session-dynamic-tools.test.ts b/packages/coding-agent/test/agent-session-dynamic-tools.test.ts index cf64a17f7..88871ac88 100644 --- a/packages/coding-agent/test/agent-session-dynamic-tools.test.ts +++ b/packages/coding-agent/test/agent-session-dynamic-tools.test.ts @@ -1,7 +1,7 @@ import { existsSync, mkdirSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { getModel } from "@earendil-works/pi-ai"; +import { getModel } from "@earendil-works/pi-ai/compat"; import { Type } from "typebox"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { DefaultResourceLoader } from "../src/core/resource-loader.ts"; diff --git a/packages/coding-agent/test/agent-session-retry.test.ts b/packages/coding-agent/test/agent-session-retry.test.ts index ebba143b3..6e3d05822 100644 --- a/packages/coding-agent/test/agent-session-retry.test.ts +++ b/packages/coding-agent/test/agent-session-retry.test.ts @@ -2,7 +2,7 @@ import { existsSync, mkdirSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { Agent, type AgentEvent, type AgentTool } from "@earendil-works/pi-agent-core"; -import { type AssistantMessage, type AssistantMessageEvent, EventStream, getModel } from "@earendil-works/pi-ai"; +import { type AssistantMessage, type AssistantMessageEvent, EventStream, getModel } from "@earendil-works/pi-ai/compat"; import { Type } from "typebox"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { AgentSession } from "../src/core/agent-session.ts"; diff --git a/packages/coding-agent/test/agent-session-stats.test.ts b/packages/coding-agent/test/agent-session-stats.test.ts index b435246e6..9ba30a94c 100644 --- a/packages/coding-agent/test/agent-session-stats.test.ts +++ b/packages/coding-agent/test/agent-session-stats.test.ts @@ -1,5 +1,5 @@ import { Agent } from "@earendil-works/pi-agent-core"; -import { type AssistantMessage, getModel, type Usage } from "@earendil-works/pi-ai"; +import { type AssistantMessage, getModel, type Usage } from "@earendil-works/pi-ai/compat"; import { describe, expect, it } from "vitest"; import { AgentSession } from "../src/core/agent-session.ts"; import { AuthStorage } from "../src/core/auth-storage.ts"; diff --git a/packages/coding-agent/test/compaction-extensions.test.ts b/packages/coding-agent/test/compaction-extensions.test.ts index 2c41210e3..6554e89aa 100644 --- a/packages/coding-agent/test/compaction-extensions.test.ts +++ b/packages/coding-agent/test/compaction-extensions.test.ts @@ -6,7 +6,7 @@ import { existsSync, mkdirSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { Agent } from "@earendil-works/pi-agent-core"; -import { getModel } from "@earendil-works/pi-ai"; +import { getModel } from "@earendil-works/pi-ai/compat"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { AgentSession } from "../src/core/agent-session.ts"; import { AuthStorage } from "../src/core/auth-storage.ts"; diff --git a/packages/coding-agent/test/compaction-summary-reasoning.test.ts b/packages/coding-agent/test/compaction-summary-reasoning.test.ts index 6609aca16..306a3b1c4 100644 --- a/packages/coding-agent/test/compaction-summary-reasoning.test.ts +++ b/packages/coding-agent/test/compaction-summary-reasoning.test.ts @@ -7,8 +7,8 @@ const { completeSimpleMock } = vi.hoisted(() => ({ completeSimpleMock: vi.fn(), })); -vi.mock("@earendil-works/pi-ai", async (importOriginal) => { - const actual = await importOriginal(); +vi.mock("@earendil-works/pi-ai/compat", async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, completeSimple: completeSimpleMock, diff --git a/packages/coding-agent/test/compaction.test.ts b/packages/coding-agent/test/compaction.test.ts index 929d06ec5..c74fe8708 100644 --- a/packages/coding-agent/test/compaction.test.ts +++ b/packages/coding-agent/test/compaction.test.ts @@ -1,6 +1,6 @@ import type { AgentMessage } from "@earendil-works/pi-agent-core"; -import type { AssistantMessage, Usage } from "@earendil-works/pi-ai"; -import { getModel } from "@earendil-works/pi-ai"; +import type { AssistantMessage, Usage } from "@earendil-works/pi-ai/compat"; +import { getModel } from "@earendil-works/pi-ai/compat"; import { readFileSync } from "fs"; import { join } from "path"; import { beforeEach, describe, expect, it } from "vitest"; diff --git a/packages/coding-agent/test/model-registry.test.ts b/packages/coding-agent/test/model-registry.test.ts index 95b70fbd1..32132c1b7 100644 --- a/packages/coding-agent/test/model-registry.test.ts +++ b/packages/coding-agent/test/model-registry.test.ts @@ -1,8 +1,14 @@ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import type { AnthropicMessagesCompat, Api, Context, Model, OpenAICompletionsCompat } from "@earendil-works/pi-ai"; -import { getApiProvider } from "@earendil-works/pi-ai"; +import type { + AnthropicMessagesCompat, + Api, + Context, + Model, + OpenAICompletionsCompat, +} from "@earendil-works/pi-ai/compat"; +import { getApiProvider } from "@earendil-works/pi-ai/compat"; import { getOAuthProvider } from "@earendil-works/pi-ai/oauth"; import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; import { AuthStorage } from "../src/core/auth-storage.ts"; diff --git a/packages/coding-agent/test/rpc-prompt-response-semantics.test.ts b/packages/coding-agent/test/rpc-prompt-response-semantics.test.ts index 08b3a56f1..5e4d5b086 100644 --- a/packages/coding-agent/test/rpc-prompt-response-semantics.test.ts +++ b/packages/coding-agent/test/rpc-prompt-response-semantics.test.ts @@ -8,7 +8,7 @@ import { EventStream, getModel, type Model, -} from "@earendil-works/pi-ai"; +} from "@earendil-works/pi-ai/compat"; import { afterEach, describe, expect, it, vi } from "vitest"; import { AgentSession } from "../src/core/agent-session.ts"; import type { AgentSessionRuntime } from "../src/core/agent-session-runtime.ts"; diff --git a/packages/coding-agent/test/sdk-codex-cache-probe-tool-loop.ts b/packages/coding-agent/test/sdk-codex-cache-probe-tool-loop.ts index 6d00a1a01..ff6a9798e 100644 --- a/packages/coding-agent/test/sdk-codex-cache-probe-tool-loop.ts +++ b/packages/coding-agent/test/sdk-codex-cache-probe-tool-loop.ts @@ -20,7 +20,7 @@ import { type Model, type SimpleStreamOptions, Type, -} from "@earendil-works/pi-ai"; +} from "@earendil-works/pi-ai/compat"; import { getOpenAICodexWebSocketDebugStats, streamSimple as streamSimpleOpenAICodexResponses, diff --git a/packages/coding-agent/test/sdk-session-manager.test.ts b/packages/coding-agent/test/sdk-session-manager.test.ts index cb7e505c7..9cdf7774c 100644 --- a/packages/coding-agent/test/sdk-session-manager.test.ts +++ b/packages/coding-agent/test/sdk-session-manager.test.ts @@ -1,7 +1,7 @@ import { existsSync, mkdirSync, realpathSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { getModel } from "@earendil-works/pi-ai"; +import { getModel } from "@earendil-works/pi-ai/compat"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { createAgentSession } from "../src/core/sdk.ts"; import { SessionManager } from "../src/core/session-manager.ts"; diff --git a/packages/coding-agent/test/suite/regressions/2835-tools-allowlist-filters-extension-tools.test.ts b/packages/coding-agent/test/suite/regressions/2835-tools-allowlist-filters-extension-tools.test.ts index e0003aa69..198b45a83 100644 --- a/packages/coding-agent/test/suite/regressions/2835-tools-allowlist-filters-extension-tools.test.ts +++ b/packages/coding-agent/test/suite/regressions/2835-tools-allowlist-filters-extension-tools.test.ts @@ -1,7 +1,7 @@ import { existsSync, mkdirSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { getModel } from "@earendil-works/pi-ai"; +import { getModel } from "@earendil-works/pi-ai/compat"; import { Type } from "typebox"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { DefaultResourceLoader } from "../../../src/core/resource-loader.ts"; diff --git a/packages/coding-agent/test/suite/regressions/3592-no-builtin-tools-keeps-extension-tools.test.ts b/packages/coding-agent/test/suite/regressions/3592-no-builtin-tools-keeps-extension-tools.test.ts index d7d1376cb..3d900ee36 100644 --- a/packages/coding-agent/test/suite/regressions/3592-no-builtin-tools-keeps-extension-tools.test.ts +++ b/packages/coding-agent/test/suite/regressions/3592-no-builtin-tools-keeps-extension-tools.test.ts @@ -1,7 +1,7 @@ import { existsSync, mkdirSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { getModel } from "@earendil-works/pi-ai"; +import { getModel } from "@earendil-works/pi-ai/compat"; import { Type } from "typebox"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { diff --git a/packages/coding-agent/test/utilities.ts b/packages/coding-agent/test/utilities.ts index 5ebf68135..73177ce9a 100644 --- a/packages/coding-agent/test/utilities.ts +++ b/packages/coding-agent/test/utilities.ts @@ -6,7 +6,7 @@ import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } import { homedir, tmpdir } from "node:os"; import { dirname, join } from "node:path"; import { Agent } from "@earendil-works/pi-agent-core"; -import { getModel, type OAuthCredentials, type OAuthProvider } from "@earendil-works/pi-ai"; +import { getModel, type OAuthCredentials, type OAuthProvider } from "@earendil-works/pi-ai/compat"; import { getOAuthApiKey } from "@earendil-works/pi-ai/oauth"; import { AgentSession } from "../src/core/agent-session.ts"; import { AuthStorage } from "../src/core/auth-storage.ts"; diff --git a/packages/coding-agent/vitest.config.ts b/packages/coding-agent/vitest.config.ts index 67ce0fca8..fe12421c0 100644 --- a/packages/coding-agent/vitest.config.ts +++ b/packages/coding-agent/vitest.config.ts @@ -2,6 +2,7 @@ import { fileURLToPath } from "node:url"; import { defineConfig } from "vitest/config"; const aiSrcIndex = fileURLToPath(new URL("../ai/src/index.ts", import.meta.url)); +const aiSrcCompat = fileURLToPath(new URL("../ai/src/compat.ts", import.meta.url)); const aiSrcOAuth = fileURLToPath(new URL("../ai/src/oauth.ts", import.meta.url)); const agentSrcIndex = fileURLToPath(new URL("../agent/src/index.ts", import.meta.url)); const tuiSrcIndex = fileURLToPath(new URL("../tui/src/index.ts", import.meta.url)); @@ -20,6 +21,7 @@ export default defineConfig({ resolve: { alias: [ { find: /^@earendil-works\/pi-ai$/, replacement: aiSrcIndex }, + { find: /^@earendil-works\/pi-ai\/compat$/, replacement: aiSrcCompat }, { find: /^@earendil-works\/pi-ai\/oauth$/, replacement: aiSrcOAuth }, { find: /^@earendil-works\/pi-agent-core$/, replacement: agentSrcIndex }, { find: /^@earendil-works\/pi-tui$/, replacement: tuiSrcIndex }, diff --git a/scripts/browser-smoke-entry.ts b/scripts/browser-smoke-entry.ts index 066a60995..3ac7bcf02 100644 --- a/scripts/browser-smoke-entry.ts +++ b/scripts/browser-smoke-entry.ts @@ -1,4 +1,5 @@ -import { complete, createAssistantMessageEventStream, getModel, getProviders, Type } from "@earendil-works/pi-ai"; +import { createAssistantMessageEventStream, Type } from "@earendil-works/pi-ai"; +import { complete, getModel, getProviders } from "@earendil-works/pi-ai/compat"; import { Agent, bashExecutionToText,