@psnext/slingcli 2.5.20260623-1 → 2.5.20260629-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js +5 -2
  2. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js +2 -1
  3. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js +1 -1
  4. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  5. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +51 -47
  6. package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.js +9 -3
  7. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js +9 -6
  8. package/node_modules/@earendil-works/pi-ai/dist/api/cloudflare.js +0 -18
  9. package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.js +6 -3
  10. package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.js +6 -3
  11. package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.js +1 -1
  12. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +15 -25
  13. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +30 -26
  14. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js +189 -204
  15. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +24 -21
  16. package/node_modules/@earendil-works/pi-ai/dist/api/openrouter-images.js +2 -5
  17. package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.js +11 -2
  18. package/node_modules/@earendil-works/pi-ai/dist/auth/helpers.js +2 -2
  19. package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.js +19 -4
  20. package/node_modules/@earendil-works/pi-ai/dist/compat.js +81 -7
  21. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +45 -0
  22. package/node_modules/@earendil-works/pi-ai/dist/images-models.js +7 -3
  23. package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
  24. package/node_modules/@earendil-works/pi-ai/dist/legacy-api-aliases.js +49 -0
  25. package/node_modules/@earendil-works/pi-ai/dist/models.js +7 -3
  26. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.js +25 -8
  27. package/node_modules/@earendil-works/pi-ai/dist/providers/ant-ling.models.js +3 -3
  28. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.js +0 -34
  29. package/node_modules/@earendil-works/pi-ai/dist/providers/cerebras.models.js +2 -0
  30. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.js +2 -2
  31. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.js +21 -9
  32. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-auth.js +85 -0
  33. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-workers-ai.js +2 -2
  34. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-workers-ai.models.js +13 -13
  35. package/node_modules/@earendil-works/pi-ai/dist/providers/deepseek.models.js +2 -2
  36. package/node_modules/@earendil-works/pi-ai/dist/providers/faux.js +1 -20
  37. package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.js +19 -1
  38. package/node_modules/@earendil-works/pi-ai/dist/providers/huggingface.models.js +72 -0
  39. package/node_modules/@earendil-works/pi-ai/dist/providers/minimax-cn.models.js +4 -4
  40. package/node_modules/@earendil-works/pi-ai/dist/providers/minimax.models.js +4 -4
  41. package/node_modules/@earendil-works/pi-ai/dist/providers/nvidia.models.js +19 -0
  42. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.js +11 -11
  43. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.js +15 -15
  44. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js +310 -39
  45. package/node_modules/@earendil-works/pi-ai/dist/providers/together.models.js +37 -18
  46. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js +349 -77
  47. package/node_modules/@earendil-works/pi-ai/dist/providers/xai.models.js +7 -0
  48. package/node_modules/@earendil-works/pi-ai/dist/providers/zai-coding-cn.models.js +6 -6
  49. package/node_modules/@earendil-works/pi-ai/dist/providers/zai.models.js +6 -6
  50. package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.js +95 -0
  51. package/node_modules/@earendil-works/pi-ai/dist/utils/headers.js +10 -0
  52. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js +2 -2
  53. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +86 -0
  54. package/node_modules/@earendil-works/pi-ai/package.json +1 -33
  55. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/file-processor.js +13 -25
  56. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +15 -30
  57. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js +6 -3
  58. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js +5 -1
  59. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-registry.js +2 -4
  60. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js +1 -1
  61. package/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js +2 -0
  62. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.js +9 -5
  63. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js +11 -0
  64. package/node_modules/@earendil-works/pi-coding-agent/dist/core/timings.js +24 -14
  65. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.js +12 -25
  66. package/node_modules/@earendil-works/pi-coding-agent/dist/main.js +20 -4
  67. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +9 -9
  68. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/extension-editor.js +12 -3
  69. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/session-selector.js +14 -3
  70. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.js +1 -1
  71. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +37 -25
  72. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-client.js +14 -0
  73. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js +16 -0
  74. package/node_modules/@earendil-works/pi-coding-agent/dist/rpc-entry.js +10 -0
  75. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-convert.js +21 -15
  76. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-process.js +83 -0
  77. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/mime.js +41 -0
  78. package/node_modules/@earendil-works/pi-coding-agent/npm-shrinkwrap.json +12 -12
  79. package/node_modules/@earendil-works/pi-coding-agent/package.json +8 -5
  80. package/node_modules/@earendil-works/pi-orchestrator/dist/cli.js +136 -0
  81. package/node_modules/@earendil-works/pi-orchestrator/dist/config.js +55 -0
  82. package/node_modules/@earendil-works/pi-orchestrator/dist/handler.js +105 -0
  83. package/node_modules/@earendil-works/pi-orchestrator/dist/index.js +11 -0
  84. package/node_modules/@earendil-works/pi-orchestrator/dist/ipc/client.js +56 -0
  85. package/node_modules/@earendil-works/pi-orchestrator/dist/ipc/protocol.js +12 -0
  86. package/node_modules/@earendil-works/pi-orchestrator/dist/ipc/server.js +145 -0
  87. package/node_modules/@earendil-works/pi-orchestrator/dist/radius.js +365 -0
  88. package/node_modules/@earendil-works/pi-orchestrator/dist/rpc-process.js +168 -0
  89. package/node_modules/@earendil-works/pi-orchestrator/dist/serve.js +70 -0
  90. package/node_modules/@earendil-works/pi-orchestrator/dist/storage.js +58 -0
  91. package/node_modules/@earendil-works/pi-orchestrator/dist/supervisor.js +290 -0
  92. package/node_modules/@earendil-works/pi-orchestrator/dist/types.js +2 -0
  93. package/node_modules/@earendil-works/pi-orchestrator/package.json +45 -0
  94. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js +3 -0
  95. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  96. package/node_modules/@types/node/README.md +1 -1
  97. package/node_modules/@types/node/diagnostics_channel.d.ts +17 -20
  98. package/node_modules/@types/node/package.json +2 -2
  99. package/node_modules/brace-expansion/dist/commonjs/index.js +24 -14
  100. package/node_modules/brace-expansion/dist/commonjs/index.js.map +1 -1
  101. package/node_modules/brace-expansion/dist/esm/index.js +24 -14
  102. package/node_modules/brace-expansion/dist/esm/index.js.map +1 -1
  103. package/node_modules/brace-expansion/package.json +2 -2
  104. package/node_modules/google-auth-library/build/src/auth/googleauth.js +1 -1
  105. package/node_modules/google-auth-library/package.json +4 -4
  106. package/package.json +7 -6
  107. package/slingshot/index.js +302 -304
  108. package/node_modules/@earendil-works/pi-ai/dist/api-registry.js +0 -44
@@ -3,12 +3,28 @@ import { clampThinkingLevel } from "../models.js";
3
3
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
4
4
  import { headersToRecord } from "../utils/headers.js";
5
5
  import { getProviderEnvValue } from "../utils/provider-env.js";
6
- import { isCloudflareProvider, resolveCloudflareBaseUrl } from "./cloudflare.js";
7
6
  import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "./github-copilot-headers.js";
8
7
  import { clampOpenAIPromptCacheKey } from "./openai-prompt-cache.js";
9
8
  import { convertResponsesMessages, convertResponsesTools, processResponsesStream } from "./openai-responses-shared.js";
10
9
  import { buildBaseOptions } from "./simple-options.js";
11
10
  const OPENAI_TOOL_CALL_PROVIDERS = new Set(["openai", "openai-codex", "opencode"]);
11
+ function hasHeader(headers, name) {
12
+ if (!headers)
13
+ return false;
14
+ const expected = name.toLowerCase();
15
+ for (const [key, value] of Object.entries(headers)) {
16
+ if (key.toLowerCase() === expected && value !== null && value.trim().length > 0)
17
+ return true;
18
+ }
19
+ return false;
20
+ }
21
+ function getClientApiKey(provider, apiKey, headers) {
22
+ if (apiKey)
23
+ return apiKey;
24
+ if (hasHeader(headers, "authorization") || hasHeader(headers, "cf-aig-authorization"))
25
+ return "unused";
26
+ throw new Error(`No API key for provider: ${provider}`);
27
+ }
12
28
  /**
13
29
  * Resolve cache retention preference.
14
30
  * Defaults to "short" and uses PI_CACHE_RETENTION for backward compatibility.
@@ -74,13 +90,10 @@ export const stream = (model, context, options) => {
74
90
  };
75
91
  try {
76
92
  // Create OpenAI client
77
- const apiKey = options?.apiKey;
78
- if (!apiKey) {
79
- throw new Error(`No API key for provider: ${model.provider}`);
80
- }
93
+ const apiKey = getClientApiKey(model.provider, options?.apiKey, options?.headers);
81
94
  const cacheRetention = resolveCacheRetention(options?.cacheRetention, options?.env);
82
95
  const cacheSessionId = cacheRetention === "none" ? undefined : options?.sessionId;
83
- const client = createClient(model, context, apiKey, options?.headers, cacheSessionId, options?.env);
96
+ const client = createClient(model, context, apiKey, options?.headers, cacheSessionId);
84
97
  let params = buildParams(model, context, options);
85
98
  const nextParams = await options?.onPayload?.(params, model);
86
99
  if (nextParams !== undefined) {
@@ -122,11 +135,8 @@ export const stream = (model, context, options) => {
122
135
  return stream;
123
136
  };
124
137
  export const streamSimple = (model, context, options) => {
125
- const apiKey = options?.apiKey;
126
- if (!apiKey) {
127
- throw new Error(`No API key for provider: ${model.provider}`);
128
- }
129
- const base = buildBaseOptions(model, options, apiKey);
138
+ getClientApiKey(model.provider, options?.apiKey, options?.headers);
139
+ const base = buildBaseOptions(model, context, options, options?.apiKey);
130
140
  const clampedReasoning = options?.reasoning ? clampThinkingLevel(model, options.reasoning) : undefined;
131
141
  const reasoningEffort = clampedReasoning === "off" ? undefined : clampedReasoning;
132
142
  return stream(model, context, {
@@ -134,7 +144,7 @@ export const streamSimple = (model, context, options) => {
134
144
  reasoningEffort,
135
145
  });
136
146
  };
137
- function createClient(model, context, apiKey, optionsHeaders, sessionId, env) {
147
+ function createClient(model, context, apiKey, optionsHeaders, sessionId) {
138
148
  const compat = getCompat(model);
139
149
  const headers = { ...model.headers };
140
150
  if (model.provider === "github-copilot") {
@@ -155,18 +165,11 @@ function createClient(model, context, apiKey, optionsHeaders, sessionId, env) {
155
165
  if (optionsHeaders) {
156
166
  Object.assign(headers, optionsHeaders);
157
167
  }
158
- const defaultHeaders = model.provider === "cloudflare-ai-gateway"
159
- ? {
160
- ...headers,
161
- Authorization: headers.Authorization ?? null,
162
- "cf-aig-authorization": `Bearer ${apiKey}`,
163
- }
164
- : headers;
165
168
  return new OpenAI({
166
169
  apiKey,
167
- baseURL: isCloudflareProvider(model.provider) ? resolveCloudflareBaseUrl(model, env) : model.baseUrl,
170
+ baseURL: model.baseUrl,
168
171
  dangerouslyAllowBrowser: true,
169
- defaultHeaders,
172
+ defaultHeaders: headers,
170
173
  });
171
174
  }
172
175
  function buildParams(model, context, options) {
@@ -1,5 +1,5 @@
1
1
  import OpenAI from "openai";
2
- import { headersToRecord } from "../utils/headers.js";
2
+ import { headersToRecord, providerHeadersToRecord } from "../utils/headers.js";
3
3
  import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
4
4
  export const generateImages = async (model, context, options) => {
5
5
  const output = {
@@ -68,10 +68,7 @@ function createClient(model, apiKey, optionsHeaders) {
68
68
  apiKey,
69
69
  baseURL: model.baseUrl,
70
70
  dangerouslyAllowBrowser: true,
71
- defaultHeaders: {
72
- ...model.headers,
73
- ...optionsHeaders,
74
- },
71
+ defaultHeaders: providerHeadersToRecord({ ...model.headers, ...optionsHeaders }),
75
72
  });
76
73
  }
77
74
  function buildParams(model, context) {
@@ -1,7 +1,16 @@
1
- export function buildBaseOptions(_model, options, apiKey) {
1
+ import { estimateContextTokens } from "../utils/estimate.js";
2
+ const CONTEXT_SAFETY_TOKENS = 4096;
3
+ const MIN_MAX_TOKENS = 1;
4
+ export function clampMaxTokensToContext(model, context, maxTokens) {
5
+ if (model.contextWindow <= 0)
6
+ return Math.max(MIN_MAX_TOKENS, maxTokens);
7
+ const available = model.contextWindow - estimateContextTokens(context).tokens - CONTEXT_SAFETY_TOKENS;
8
+ return Math.min(maxTokens, Math.max(MIN_MAX_TOKENS, available));
9
+ }
10
+ export function buildBaseOptions(model, context, options, apiKey) {
2
11
  return {
3
12
  temperature: options?.temperature,
4
- maxTokens: options?.maxTokens,
13
+ maxTokens: clampMaxTokensToContext(model, context, options?.maxTokens ?? model.maxTokens),
5
14
  signal: options?.signal,
6
15
  apiKey: apiKey || options?.apiKey,
7
16
  transport: options?.transport,
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Standard api-key auth: a stored credential key wins, otherwise the first
3
3
  * set env var resolves. Includes a `login` that prompts for the key.
4
- * Providers with non-standard resolution (metadata, ambient files, IAM)
4
+ * Providers with non-standard resolution (provider env, ambient files, IAM)
5
5
  * write their own `ApiKeyAuth`.
6
6
  */
7
7
  export function envApiKeyAuth(name, envVars) {
@@ -9,7 +9,7 @@ export function envApiKeyAuth(name, envVars) {
9
9
  name,
10
10
  login: async (callbacks) => {
11
11
  const key = await callbacks.prompt({ type: "secret", message: `Enter ${name}` });
12
- return { type: "api-key", key };
12
+ return { type: "api_key", key };
13
13
  },
14
14
  resolve: async ({ ctx, credential }) => {
15
15
  if (credential?.key)
@@ -12,19 +12,34 @@ export class ModelsError extends Error {
12
12
  * nothing is stored. No silent env fallback after a failed refresh or for a
13
13
  * credential type without a matching handler.
14
14
  */
15
- export async function resolveProviderAuth(provider, model, credentials, authContext) {
15
+ export async function resolveProviderAuth(provider, model, credentials, authContext, overrides) {
16
+ const requestAuthContext = overrides?.env ? overlayEnvAuthContext(authContext, overrides.env) : authContext;
17
+ if (overrides?.apiKey !== undefined && provider.auth.apiKey) {
18
+ return resolveApiKey(requestAuthContext, provider.auth.apiKey, model, {
19
+ type: "api_key",
20
+ key: overrides.apiKey,
21
+ env: overrides.env,
22
+ });
23
+ }
16
24
  const stored = await readCredential(credentials, provider.id);
17
25
  if (stored) {
18
26
  if (stored.type === "oauth" && provider.auth.oauth) {
19
27
  return resolveStoredOAuth(credentials, provider.id, provider.auth.oauth, stored);
20
28
  }
21
- if (stored.type === "api-key" && provider.auth.apiKey) {
22
- return resolveApiKey(authContext, provider.auth.apiKey, model, stored);
29
+ if (stored.type === "api_key" && provider.auth.apiKey) {
30
+ const credential = overrides?.env ? { ...stored, env: { ...stored.env, ...overrides.env } } : stored;
31
+ return resolveApiKey(requestAuthContext, provider.auth.apiKey, model, credential);
23
32
  }
24
33
  return undefined;
25
34
  }
26
35
  // Ambient (env vars, AWS profiles, ADC files).
27
- return provider.auth.apiKey ? resolveApiKey(authContext, provider.auth.apiKey, model, undefined) : undefined;
36
+ return provider.auth.apiKey ? resolveApiKey(requestAuthContext, provider.auth.apiKey, model, undefined) : undefined;
37
+ }
38
+ function overlayEnvAuthContext(base, env) {
39
+ return {
40
+ env: async (name) => env[name] || (await base.env(name)),
41
+ fileExists: (path) => base.fileExists(path),
42
+ };
28
43
  }
29
44
  /**
30
45
  * OAuth resolution with double-checked locking (same pattern as today's
@@ -18,12 +18,12 @@ export * from "./api/mistral-conversations.lazy.js";
18
18
  export * from "./api/openai-codex-responses.lazy.js";
19
19
  export * from "./api/openai-completions.lazy.js";
20
20
  export * from "./api/openai-responses.lazy.js";
21
- export * from "./api-registry.js";
22
21
  export * from "./env-api-keys.js";
23
22
  export * from "./image-models.js";
24
23
  export * from "./images.js";
25
24
  export * from "./images-api-registry.js";
26
25
  export * from "./index.js";
26
+ export * from "./legacy-api-aliases.js";
27
27
  export * from "./providers/images/register-builtins.js";
28
28
  import { anthropicMessagesApi } from "./api/anthropic-messages.lazy.js";
29
29
  import { azureOpenAIResponsesApi } from "./api/azure-openai-responses.lazy.js";
@@ -34,15 +34,75 @@ import { mistralConversationsApi } from "./api/mistral-conversations.lazy.js";
34
34
  import { openAICodexResponsesApi } from "./api/openai-codex-responses.lazy.js";
35
35
  import { openAICompletionsApi } from "./api/openai-completions.lazy.js";
36
36
  import { openAIResponsesApi } from "./api/openai-responses.lazy.js";
37
- import { clearApiProviders, getApiProvider, registerApiProvider } from "./api-registry.js";
38
37
  import { getEnvApiKey } from "./env-api-keys.js";
39
- import { getBuiltinModel, getBuiltinModels, getBuiltinProviders } from "./providers/all.js";
38
+ import { builtinModels, getBuiltinModel, getBuiltinModels, getBuiltinProviders } from "./providers/all.js";
39
+ import { createFauxCore } from "./providers/faux.js";
40
40
  /** @deprecated Static catalog read. Use `getBuiltinModel` from "@earendil-works/pi-ai/providers/all" or `Models.getModel()`. */
41
41
  export const getModel = getBuiltinModel;
42
42
  /** @deprecated Static catalog read. Use `getBuiltinModels` from "@earendil-works/pi-ai/providers/all" or `Models.getModels()`. */
43
43
  export const getModels = getBuiltinModels;
44
44
  /** @deprecated Static catalog read. Use `getBuiltinProviders` from "@earendil-works/pi-ai/providers/all" or `Models.getProviders()`. */
45
45
  export const getProviders = getBuiltinProviders;
46
+ const apiProviderRegistry = new Map();
47
+ function wrapStream(api, stream) {
48
+ return (model, context, options) => {
49
+ if (model.api !== api) {
50
+ throw new Error(`Mismatched api: ${model.api} expected ${api}`);
51
+ }
52
+ return stream(model, context, options);
53
+ };
54
+ }
55
+ function wrapStreamSimple(api, streamSimple) {
56
+ return (model, context, options) => {
57
+ if (model.api !== api) {
58
+ throw new Error(`Mismatched api: ${model.api} expected ${api}`);
59
+ }
60
+ return streamSimple(model, context, options);
61
+ };
62
+ }
63
+ export function registerApiProvider(provider, sourceId) {
64
+ apiProviderRegistry.set(provider.api, {
65
+ provider: {
66
+ api: provider.api,
67
+ stream: wrapStream(provider.api, provider.stream),
68
+ streamSimple: wrapStreamSimple(provider.api, provider.streamSimple),
69
+ },
70
+ sourceId,
71
+ });
72
+ }
73
+ export function getApiProvider(api) {
74
+ return apiProviderRegistry.get(api)?.provider;
75
+ }
76
+ export function getApiProviders() {
77
+ return Array.from(apiProviderRegistry.values(), (entry) => entry.provider);
78
+ }
79
+ export function unregisterApiProviders(sourceId) {
80
+ for (const [api, entry] of apiProviderRegistry.entries()) {
81
+ if (entry.sourceId === sourceId) {
82
+ apiProviderRegistry.delete(api);
83
+ }
84
+ }
85
+ }
86
+ function clearApiProviders() {
87
+ apiProviderRegistry.clear();
88
+ }
89
+ export function registerFauxProvider(options = {}) {
90
+ const core = createFauxCore(options);
91
+ const sourceId = `faux-provider-${Math.random().toString(36).slice(2, 10)}`;
92
+ registerApiProvider({ api: core.api, stream: core.stream, streamSimple: core.streamSimple }, sourceId);
93
+ return {
94
+ api: core.api,
95
+ models: core.models,
96
+ getModel: core.getModel,
97
+ state: core.state,
98
+ setResponses: core.setResponses,
99
+ appendResponses: core.appendResponses,
100
+ getPendingResponseCount: core.getPendingResponseCount,
101
+ unregister() {
102
+ unregisterApiProviders(sourceId);
103
+ },
104
+ };
105
+ }
46
106
  const BUILTIN_APIS = [
47
107
  ["anthropic-messages", anthropicMessagesApi()],
48
108
  ["openai-completions", openAICompletionsApi()],
@@ -54,6 +114,7 @@ const BUILTIN_APIS = [
54
114
  ["mistral-conversations", mistralConversationsApi()],
55
115
  ["bedrock-converse-stream", bedrockConverseStreamApi()],
56
116
  ];
117
+ const builtinApiProviderInstances = new Map();
57
118
  /**
58
119
  * Registers the builtin API implementations into the api-registry without
59
120
  * clobbering existing entries: compat may load after a test or extension has
@@ -61,27 +122,34 @@ const BUILTIN_APIS = [
61
122
  */
62
123
  export function registerBuiltInApiProviders() {
63
124
  for (const [api, streams] of BUILTIN_APIS) {
64
- if (getApiProvider(api))
65
- continue;
66
- registerApiProvider({ api, stream: streams.stream, streamSimple: streams.streamSimple });
125
+ if (!getApiProvider(api)) {
126
+ registerApiProvider({ api, stream: streams.stream, streamSimple: streams.streamSimple });
127
+ }
128
+ builtinApiProviderInstances.set(api, getApiProvider(api));
67
129
  }
68
130
  }
69
131
  export function resetApiProviders() {
70
132
  clearApiProviders();
133
+ builtinApiProviderInstances.clear();
71
134
  registerBuiltInApiProviders();
72
135
  }
73
136
  registerBuiltInApiProviders();
137
+ const compatModels = builtinModels();
74
138
  function hasExplicitApiKey(apiKey) {
75
139
  return typeof apiKey === "string" && apiKey.trim().length > 0;
76
140
  }
77
141
  function withEnvApiKey(model, options) {
78
142
  if (hasExplicitApiKey(options?.apiKey))
79
143
  return options;
80
- const apiKey = getEnvApiKey(model.provider);
144
+ const apiKey = getEnvApiKey(model.provider, options?.env);
81
145
  if (!apiKey)
82
146
  return options;
83
147
  return { ...options, apiKey };
84
148
  }
149
+ function shouldUseBuiltinModels(model) {
150
+ const builtin = compatModels.getModel(model.provider, model.id);
151
+ return builtin?.api === model.api && getApiProvider(model.api) === builtinApiProviderInstances.get(model.api);
152
+ }
85
153
  function resolveApiProvider(api) {
86
154
  const provider = getApiProvider(api);
87
155
  if (!provider) {
@@ -90,6 +158,9 @@ function resolveApiProvider(api) {
90
158
  return provider;
91
159
  }
92
160
  export function stream(model, context, options) {
161
+ if (shouldUseBuiltinModels(model)) {
162
+ return compatModels.stream(model, context, options);
163
+ }
93
164
  const provider = resolveApiProvider(model.api);
94
165
  return provider.stream(model, context, withEnvApiKey(model, options));
95
166
  }
@@ -98,6 +169,9 @@ export async function complete(model, context, options) {
98
169
  return s.result();
99
170
  }
100
171
  export function streamSimple(model, context, options) {
172
+ if (shouldUseBuiltinModels(model)) {
173
+ return compatModels.streamSimple(model, context, options);
174
+ }
101
175
  const provider = resolveApiProvider(model.api);
102
176
  return provider.streamSimple(model, context, withEnvApiKey(model, options));
103
177
  }
@@ -212,6 +212,51 @@ export const IMAGE_MODELS = {
212
212
  "cacheWrite": 0
213
213
  }
214
214
  },
215
+ "openai/gpt-image-1": {
216
+ id: "openai/gpt-image-1",
217
+ name: "OpenAI: GPT Image 1",
218
+ api: "openrouter-images",
219
+ provider: "openrouter",
220
+ baseUrl: "https://openrouter.ai/api/v1",
221
+ input: ["text", "image"],
222
+ output: ["image"],
223
+ cost: {
224
+ "input": 10,
225
+ "output": 10,
226
+ "cacheRead": 1.25,
227
+ "cacheWrite": 0
228
+ }
229
+ },
230
+ "openai/gpt-image-1-mini": {
231
+ id: "openai/gpt-image-1-mini",
232
+ name: "OpenAI: GPT Image 1 Mini",
233
+ api: "openrouter-images",
234
+ provider: "openrouter",
235
+ baseUrl: "https://openrouter.ai/api/v1",
236
+ input: ["text", "image"],
237
+ output: ["image"],
238
+ cost: {
239
+ "input": 2.5,
240
+ "output": 2.5,
241
+ "cacheRead": 0.25,
242
+ "cacheWrite": 0
243
+ }
244
+ },
245
+ "openai/gpt-image-2": {
246
+ id: "openai/gpt-image-2",
247
+ name: "OpenAI: GPT Image 2",
248
+ api: "openrouter-images",
249
+ provider: "openrouter",
250
+ baseUrl: "https://openrouter.ai/api/v1",
251
+ input: ["text", "image"],
252
+ output: ["image"],
253
+ cost: {
254
+ "input": 8,
255
+ "output": 8,
256
+ "cacheRead": 2,
257
+ "cacheWrite": 0
258
+ }
259
+ },
215
260
  "openrouter/auto": {
216
261
  id: "openrouter/auto",
217
262
  name: "Auto Router",
@@ -81,16 +81,20 @@ class ImagesModelsImpl {
81
81
  if (!provider) {
82
82
  throw new ModelsError("provider", `Unknown provider: ${model.provider}`);
83
83
  }
84
- const resolution = await this.getAuth(model);
84
+ const resolution = await resolveProviderAuth(provider, model, this.credentials, this.authContext, {
85
+ apiKey: options?.apiKey,
86
+ env: options?.env,
87
+ });
85
88
  const auth = resolution?.auth;
86
89
  if (!auth) {
87
90
  return provider.generateImages(model, context, options);
88
91
  }
89
92
  const requestModel = auth.baseUrl ? { ...model, baseUrl: auth.baseUrl } : model;
90
- // Explicit request options win per-field; headers merge per header.
93
+ // Explicit request options win per-field; headers/env merge per key.
91
94
  const apiKey = options?.apiKey ?? auth.apiKey;
92
95
  const headers = auth.headers || options?.headers ? { ...auth.headers, ...options?.headers } : undefined;
93
- return await provider.generateImages(requestModel, context, { ...options, apiKey, headers });
96
+ const env = resolution.env || options?.env ? { ...(resolution.env ?? {}), ...(options?.env ?? {}) } : undefined;
97
+ return await provider.generateImages(requestModel, context, { ...options, apiKey, headers, env });
94
98
  }
95
99
  catch (error) {
96
100
  return {
@@ -13,6 +13,7 @@ export * from "./utils/diagnostics.js";
13
13
  export * from "./utils/event-stream.js";
14
14
  export * from "./utils/json-parse.js";
15
15
  export * from "./utils/overflow.js";
16
+ export * from "./utils/retry.js";
16
17
  export * from "./utils/typebox-helpers.js";
17
18
  export * from "./utils/validation.js";
18
19
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,49 @@
1
+ import { anthropicMessagesApi } from "./api/anthropic-messages.lazy.js";
2
+ import { azureOpenAIResponsesApi } from "./api/azure-openai-responses.lazy.js";
3
+ import { googleGenerativeAIApi } from "./api/google-generative-ai.lazy.js";
4
+ import { googleVertexApi } from "./api/google-vertex.lazy.js";
5
+ import { mistralConversationsApi } from "./api/mistral-conversations.lazy.js";
6
+ import { openAICodexResponsesApi } from "./api/openai-codex-responses.lazy.js";
7
+ import { openAICompletionsApi } from "./api/openai-completions.lazy.js";
8
+ import { openAIResponsesApi } from "./api/openai-responses.lazy.js";
9
+ const anthropicMessagesStreams = anthropicMessagesApi();
10
+ const azureOpenAIResponsesStreams = azureOpenAIResponsesApi();
11
+ const googleGenerativeAIStreams = googleGenerativeAIApi();
12
+ const googleVertexStreams = googleVertexApi();
13
+ const mistralConversationsStreams = mistralConversationsApi();
14
+ const openAICodexResponsesStreams = openAICodexResponsesApi();
15
+ const openAICompletionsStreams = openAICompletionsApi();
16
+ const openAIResponsesStreams = openAIResponsesApi();
17
+ /** @deprecated Use `stream` from `@earendil-works/pi-ai/api/anthropic-messages` or `anthropicMessagesApi().stream`. */
18
+ export const streamAnthropic = anthropicMessagesStreams.stream;
19
+ /** @deprecated Use `streamSimple` from `@earendil-works/pi-ai/api/anthropic-messages` or `anthropicMessagesApi().streamSimple`. */
20
+ export const streamSimpleAnthropic = anthropicMessagesStreams.streamSimple;
21
+ /** @deprecated Use `stream` from `@earendil-works/pi-ai/api/azure-openai-responses` or `azureOpenAIResponsesApi().stream`. */
22
+ export const streamAzureOpenAIResponses = azureOpenAIResponsesStreams.stream;
23
+ /** @deprecated Use `streamSimple` from `@earendil-works/pi-ai/api/azure-openai-responses` or `azureOpenAIResponsesApi().streamSimple`. */
24
+ export const streamSimpleAzureOpenAIResponses = azureOpenAIResponsesStreams.streamSimple;
25
+ /** @deprecated Use `stream` from `@earendil-works/pi-ai/api/google-generative-ai` or `googleGenerativeAIApi().stream`. */
26
+ export const streamGoogle = googleGenerativeAIStreams.stream;
27
+ /** @deprecated Use `streamSimple` from `@earendil-works/pi-ai/api/google-generative-ai` or `googleGenerativeAIApi().streamSimple`. */
28
+ export const streamSimpleGoogle = googleGenerativeAIStreams.streamSimple;
29
+ /** @deprecated Use `stream` from `@earendil-works/pi-ai/api/google-vertex` or `googleVertexApi().stream`. */
30
+ export const streamGoogleVertex = googleVertexStreams.stream;
31
+ /** @deprecated Use `streamSimple` from `@earendil-works/pi-ai/api/google-vertex` or `googleVertexApi().streamSimple`. */
32
+ export const streamSimpleGoogleVertex = googleVertexStreams.streamSimple;
33
+ /** @deprecated Use `stream` from `@earendil-works/pi-ai/api/mistral-conversations` or `mistralConversationsApi().stream`. */
34
+ export const streamMistral = mistralConversationsStreams.stream;
35
+ /** @deprecated Use `streamSimple` from `@earendil-works/pi-ai/api/mistral-conversations` or `mistralConversationsApi().streamSimple`. */
36
+ export const streamSimpleMistral = mistralConversationsStreams.streamSimple;
37
+ /** @deprecated Use `stream` from `@earendil-works/pi-ai/api/openai-codex-responses` or `openAICodexResponsesApi().stream`. */
38
+ export const streamOpenAICodexResponses = openAICodexResponsesStreams.stream;
39
+ /** @deprecated Use `streamSimple` from `@earendil-works/pi-ai/api/openai-codex-responses` or `openAICodexResponsesApi().streamSimple`. */
40
+ export const streamSimpleOpenAICodexResponses = openAICodexResponsesStreams.streamSimple;
41
+ /** @deprecated Use `stream` from `@earendil-works/pi-ai/api/openai-completions` or `openAICompletionsApi().stream`. */
42
+ export const streamOpenAICompletions = openAICompletionsStreams.stream;
43
+ /** @deprecated Use `streamSimple` from `@earendil-works/pi-ai/api/openai-completions` or `openAICompletionsApi().streamSimple`. */
44
+ export const streamSimpleOpenAICompletions = openAICompletionsStreams.streamSimple;
45
+ /** @deprecated Use `stream` from `@earendil-works/pi-ai/api/openai-responses` or `openAIResponsesApi().stream`. */
46
+ export const streamOpenAIResponses = openAIResponsesStreams.stream;
47
+ /** @deprecated Use `streamSimple` from `@earendil-works/pi-ai/api/openai-responses` or `openAIResponsesApi().streamSimple`. */
48
+ export const streamSimpleOpenAIResponses = openAIResponsesStreams.streamSimple;
49
+ //# sourceMappingURL=legacy-api-aliases.js.map
@@ -85,15 +85,19 @@ class ModelsImpl {
85
85
  return provider;
86
86
  }
87
87
  async applyAuth(model, options) {
88
- const resolution = await this.getAuth(model);
88
+ const resolution = await resolveProviderAuth(this.requireProvider(model), model, this.credentials, this.authContext, {
89
+ apiKey: options?.apiKey,
90
+ env: options?.env,
91
+ });
89
92
  const auth = resolution?.auth;
90
93
  if (!auth)
91
94
  return { requestModel: model, requestOptions: options };
92
95
  const requestModel = auth.baseUrl ? { ...model, baseUrl: auth.baseUrl } : model;
93
- // Explicit request options win per-field; headers merge per header.
96
+ // Explicit request options win per-field; headers/env merge per key.
94
97
  const apiKey = options?.apiKey ?? auth.apiKey;
95
98
  const headers = auth.headers || options?.headers ? { ...auth.headers, ...options?.headers } : undefined;
96
- const requestOptions = { ...options, apiKey, headers };
99
+ const env = resolution.env || options?.env ? { ...(resolution.env ?? {}), ...(options?.env ?? {}) } : undefined;
100
+ const requestOptions = { ...options, apiKey, headers, env };
97
101
  return { requestModel, requestOptions };
98
102
  }
99
103
  stream(model, context, options) {
@@ -373,10 +373,10 @@ export const AMAZON_BEDROCK_MODELS = {
373
373
  reasoning: true,
374
374
  input: ["text", "image"],
375
375
  cost: {
376
- input: 1,
377
- output: 5,
378
- cacheRead: 0.1,
379
- cacheWrite: 1.25,
376
+ input: 1.1,
377
+ output: 5.5,
378
+ cacheRead: 0.11,
379
+ cacheWrite: 1.375,
380
380
  },
381
381
  contextWindow: 200000,
382
382
  maxTokens: 64000,
@@ -390,10 +390,10 @@ export const AMAZON_BEDROCK_MODELS = {
390
390
  reasoning: true,
391
391
  input: ["text", "image"],
392
392
  cost: {
393
- input: 5,
394
- output: 25,
395
- cacheRead: 0.5,
396
- cacheWrite: 6.25,
393
+ input: 5.5,
394
+ output: 27.5,
395
+ cacheRead: 0.55,
396
+ cacheWrite: 6.875,
397
397
  },
398
398
  contextWindow: 200000,
399
399
  maxTokens: 64000,
@@ -1620,6 +1620,23 @@ export const AMAZON_BEDROCK_MODELS = {
1620
1620
  contextWindow: 1040000,
1621
1621
  maxTokens: 8192,
1622
1622
  },
1623
+ "xai.grok-4.3": {
1624
+ id: "xai.grok-4.3",
1625
+ name: "Grok 4.3",
1626
+ api: "bedrock-converse-stream",
1627
+ provider: "amazon-bedrock",
1628
+ baseUrl: "https://bedrock-runtime.us-east-1.amazonaws.com",
1629
+ reasoning: true,
1630
+ input: ["text", "image"],
1631
+ cost: {
1632
+ input: 1.25,
1633
+ output: 2.5,
1634
+ cacheRead: 0.2,
1635
+ cacheWrite: 0,
1636
+ },
1637
+ contextWindow: 1000000,
1638
+ maxTokens: 131072,
1639
+ },
1623
1640
  "zai.glm-4.7": {
1624
1641
  id: "zai.glm-4.7",
1625
1642
  name: "GLM-4.7",
@@ -7,7 +7,7 @@ export const ANT_LING_MODELS = {
7
7
  api: "openai-completions",
8
8
  provider: "ant-ling",
9
9
  baseUrl: "https://api.ant-ling.com/v1",
10
- compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false, "maxTokensField": "max_tokens", "supportsLongCacheRetention": false },
10
+ compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false, "maxTokensField": "max_tokens", "thinkingFormat": "ant-ling", "supportsLongCacheRetention": false },
11
11
  reasoning: false,
12
12
  input: ["text"],
13
13
  cost: {
@@ -25,7 +25,7 @@ export const ANT_LING_MODELS = {
25
25
  api: "openai-completions",
26
26
  provider: "ant-ling",
27
27
  baseUrl: "https://api.ant-ling.com/v1",
28
- compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false, "maxTokensField": "max_tokens", "supportsLongCacheRetention": false },
28
+ compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false, "maxTokensField": "max_tokens", "thinkingFormat": "ant-ling", "supportsLongCacheRetention": false },
29
29
  reasoning: false,
30
30
  input: ["text"],
31
31
  cost: {
@@ -43,7 +43,7 @@ export const ANT_LING_MODELS = {
43
43
  api: "openai-completions",
44
44
  provider: "ant-ling",
45
45
  baseUrl: "https://api.ant-ling.com/v1",
46
- compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false, "maxTokensField": "max_tokens", "supportsLongCacheRetention": false, "thinkingFormat": "ant-ling" },
46
+ compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false, "maxTokensField": "max_tokens", "thinkingFormat": "ant-ling", "supportsLongCacheRetention": false },
47
47
  reasoning: true,
48
48
  thinkingLevelMap: { "off": null, "minimal": null, "low": null, "medium": null, "high": "high", "xhigh": "xhigh" },
49
49
  input: ["text"],
@@ -1,40 +1,6 @@
1
1
  // This file is auto-generated by scripts/generate-models.ts
2
2
  // Do not edit manually - run 'npm run generate-models' to update
3
3
  export const ANTHROPIC_MODELS = {
4
- "claude-3-5-haiku-20241022": {
5
- id: "claude-3-5-haiku-20241022",
6
- name: "Claude Haiku 3.5",
7
- api: "anthropic-messages",
8
- provider: "anthropic",
9
- baseUrl: "https://api.anthropic.com",
10
- reasoning: false,
11
- input: ["text", "image"],
12
- cost: {
13
- input: 0.8,
14
- output: 4,
15
- cacheRead: 0.08,
16
- cacheWrite: 1,
17
- },
18
- contextWindow: 200000,
19
- maxTokens: 8192,
20
- },
21
- "claude-3-5-haiku-latest": {
22
- id: "claude-3-5-haiku-latest",
23
- name: "Claude Haiku 3.5 (latest)",
24
- api: "anthropic-messages",
25
- provider: "anthropic",
26
- baseUrl: "https://api.anthropic.com",
27
- reasoning: false,
28
- input: ["text", "image"],
29
- cost: {
30
- input: 0.8,
31
- output: 4,
32
- cacheRead: 0.08,
33
- cacheWrite: 1,
34
- },
35
- contextWindow: 200000,
36
- maxTokens: 8192,
37
- },
38
4
  "claude-3-5-sonnet-20240620": {
39
5
  id: "claude-3-5-sonnet-20240620",
40
6
  name: "Claude Sonnet 3.5",
@@ -7,6 +7,7 @@ export const CEREBRAS_MODELS = {
7
7
  api: "openai-completions",
8
8
  provider: "cerebras",
9
9
  baseUrl: "https://api.cerebras.ai/v1",
10
+ compat: { "supportsStore": false, "supportsDeveloperRole": false },
10
11
  reasoning: true,
11
12
  input: ["text"],
12
13
  cost: {
@@ -24,6 +25,7 @@ export const CEREBRAS_MODELS = {
24
25
  api: "openai-completions",
25
26
  provider: "cerebras",
26
27
  baseUrl: "https://api.cerebras.ai/v1",
28
+ compat: { "supportsStore": false, "supportsDeveloperRole": false },
27
29
  reasoning: true,
28
30
  input: ["text"],
29
31
  cost: {