@ottocode/server 0.1.245 → 0.1.247

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 (36) hide show
  1. package/package.json +4 -3
  2. package/src/index.ts +5 -0
  3. package/src/openapi/paths/config.ts +118 -2
  4. package/src/openapi/paths/skills.ts +122 -0
  5. package/src/openapi/schemas.ts +35 -3
  6. package/src/presets.ts +1 -1
  7. package/src/routes/auth.ts +24 -30
  8. package/src/routes/branch.ts +3 -2
  9. package/src/routes/config/defaults.ts +10 -3
  10. package/src/routes/config/main.ts +3 -0
  11. package/src/routes/config/models.ts +84 -14
  12. package/src/routes/config/providers.ts +137 -4
  13. package/src/routes/config/utils.ts +72 -2
  14. package/src/routes/doctor.ts +15 -27
  15. package/src/routes/git/commit.ts +16 -5
  16. package/src/routes/research.ts +3 -3
  17. package/src/routes/session-messages.ts +14 -8
  18. package/src/routes/sessions.ts +12 -18
  19. package/src/routes/skills.ts +140 -59
  20. package/src/runtime/agent/registry.ts +8 -5
  21. package/src/runtime/agent/runner-setup.ts +136 -39
  22. package/src/runtime/agent/runner.ts +140 -4
  23. package/src/runtime/ask/service.ts +13 -10
  24. package/src/runtime/message/history-builder.ts +22 -6
  25. package/src/runtime/message/service.ts +7 -1
  26. package/src/runtime/prompt/builder.ts +12 -0
  27. package/src/runtime/prompt/capabilities.ts +200 -0
  28. package/src/runtime/provider/index.ts +98 -0
  29. package/src/runtime/provider/reasoning.ts +73 -17
  30. package/src/runtime/provider/selection.ts +16 -14
  31. package/src/runtime/session/manager.ts +1 -1
  32. package/src/runtime/session/queue.ts +7 -2
  33. package/src/runtime/tools/approval.ts +1 -0
  34. package/src/runtime/tools/guards.ts +4 -3
  35. package/src/runtime/tools/mapping.ts +4 -2
  36. package/src/tools/adapter.ts +3 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ottocode/server",
3
- "version": "0.1.245",
3
+ "version": "0.1.247",
4
4
  "description": "HTTP API server for ottocode",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -49,8 +49,9 @@
49
49
  "typecheck": "tsc --noEmit"
50
50
  },
51
51
  "dependencies": {
52
- "@ottocode/sdk": "0.1.245",
53
- "@ottocode/database": "0.1.245",
52
+ "@ottocode/database": "0.1.247",
53
+ "@ottocode/sdk": "0.1.247",
54
+ "ai-sdk-ollama": "^3.8.3",
54
55
  "drizzle-orm": "^0.44.5",
55
56
  "hono": "^4.9.9",
56
57
  "zod": "^4.3.6"
package/src/index.ts CHANGED
@@ -284,6 +284,11 @@ export {
284
284
  composeSystemPrompt,
285
285
  type ComposedSystemPrompt,
286
286
  } from './runtime/prompt/builder.ts';
287
+ export {
288
+ buildCapabilitySummary,
289
+ type CapabilitySummaryResult,
290
+ type CapabilitySummaryMCPTool,
291
+ } from './runtime/prompt/capabilities.ts';
287
292
  export {
288
293
  AskServiceError,
289
294
  handleAskRequest,
@@ -91,9 +91,123 @@ export const configPaths = {
91
91
  type: 'array',
92
92
  items: { $ref: '#/components/schemas/Provider' },
93
93
  },
94
+ details: {
95
+ type: 'array',
96
+ items: { $ref: '#/components/schemas/ProviderDetail' },
97
+ },
94
98
  default: { $ref: '#/components/schemas/Provider' },
95
99
  },
96
- required: ['providers', 'default'],
100
+ required: ['providers', 'details', 'default'],
101
+ },
102
+ },
103
+ },
104
+ },
105
+ },
106
+ },
107
+ },
108
+ '/v1/config/providers/{provider}': {
109
+ put: {
110
+ tags: ['config'],
111
+ operationId: 'updateProviderSettings',
112
+ summary: 'Create or update provider settings',
113
+ parameters: [
114
+ projectQueryParam(),
115
+ {
116
+ in: 'path',
117
+ name: 'provider',
118
+ required: true,
119
+ schema: { $ref: '#/components/schemas/Provider' },
120
+ },
121
+ ],
122
+ requestBody: {
123
+ required: true,
124
+ content: {
125
+ 'application/json': {
126
+ schema: {
127
+ type: 'object',
128
+ properties: {
129
+ enabled: { type: 'boolean' },
130
+ custom: { type: 'boolean' },
131
+ label: { type: 'string' },
132
+ compatibility: { type: 'string' },
133
+ family: { type: 'string' },
134
+ baseURL: { type: 'string', nullable: true },
135
+ apiKey: { type: 'string', nullable: true },
136
+ apiKeyEnv: { type: 'string', nullable: true },
137
+ models: {
138
+ type: 'array',
139
+ items: { type: 'string' },
140
+ },
141
+ allowAnyModel: { type: 'boolean' },
142
+ scope: {
143
+ type: 'string',
144
+ enum: ['global', 'local'],
145
+ },
146
+ },
147
+ },
148
+ },
149
+ },
150
+ },
151
+ responses: {
152
+ 200: {
153
+ description: 'OK',
154
+ content: {
155
+ 'application/json': {
156
+ schema: {
157
+ type: 'object',
158
+ properties: {
159
+ success: { type: 'boolean' },
160
+ provider: { $ref: '#/components/schemas/Provider' },
161
+ details: {
162
+ type: 'array',
163
+ items: { $ref: '#/components/schemas/ProviderDetail' },
164
+ },
165
+ },
166
+ required: ['success', 'provider', 'details'],
167
+ },
168
+ },
169
+ },
170
+ },
171
+ },
172
+ },
173
+ delete: {
174
+ tags: ['config'],
175
+ operationId: 'deleteProviderSettings',
176
+ summary: 'Delete provider override or custom provider entry',
177
+ parameters: [
178
+ projectQueryParam(),
179
+ {
180
+ in: 'query',
181
+ name: 'scope',
182
+ required: false,
183
+ schema: {
184
+ type: 'string',
185
+ enum: ['global', 'local'],
186
+ },
187
+ },
188
+ {
189
+ in: 'path',
190
+ name: 'provider',
191
+ required: true,
192
+ schema: { $ref: '#/components/schemas/Provider' },
193
+ },
194
+ ],
195
+ responses: {
196
+ 200: {
197
+ description: 'OK',
198
+ content: {
199
+ 'application/json': {
200
+ schema: {
201
+ type: 'object',
202
+ properties: {
203
+ success: { type: 'boolean' },
204
+ provider: { $ref: '#/components/schemas/Provider' },
205
+ details: {
206
+ type: 'array',
207
+ items: { $ref: '#/components/schemas/ProviderDetail' },
208
+ },
209
+ },
210
+ required: ['success', 'provider', 'details'],
97
211
  },
98
212
  },
99
213
  },
@@ -128,8 +242,10 @@ export const configPaths = {
128
242
  items: { $ref: '#/components/schemas/Model' },
129
243
  },
130
244
  default: { type: 'string', nullable: true },
245
+ allowAnyModel: { type: 'boolean' },
246
+ label: { type: 'string' },
131
247
  },
132
- required: ['models'],
248
+ required: ['models', 'allowAnyModel', 'label'],
133
249
  },
134
250
  },
135
251
  },
@@ -1,6 +1,128 @@
1
1
  import { errorResponse, projectQueryParam } from '../helpers';
2
2
 
3
3
  export const skillsPaths = {
4
+ '/v1/config/skills': {
5
+ get: {
6
+ tags: ['config'],
7
+ operationId: 'getSkillsConfig',
8
+ summary: 'Get skills enable/disable config and counts',
9
+ parameters: [projectQueryParam()],
10
+ responses: {
11
+ 200: {
12
+ description: 'OK',
13
+ content: {
14
+ 'application/json': {
15
+ schema: {
16
+ type: 'object',
17
+ properties: {
18
+ enabled: { type: 'boolean' },
19
+ totalCount: { type: 'number' },
20
+ enabledCount: { type: 'number' },
21
+ items: {
22
+ type: 'array',
23
+ items: {
24
+ type: 'object',
25
+ properties: {
26
+ name: { type: 'string' },
27
+ description: { type: 'string' },
28
+ scope: { type: 'string' },
29
+ path: { type: 'string' },
30
+ enabled: { type: 'boolean' },
31
+ },
32
+ required: [
33
+ 'name',
34
+ 'description',
35
+ 'scope',
36
+ 'path',
37
+ 'enabled',
38
+ ],
39
+ },
40
+ },
41
+ },
42
+ required: ['enabled', 'totalCount', 'enabledCount', 'items'],
43
+ },
44
+ },
45
+ },
46
+ },
47
+ 500: errorResponse(),
48
+ },
49
+ },
50
+ put: {
51
+ tags: ['config'],
52
+ operationId: 'updateSkillsConfig',
53
+ summary: 'Update skills enable/disable config',
54
+ parameters: [projectQueryParam()],
55
+ requestBody: {
56
+ required: true,
57
+ content: {
58
+ 'application/json': {
59
+ schema: {
60
+ type: 'object',
61
+ properties: {
62
+ enabled: { type: 'boolean' },
63
+ scope: { type: 'string', enum: ['global', 'local'] },
64
+ items: {
65
+ type: 'object',
66
+ additionalProperties: {
67
+ type: 'object',
68
+ properties: {
69
+ enabled: { type: 'boolean' },
70
+ },
71
+ },
72
+ },
73
+ },
74
+ },
75
+ },
76
+ },
77
+ },
78
+ responses: {
79
+ 200: {
80
+ description: 'OK',
81
+ content: {
82
+ 'application/json': {
83
+ schema: {
84
+ type: 'object',
85
+ properties: {
86
+ success: { type: 'boolean' },
87
+ enabled: { type: 'boolean' },
88
+ totalCount: { type: 'number' },
89
+ enabledCount: { type: 'number' },
90
+ items: {
91
+ type: 'array',
92
+ items: {
93
+ type: 'object',
94
+ properties: {
95
+ name: { type: 'string' },
96
+ description: { type: 'string' },
97
+ scope: { type: 'string' },
98
+ path: { type: 'string' },
99
+ enabled: { type: 'boolean' },
100
+ },
101
+ required: [
102
+ 'name',
103
+ 'description',
104
+ 'scope',
105
+ 'path',
106
+ 'enabled',
107
+ ],
108
+ },
109
+ },
110
+ },
111
+ required: [
112
+ 'success',
113
+ 'enabled',
114
+ 'totalCount',
115
+ 'enabledCount',
116
+ 'items',
117
+ ],
118
+ },
119
+ },
120
+ },
121
+ },
122
+ 500: errorResponse(),
123
+ },
124
+ },
125
+ },
4
126
  '/v1/skills': {
5
127
  get: {
6
128
  tags: ['config'],
@@ -1,9 +1,37 @@
1
- import { providerIds } from '@ottocode/sdk';
2
-
3
1
  export const schemas = {
4
2
  Provider: {
5
3
  type: 'string',
6
- enum: providerIds,
4
+ description: 'Built-in or custom provider identifier',
5
+ },
6
+ ProviderDetail: {
7
+ type: 'object',
8
+ properties: {
9
+ id: { $ref: '#/components/schemas/Provider' },
10
+ label: { type: 'string' },
11
+ source: { type: 'string', enum: ['built-in', 'custom'] },
12
+ enabled: { type: 'boolean' },
13
+ authorized: { type: 'boolean' },
14
+ custom: { type: 'boolean' },
15
+ compatibility: { type: 'string', nullable: true },
16
+ family: { type: 'string', nullable: true },
17
+ baseURL: { type: 'string', nullable: true },
18
+ apiKeyEnv: { type: 'string', nullable: true },
19
+ hasApiKey: { type: 'boolean' },
20
+ allowAnyModel: { type: 'boolean' },
21
+ modelCount: { type: 'integer' },
22
+ authType: { type: 'string', nullable: true },
23
+ },
24
+ required: [
25
+ 'id',
26
+ 'label',
27
+ 'source',
28
+ 'enabled',
29
+ 'authorized',
30
+ 'custom',
31
+ 'hasApiKey',
32
+ 'allowAnyModel',
33
+ 'modelCount',
34
+ ],
7
35
  },
8
36
  AskResponse: {
9
37
  type: 'object',
@@ -190,6 +218,10 @@ export const schemas = {
190
218
  type: 'array',
191
219
  items: { $ref: '#/components/schemas/Provider' },
192
220
  },
221
+ providerDetails: {
222
+ type: 'array',
223
+ items: { $ref: '#/components/schemas/ProviderDetail' },
224
+ },
193
225
  defaults: {
194
226
  type: 'object',
195
227
  properties: {
package/src/presets.ts CHANGED
@@ -68,7 +68,7 @@ export const BUILTIN_TOOLS = [
68
68
  'tree',
69
69
  'pwd',
70
70
  'cd',
71
- 'bash',
71
+ 'shell',
72
72
  'terminal',
73
73
  'ripgrep',
74
74
  'glob',
@@ -16,8 +16,8 @@ import {
16
16
  exchange,
17
17
  authorizeWeb,
18
18
  exchangeWeb,
19
- authorizeOpenAI,
20
- exchangeOpenAI,
19
+ authorizeOpenAIWeb,
20
+ exchangeOpenAIWeb,
21
21
  authorizeCopilot,
22
22
  pollForCopilotTokenOnce,
23
23
  type ProviderId,
@@ -488,45 +488,23 @@ export function registerAuthRoutes(app: Hono) {
488
488
  try {
489
489
  const provider = c.req.param('provider');
490
490
  const mode = c.req.query('mode') || 'max';
491
+ const host = c.req.header('host') || 'localhost:3000';
492
+ const protocol = c.req.header('x-forwarded-proto') || 'http';
491
493
 
492
494
  let url: string;
493
495
  let verifier: string;
494
496
  let callbackUrl = '';
495
497
 
496
498
  if (provider === 'anthropic') {
497
- const host = c.req.header('host') || 'localhost:3000';
498
- const protocol = c.req.header('x-forwarded-proto') || 'http';
499
499
  callbackUrl = `${protocol}://${host}/v1/auth/${provider}/oauth/callback`;
500
500
  const result = authorizeWeb(mode as 'max' | 'console', callbackUrl);
501
501
  url = result.url;
502
502
  verifier = result.verifier;
503
503
  } else if (provider === 'openai') {
504
- const result = await authorizeOpenAI();
504
+ callbackUrl = `${protocol}://${host}/v1/auth/${provider}/oauth/callback`;
505
+ const result = authorizeOpenAIWeb(callbackUrl);
505
506
  url = result.url;
506
507
  verifier = result.verifier;
507
- callbackUrl = 'localhost';
508
- result
509
- .waitForCallback()
510
- .then(async (code) => {
511
- const tokens = await exchangeOpenAI(code, verifier);
512
- await setAuth(
513
- 'openai',
514
- {
515
- type: 'oauth',
516
- refresh: tokens.refresh,
517
- access: tokens.access,
518
- expires: tokens.expires,
519
- accountId: tokens.accountId,
520
- idToken: tokens.idToken,
521
- },
522
- undefined,
523
- 'global',
524
- );
525
- result.close();
526
- })
527
- .catch(() => {
528
- result.close();
529
- });
530
508
  } else {
531
509
  return c.json({ error: 'OAuth not supported for this provider' }, 400);
532
510
  }
@@ -594,8 +572,24 @@ export function registerAuthRoutes(app: Hono) {
594
572
  'global',
595
573
  );
596
574
  } else if (provider === 'openai') {
597
- return c.html(
598
- '<html><body><h1>OpenAI uses localhost callback</h1><p>This route is not used for OpenAI. Please close this window.</p><script>setTimeout(() => window.close(), 3000);</script></body></html>',
575
+ const tokens = await exchangeOpenAIWeb(
576
+ code ?? '',
577
+ verifier,
578
+ callbackUrl,
579
+ );
580
+
581
+ await setAuth(
582
+ 'openai',
583
+ {
584
+ type: 'oauth',
585
+ refresh: tokens.refresh,
586
+ access: tokens.access,
587
+ expires: tokens.expires,
588
+ accountId: tokens.accountId,
589
+ idToken: tokens.idToken,
590
+ },
591
+ undefined,
592
+ 'global',
599
593
  );
600
594
  }
601
595
 
@@ -1,7 +1,7 @@
1
1
  import type { Hono } from 'hono';
2
2
  import { loadConfig } from '@ottocode/sdk';
3
3
  import { getDb } from '@ottocode/database';
4
- import { isProviderId, logger } from '@ottocode/sdk';
4
+ import { hasConfiguredProvider, logger } from '@ottocode/sdk';
5
5
  import {
6
6
  createBranch,
7
7
  listBranches,
@@ -28,7 +28,8 @@ export function registerBranchRoutes(app: Hono) {
28
28
  }
29
29
 
30
30
  const provider =
31
- typeof body.provider === 'string' && isProviderId(body.provider)
31
+ typeof body.provider === 'string' &&
32
+ hasConfiguredProvider(cfg, body.provider)
32
33
  ? body.provider
33
34
  : undefined;
34
35
 
@@ -2,6 +2,7 @@ import type { Hono } from 'hono';
2
2
  import {
3
3
  setConfig,
4
4
  loadConfig,
5
+ hasConfiguredProvider,
5
6
  type ProviderId,
6
7
  type ReasoningLevel,
7
8
  } from '@ottocode/sdk';
@@ -12,6 +13,7 @@ export function registerDefaultsRoute(app: Hono) {
12
13
  app.patch('/v1/config/defaults', async (c) => {
13
14
  try {
14
15
  const projectRoot = c.req.query('project') || process.cwd();
16
+ const cfg = await loadConfig(projectRoot);
15
17
  const body = await c.req.json<{
16
18
  agent?: string;
17
19
  provider?: string;
@@ -41,7 +43,12 @@ export function registerDefaultsRoute(app: Hono) {
41
43
  }> = {};
42
44
 
43
45
  if (body.agent) updates.agent = body.agent;
44
- if (body.provider) updates.provider = body.provider as ProviderId;
46
+ if (body.provider) {
47
+ if (!hasConfiguredProvider(cfg, body.provider)) {
48
+ return c.json({ error: `Invalid provider: ${body.provider}` }, 400);
49
+ }
50
+ updates.provider = body.provider as ProviderId;
51
+ }
45
52
  if (body.model) updates.model = body.model;
46
53
  if (body.toolApproval) updates.toolApproval = body.toolApproval;
47
54
  if (body.guidedMode !== undefined) updates.guidedMode = body.guidedMode;
@@ -62,11 +69,11 @@ export function registerDefaultsRoute(app: Hono) {
62
69
 
63
70
  await setConfig(scope, updates, projectRoot);
64
71
 
65
- const cfg = await loadConfig(projectRoot);
72
+ const nextCfg = await loadConfig(projectRoot);
66
73
 
67
74
  return c.json({
68
75
  success: true,
69
- defaults: cfg.defaults,
76
+ defaults: nextCfg.defaults,
70
77
  });
71
78
  } catch (error) {
72
79
  logger.error('Failed to update defaults', error);
@@ -7,6 +7,7 @@ import {
7
7
  discoverAllAgents,
8
8
  getAuthorizedProviders,
9
9
  getDefault,
10
+ getProviderDetails,
10
11
  } from './utils.ts';
11
12
 
12
13
  export function registerMainConfigRoute(app: Hono) {
@@ -37,6 +38,7 @@ export function registerMainConfigRoute(app: Hono) {
37
38
  embeddedConfig,
38
39
  cfg,
39
40
  );
41
+ const providerDetails = await getProviderDetails(embeddedConfig, cfg);
40
42
 
41
43
  const defaults = {
42
44
  agent: getDefault(
@@ -80,6 +82,7 @@ export function registerMainConfigRoute(app: Hono) {
80
82
  return c.json({
81
83
  agents: allAgents,
82
84
  providers: authorizedProviders,
85
+ providerDetails,
83
86
  defaults,
84
87
  });
85
88
  } catch (error) {
@@ -1,10 +1,15 @@
1
1
  import type { Hono } from 'hono';
2
2
  import {
3
+ discoverOllamaModels,
3
4
  loadConfig,
4
5
  catalog,
6
+ getConfiguredProviderModels,
7
+ getProviderDefinition,
8
+ providerAllowsAnyModel,
5
9
  getAuth,
6
10
  logger,
7
11
  readEnvKey,
12
+ type ModelInfo,
8
13
  type ProviderId,
9
14
  filterModelsForAuthType,
10
15
  } from '@ottocode/sdk';
@@ -79,6 +84,49 @@ async function getAuthorizedCopilotModels(
79
84
  return successful ? merged : null;
80
85
  }
81
86
 
87
+ async function discoverProviderModels(args: {
88
+ provider: ProviderId;
89
+ providerDefinition: NonNullable<ReturnType<typeof getProviderDefinition>>;
90
+ projectRoot: string;
91
+ }): Promise<ModelInfo[] | undefined> {
92
+ const { provider, providerDefinition, projectRoot } = args;
93
+ if (
94
+ providerDefinition.compatibility !== 'ollama' ||
95
+ !providerDefinition.baseURL
96
+ ) {
97
+ return undefined;
98
+ }
99
+
100
+ try {
101
+ const auth = await getAuth(provider, projectRoot);
102
+ const apiKey =
103
+ auth?.type === 'api'
104
+ ? auth.key
105
+ : (readEnvKey(provider) ?? providerDefinition.apiKey);
106
+ const discovered = await discoverOllamaModels({
107
+ baseURL: providerDefinition.baseURL,
108
+ apiKey,
109
+ includeDetails: false,
110
+ });
111
+ return discovered.models;
112
+ } catch (error) {
113
+ logger.warn('Failed to discover Ollama models', {
114
+ provider,
115
+ error: error instanceof Error ? error.message : String(error),
116
+ });
117
+ return [];
118
+ }
119
+ }
120
+
121
+ function shouldLazyLoadProviderModels(
122
+ providerDefinition: NonNullable<ReturnType<typeof getProviderDefinition>>,
123
+ ): boolean {
124
+ return (
125
+ providerDefinition.compatibility === 'ollama' ||
126
+ providerDefinition.source === 'custom'
127
+ );
128
+ }
129
+
82
130
  export function registerModelsRoutes(app: Hono) {
83
131
  app.get('/v1/config/providers/:provider/models', async (c) => {
84
132
  try {
@@ -103,8 +151,9 @@ export function registerModelsRoutes(app: Hono) {
103
151
  return c.json({ error: 'Provider not authorized' }, 403);
104
152
  }
105
153
 
106
- const providerCatalog = catalog[provider];
107
- if (!providerCatalog) {
154
+ const providerCatalog = catalog[provider as keyof typeof catalog];
155
+ const providerDefinition = getProviderDefinition(cfg, provider);
156
+ if (!providerDefinition) {
108
157
  logger.warn('Provider not found in catalog', { provider });
109
158
  return c.json({ error: 'Provider not found' }, 404);
110
159
  }
@@ -114,11 +163,21 @@ export function registerModelsRoutes(app: Hono) {
114
163
  provider,
115
164
  projectRoot,
116
165
  );
117
- const filteredModels = filterModelsForAuthType(
166
+ const discoveredModels = await discoverProviderModels({
118
167
  provider,
119
- providerCatalog.models,
120
- authType,
121
- );
168
+ providerDefinition,
169
+ projectRoot,
170
+ });
171
+ const filteredModels =
172
+ providerDefinition.compatibility === 'ollama'
173
+ ? (discoveredModels ?? [])
174
+ : providerCatalog
175
+ ? filterModelsForAuthType(
176
+ provider,
177
+ providerCatalog.models,
178
+ authType,
179
+ )
180
+ : getConfiguredProviderModels(cfg, provider);
122
181
  const copilotAllowedModels =
123
182
  provider === 'copilot'
124
183
  ? await getAuthorizedCopilotModels(projectRoot)
@@ -145,6 +204,8 @@ export function registerModelsRoutes(app: Hono) {
145
204
  embeddedConfig?.defaults?.model,
146
205
  cfg.defaults.model,
147
206
  ),
207
+ allowAnyModel: providerAllowsAnyModel(cfg, provider),
208
+ label: providerDefinition.label,
148
209
  });
149
210
  } catch (error) {
150
211
  logger.error('Failed to get provider models', error);
@@ -184,21 +245,30 @@ export function registerModelsRoutes(app: Hono) {
184
245
  > = {};
185
246
 
186
247
  for (const provider of authorizedProviders) {
187
- const providerCatalog = catalog[provider];
188
- if (providerCatalog) {
248
+ const providerCatalog = catalog[provider as keyof typeof catalog];
249
+ const providerDefinition = getProviderDefinition(cfg, provider);
250
+ if (providerDefinition) {
251
+ const dynamicModels =
252
+ shouldLazyLoadProviderModels(providerDefinition);
189
253
  const authType = await getAuthTypeForProvider(
190
254
  embeddedConfig,
191
255
  provider,
192
256
  projectRoot,
193
257
  );
194
- const filteredModels = filterModelsForAuthType(
195
- provider,
196
- providerCatalog.models,
197
- authType,
198
- );
258
+ const filteredModels = dynamicModels
259
+ ? getConfiguredProviderModels(cfg, provider)
260
+ : providerCatalog
261
+ ? filterModelsForAuthType(
262
+ provider,
263
+ providerCatalog.models,
264
+ authType,
265
+ )
266
+ : getConfiguredProviderModels(cfg, provider);
199
267
  modelsMap[provider] = {
200
- label: providerCatalog.label || provider,
268
+ label: providerDefinition.label,
201
269
  authType,
270
+ allowAnyModel: providerDefinition.allowAnyModel,
271
+ dynamicModels,
202
272
  models: filteredModels.map((m) => ({
203
273
  id: m.id,
204
274
  label: m.label || m.id,