@open-mercato/shared 0.6.6-develop.6493.1.333455a884 → 0.6.6-develop.6505.1.6f6b83559f

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- [build:shared] found 235 entry points
1
+ [build:shared] found 236 entry points
2
2
  [build:shared] built successfully
@@ -0,0 +1,8 @@
1
+ function joinProviderModel(providerId, modelId) {
2
+ const prefix = `${providerId}/`;
3
+ return modelId.startsWith(prefix) ? modelId : `${providerId}/${modelId}`;
4
+ }
5
+ export {
6
+ joinProviderModel
7
+ };
8
+ //# sourceMappingURL=model-id.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/ai/model-id.ts"],
4
+ "sourcesContent": ["/**\n * Helpers for composing the `provider/model` display/persistence string used\n * across the AI runtime (e.g. inbox_ops `InboxProposal.llmModel`, the AI route\n * and settings responses).\n *\n * @see packages/ai-assistant/src/modules/ai_assistant/lib/model-factory.ts\n */\n\n/**\n * Joins a provider id and a model id into the canonical `provider/model`\n * string, without doubling an existing prefix.\n *\n * A gateway model id may itself be `vendor/model` (e.g. OpenRouter's\n * `anthropic/claude-sonnet-4.5`); that MUST still be prefixed with the gateway\n * to `openrouter/anthropic/claude-sonnet-4.5`. Only an already-`${providerId}/`\n * prefixed model id is passed through unchanged, so re-composing a value that\n * already carries the provider (e.g. `openrouter/anthropic/\u2026`) never yields\n * `openrouter/openrouter/anthropic/\u2026`.\n */\nexport function joinProviderModel(providerId: string, modelId: string): string {\n const prefix = `${providerId}/`\n return modelId.startsWith(prefix) ? modelId : `${providerId}/${modelId}`\n}\n"],
5
+ "mappings": "AAmBO,SAAS,kBAAkB,YAAoB,SAAyB;AAC7E,QAAM,SAAS,GAAG,UAAU;AAC5B,SAAO,QAAQ,WAAW,MAAM,IAAI,UAAU,GAAG,UAAU,IAAI,OAAO;AACxE;",
6
+ "names": []
7
+ }
@@ -1,3 +1,4 @@
1
+ import { joinProviderModel } from "./model-id.js";
1
2
  const OPEN_CODE_PROVIDER_IDS = ["anthropic", "openai", "google"];
2
3
  const DEFAULT_AI_PROVIDER_ID = "openai";
3
4
  const OPEN_CODE_PROVIDERS = {
@@ -132,7 +133,7 @@ function resolveOpenCodeModel(providerId, options) {
132
133
  }
133
134
  return {
134
135
  modelId,
135
- modelWithProvider: `${providerId}/${modelId}`,
136
+ modelWithProvider: joinProviderModel(providerId, modelId),
136
137
  source
137
138
  };
138
139
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/ai/opencode-provider.ts"],
4
- "sourcesContent": ["type EnvLookup = Record<string, string | undefined>\n\nexport type OpenCodeProviderId = 'anthropic' | 'openai' | 'google'\n\nexport type OpenCodeProviderDefinition = {\n id: OpenCodeProviderId\n name: string\n envKeys: readonly string[]\n defaultModel: string\n}\n\nexport const OPEN_CODE_PROVIDER_IDS = ['anthropic', 'openai', 'google'] as const\n\n/**\n * Default provider id used by the unified AI framework when neither\n * `OM_AI_PROVIDER` nor the legacy `OPENCODE_PROVIDER` is set. The default\n * targets OpenAI + `gpt-5-mini` (see {@link OPEN_CODE_PROVIDERS.openai}).\n */\nexport const DEFAULT_AI_PROVIDER_ID: OpenCodeProviderId = 'openai'\n\nexport const OPEN_CODE_PROVIDERS: Record<OpenCodeProviderId, OpenCodeProviderDefinition> = {\n anthropic: {\n id: 'anthropic',\n name: 'Anthropic',\n envKeys: ['ANTHROPIC_API_KEY', 'OPENCODE_ANTHROPIC_API_KEY'],\n defaultModel: 'claude-haiku-4-5-20251001',\n },\n openai: {\n id: 'openai',\n name: 'OpenAI',\n envKeys: ['OPENAI_API_KEY', 'OPENCODE_OPENAI_API_KEY'],\n defaultModel: 'gpt-5-mini',\n },\n google: {\n id: 'google',\n name: 'Google',\n envKeys: ['GOOGLE_GENERATIVE_AI_API_KEY', 'OPENCODE_GOOGLE_API_KEY'],\n defaultModel: 'gemini-3-flash',\n },\n}\n\nexport type OpenCodeModelResolution = {\n modelId: string\n modelWithProvider: string\n source: 'override' | 'om_ai_model' | 'opencode_model' | 'default'\n}\n\n/**\n * Resolves the requested AI provider id from the unified env vars.\n *\n * Precedence (highest first):\n * 1. `OM_AI_PROVIDER` \u2014 new canonical variable for the unified AI module.\n * 2. `OPENCODE_PROVIDER` \u2014 legacy variable, kept for backward compatibility.\n * 3. {@link DEFAULT_AI_PROVIDER_ID} (currently `openai`).\n *\n * Unknown / empty values fall through to the next tier so a typo in\n * `OM_AI_PROVIDER` does not silently disable the legacy fallback.\n */\nexport function resolveAiProviderIdFromEnv(\n env: EnvLookup = process.env,\n fallback: OpenCodeProviderId = DEFAULT_AI_PROVIDER_ID,\n): OpenCodeProviderId {\n const candidates = [env.OM_AI_PROVIDER, env.OPENCODE_PROVIDER]\n for (const candidate of candidates) {\n const normalized = normalizeToken(candidate)?.toLowerCase()\n if (normalized && isOpenCodeProviderId(normalized)) {\n return normalized\n }\n }\n return fallback\n}\n\nfunction normalizeToken(value: string | null | undefined): string | null {\n if (typeof value !== 'string') return null\n const normalized = value.trim()\n return normalized.length > 0 ? normalized : null\n}\n\nfunction parseModelToken(token: string): { providerPrefix: string | null; modelId: string } {\n const slashIndex = token.indexOf('/')\n if (slashIndex <= 0) {\n return {\n providerPrefix: null,\n modelId: token,\n }\n }\n\n return {\n providerPrefix: token.slice(0, slashIndex).trim().toLowerCase(),\n modelId: token.slice(slashIndex + 1).trim(),\n }\n}\n\nexport function isOpenCodeProviderId(value: string): value is OpenCodeProviderId {\n return (OPEN_CODE_PROVIDER_IDS as readonly string[]).includes(value)\n}\n\nexport function resolveOpenCodeProviderId(\n providerId: string | null | undefined,\n fallback: OpenCodeProviderId = 'anthropic',\n): OpenCodeProviderId {\n const normalized = normalizeToken(providerId)?.toLowerCase()\n if (normalized && isOpenCodeProviderId(normalized)) {\n return normalized\n }\n\n return fallback\n}\n\nexport function resolveFirstConfiguredOpenCodeProvider(\n options?: {\n env?: EnvLookup\n order?: readonly OpenCodeProviderId[]\n },\n): OpenCodeProviderId | null {\n const env = options?.env ?? process.env\n const order = options?.order ?? OPEN_CODE_PROVIDER_IDS\n\n for (const providerId of order) {\n if (isOpenCodeProviderConfigured(providerId, env)) {\n return providerId\n }\n }\n\n return null\n}\n\nexport function resolveOpenCodeProviderApiKey(\n providerId: OpenCodeProviderId,\n env: EnvLookup = process.env,\n): string | null {\n const provider = OPEN_CODE_PROVIDERS[providerId]\n for (const key of provider.envKeys) {\n const value = normalizeToken(env[key])\n if (value) {\n return value\n }\n }\n\n return null\n}\n\nexport function requireOpenCodeProviderApiKey(\n providerId: OpenCodeProviderId,\n env: EnvLookup = process.env,\n): string {\n const apiKey = resolveOpenCodeProviderApiKey(providerId, env)\n if (apiKey) {\n return apiKey\n }\n\n const provider = OPEN_CODE_PROVIDERS[providerId]\n const envKeysHint = provider.envKeys.join(' or ')\n throw new Error(\n `Missing API key for provider \"${providerId}\". Set ${envKeysHint} in your .env file.`,\n )\n}\n\nexport function getOpenCodeProviderConfiguredEnvKey(\n providerId: OpenCodeProviderId,\n env: EnvLookup = process.env,\n): string {\n const provider = OPEN_CODE_PROVIDERS[providerId]\n for (const key of provider.envKeys) {\n if (normalizeToken(env[key])) {\n return key\n }\n }\n\n return provider.envKeys[0]\n}\n\nexport function isOpenCodeProviderConfigured(\n providerId: OpenCodeProviderId,\n env: EnvLookup = process.env,\n): boolean {\n return resolveOpenCodeProviderApiKey(providerId, env) !== null\n}\n\nexport function resolveOpenCodeModel(\n providerId: OpenCodeProviderId,\n options?: {\n overrideModel?: string | null | undefined\n env?: EnvLookup\n },\n): OpenCodeModelResolution {\n const env = options?.env ?? process.env\n const overrideModel = normalizeToken(options?.overrideModel)\n const omAiModel = normalizeToken(env.OM_AI_MODEL)\n const opencodeModel = normalizeToken(env.OPENCODE_MODEL)\n\n let source: OpenCodeModelResolution['source'] = 'default'\n let selectedModel = OPEN_CODE_PROVIDERS[providerId].defaultModel\n\n if (opencodeModel) {\n selectedModel = opencodeModel\n source = 'opencode_model'\n }\n\n if (omAiModel) {\n selectedModel = omAiModel\n source = 'om_ai_model'\n }\n\n if (overrideModel) {\n selectedModel = overrideModel\n source = 'override'\n }\n\n const parsed = parseModelToken(selectedModel)\n if (parsed.providerPrefix && parsed.providerPrefix !== providerId) {\n throw new Error(\n `Model \"${selectedModel}\" does not match configured provider \"${providerId}\"`,\n )\n }\n\n const modelId = normalizeToken(parsed.modelId)\n if (!modelId) {\n throw new Error(`Model \"${selectedModel}\" is invalid`)\n }\n\n return {\n modelId,\n modelWithProvider: `${providerId}/${modelId}`,\n source,\n }\n}\n"],
5
- "mappings": "AAWO,MAAM,yBAAyB,CAAC,aAAa,UAAU,QAAQ;AAO/D,MAAM,yBAA6C;AAEnD,MAAM,sBAA8E;AAAA,EACzF,WAAW;AAAA,IACT,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,SAAS,CAAC,qBAAqB,4BAA4B;AAAA,IAC3D,cAAc;AAAA,EAChB;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,SAAS,CAAC,kBAAkB,yBAAyB;AAAA,IACrD,cAAc;AAAA,EAChB;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,SAAS,CAAC,gCAAgC,yBAAyB;AAAA,IACnE,cAAc;AAAA,EAChB;AACF;AAmBO,SAAS,2BACd,MAAiB,QAAQ,KACzB,WAA+B,wBACX;AACpB,QAAM,aAAa,CAAC,IAAI,gBAAgB,IAAI,iBAAiB;AAC7D,aAAW,aAAa,YAAY;AAClC,UAAM,aAAa,eAAe,SAAS,GAAG,YAAY;AAC1D,QAAI,cAAc,qBAAqB,UAAU,GAAG;AAClD,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,eAAe,OAAiD;AACvE,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,aAAa,MAAM,KAAK;AAC9B,SAAO,WAAW,SAAS,IAAI,aAAa;AAC9C;AAEA,SAAS,gBAAgB,OAAmE;AAC1F,QAAM,aAAa,MAAM,QAAQ,GAAG;AACpC,MAAI,cAAc,GAAG;AACnB,WAAO;AAAA,MACL,gBAAgB;AAAA,MAChB,SAAS;AAAA,IACX;AAAA,EACF;AAEA,SAAO;AAAA,IACL,gBAAgB,MAAM,MAAM,GAAG,UAAU,EAAE,KAAK,EAAE,YAAY;AAAA,IAC9D,SAAS,MAAM,MAAM,aAAa,CAAC,EAAE,KAAK;AAAA,EAC5C;AACF;AAEO,SAAS,qBAAqB,OAA4C;AAC/E,SAAQ,uBAA6C,SAAS,KAAK;AACrE;AAEO,SAAS,0BACd,YACA,WAA+B,aACX;AACpB,QAAM,aAAa,eAAe,UAAU,GAAG,YAAY;AAC3D,MAAI,cAAc,qBAAqB,UAAU,GAAG;AAClD,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEO,SAAS,uCACd,SAI2B;AAC3B,QAAM,MAAM,SAAS,OAAO,QAAQ;AACpC,QAAM,QAAQ,SAAS,SAAS;AAEhC,aAAW,cAAc,OAAO;AAC9B,QAAI,6BAA6B,YAAY,GAAG,GAAG;AACjD,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,8BACd,YACA,MAAiB,QAAQ,KACV;AACf,QAAM,WAAW,oBAAoB,UAAU;AAC/C,aAAW,OAAO,SAAS,SAAS;AAClC,UAAM,QAAQ,eAAe,IAAI,GAAG,CAAC;AACrC,QAAI,OAAO;AACT,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,8BACd,YACA,MAAiB,QAAQ,KACjB;AACR,QAAM,SAAS,8BAA8B,YAAY,GAAG;AAC5D,MAAI,QAAQ;AACV,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,oBAAoB,UAAU;AAC/C,QAAM,cAAc,SAAS,QAAQ,KAAK,MAAM;AAChD,QAAM,IAAI;AAAA,IACR,iCAAiC,UAAU,UAAU,WAAW;AAAA,EAClE;AACF;AAEO,SAAS,oCACd,YACA,MAAiB,QAAQ,KACjB;AACR,QAAM,WAAW,oBAAoB,UAAU;AAC/C,aAAW,OAAO,SAAS,SAAS;AAClC,QAAI,eAAe,IAAI,GAAG,CAAC,GAAG;AAC5B,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO,SAAS,QAAQ,CAAC;AAC3B;AAEO,SAAS,6BACd,YACA,MAAiB,QAAQ,KAChB;AACT,SAAO,8BAA8B,YAAY,GAAG,MAAM;AAC5D;AAEO,SAAS,qBACd,YACA,SAIyB;AACzB,QAAM,MAAM,SAAS,OAAO,QAAQ;AACpC,QAAM,gBAAgB,eAAe,SAAS,aAAa;AAC3D,QAAM,YAAY,eAAe,IAAI,WAAW;AAChD,QAAM,gBAAgB,eAAe,IAAI,cAAc;AAEvD,MAAI,SAA4C;AAChD,MAAI,gBAAgB,oBAAoB,UAAU,EAAE;AAEpD,MAAI,eAAe;AACjB,oBAAgB;AAChB,aAAS;AAAA,EACX;AAEA,MAAI,WAAW;AACb,oBAAgB;AAChB,aAAS;AAAA,EACX;AAEA,MAAI,eAAe;AACjB,oBAAgB;AAChB,aAAS;AAAA,EACX;AAEA,QAAM,SAAS,gBAAgB,aAAa;AAC5C,MAAI,OAAO,kBAAkB,OAAO,mBAAmB,YAAY;AACjE,UAAM,IAAI;AAAA,MACR,UAAU,aAAa,yCAAyC,UAAU;AAAA,IAC5E;AAAA,EACF;AAEA,QAAM,UAAU,eAAe,OAAO,OAAO;AAC7C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,UAAU,aAAa,cAAc;AAAA,EACvD;AAEA,SAAO;AAAA,IACL;AAAA,IACA,mBAAmB,GAAG,UAAU,IAAI,OAAO;AAAA,IAC3C;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { joinProviderModel } from './model-id'\n\ntype EnvLookup = Record<string, string | undefined>\n\nexport type OpenCodeProviderId = 'anthropic' | 'openai' | 'google'\n\nexport type OpenCodeProviderDefinition = {\n id: OpenCodeProviderId\n name: string\n envKeys: readonly string[]\n defaultModel: string\n}\n\nexport const OPEN_CODE_PROVIDER_IDS = ['anthropic', 'openai', 'google'] as const\n\n/**\n * Default provider id used by the unified AI framework when neither\n * `OM_AI_PROVIDER` nor the legacy `OPENCODE_PROVIDER` is set. The default\n * targets OpenAI + `gpt-5-mini` (see {@link OPEN_CODE_PROVIDERS.openai}).\n */\nexport const DEFAULT_AI_PROVIDER_ID: OpenCodeProviderId = 'openai'\n\nexport const OPEN_CODE_PROVIDERS: Record<OpenCodeProviderId, OpenCodeProviderDefinition> = {\n anthropic: {\n id: 'anthropic',\n name: 'Anthropic',\n envKeys: ['ANTHROPIC_API_KEY', 'OPENCODE_ANTHROPIC_API_KEY'],\n defaultModel: 'claude-haiku-4-5-20251001',\n },\n openai: {\n id: 'openai',\n name: 'OpenAI',\n envKeys: ['OPENAI_API_KEY', 'OPENCODE_OPENAI_API_KEY'],\n defaultModel: 'gpt-5-mini',\n },\n google: {\n id: 'google',\n name: 'Google',\n envKeys: ['GOOGLE_GENERATIVE_AI_API_KEY', 'OPENCODE_GOOGLE_API_KEY'],\n defaultModel: 'gemini-3-flash',\n },\n}\n\nexport type OpenCodeModelResolution = {\n modelId: string\n modelWithProvider: string\n source: 'override' | 'om_ai_model' | 'opencode_model' | 'default'\n}\n\n/**\n * Resolves the requested AI provider id from the unified env vars.\n *\n * Precedence (highest first):\n * 1. `OM_AI_PROVIDER` \u2014 new canonical variable for the unified AI module.\n * 2. `OPENCODE_PROVIDER` \u2014 legacy variable, kept for backward compatibility.\n * 3. {@link DEFAULT_AI_PROVIDER_ID} (currently `openai`).\n *\n * Unknown / empty values fall through to the next tier so a typo in\n * `OM_AI_PROVIDER` does not silently disable the legacy fallback.\n */\nexport function resolveAiProviderIdFromEnv(\n env: EnvLookup = process.env,\n fallback: OpenCodeProviderId = DEFAULT_AI_PROVIDER_ID,\n): OpenCodeProviderId {\n const candidates = [env.OM_AI_PROVIDER, env.OPENCODE_PROVIDER]\n for (const candidate of candidates) {\n const normalized = normalizeToken(candidate)?.toLowerCase()\n if (normalized && isOpenCodeProviderId(normalized)) {\n return normalized\n }\n }\n return fallback\n}\n\nfunction normalizeToken(value: string | null | undefined): string | null {\n if (typeof value !== 'string') return null\n const normalized = value.trim()\n return normalized.length > 0 ? normalized : null\n}\n\nfunction parseModelToken(token: string): { providerPrefix: string | null; modelId: string } {\n const slashIndex = token.indexOf('/')\n if (slashIndex <= 0) {\n return {\n providerPrefix: null,\n modelId: token,\n }\n }\n\n return {\n providerPrefix: token.slice(0, slashIndex).trim().toLowerCase(),\n modelId: token.slice(slashIndex + 1).trim(),\n }\n}\n\nexport function isOpenCodeProviderId(value: string): value is OpenCodeProviderId {\n return (OPEN_CODE_PROVIDER_IDS as readonly string[]).includes(value)\n}\n\nexport function resolveOpenCodeProviderId(\n providerId: string | null | undefined,\n fallback: OpenCodeProviderId = 'anthropic',\n): OpenCodeProviderId {\n const normalized = normalizeToken(providerId)?.toLowerCase()\n if (normalized && isOpenCodeProviderId(normalized)) {\n return normalized\n }\n\n return fallback\n}\n\nexport function resolveFirstConfiguredOpenCodeProvider(\n options?: {\n env?: EnvLookup\n order?: readonly OpenCodeProviderId[]\n },\n): OpenCodeProviderId | null {\n const env = options?.env ?? process.env\n const order = options?.order ?? OPEN_CODE_PROVIDER_IDS\n\n for (const providerId of order) {\n if (isOpenCodeProviderConfigured(providerId, env)) {\n return providerId\n }\n }\n\n return null\n}\n\nexport function resolveOpenCodeProviderApiKey(\n providerId: OpenCodeProviderId,\n env: EnvLookup = process.env,\n): string | null {\n const provider = OPEN_CODE_PROVIDERS[providerId]\n for (const key of provider.envKeys) {\n const value = normalizeToken(env[key])\n if (value) {\n return value\n }\n }\n\n return null\n}\n\nexport function requireOpenCodeProviderApiKey(\n providerId: OpenCodeProviderId,\n env: EnvLookup = process.env,\n): string {\n const apiKey = resolveOpenCodeProviderApiKey(providerId, env)\n if (apiKey) {\n return apiKey\n }\n\n const provider = OPEN_CODE_PROVIDERS[providerId]\n const envKeysHint = provider.envKeys.join(' or ')\n throw new Error(\n `Missing API key for provider \"${providerId}\". Set ${envKeysHint} in your .env file.`,\n )\n}\n\nexport function getOpenCodeProviderConfiguredEnvKey(\n providerId: OpenCodeProviderId,\n env: EnvLookup = process.env,\n): string {\n const provider = OPEN_CODE_PROVIDERS[providerId]\n for (const key of provider.envKeys) {\n if (normalizeToken(env[key])) {\n return key\n }\n }\n\n return provider.envKeys[0]\n}\n\nexport function isOpenCodeProviderConfigured(\n providerId: OpenCodeProviderId,\n env: EnvLookup = process.env,\n): boolean {\n return resolveOpenCodeProviderApiKey(providerId, env) !== null\n}\n\nexport function resolveOpenCodeModel(\n providerId: OpenCodeProviderId,\n options?: {\n overrideModel?: string | null | undefined\n env?: EnvLookup\n },\n): OpenCodeModelResolution {\n const env = options?.env ?? process.env\n const overrideModel = normalizeToken(options?.overrideModel)\n const omAiModel = normalizeToken(env.OM_AI_MODEL)\n const opencodeModel = normalizeToken(env.OPENCODE_MODEL)\n\n let source: OpenCodeModelResolution['source'] = 'default'\n let selectedModel = OPEN_CODE_PROVIDERS[providerId].defaultModel\n\n if (opencodeModel) {\n selectedModel = opencodeModel\n source = 'opencode_model'\n }\n\n if (omAiModel) {\n selectedModel = omAiModel\n source = 'om_ai_model'\n }\n\n if (overrideModel) {\n selectedModel = overrideModel\n source = 'override'\n }\n\n const parsed = parseModelToken(selectedModel)\n if (parsed.providerPrefix && parsed.providerPrefix !== providerId) {\n throw new Error(\n `Model \"${selectedModel}\" does not match configured provider \"${providerId}\"`,\n )\n }\n\n const modelId = normalizeToken(parsed.modelId)\n if (!modelId) {\n throw new Error(`Model \"${selectedModel}\" is invalid`)\n }\n\n return {\n modelId,\n modelWithProvider: joinProviderModel(providerId, modelId),\n source,\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,4 @@
1
- const APP_VERSION = "0.6.6-develop.6493.1.333455a884";
1
+ const APP_VERSION = "0.6.6-develop.6505.1.6f6b83559f";
2
2
  const appVersion = APP_VERSION;
3
3
  export {
4
4
  APP_VERSION,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/version.ts"],
4
- "sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.6.6-develop.6493.1.333455a884'\nexport const appVersion = APP_VERSION\n"],
4
+ "sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.6.6-develop.6505.1.6f6b83559f'\nexport const appVersion = APP_VERSION\n"],
5
5
  "mappings": "AACO,MAAM,cAAc;AACpB,MAAM,aAAa;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/shared",
3
- "version": "0.6.6-develop.6493.1.333455a884",
3
+ "version": "0.6.6-develop.6505.1.6f6b83559f",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -97,7 +97,7 @@
97
97
  "@mikro-orm/core": "^7.1.5",
98
98
  "@mikro-orm/decorators": "^7.1.5",
99
99
  "@mikro-orm/postgresql": "^7.1.5",
100
- "@open-mercato/cache": "0.6.6-develop.6493.1.333455a884",
100
+ "@open-mercato/cache": "0.6.6-develop.6505.1.6f6b83559f",
101
101
  "@types/sanitize-html": "^2.16.1",
102
102
  "dotenv": "^17.4.2",
103
103
  "pino": "^10.3.1",
@@ -0,0 +1,26 @@
1
+ import { joinProviderModel } from '../model-id'
2
+
3
+ describe('joinProviderModel', () => {
4
+ it('prepends the provider id to a bare model id', () => {
5
+ expect(joinProviderModel('anthropic', 'claude-haiku-4-5')).toBe('anthropic/claude-haiku-4-5')
6
+ })
7
+
8
+ it('passes a vendor-prefixed gateway model id through with a single gateway prefix', () => {
9
+ expect(joinProviderModel('openrouter', 'anthropic/claude-sonnet-4.5')).toBe(
10
+ 'openrouter/anthropic/claude-sonnet-4.5',
11
+ )
12
+ })
13
+
14
+ it('does not double an already-prefixed value', () => {
15
+ expect(joinProviderModel('openrouter', 'openrouter/anthropic/claude-sonnet-4.5')).toBe(
16
+ 'openrouter/anthropic/claude-sonnet-4.5',
17
+ )
18
+ })
19
+
20
+ it('only treats an exact `${providerId}/` prefix as already-joined', () => {
21
+ // A different vendor prefix must still be prefixed with the provider.
22
+ expect(joinProviderModel('openrouter', 'openai/gpt-4o-mini')).toBe(
23
+ 'openrouter/openai/gpt-4o-mini',
24
+ )
25
+ })
26
+ })
@@ -96,6 +96,20 @@ export interface LlmProvider {
96
96
  readonly defaultModel: string
97
97
  /** Curated list of models shown in the UI dropdown for this provider. */
98
98
  readonly defaultModels: readonly LlmModelInfo[]
99
+ /**
100
+ * True when this provider is an OpenAI-compatible gateway whose model ids
101
+ * are themselves `vendor/model` strings (e.g. OpenRouter's
102
+ * `anthropic/claude-sonnet-4.5`, Requesty's `openai/gpt-4o-mini`, a LiteLLM
103
+ * proxy relaying `anthropic/…`).
104
+ *
105
+ * The routing layer uses this to suppress intra-tier slash provider-splitting:
106
+ * when a resolution tier explicitly selects such a configured gateway, that
107
+ * tier's model token's leading `vendor/` is part of the gateway model id, not
108
+ * a pin to the native `vendor` adapter. Omitted / `undefined` is treated as
109
+ * `false` — native adapters (Anthropic, OpenAI, Google) and non-gateway
110
+ * OpenAI-compatible backends (DeepInfra, Together, …) leave it unset.
111
+ */
112
+ readonly usesVendorPrefixedModelIds?: boolean
99
113
 
100
114
  /**
101
115
  * Returns true when the provider has all required configuration present
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Helpers for composing the `provider/model` display/persistence string used
3
+ * across the AI runtime (e.g. inbox_ops `InboxProposal.llmModel`, the AI route
4
+ * and settings responses).
5
+ *
6
+ * @see packages/ai-assistant/src/modules/ai_assistant/lib/model-factory.ts
7
+ */
8
+
9
+ /**
10
+ * Joins a provider id and a model id into the canonical `provider/model`
11
+ * string, without doubling an existing prefix.
12
+ *
13
+ * A gateway model id may itself be `vendor/model` (e.g. OpenRouter's
14
+ * `anthropic/claude-sonnet-4.5`); that MUST still be prefixed with the gateway
15
+ * to `openrouter/anthropic/claude-sonnet-4.5`. Only an already-`${providerId}/`
16
+ * prefixed model id is passed through unchanged, so re-composing a value that
17
+ * already carries the provider (e.g. `openrouter/anthropic/…`) never yields
18
+ * `openrouter/openrouter/anthropic/…`.
19
+ */
20
+ export function joinProviderModel(providerId: string, modelId: string): string {
21
+ const prefix = `${providerId}/`
22
+ return modelId.startsWith(prefix) ? modelId : `${providerId}/${modelId}`
23
+ }
@@ -1,3 +1,5 @@
1
+ import { joinProviderModel } from './model-id'
2
+
1
3
  type EnvLookup = Record<string, string | undefined>
2
4
 
3
5
  export type OpenCodeProviderId = 'anthropic' | 'openai' | 'google'
@@ -221,7 +223,7 @@ export function resolveOpenCodeModel(
221
223
 
222
224
  return {
223
225
  modelId,
224
- modelWithProvider: `${providerId}/${modelId}`,
226
+ modelWithProvider: joinProviderModel(providerId, modelId),
225
227
  source,
226
228
  }
227
229
  }