@open-mercato/shared 0.6.1-develop.3188.1.499d530779 → 0.6.1-develop.3198.1.aa979623f0

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,4 +1,5 @@
1
1
  const OPEN_CODE_PROVIDER_IDS = ["anthropic", "openai", "google"];
2
+ const DEFAULT_AI_PROVIDER_ID = "openai";
2
3
  const OPEN_CODE_PROVIDERS = {
3
4
  anthropic: {
4
5
  id: "anthropic",
@@ -10,7 +11,7 @@ const OPEN_CODE_PROVIDERS = {
10
11
  id: "openai",
11
12
  name: "OpenAI",
12
13
  envKeys: ["OPENAI_API_KEY", "OPENCODE_OPENAI_API_KEY"],
13
- defaultModel: "gpt-4o-mini"
14
+ defaultModel: "gpt-5-mini"
14
15
  },
15
16
  google: {
16
17
  id: "google",
@@ -19,6 +20,16 @@ const OPEN_CODE_PROVIDERS = {
19
20
  defaultModel: "gemini-3-flash"
20
21
  }
21
22
  };
23
+ function resolveAiProviderIdFromEnv(env = process.env, fallback = DEFAULT_AI_PROVIDER_ID) {
24
+ const candidates = [env.OM_AI_PROVIDER, env.OPENCODE_PROVIDER];
25
+ for (const candidate of candidates) {
26
+ const normalized = normalizeToken(candidate)?.toLowerCase();
27
+ if (normalized && isOpenCodeProviderId(normalized)) {
28
+ return normalized;
29
+ }
30
+ }
31
+ return fallback;
32
+ }
22
33
  function normalizeToken(value) {
23
34
  if (typeof value !== "string") return null;
24
35
  const normalized = value.trim();
@@ -93,6 +104,7 @@ function isOpenCodeProviderConfigured(providerId, env = process.env) {
93
104
  function resolveOpenCodeModel(providerId, options) {
94
105
  const env = options?.env ?? process.env;
95
106
  const overrideModel = normalizeToken(options?.overrideModel);
107
+ const omAiModel = normalizeToken(env.OM_AI_MODEL);
96
108
  const opencodeModel = normalizeToken(env.OPENCODE_MODEL);
97
109
  let source = "default";
98
110
  let selectedModel = OPEN_CODE_PROVIDERS[providerId].defaultModel;
@@ -100,6 +112,10 @@ function resolveOpenCodeModel(providerId, options) {
100
112
  selectedModel = opencodeModel;
101
113
  source = "opencode_model";
102
114
  }
115
+ if (omAiModel) {
116
+ selectedModel = omAiModel;
117
+ source = "om_ai_model";
118
+ }
103
119
  if (overrideModel) {
104
120
  selectedModel = overrideModel;
105
121
  source = "override";
@@ -121,12 +137,14 @@ function resolveOpenCodeModel(providerId, options) {
121
137
  };
122
138
  }
123
139
  export {
140
+ DEFAULT_AI_PROVIDER_ID,
124
141
  OPEN_CODE_PROVIDERS,
125
142
  OPEN_CODE_PROVIDER_IDS,
126
143
  getOpenCodeProviderConfiguredEnvKey,
127
144
  isOpenCodeProviderConfigured,
128
145
  isOpenCodeProviderId,
129
146
  requireOpenCodeProviderApiKey,
147
+ resolveAiProviderIdFromEnv,
130
148
  resolveFirstConfiguredOpenCodeProvider,
131
149
  resolveOpenCodeModel,
132
150
  resolveOpenCodeProviderApiKey,
@@ -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\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-4o-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' | 'opencode_model' | 'default'\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 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 (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": "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",
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": "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",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,4 @@
1
- const APP_VERSION = "0.6.1-develop.3188.1.499d530779";
1
+ const APP_VERSION = "0.6.1-develop.3198.1.aa979623f0";
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.1-develop.3188.1.499d530779'\nexport const appVersion = APP_VERSION\n"],
4
+ "sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.6.1-develop.3198.1.aa979623f0'\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.1-develop.3188.1.499d530779",
3
+ "version": "0.6.1-develop.3198.1.aa979623f0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -92,7 +92,7 @@
92
92
  "@mikro-orm/core": "^7.0.14",
93
93
  "@mikro-orm/decorators": "^7.0.14",
94
94
  "@mikro-orm/postgresql": "^7.0.14",
95
- "@open-mercato/cache": "0.6.1-develop.3188.1.499d530779",
95
+ "@open-mercato/cache": "0.6.1-develop.3198.1.aa979623f0",
96
96
  "dotenv": "^17.4.2",
97
97
  "rate-limiter-flexible": "^11.0.1",
98
98
  "reflect-metadata": "^0.2.2",
@@ -1,4 +1,5 @@
1
1
  import {
2
+ resolveAiProviderIdFromEnv,
2
3
  resolveOpenCodeProviderId,
3
4
  resolveFirstConfiguredOpenCodeProvider,
4
5
  resolveOpenCodeModel,
@@ -47,8 +48,8 @@ describe('opencode provider helpers', () => {
47
48
  it('resolves default model when no overrides are set', () => {
48
49
  const model = resolveOpenCodeModel('openai', { env: {} })
49
50
  expect(model).toEqual({
50
- modelId: 'gpt-4o-mini',
51
- modelWithProvider: 'openai/gpt-4o-mini',
51
+ modelId: 'gpt-5-mini',
52
+ modelWithProvider: 'openai/gpt-5-mini',
52
53
  source: 'default',
53
54
  })
54
55
  })
@@ -64,11 +65,36 @@ describe('opencode provider helpers', () => {
64
65
  })
65
66
  })
66
67
 
67
- it('prefers explicit override model over OPENCODE_MODEL', () => {
68
+ it('prefers OM_AI_MODEL over the legacy OPENCODE_MODEL', () => {
69
+ const model = resolveOpenCodeModel('openai', {
70
+ env: {
71
+ OM_AI_MODEL: 'openai/gpt-5-mini',
72
+ OPENCODE_MODEL: 'openai/gpt-4.1-mini',
73
+ },
74
+ })
75
+ expect(model).toEqual({
76
+ modelId: 'gpt-5-mini',
77
+ modelWithProvider: 'openai/gpt-5-mini',
78
+ source: 'om_ai_model',
79
+ })
80
+ })
81
+
82
+ it('falls back to OPENCODE_MODEL when OM_AI_MODEL is unset', () => {
68
83
  const model = resolveOpenCodeModel('openai', {
69
- overrideModel: 'gpt-4.1',
70
84
  env: { OPENCODE_MODEL: 'openai/gpt-4.1-mini' },
71
85
  })
86
+ expect(model.source).toBe('opencode_model')
87
+ expect(model.modelId).toBe('gpt-4.1-mini')
88
+ })
89
+
90
+ it('prefers explicit override model over OM_AI_MODEL and OPENCODE_MODEL', () => {
91
+ const model = resolveOpenCodeModel('openai', {
92
+ overrideModel: 'gpt-4.1',
93
+ env: {
94
+ OM_AI_MODEL: 'openai/gpt-5-mini',
95
+ OPENCODE_MODEL: 'openai/gpt-4.1-mini',
96
+ },
97
+ })
72
98
  expect(model).toEqual({
73
99
  modelId: 'gpt-4.1',
74
100
  modelWithProvider: 'openai/gpt-4.1',
@@ -76,6 +102,40 @@ describe('opencode provider helpers', () => {
76
102
  })
77
103
  })
78
104
 
105
+ describe('resolveAiProviderIdFromEnv', () => {
106
+ it('returns OM_AI_PROVIDER when set', () => {
107
+ expect(resolveAiProviderIdFromEnv({ OM_AI_PROVIDER: 'anthropic' })).toBe('anthropic')
108
+ })
109
+
110
+ it('falls back to OPENCODE_PROVIDER when OM_AI_PROVIDER is unset', () => {
111
+ expect(
112
+ resolveAiProviderIdFromEnv({ OPENCODE_PROVIDER: 'google' }),
113
+ ).toBe('google')
114
+ })
115
+
116
+ it('prefers OM_AI_PROVIDER over OPENCODE_PROVIDER', () => {
117
+ expect(
118
+ resolveAiProviderIdFromEnv({
119
+ OM_AI_PROVIDER: 'openai',
120
+ OPENCODE_PROVIDER: 'anthropic',
121
+ }),
122
+ ).toBe('openai')
123
+ })
124
+
125
+ it('skips unknown values and falls through to the legacy var', () => {
126
+ expect(
127
+ resolveAiProviderIdFromEnv({
128
+ OM_AI_PROVIDER: 'pirate',
129
+ OPENCODE_PROVIDER: 'google',
130
+ }),
131
+ ).toBe('google')
132
+ })
133
+
134
+ it('defaults to openai when neither var is set', () => {
135
+ expect(resolveAiProviderIdFromEnv({})).toBe('openai')
136
+ })
137
+ })
138
+
79
139
  it('throws when model provider prefix conflicts with selected provider', () => {
80
140
  expect(() =>
81
141
  resolveOpenCodeModel('openai', {
@@ -11,6 +11,13 @@ export type OpenCodeProviderDefinition = {
11
11
 
12
12
  export const OPEN_CODE_PROVIDER_IDS = ['anthropic', 'openai', 'google'] as const
13
13
 
14
+ /**
15
+ * Default provider id used by the unified AI framework when neither
16
+ * `OM_AI_PROVIDER` nor the legacy `OPENCODE_PROVIDER` is set. The default
17
+ * targets OpenAI + `gpt-5-mini` (see {@link OPEN_CODE_PROVIDERS.openai}).
18
+ */
19
+ export const DEFAULT_AI_PROVIDER_ID: OpenCodeProviderId = 'openai'
20
+
14
21
  export const OPEN_CODE_PROVIDERS: Record<OpenCodeProviderId, OpenCodeProviderDefinition> = {
15
22
  anthropic: {
16
23
  id: 'anthropic',
@@ -22,7 +29,7 @@ export const OPEN_CODE_PROVIDERS: Record<OpenCodeProviderId, OpenCodeProviderDef
22
29
  id: 'openai',
23
30
  name: 'OpenAI',
24
31
  envKeys: ['OPENAI_API_KEY', 'OPENCODE_OPENAI_API_KEY'],
25
- defaultModel: 'gpt-4o-mini',
32
+ defaultModel: 'gpt-5-mini',
26
33
  },
27
34
  google: {
28
35
  id: 'google',
@@ -35,7 +42,32 @@ export const OPEN_CODE_PROVIDERS: Record<OpenCodeProviderId, OpenCodeProviderDef
35
42
  export type OpenCodeModelResolution = {
36
43
  modelId: string
37
44
  modelWithProvider: string
38
- source: 'override' | 'opencode_model' | 'default'
45
+ source: 'override' | 'om_ai_model' | 'opencode_model' | 'default'
46
+ }
47
+
48
+ /**
49
+ * Resolves the requested AI provider id from the unified env vars.
50
+ *
51
+ * Precedence (highest first):
52
+ * 1. `OM_AI_PROVIDER` — new canonical variable for the unified AI module.
53
+ * 2. `OPENCODE_PROVIDER` — legacy variable, kept for backward compatibility.
54
+ * 3. {@link DEFAULT_AI_PROVIDER_ID} (currently `openai`).
55
+ *
56
+ * Unknown / empty values fall through to the next tier so a typo in
57
+ * `OM_AI_PROVIDER` does not silently disable the legacy fallback.
58
+ */
59
+ export function resolveAiProviderIdFromEnv(
60
+ env: EnvLookup = process.env,
61
+ fallback: OpenCodeProviderId = DEFAULT_AI_PROVIDER_ID,
62
+ ): OpenCodeProviderId {
63
+ const candidates = [env.OM_AI_PROVIDER, env.OPENCODE_PROVIDER]
64
+ for (const candidate of candidates) {
65
+ const normalized = normalizeToken(candidate)?.toLowerCase()
66
+ if (normalized && isOpenCodeProviderId(normalized)) {
67
+ return normalized
68
+ }
69
+ }
70
+ return fallback
39
71
  }
40
72
 
41
73
  function normalizeToken(value: string | null | undefined): string | null {
@@ -154,6 +186,7 @@ export function resolveOpenCodeModel(
154
186
  ): OpenCodeModelResolution {
155
187
  const env = options?.env ?? process.env
156
188
  const overrideModel = normalizeToken(options?.overrideModel)
189
+ const omAiModel = normalizeToken(env.OM_AI_MODEL)
157
190
  const opencodeModel = normalizeToken(env.OPENCODE_MODEL)
158
191
 
159
192
  let source: OpenCodeModelResolution['source'] = 'default'
@@ -164,6 +197,11 @@ export function resolveOpenCodeModel(
164
197
  source = 'opencode_model'
165
198
  }
166
199
 
200
+ if (omAiModel) {
201
+ selectedModel = omAiModel
202
+ source = 'om_ai_model'
203
+ }
204
+
167
205
  if (overrideModel) {
168
206
  selectedModel = overrideModel
169
207
  source = 'override'