@agent-native/core 0.202.0 → 0.202.1-nightly-20261007011022

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 (75) hide show
  1. package/dist/agent/actions/list-model-providers.d.ts +2 -0
  2. package/dist/agent/actions/list-model-providers.js +32 -5
  3. package/dist/agent/actions/manage-provider-models.d.ts +1 -0
  4. package/dist/agent/engine/builder-engine.d.ts +1 -1
  5. package/dist/agent/engine/builtin.js +1 -1
  6. package/dist/agent/engine/index.d.ts +1 -1
  7. package/dist/agent/engine/index.js +1 -1
  8. package/dist/agent/engine/registry.d.ts +2 -0
  9. package/dist/agent/engine/registry.js +63 -47
  10. package/dist/agent/model-config.d.ts +6 -6
  11. package/dist/agent/model-config.js +7 -11
  12. package/dist/agent/model-version.d.ts +3 -0
  13. package/dist/agent/model-version.js +67 -0
  14. package/dist/agent/provider-model-selection.d.ts +6 -1
  15. package/dist/agent/provider-model-selection.js +131 -13
  16. package/dist/automation/index.d.ts +1 -1
  17. package/dist/cli/create.d.ts +2 -1
  18. package/dist/cli/create.js +17 -4
  19. package/dist/cli/template-sync.js +2 -1
  20. package/dist/cli/workspacify.d.ts +1 -0
  21. package/dist/cli/workspacify.js +4 -0
  22. package/dist/client/app-config.d.ts +1 -0
  23. package/dist/client/app-config.js +19 -0
  24. package/dist/client/chat/agentkit-protocol.js +122 -16
  25. package/dist/client/chat/runtime.js +943 -50
  26. package/dist/client/chat-model-groups.d.ts +1 -0
  27. package/dist/client/chat-model-groups.js +1 -0
  28. package/dist/client/use-chat-models.js +47 -6
  29. package/dist/deploy/build.js +23 -9
  30. package/dist/email-catalog/redact-body.js +1 -1
  31. package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
  32. package/dist/jobs/background-automation-runner.d.ts +3 -1
  33. package/dist/jobs/background-automation-runner.js +15 -8
  34. package/dist/jobs/scheduler.js +1 -1
  35. package/dist/mcp/build-server.d.ts +0 -1
  36. package/dist/mcp/build-server.js +10 -59
  37. package/dist/observability/routes.d.ts +6 -6
  38. package/dist/onboarding/app-profile-data.d.ts +10 -0
  39. package/dist/onboarding/app-profile-data.js +423 -0
  40. package/dist/onboarding/app-profile.d.ts +1 -7
  41. package/dist/onboarding/app-profile.js +5 -422
  42. package/dist/onboarding/index.d.ts +1 -0
  43. package/dist/onboarding/index.js +1 -0
  44. package/dist/org/handlers.d.ts +1 -1
  45. package/dist/org/index.d.ts +1 -1
  46. package/dist/progress/routes.d.ts +1 -1
  47. package/dist/provider-api/actions/custom-provider-registration.d.ts +2 -2
  48. package/dist/provider-api/actions/provider-api.d.ts +10 -10
  49. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  50. package/dist/scripts/agent-engines/manage-agent-engine.js +1 -1
  51. package/dist/scripts/agent-engines/set-agent-engine.js +1 -1
  52. package/dist/secrets/routes.d.ts +11 -11
  53. package/dist/server/action-routes.js +1 -0
  54. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  55. package/dist/server/app-origin-config.d.ts +2 -0
  56. package/dist/server/app-origin-config.js +4 -0
  57. package/dist/server/auth.js +43 -1
  58. package/dist/server/better-auth-instance.js +7 -12
  59. package/dist/server/email-auth-links.d.ts +11 -0
  60. package/dist/server/email-auth-links.js +126 -0
  61. package/dist/server/fusion-app.js +11 -1
  62. package/dist/shared/agent-chat-context.d.ts +3 -3
  63. package/dist/shared/agent-chat-context.js +2 -2
  64. package/dist/shared/auth-copy.d.ts +3 -0
  65. package/dist/shared/auth-copy.js +33 -0
  66. package/dist/shared/design-system-agent-context.d.ts +5 -0
  67. package/dist/shared/design-system-agent-context.js +37 -8
  68. package/dist/shared/index.d.ts +1 -1
  69. package/dist/triggers/actions/manage-automation.d.ts +7 -7
  70. package/dist/triggers/condition-evaluator.d.ts +21 -5
  71. package/dist/triggers/condition-evaluator.js +113 -59
  72. package/dist/triggers/dispatcher.js +39 -21
  73. package/dist/vite/client.js +10 -0
  74. package/docs/content/durable-resume.mdx +9 -3
  75. package/package.json +5 -3
@@ -29,6 +29,8 @@ export interface ModelProviderKey {
29
29
  export interface ModelProviderEntry {
30
30
  provider: AgentProviderId;
31
31
  label: string;
32
+ /** A usable deployment fallback when no saved key at the default-model scope takes precedence. */
33
+ deploymentConfigured: boolean;
32
34
  /** The organization's key, or null when it has none. */
33
35
  org: ModelProviderKey | null;
34
36
  /** The caller's own key, or null when they have none. */
@@ -7,6 +7,8 @@ import { readAppSecrets, } from "../../secrets/storage.js";
7
7
  import { readProviderCredentialRejections } from "../../server/credential-provider.js";
8
8
  import { isPersonalProviderKeyUseRestricted, readOrgMemberRole, } from "../../server/personal-provider-key-policy.js";
9
9
  import { readDefaultAgentEngineSettingDetailed, resolveDefaultAgentEngineAuthority, } from "../default-agent-engine.js";
10
+ import { registerBuiltinEngines } from "../engine/builtin.js";
11
+ import { getAgentEngineEntry, isDeploymentEngineUsableForRequest, } from "../engine/registry.js";
10
12
  function providerKeyNames() {
11
13
  return AGENT_PROVIDER_CATALOG.map((option) => {
12
14
  if (option.id === "ollama") {
@@ -49,8 +51,12 @@ async function readScopeKeys(scope, scopeId, reveal) {
49
51
  const sources = storedRowsFor(scope, scopeId);
50
52
  const reads = await Promise.all(sources.map((source) => readAppSecrets({ keys, ...source })));
51
53
  const found = [];
54
+ const endpointProviders = new Set();
52
55
  for (const entry of names) {
53
56
  for (const [index, rows] of reads.entries()) {
57
+ if (entry.endpoint && firstPresent(rows, [entry.endpoint])) {
58
+ endpointProviders.add(entry.option.id);
59
+ }
54
60
  const row = firstPresent(rows, entry.primary);
55
61
  if (!row)
56
62
  continue;
@@ -98,7 +104,7 @@ async function readScopeKeys(scope, scopeId, reveal) {
98
104
  ...(legacy ? { legacyWorkspaceRow: true } : {}),
99
105
  });
100
106
  }
101
- return result;
107
+ return { keys: result, endpointProviders };
102
108
  }
103
109
  export default defineAction({
104
110
  description: "List the model providers on Settings › Model: for each provider (OpenRouter, Ollama, Anthropic, OpenAI, Google Gemini, Groq, Mistral, Cohere), the organization's key and the caller's personal key, masked, with the endpoint URL saved for Ollama or an OpenAI-compatible gateway and when the provider rejected the key (rejectedAt: chats that use it stop until someone replaces it). Members see only that an organization key exists, not its mask. A key in a legacy workspace row (the organization's, or the caller's pre-organization solo row) lists at the scope chats resolve it as, marked legacyWorkspaceRow; removing the provider at that scope deletes it. Also returns whether the caller can manage organization providers, whether personal API keys are restricted for them (their personal keys are then stored but unused), and the stored default model. Builder.io connections come from the Builder.io status, not here. Never returns a key value. Related: get-provider-models and manage-provider-models (which models each provider shows), manage-agent-engine (the default model), manage-provider-key-policy (Restrict personal API keys), preview-secret-removal (what removing a key stops).",
@@ -112,17 +118,37 @@ export default defineAction({
112
118
  const orgId = ctx?.orgId?.trim() || null;
113
119
  const role = orgId ? await readOrgMemberRole(orgId, email) : null;
114
120
  const manages = orgId ? canManageOrg(role) : false;
115
- const [personal, org, restricted, defaultRead, authority] = await Promise.all([
121
+ registerBuiltinEngines();
122
+ const [personal, org, restricted, defaultRead, authority, deploymentStates,] = await Promise.all([
116
123
  readScopeKeys("user", email, true),
117
124
  orgId
118
125
  ? readScopeKeys("org", orgId, manages)
119
- : Promise.resolve(new Map()),
126
+ : Promise.resolve({
127
+ keys: new Map(),
128
+ endpointProviders: new Set(),
129
+ }),
120
130
  orgId
121
131
  ? isPersonalProviderKeyUseRestricted({ email, orgId, role })
122
132
  : Promise.resolve(false),
123
133
  readDefaultAgentEngineSettingDetailed({ userEmail: email, orgId }),
124
134
  resolveDefaultAgentEngineAuthority({ userEmail: email, orgId }),
135
+ Promise.all(AGENT_PROVIDER_CATALOG.map(async (option) => {
136
+ const engine = getAgentEngineEntry(option.engine);
137
+ return [
138
+ option.id,
139
+ engine ? await isDeploymentEngineUsableForRequest(engine) : false,
140
+ ];
141
+ })),
125
142
  ]);
143
+ const deploymentConfigured = new Map(deploymentStates.map(([provider, configured]) => {
144
+ const defaultScope = orgId ? org : personal;
145
+ return [
146
+ provider,
147
+ configured &&
148
+ !defaultScope.keys.has(provider) &&
149
+ !defaultScope.endpointProviders.has(provider),
150
+ ];
151
+ }));
126
152
  const stored = defaultRead.value;
127
153
  const engine = typeof stored?.engine === "string" && stored.engine.trim()
128
154
  ? stored.engine.trim()
@@ -134,8 +160,9 @@ export default defineAction({
134
160
  providers: AGENT_PROVIDER_CATALOG.map((option) => ({
135
161
  provider: option.id,
136
162
  label: option.label,
137
- org: org.get(option.id) ?? null,
138
- personal: personal.get(option.id) ?? null,
163
+ deploymentConfigured: deploymentConfigured.get(option.id) ?? false,
164
+ org: org.keys.get(option.id) ?? null,
165
+ personal: personal.keys.get(option.id) ?? null,
139
166
  })),
140
167
  hasOrganization: !!orgId,
141
168
  canManageOrg: manages,
@@ -8,6 +8,7 @@ declare const _default: import("../../action.js").ActionDefinition<{
8
8
  provider: ProviderModelSelectionProvider;
9
9
  scope: ProviderModelSelectionScope;
10
10
  models: string[] | null;
11
+ preserveCustomModels?: boolean;
11
12
  updatedAt?: number;
12
13
  updatedBy?: string;
13
14
  recommendedModels: string[];
@@ -1,7 +1,7 @@
1
1
  import { type BuilderGatewayLane } from "../../server/credential-provider.js";
2
2
  import type { AgentEngine, EngineCapabilities } from "./types.js";
3
3
  export declare const BUILDER_CAPABILITIES: EngineCapabilities;
4
- export declare const BUILDER_SUPPORTED_MODELS: readonly ["auto", "claude-haiku-4-5", "claude-sonnet-5", "claude-opus-5-5", "gpt-5-4", "gpt-5-5", "gpt-5-4-mini", "gpt-5-1-codex-mini", "gpt-6-sol", "gpt-5-6-terra", "gpt-6-luna", "gemini-3-1-pro", "gemini-3-8-flash", "gemini-3-5-flash-lite", "gemini-3-1-flash-lite", "grok-code-fast", "qwen3-coder", "kimi-k2-5", "deepseek-v4-pro", "deepseek-v3-1", "z-ai-glm-4-5", "z-ai-glm-5-1"];
4
+ export declare const BUILDER_SUPPORTED_MODELS: readonly ["auto", "claude-haiku-4-5", "claude-sonnet-5", "claude-opus-5-5", "gpt-5-4", "gpt-5-5", "gpt-5-4-mini", "gpt-5-1-codex-mini", "gpt-6.1-sol", "gpt-5-6-terra", "gpt-6-luna", "gemini-3-1-pro", "gemini-3-8-flash", "gemini-3-5-flash-lite", "gemini-3-1-flash-lite", "grok-code-fast", "qwen3-coder", "kimi-k2-5", "deepseek-v4-pro", "deepseek-v3-1", "z-ai-glm-4-5", "z-ai-glm-5-1"];
5
5
  export declare const BUILDER_MODEL_UNAUTHORIZED_ERROR_CODE = "builder_model_unauthorized";
6
6
  /**
7
7
  * A truncated stream, not a rejected request: the client continues the partial
@@ -30,7 +30,7 @@ const providerLabels = {
30
30
  const providerDescriptions = {
31
31
  anthropic: "Claude models through the Vercel AI SDK. Supports thinking and caching via AI SDK providerOptions.",
32
32
  openai: "OpenAI GPT models via the Vercel AI SDK. Requires OPENAI_API_KEY.",
33
- openrouter: "300+ models from Anthropic, OpenAI, Google, Z.ai, and more routed through a single endpoint. Use model IDs like 'anthropic/claude-sonnet-5.5', 'openai/gpt-6-sol', or 'z-ai/glm-5.2'. Requires OPENROUTER_API_KEY.",
33
+ openrouter: "300+ models from Anthropic, OpenAI, Google, Z.ai, and more routed through a single endpoint. Use model IDs like 'anthropic/claude-sonnet-5.5', 'openai/gpt-6.1-sol', or 'z-ai/glm-5.2'. Requires OPENROUTER_API_KEY.",
34
34
  google: "Google Gemini models via the Vercel AI SDK. Requires GOOGLE_GENERATIVE_AI_API_KEY.",
35
35
  groq: "Groq LPU inference via the Vercel AI SDK. Requires GROQ_API_KEY.",
36
36
  mistral: "Mistral models via the Vercel AI SDK. Requires MISTRAL_API_KEY.",
@@ -1,5 +1,5 @@
1
1
  export type { AgentEngine, EngineCapabilities, EngineTool, EngineMessage, EngineContentPart, EngineTextPart, EngineImagePart, EngineToolCallPart, EngineToolResultPart, EngineThinkingPart, EngineEvent, EngineStreamOptions, } from "./types.js";
2
- export { registerAgentEngine, getAgentEngineEntry, listAgentEngines, resolveEngine, explicitEngineName, getConfiguredEngineNameForRequest, getStoredModelForEngine, normalizeModelForEngine, resolveDelegatedRunModel, resolveEngineAcceptsCustomModels, resolveEnginePreservesCustomModels, type NormalizeModelOptions, detectEngineFromEnv, detectEngineFromEnvForRequest, detectEngineFromUserSecrets, isAgentEngineSettingConfigured, isAgentEnginePackageInstalled, isStoredEngineUsable, isStoredEngineUsableForRequest, isResolvedEngineUsableForRequest, type AgentEngineEntry, type ResolveEngineConfig, } from "./registry.js";
2
+ export { registerAgentEngine, getAgentEngineEntry, listAgentEngines, resolveEngine, explicitEngineName, getConfiguredEngineNameForRequest, getStoredModelForEngine, normalizeModelForEngine, resolveDelegatedRunModel, resolveEngineAcceptsCustomModels, resolveEnginePreservesCustomModels, type NormalizeModelOptions, detectEngineFromEnv, detectEngineFromEnvForRequest, detectEngineFromUserSecrets, isAgentEngineSettingConfigured, isAgentEnginePackageInstalled, isDeploymentEngineUsableForRequest, isStoredEngineUsable, isStoredEngineUsableForRequest, isResolvedEngineUsableForRequest, type AgentEngineEntry, type ResolveEngineConfig, } from "./registry.js";
3
3
  export { readDefaultAgentEngineSetting, readDefaultAgentEngineSettingDetailed, type DefaultAgentEngineRead, type DefaultAgentEngineSource, } from "../default-agent-engine.js";
4
4
  export { createBuilderEngine, BUILDER_DEFAULT_MODEL, BUILDER_SUPPORTED_MODELS, BUILDER_CAPABILITIES, } from "./builder-engine.js";
5
5
  export { createAnthropicEngine, ANTHROPIC_DEFAULT_MODEL, ANTHROPIC_SUPPORTED_MODELS, ANTHROPIC_CAPABILITIES, } from "./anthropic-engine.js";
@@ -1,4 +1,4 @@
1
- export { registerAgentEngine, getAgentEngineEntry, listAgentEngines, resolveEngine, explicitEngineName, getConfiguredEngineNameForRequest, getStoredModelForEngine, normalizeModelForEngine, resolveDelegatedRunModel, resolveEngineAcceptsCustomModels, resolveEnginePreservesCustomModels, detectEngineFromEnv, detectEngineFromEnvForRequest, detectEngineFromUserSecrets, isAgentEngineSettingConfigured, isAgentEnginePackageInstalled, isStoredEngineUsable, isStoredEngineUsableForRequest, isResolvedEngineUsableForRequest, } from "./registry.js";
1
+ export { registerAgentEngine, getAgentEngineEntry, listAgentEngines, resolveEngine, explicitEngineName, getConfiguredEngineNameForRequest, getStoredModelForEngine, normalizeModelForEngine, resolveDelegatedRunModel, resolveEngineAcceptsCustomModels, resolveEnginePreservesCustomModels, detectEngineFromEnv, detectEngineFromEnvForRequest, detectEngineFromUserSecrets, isAgentEngineSettingConfigured, isAgentEnginePackageInstalled, isDeploymentEngineUsableForRequest, isStoredEngineUsable, isStoredEngineUsableForRequest, isResolvedEngineUsableForRequest, } from "./registry.js";
2
2
  export { readDefaultAgentEngineSetting, readDefaultAgentEngineSettingDetailed, } from "../default-agent-engine.js";
3
3
  export { createBuilderEngine, BUILDER_DEFAULT_MODEL, BUILDER_SUPPORTED_MODELS, BUILDER_CAPABILITIES, } from "./builder-engine.js";
4
4
  export { createAnthropicEngine, ANTHROPIC_DEFAULT_MODEL, ANTHROPIC_SUPPORTED_MODELS, ANTHROPIC_CAPABILITIES, } from "./anthropic-engine.js";
@@ -42,6 +42,8 @@ export declare function resolveDelegatedRunModel(engine: ModelResolvableEngine,
42
42
  export declare function resolveEnginePreservesCustomModels(entry: Pick<AgentEngineEntry, "name">): Promise<boolean>;
43
43
  export declare function resolveEngineAcceptsCustomModels(entry: Pick<AgentEngineEntry, "acceptsCustomModels">): Promise<boolean>;
44
44
  export declare function detectEngineFromEnv(): AgentEngineEntry | null;
45
+ /** Whether a deployment credential can serve this engine for the current request. */
46
+ export declare function isDeploymentEngineUsableForRequest(entry: AgentEngineEntry): Promise<boolean>;
45
47
  export declare function detectEngineFromEnvForRequest(): Promise<AgentEngineEntry | null>;
46
48
  /**
47
49
  * Detect a usable engine from the current request user's accessible
@@ -11,6 +11,7 @@ import { getAgentAppModelDefaultForCurrentRequest } from "../app-model-defaults.
11
11
  import { CHATGPT_SUBSCRIPTION_ENGINE_NAME } from "../chatgpt-subscription-contract.js";
12
12
  import { readDefaultAgentEngineSetting } from "../default-agent-engine.js";
13
13
  import { BUILDER_CLAUDE_SONNET_MODEL_ID, CLAUDE_SONNET_MODEL_ID, } from "../model-config.js";
14
+ import { findLatestSupportedVersionMatch, isNewerVersionedModel, upgradeModelToLatestSupportedVersion, } from "../model-version.js";
14
15
  import { createProviderEndpointFetch } from "./ai-sdk-engine.js";
15
16
  import { AI_SDK_ANTHROPIC_DEFAULT_BASE_URL, OLLAMA_DEFAULT_BASE_URL, OLLAMA_BASE_URL_ENV_VAR, OPENAI_BASE_URL_ENV_VAR, OPENAI_DEFAULT_BASE_URL, isCustomOpenAiBaseUrl, } from "./openai-compatible-endpoint.js";
16
17
  import { isLocalNetworkOllamaEndpoint, validateProviderBaseUrl, } from "./provider-endpoint-validation.js";
@@ -128,50 +129,6 @@ export function isAgentEnginePackageInstalled(entry) {
128
129
  .filter((name) => Boolean(name)) ?? [];
129
130
  return packageNames.every(canResolvePackage);
130
131
  }
131
- function parseVersionedModelId(model) {
132
- const match = /^(?<family>.+?)[-.](?<version>\d+(?:[-.]\d+)*)(?<suffix>(?:[-.][a-z][a-z0-9]*)*)$/i.exec(model.trim().toLowerCase());
133
- const groups = match?.groups;
134
- if (!groups?.family || !groups.version)
135
- return null;
136
- const version = groups.version.split(/[-.]/).map((part) => Number(part));
137
- if (version.some((part) => !Number.isSafeInteger(part)))
138
- return null;
139
- return {
140
- family: groups.family,
141
- version,
142
- suffix: groups.suffix ?? "",
143
- };
144
- }
145
- function compareModelVersions(left, right) {
146
- const length = Math.max(left.length, right.length);
147
- for (let index = 0; index < length; index += 1) {
148
- const delta = (left[index] ?? 0) - (right[index] ?? 0);
149
- if (delta !== 0)
150
- return delta;
151
- }
152
- return 0;
153
- }
154
- function findLatestSupportedVersionMatch(candidate, supportedModels) {
155
- const parsedCandidate = parseVersionedModelId(candidate);
156
- if (!parsedCandidate)
157
- return undefined;
158
- let best;
159
- for (const supportedModel of supportedModels) {
160
- const parsedSupported = parseVersionedModelId(supportedModel);
161
- if (!parsedSupported)
162
- continue;
163
- if (parsedSupported.family !== parsedCandidate.family)
164
- continue;
165
- if (parsedSupported.suffix !== parsedCandidate.suffix)
166
- continue;
167
- if (best &&
168
- compareModelVersions(parsedSupported.version, best.version) <= 0) {
169
- continue;
170
- }
171
- best = { model: supportedModel, version: parsedSupported.version };
172
- }
173
- return best?.model;
174
- }
175
132
  export function normalizeModelForEngine(engine, model, options = {}) {
176
133
  const candidate = typeof model === "string" ? model.trim() : "";
177
134
  if (!candidate)
@@ -179,9 +136,12 @@ export function normalizeModelForEngine(engine, model, options = {}) {
179
136
  if (engine.preserveCustomModels || options.preserveCustomModels) {
180
137
  return candidate;
181
138
  }
182
- if (engine.supportedModels.length === 0)
183
- return candidate;
184
- if (candidate === "auto" || engine.supportedModels.includes(candidate)) {
139
+ const upgradedModel = upgradeModelToLatestSupportedVersion(candidate, engine.supportedModels);
140
+ if (upgradedModel)
141
+ return upgradedModel;
142
+ if (candidate === "auto" ||
143
+ engine.supportedModels.includes(candidate) ||
144
+ engine.supportedModels.length === 0) {
185
145
  return candidate;
186
146
  }
187
147
  if (engine.acceptsCustomModels || options.acceptsCustomModels) {
@@ -191,6 +151,9 @@ export function normalizeModelForEngine(engine, model, options = {}) {
191
151
  : candidate;
192
152
  }
193
153
  const versionMatch = findLatestSupportedVersionMatch(candidate, engine.supportedModels);
154
+ if (versionMatch && isNewerVersionedModel(candidate, versionMatch)) {
155
+ return versionMatch;
156
+ }
194
157
  if (versionMatch)
195
158
  return versionMatch;
196
159
  return engine.defaultModel;
@@ -336,6 +299,59 @@ async function usableEnvCredentialMatch(entry) {
336
299
  }
337
300
  return null;
338
301
  }
302
+ /** Whether a deployment credential can serve this engine for the current request. */
303
+ export async function isDeploymentEngineUsableForRequest(entry) {
304
+ if (!isAgentEnginePackageInstalled(entry))
305
+ return false;
306
+ if (entry.name === "ai-sdk:ollama") {
307
+ return isDeploymentProviderEndpointUsable(entry);
308
+ }
309
+ for (const set of envCredentialSetsForEntry(entry)) {
310
+ if (!set.envVars.every((key) => canUseDeployCredentialFallbackForRequest(key) &&
311
+ !!readDeployCredentialEnv(key))) {
312
+ continue;
313
+ }
314
+ if (await isEnvCredentialSetUsable(set)) {
315
+ return isDeploymentProviderEndpointUsable(entry);
316
+ }
317
+ }
318
+ return false;
319
+ }
320
+ async function isDeploymentProviderEndpointUsable(entry) {
321
+ const endpointConfig = entry.name === "ai-sdk:ollama"
322
+ ? {
323
+ envVar: OLLAMA_BASE_URL_ENV_VAR,
324
+ provider: "Ollama",
325
+ required: true,
326
+ isOllama: true,
327
+ }
328
+ : entry.name === "ai-sdk:openai"
329
+ ? {
330
+ envVar: OPENAI_BASE_URL_ENV_VAR,
331
+ provider: "OpenAI",
332
+ required: false,
333
+ isOllama: false,
334
+ }
335
+ : undefined;
336
+ if (!endpointConfig)
337
+ return true;
338
+ const endpoint = canUseDeployCredentialFallbackForRequest(endpointConfig.envVar)
339
+ ? readDeployCredentialEnv(endpointConfig.envVar)
340
+ : undefined;
341
+ if (!endpoint)
342
+ return !endpointConfig.required;
343
+ try {
344
+ await validateProviderBaseUrl(endpoint, {
345
+ allowPrivate: true,
346
+ isOllama: endpointConfig.isOllama,
347
+ });
348
+ }
349
+ catch (error) {
350
+ console.warn(`[agent-engine] Invalid deployment ${endpointConfig.provider} endpoint; provider unavailable.`, { error: error instanceof Error ? error.message : String(error) });
351
+ return false;
352
+ }
353
+ return true;
354
+ }
339
355
  export async function detectEngineFromEnvForRequest() {
340
356
  const preferByo = getAppConfig().agent.preferBringYourOwnKey;
341
357
  const matches = [];
@@ -21,7 +21,7 @@ export declare const BUILDER_CLAUDE_SONNET_MODEL_LABEL = "Claude Sonnet 5";
21
21
  export declare const AGENT_MODEL_CONFIG: {
22
22
  readonly builder: {
23
23
  readonly defaultModel: "gpt-6-luna";
24
- readonly supportedModels: readonly ["auto", "claude-haiku-4-5", "claude-sonnet-5", "claude-opus-5-5", "gpt-5-4", "gpt-5-5", "gpt-5-4-mini", "gpt-5-1-codex-mini", "gpt-6-sol", "gpt-5-6-terra", "gpt-6-luna", "gemini-3-1-pro", "gemini-3-8-flash", "gemini-3-5-flash-lite", "gemini-3-1-flash-lite", "grok-code-fast", "qwen3-coder", "kimi-k2-5", "deepseek-v4-pro", "deepseek-v3-1", "z-ai-glm-4-5", "z-ai-glm-5-1"];
24
+ readonly supportedModels: readonly ["auto", "claude-haiku-4-5", "claude-sonnet-5", "claude-opus-5-5", "gpt-5-4", "gpt-5-5", "gpt-5-4-mini", "gpt-5-1-codex-mini", "gpt-6.1-sol", "gpt-5-6-terra", "gpt-6-luna", "gemini-3-1-pro", "gemini-3-8-flash", "gemini-3-5-flash-lite", "gemini-3-1-flash-lite", "grok-code-fast", "qwen3-coder", "kimi-k2-5", "deepseek-v4-pro", "deepseek-v3-1", "z-ai-glm-4-5", "z-ai-glm-5-1"];
25
25
  };
26
26
  readonly anthropic: {
27
27
  readonly defaultModel: "claude-sonnet-5-5";
@@ -34,11 +34,11 @@ export declare const AGENT_MODEL_CONFIG: {
34
34
  };
35
35
  readonly openai: {
36
36
  readonly defaultModel: "gpt-6-luna";
37
- readonly supportedModels: readonly ["gpt-6-luna", "gpt-5.6-luna", "gpt-5.6-terra", "gpt-5.6-sol", "gpt-6-sol"];
37
+ readonly supportedModels: readonly ["gpt-6-luna", "gpt-5.6-terra", "gpt-6.1-sol"];
38
38
  };
39
39
  readonly openrouter: {
40
40
  readonly defaultModel: string;
41
- readonly supportedModels: readonly ["openai/gpt-6-luna", "openai/gpt-5.6-luna", "openai/gpt-5.6-terra", "openai/gpt-5.6-sol", "openai/gpt-6-sol", "openai/gpt-6-astra", "openai/gpt-6-astra-pro", "anthropic/claude-sonnet-5.5", "anthropic/claude-opus-5.5", "anthropic/claude-opus-4.8", "anthropic/claude-fable-5", "anthropic/claude-fable-5.1", "google/gemini-3.8-flash", "qwen/qwen3.8-max-0902", "meta/muse-spark-1.3", "inception/mercury-2.5", "z-ai/glm-5.2"];
41
+ readonly supportedModels: readonly ["openai/gpt-6-luna", "openai/gpt-5.6-terra", "openai/gpt-6.1-sol", "openai/gpt-6-astra", "openai/gpt-6-astra-pro", "anthropic/claude-sonnet-5.5", "anthropic/claude-opus-5.5", "anthropic/claude-opus-4.8", "anthropic/claude-fable-5", "anthropic/claude-fable-5.1", "google/gemini-3.8-flash", "qwen/qwen3.8-max-0902", "meta/muse-spark-1.3", "inception/mercury-2.5", "z-ai/glm-5.2"];
42
42
  };
43
43
  readonly google: {
44
44
  readonly defaultModel: "gemini-3.8-flash";
@@ -64,7 +64,7 @@ export declare const AGENT_MODEL_CONFIG: {
64
64
  };
65
65
  export declare const BUILDER_MODEL_CONFIG: {
66
66
  readonly defaultModel: "gpt-6-luna";
67
- readonly supportedModels: readonly ["auto", "claude-haiku-4-5", "claude-sonnet-5", "claude-opus-5-5", "gpt-5-4", "gpt-5-5", "gpt-5-4-mini", "gpt-5-1-codex-mini", "gpt-6-sol", "gpt-5-6-terra", "gpt-6-luna", "gemini-3-1-pro", "gemini-3-8-flash", "gemini-3-5-flash-lite", "gemini-3-1-flash-lite", "grok-code-fast", "qwen3-coder", "kimi-k2-5", "deepseek-v4-pro", "deepseek-v3-1", "z-ai-glm-4-5", "z-ai-glm-5-1"];
67
+ readonly supportedModels: readonly ["auto", "claude-haiku-4-5", "claude-sonnet-5", "claude-opus-5-5", "gpt-5-4", "gpt-5-5", "gpt-5-4-mini", "gpt-5-1-codex-mini", "gpt-6.1-sol", "gpt-5-6-terra", "gpt-6-luna", "gemini-3-1-pro", "gemini-3-8-flash", "gemini-3-5-flash-lite", "gemini-3-1-flash-lite", "grok-code-fast", "qwen3-coder", "kimi-k2-5", "deepseek-v4-pro", "deepseek-v3-1", "z-ai-glm-4-5", "z-ai-glm-5-1"];
68
68
  };
69
69
  export declare const ANTHROPIC_MODEL_CONFIG: {
70
70
  readonly defaultModel: "claude-sonnet-5-5";
@@ -77,11 +77,11 @@ export declare const AI_SDK_MODEL_CONFIG: {
77
77
  };
78
78
  readonly openai: {
79
79
  readonly defaultModel: "gpt-6-luna";
80
- readonly supportedModels: readonly ["gpt-6-luna", "gpt-5.6-luna", "gpt-5.6-terra", "gpt-5.6-sol", "gpt-6-sol"];
80
+ readonly supportedModels: readonly ["gpt-6-luna", "gpt-5.6-terra", "gpt-6.1-sol"];
81
81
  };
82
82
  readonly openrouter: {
83
83
  readonly defaultModel: string;
84
- readonly supportedModels: readonly ["openai/gpt-6-luna", "openai/gpt-5.6-luna", "openai/gpt-5.6-terra", "openai/gpt-5.6-sol", "openai/gpt-6-sol", "openai/gpt-6-astra", "openai/gpt-6-astra-pro", "anthropic/claude-sonnet-5.5", "anthropic/claude-opus-5.5", "anthropic/claude-opus-4.8", "anthropic/claude-fable-5", "anthropic/claude-fable-5.1", "google/gemini-3.8-flash", "qwen/qwen3.8-max-0902", "meta/muse-spark-1.3", "inception/mercury-2.5", "z-ai/glm-5.2"];
84
+ readonly supportedModels: readonly ["openai/gpt-6-luna", "openai/gpt-5.6-terra", "openai/gpt-6.1-sol", "openai/gpt-6-astra", "openai/gpt-6-astra-pro", "anthropic/claude-sonnet-5.5", "anthropic/claude-opus-5.5", "anthropic/claude-opus-4.8", "anthropic/claude-fable-5", "anthropic/claude-fable-5.1", "google/gemini-3.8-flash", "qwen/qwen3.8-max-0902", "meta/muse-spark-1.3", "inception/mercury-2.5", "z-ai/glm-5.2"];
85
85
  };
86
86
  readonly google: {
87
87
  readonly defaultModel: "gemini-3.8-flash";
@@ -24,11 +24,13 @@ const MODEL_CONTEXT_WINDOWS = {
24
24
  "openai/gpt-5.6-sol": 1_050_000,
25
25
  "openai/gpt-5.6-terra": 1_050_000,
26
26
  "openai/gpt-5.6-luna": 1_050_000,
27
+ "openai/gpt-6.1-sol": 1_050_000,
27
28
  "openai/gpt-6-sol": 1_050_000,
28
29
  "openai/gpt-6-luna": 1_050_000,
29
30
  "google/gemini-2.5-flash": 1_048_576,
30
31
  "z-ai/glm-5.2": 1_048_576,
31
32
  "gpt-6-sol": 1_050_000,
33
+ "gpt-6.1-sol": 1_050_000,
32
34
  "gpt-6-luna": 1_050_000,
33
35
  "gpt-5.6-sol": 1_050_000,
34
36
  "gpt-5.6-terra": 1_050_000,
@@ -97,9 +99,11 @@ const MODEL_MAX_OUTPUT_TOKENS = {
97
99
  "openai/gpt-5.6-sol": 40_000,
98
100
  "openai/gpt-5.6-terra": 40_000,
99
101
  "openai/gpt-5.6-luna": 128_000,
102
+ "openai/gpt-6.1-sol": 128_000,
100
103
  "openai/gpt-6-sol": 128_000,
101
104
  "openai/gpt-6-luna": 128_000,
102
105
  "gpt-6-sol": 128_000,
106
+ "gpt-6.1-sol": 128_000,
103
107
  "gpt-6-luna": 128_000,
104
108
  "gpt-5.6-sol": 40_000,
105
109
  "gpt-5.6-terra": 40_000,
@@ -163,7 +167,7 @@ export const AGENT_MODEL_CONFIG = {
163
167
  "gpt-5-5",
164
168
  "gpt-5-4-mini",
165
169
  "gpt-5-1-codex-mini",
166
- "gpt-6-sol",
170
+ "gpt-6.1-sol",
167
171
  "gpt-5-6-terra",
168
172
  "gpt-6-luna",
169
173
  "gemini-3-1-pro",
@@ -202,22 +206,14 @@ export const AGENT_MODEL_CONFIG = {
202
206
  },
203
207
  openai: {
204
208
  defaultModel: FRAMEWORK_DEFAULT_OPENAI_MODEL,
205
- supportedModels: [
206
- "gpt-6-luna",
207
- "gpt-5.6-luna",
208
- "gpt-5.6-terra",
209
- "gpt-5.6-sol",
210
- "gpt-6-sol",
211
- ],
209
+ supportedModels: ["gpt-6-luna", "gpt-5.6-terra", "gpt-6.1-sol"],
212
210
  },
213
211
  openrouter: {
214
212
  defaultModel: FRAMEWORK_DEFAULT_OPENROUTER_MODEL,
215
213
  supportedModels: [
216
214
  "openai/gpt-6-luna",
217
- "openai/gpt-5.6-luna",
218
215
  "openai/gpt-5.6-terra",
219
- "openai/gpt-5.6-sol",
220
- "openai/gpt-6-sol",
216
+ "openai/gpt-6.1-sol",
221
217
  "openai/gpt-6-astra",
222
218
  "openai/gpt-6-astra-pro",
223
219
  OPENROUTER_CLAUDE_SONNET_MODEL_ID,
@@ -0,0 +1,3 @@
1
+ export declare function findLatestSupportedVersionMatch(candidate: string, supportedModels: readonly string[]): string | undefined;
2
+ export declare function isNewerVersionedModel(candidate: string, newerModel: string): boolean;
3
+ export declare function upgradeModelToLatestSupportedVersion(candidate: string, supportedModels: readonly string[]): string | undefined;
@@ -0,0 +1,67 @@
1
+ const UPGRADEABLE_GPT_TIERS = new Set(["-sol", "-terra", "-luna"]);
2
+ function parseVersionedModelId(model) {
3
+ const match = /^(?<family>.+?)[-.](?<version>\d+(?:[-.]\d+)*)(?<suffix>(?:[-.][a-z][a-z0-9]*)*)$/i.exec(model.trim().toLowerCase());
4
+ const groups = match?.groups;
5
+ if (!groups?.family || !groups.version)
6
+ return null;
7
+ const version = groups.version.split(/[-.]/).map((part) => Number(part));
8
+ if (version.some((part) => !Number.isSafeInteger(part)))
9
+ return null;
10
+ return {
11
+ family: groups.family,
12
+ version,
13
+ suffix: groups.suffix ?? "",
14
+ };
15
+ }
16
+ function compareModelVersions(left, right) {
17
+ const length = Math.max(left.length, right.length);
18
+ for (let index = 0; index < length; index += 1) {
19
+ const delta = (left[index] ?? 0) - (right[index] ?? 0);
20
+ if (delta !== 0)
21
+ return delta;
22
+ }
23
+ return 0;
24
+ }
25
+ export function findLatestSupportedVersionMatch(candidate, supportedModels) {
26
+ const parsedCandidate = parseVersionedModelId(candidate);
27
+ if (!parsedCandidate)
28
+ return undefined;
29
+ let best;
30
+ for (const supportedModel of supportedModels) {
31
+ const parsedSupported = parseVersionedModelId(supportedModel);
32
+ if (!parsedSupported)
33
+ continue;
34
+ if (parsedSupported.family !== parsedCandidate.family)
35
+ continue;
36
+ if (parsedSupported.suffix !== parsedCandidate.suffix)
37
+ continue;
38
+ if (best &&
39
+ compareModelVersions(parsedSupported.version, best.version) <= 0) {
40
+ continue;
41
+ }
42
+ best = { model: supportedModel, version: parsedSupported.version };
43
+ }
44
+ return best?.model;
45
+ }
46
+ export function isNewerVersionedModel(candidate, newerModel) {
47
+ const current = parseVersionedModelId(candidate);
48
+ const newer = parseVersionedModelId(newerModel);
49
+ return (current !== null &&
50
+ newer !== null &&
51
+ current.family === newer.family &&
52
+ current.suffix === newer.suffix &&
53
+ compareModelVersions(newer.version, current.version) > 0);
54
+ }
55
+ export function upgradeModelToLatestSupportedVersion(candidate, supportedModels) {
56
+ const parsedCandidate = parseVersionedModelId(candidate);
57
+ if (!parsedCandidate ||
58
+ (parsedCandidate.family !== "gpt" &&
59
+ parsedCandidate.family !== "openai/gpt") ||
60
+ !UPGRADEABLE_GPT_TIERS.has(parsedCandidate.suffix)) {
61
+ return undefined;
62
+ }
63
+ const latest = findLatestSupportedVersionMatch(candidate, supportedModels);
64
+ return latest && isNewerVersionedModel(candidate, latest)
65
+ ? latest
66
+ : undefined;
67
+ }
@@ -26,6 +26,7 @@ export interface ProviderModelSelectionRow {
26
26
  provider: ProviderModelSelectionProvider;
27
27
  scope: ProviderModelSelectionScope;
28
28
  models: string[] | null;
29
+ preserveCustomModels?: boolean;
29
30
  updatedAt?: number;
30
31
  updatedBy?: string;
31
32
  }
@@ -37,11 +38,13 @@ export type EffectiveProviderModelSelection = {
37
38
  state: "default";
38
39
  provider: ProviderModelSelectionProvider;
39
40
  scope: ProviderModelSelectionScope;
41
+ preserveCustomModels?: boolean;
40
42
  } | {
41
43
  state: "selected";
42
44
  provider: ProviderModelSelectionProvider;
43
45
  scope: ProviderModelSelectionScope;
44
46
  models: string[];
47
+ preserveCustomModels?: boolean;
45
48
  } | {
46
49
  state: "unreadable";
47
50
  provider: ProviderModelSelectionProvider;
@@ -67,7 +70,9 @@ export declare function recommendedProviderModels(provider: ProviderModelSelecti
67
70
  * only routes its own catalog, so ids outside it are refused rather than
68
71
  * stored as choices the gateway would silently swap for its default.
69
72
  */
70
- export declare function normalizeSelectedModels(provider: ProviderModelSelectionProvider, models: readonly unknown[]): string[];
73
+ export declare function normalizeSelectedModels(provider: ProviderModelSelectionProvider, models: readonly unknown[], options?: {
74
+ preserveCustomModels?: boolean;
75
+ }): string[];
71
76
  /** Read one scope's row. Throws when the store can't be read. */
72
77
  export declare function readProviderModelSelection(ctx: ProviderModelSelectionContext, provider: ProviderModelSelectionProvider, scope: ProviderModelSelectionScope): Promise<ProviderModelSelectionRow>;
73
78
  /**