@dexto/server 1.6.20 → 1.6.22

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 (133) hide show
  1. package/dist/a2a/jsonrpc/methods.cjs +1 -1
  2. package/dist/a2a/jsonrpc/methods.d.ts +14 -4
  3. package/dist/a2a/jsonrpc/methods.d.ts.map +1 -1
  4. package/dist/a2a/jsonrpc/methods.js +1 -1
  5. package/dist/approval/wire-approval-events.cjs +44 -0
  6. package/dist/approval/wire-approval-events.d.ts +4 -0
  7. package/dist/approval/wire-approval-events.d.ts.map +1 -0
  8. package/dist/approval/wire-approval-events.js +20 -0
  9. package/dist/events/session-sse-subscriber.cjs +167 -0
  10. package/dist/events/session-sse-subscriber.d.ts +13 -0
  11. package/dist/events/session-sse-subscriber.d.ts.map +1 -0
  12. package/dist/events/session-sse-subscriber.js +143 -0
  13. package/dist/hono/__tests__/test-fixtures.cjs +8 -0
  14. package/dist/hono/__tests__/test-fixtures.d.ts +1 -0
  15. package/dist/hono/__tests__/test-fixtures.d.ts.map +1 -1
  16. package/dist/hono/__tests__/test-fixtures.js +8 -0
  17. package/dist/hono/index.cjs +40 -8
  18. package/dist/hono/index.d.ts +45 -4531
  19. package/dist/hono/index.d.ts.map +1 -1
  20. package/dist/hono/index.js +43 -9
  21. package/dist/hono/node/index.cjs +51 -6
  22. package/dist/hono/node/index.d.ts.map +1 -1
  23. package/dist/hono/node/index.js +51 -6
  24. package/dist/hono/routes/a2a-jsonrpc.d.ts.map +1 -1
  25. package/dist/hono/routes/a2a-tasks.cjs +158 -32
  26. package/dist/hono/routes/a2a-tasks.d.ts +1 -502
  27. package/dist/hono/routes/a2a-tasks.d.ts.map +1 -1
  28. package/dist/hono/routes/a2a-tasks.js +162 -32
  29. package/dist/hono/routes/a2a.d.ts.map +1 -1
  30. package/dist/hono/routes/agents.cjs +410 -329
  31. package/dist/hono/routes/agents.d.ts +16043 -68
  32. package/dist/hono/routes/agents.d.ts.map +1 -1
  33. package/dist/hono/routes/agents.js +418 -330
  34. package/dist/hono/routes/approvals.cjs +102 -88
  35. package/dist/hono/routes/approvals.d.ts +2089 -142
  36. package/dist/hono/routes/approvals.d.ts.map +1 -1
  37. package/dist/hono/routes/approvals.js +108 -89
  38. package/dist/hono/routes/dexto-auth.cjs +40 -33
  39. package/dist/hono/routes/dexto-auth.d.ts +401 -2
  40. package/dist/hono/routes/dexto-auth.d.ts.map +1 -1
  41. package/dist/hono/routes/dexto-auth.js +40 -33
  42. package/dist/hono/routes/discovery.cjs +16 -14
  43. package/dist/hono/routes/discovery.d.ts +586 -1
  44. package/dist/hono/routes/discovery.d.ts.map +1 -1
  45. package/dist/hono/routes/discovery.js +16 -14
  46. package/dist/hono/routes/greeting.cjs +26 -22
  47. package/dist/hono/routes/greeting.d.ts +787 -3
  48. package/dist/hono/routes/greeting.d.ts.map +1 -1
  49. package/dist/hono/routes/greeting.js +26 -22
  50. package/dist/hono/routes/health.d.ts +1 -1
  51. package/dist/hono/routes/key.cjs +60 -52
  52. package/dist/hono/routes/key.d.ts +1597 -1
  53. package/dist/hono/routes/key.d.ts.map +1 -1
  54. package/dist/hono/routes/key.js +60 -52
  55. package/dist/hono/routes/llm.cjs +382 -349
  56. package/dist/hono/routes/llm.d.ts +12148 -98
  57. package/dist/hono/routes/llm.d.ts.map +1 -1
  58. package/dist/hono/routes/llm.js +386 -349
  59. package/dist/hono/routes/mcp.cjs +257 -226
  60. package/dist/hono/routes/mcp.d.ts +6605 -309
  61. package/dist/hono/routes/mcp.d.ts.map +1 -1
  62. package/dist/hono/routes/mcp.js +263 -225
  63. package/dist/hono/routes/memory.cjs +102 -89
  64. package/dist/hono/routes/memory.d.ts +5368 -4
  65. package/dist/hono/routes/memory.d.ts.map +1 -1
  66. package/dist/hono/routes/memory.js +108 -90
  67. package/dist/hono/routes/messages.cjs +189 -191
  68. package/dist/hono/routes/messages.d.ts +3900 -12
  69. package/dist/hono/routes/messages.d.ts.map +1 -1
  70. package/dist/hono/routes/messages.js +192 -191
  71. package/dist/hono/routes/models.cjs +106 -64
  72. package/dist/hono/routes/models.d.ts +2875 -2
  73. package/dist/hono/routes/models.d.ts.map +1 -1
  74. package/dist/hono/routes/models.js +108 -64
  75. package/dist/hono/routes/openrouter.cjs +79 -65
  76. package/dist/hono/routes/openrouter.d.ts +854 -1
  77. package/dist/hono/routes/openrouter.d.ts.map +1 -1
  78. package/dist/hono/routes/openrouter.js +79 -65
  79. package/dist/hono/routes/prompts.cjs +136 -109
  80. package/dist/hono/routes/prompts.d.ts +2818 -10
  81. package/dist/hono/routes/prompts.d.ts.map +1 -1
  82. package/dist/hono/routes/prompts.js +138 -109
  83. package/dist/hono/routes/queue.cjs +133 -120
  84. package/dist/hono/routes/queue.d.ts +5240 -11
  85. package/dist/hono/routes/queue.d.ts.map +1 -1
  86. package/dist/hono/routes/queue.js +136 -120
  87. package/dist/hono/routes/resources.cjs +65 -46
  88. package/dist/hono/routes/resources.d.ts +1983 -5
  89. package/dist/hono/routes/resources.d.ts.map +1 -1
  90. package/dist/hono/routes/resources.js +72 -47
  91. package/dist/hono/routes/schedules.cjs +233 -226
  92. package/dist/hono/routes/schedules.d.ts +4198 -22
  93. package/dist/hono/routes/schedules.d.ts.map +1 -1
  94. package/dist/hono/routes/schedules.js +233 -226
  95. package/dist/hono/routes/search.cjs +34 -30
  96. package/dist/hono/routes/search.d.ts +3094 -17
  97. package/dist/hono/routes/search.d.ts.map +1 -1
  98. package/dist/hono/routes/search.js +40 -31
  99. package/dist/hono/routes/sessions.cjs +491 -393
  100. package/dist/hono/routes/sessions.d.ts +18263 -65
  101. package/dist/hono/routes/sessions.d.ts.map +1 -1
  102. package/dist/hono/routes/sessions.js +497 -395
  103. package/dist/hono/routes/static.d.ts.map +1 -1
  104. package/dist/hono/routes/system-prompt.cjs +57 -61
  105. package/dist/hono/routes/system-prompt.d.ts +1228 -2
  106. package/dist/hono/routes/system-prompt.d.ts.map +1 -1
  107. package/dist/hono/routes/system-prompt.js +58 -62
  108. package/dist/hono/routes/tools.cjs +29 -34
  109. package/dist/hono/routes/tools.d.ts +1755 -6
  110. package/dist/hono/routes/tools.d.ts.map +1 -1
  111. package/dist/hono/routes/tools.js +33 -33
  112. package/dist/hono/routes/webhooks.cjs +115 -123
  113. package/dist/hono/routes/webhooks.d.ts +2501 -11
  114. package/dist/hono/routes/webhooks.d.ts.map +1 -1
  115. package/dist/hono/routes/webhooks.js +120 -124
  116. package/dist/hono/routes/workspaces.cjs +84 -79
  117. package/dist/hono/routes/workspaces.d.ts +2093 -2
  118. package/dist/hono/routes/workspaces.d.ts.map +1 -1
  119. package/dist/hono/routes/workspaces.js +89 -80
  120. package/dist/hono/schemas/responses.cjs +463 -260
  121. package/dist/hono/schemas/responses.d.ts +1893 -209
  122. package/dist/hono/schemas/responses.d.ts.map +1 -1
  123. package/dist/hono/schemas/responses.js +203 -14
  124. package/dist/hono/start-server.cjs +9 -0
  125. package/dist/hono/start-server.d.ts.map +1 -1
  126. package/dist/hono/start-server.js +9 -0
  127. package/dist/hono/types.d.ts +11 -0
  128. package/dist/hono/types.d.ts.map +1 -1
  129. package/dist/index.cjs +5 -1
  130. package/dist/index.d.ts +2 -0
  131. package/dist/index.d.ts.map +1 -1
  132. package/dist/index.js +2 -0
  133. package/package.json +7 -7
@@ -30,9 +30,13 @@ import {
30
30
  isDextoAuthEnabled
31
31
  } from "@dexto/agent-management";
32
32
  import {
33
+ BadRequestErrorResponse,
34
+ ConflictErrorResponse,
35
+ InternalErrorResponse,
33
36
  ProviderCatalogSchema,
34
37
  ModelFlatSchema,
35
38
  LLMConfigResponseSchema,
39
+ NotFoundErrorResponse,
36
40
  StandardErrorEnvelopeSchema
37
41
  } from "../schemas/responses.js";
38
42
  const MODEL_PICKER_FEATURED_LIMIT = 8;
@@ -52,7 +56,7 @@ const CatalogQuerySchema = z.object({
52
56
  hasKey: z.union([z.literal("true"), z.literal("false"), z.literal("1"), z.literal("0")]).optional().transform(
53
57
  (raw) => raw === "true" || raw === "1" ? true : raw === "false" || raw === "0" ? false : void 0
54
58
  ).describe("Filter by API key presence (true or false)"),
55
- fileType: z.enum(SUPPORTED_FILE_TYPES).optional().describe("Filter by supported file type (audio, pdf, or image)"),
59
+ fileType: z.enum(SUPPORTED_FILE_TYPES).optional().describe("Filter by supported file type (audio, pdf, image, video, or document)"),
56
60
  defaultOnly: z.union([z.literal("true"), z.literal("false"), z.literal("1"), z.literal("0")]).optional().transform(
57
61
  (raw) => raw === "true" || raw === "1" ? true : raw === "false" || raw === "0" ? false : void 0
58
62
  ).describe("Include only default models (true or false)"),
@@ -105,356 +109,374 @@ const ModelPickerErrorResponses = {
105
109
  }
106
110
  }
107
111
  };
108
- function createLlmRouter(getAgent) {
109
- const app = new OpenAPIHono();
110
- const currentRoute = createRoute({
111
- method: "get",
112
- path: "/llm/current",
113
- summary: "Get Current LLM Config",
114
- description: "Retrieves the current LLM configuration for the agent or a specific session",
115
- tags: ["llm"],
116
- request: { query: CurrentQuerySchema },
117
- responses: {
118
- 200: {
119
- description: "Current LLM config",
120
- content: {
121
- "application/json": {
122
- schema: z.object({
123
- config: LLMConfigResponseSchema.partial({
124
- maxIterations: true
125
- }).extend({
126
- displayName: z.string().optional().describe("Human-readable model display name")
127
- }),
128
- routing: z.object({
129
- viaDexto: z.boolean().describe(
130
- "Whether requests route through Dexto gateway"
131
- )
132
- }).describe(
133
- "Routing information for the current LLM configuration"
112
+ const CapabilitiesQuerySchema = z.object({
113
+ provider: z.enum(LLM_PROVIDERS).describe("LLM provider name"),
114
+ model: z.string().min(1).describe("Model name (supports both native and OpenRouter format)")
115
+ }).strict().describe("Query parameters for model capability lookup");
116
+ const SetFavoritesBodySchema = z.object({
117
+ favorites: z.array(ModelPickerModelRefSchema).describe("Complete list of favorite model references")
118
+ }).strict().describe("Request body for setting favorite models");
119
+ const currentRoute = createRoute({
120
+ method: "get",
121
+ path: "/llm/current",
122
+ summary: "Get Current LLM Config",
123
+ description: "Retrieves the current LLM configuration for the agent or a specific session",
124
+ tags: ["llm"],
125
+ request: { query: CurrentQuerySchema },
126
+ responses: {
127
+ 200: {
128
+ description: "Current LLM config",
129
+ content: {
130
+ "application/json": {
131
+ schema: z.object({
132
+ config: LLMConfigResponseSchema.partial({
133
+ maxIterations: true
134
+ }).extend({
135
+ displayName: z.string().optional().describe("Human-readable model display name")
136
+ }),
137
+ routing: z.object({
138
+ viaDexto: z.boolean().describe("Whether requests route through Dexto gateway")
139
+ }).describe("Routing information for the current LLM configuration")
140
+ }).describe("Response containing current LLM configuration")
141
+ }
142
+ }
143
+ },
144
+ 400: BadRequestErrorResponse,
145
+ 404: NotFoundErrorResponse,
146
+ 500: InternalErrorResponse
147
+ }
148
+ });
149
+ const catalogRoute = createRoute({
150
+ method: "get",
151
+ path: "/llm/catalog",
152
+ summary: "LLM Catalog",
153
+ description: "Providers, models, capabilities, and API key status",
154
+ tags: ["llm"],
155
+ request: { query: CatalogQuerySchema },
156
+ responses: {
157
+ 200: {
158
+ description: "LLM catalog",
159
+ content: {
160
+ "application/json": {
161
+ schema: z.union([
162
+ z.object({
163
+ providers: z.record(z.enum(LLM_PROVIDERS), ProviderCatalogSchema).describe(
164
+ "Providers grouped by ID with their models and capabilities"
165
+ )
166
+ }).strict().describe("Grouped catalog response (mode=grouped)"),
167
+ z.object({
168
+ models: z.array(ModelFlatSchema).describe(
169
+ "Flat list of all models with provider information"
134
170
  )
135
- }).describe("Response containing current LLM configuration")
136
- }
171
+ }).strict().describe("Flat catalog response (mode=flat)")
172
+ ]).describe(
173
+ "LLM catalog in grouped or flat format based on mode query parameter"
174
+ )
137
175
  }
138
176
  }
139
- }
140
- });
141
- const catalogRoute = createRoute({
142
- method: "get",
143
- path: "/llm/catalog",
144
- summary: "LLM Catalog",
145
- description: "Providers, models, capabilities, and API key status",
146
- tags: ["llm"],
147
- request: { query: CatalogQuerySchema },
148
- responses: {
149
- 200: {
150
- description: "LLM catalog",
151
- content: {
152
- "application/json": {
153
- schema: z.union([
154
- z.object({
155
- providers: z.record(z.enum(LLM_PROVIDERS), ProviderCatalogSchema).describe(
156
- "Providers grouped by ID with their models and capabilities"
157
- )
158
- }).strict().describe("Grouped catalog response (mode=grouped)"),
159
- z.object({
160
- models: z.array(ModelFlatSchema).describe(
161
- "Flat list of all models with provider information"
162
- )
163
- }).strict().describe("Flat catalog response (mode=flat)")
164
- ]).describe(
165
- "LLM catalog in grouped or flat format based on mode query parameter"
166
- )
167
- }
177
+ },
178
+ 400: BadRequestErrorResponse,
179
+ 404: NotFoundErrorResponse,
180
+ 500: InternalErrorResponse
181
+ }
182
+ });
183
+ const switchRoute = createRoute({
184
+ method: "post",
185
+ path: "/llm/switch",
186
+ summary: "Switch LLM",
187
+ description: "Switches the LLM configuration for the agent or a specific session",
188
+ tags: ["llm"],
189
+ request: {
190
+ body: {
191
+ content: {
192
+ "application/json": {
193
+ schema: SwitchLLMBodySchema
168
194
  }
169
195
  }
170
196
  }
171
- });
172
- const switchRoute = createRoute({
173
- method: "post",
174
- path: "/llm/switch",
175
- summary: "Switch LLM",
176
- description: "Switches the LLM configuration for the agent or a specific session",
177
- tags: ["llm"],
178
- request: {
179
- body: {
180
- content: {
181
- "application/json": {
182
- schema: SwitchLLMBodySchema
183
- }
197
+ },
198
+ responses: {
199
+ 200: {
200
+ description: "LLM switch result",
201
+ content: {
202
+ "application/json": {
203
+ schema: z.object({
204
+ config: LLMConfigResponseSchema.describe(
205
+ "New LLM configuration with all defaults applied (apiKey omitted)"
206
+ ),
207
+ sessionId: z.string().optional().describe("Session ID if session-specific switch")
208
+ }).describe("LLM switch result")
184
209
  }
185
210
  }
186
211
  },
187
- responses: {
188
- 200: {
189
- description: "LLM switch result",
190
- content: {
191
- "application/json": {
192
- schema: z.object({
193
- config: LLMConfigResponseSchema.describe(
194
- "New LLM configuration with all defaults applied (apiKey omitted)"
195
- ),
196
- sessionId: z.string().optional().describe("Session ID if session-specific switch")
197
- }).describe("LLM switch result")
198
- }
212
+ 400: BadRequestErrorResponse,
213
+ 404: NotFoundErrorResponse,
214
+ 409: ConflictErrorResponse,
215
+ 500: InternalErrorResponse
216
+ }
217
+ });
218
+ const listCustomModelsRoute = createRoute({
219
+ method: "get",
220
+ path: "/llm/custom-models",
221
+ summary: "List Custom Models",
222
+ description: "Returns all saved custom openai-compatible model configurations",
223
+ tags: ["llm"],
224
+ responses: {
225
+ 200: {
226
+ description: "List of custom models",
227
+ content: {
228
+ "application/json": {
229
+ schema: z.object({
230
+ models: z.array(CustomModelSchema).describe("List of custom models")
231
+ })
199
232
  }
200
233
  }
201
- }
202
- });
203
- const listCustomModelsRoute = createRoute({
204
- method: "get",
205
- path: "/llm/custom-models",
206
- summary: "List Custom Models",
207
- description: "Returns all saved custom openai-compatible model configurations",
208
- tags: ["llm"],
209
- responses: {
210
- 200: {
211
- description: "List of custom models",
212
- content: {
213
- "application/json": {
214
- schema: z.object({
215
- models: z.array(CustomModelSchema).describe("List of custom models")
216
- })
217
- }
234
+ },
235
+ 400: BadRequestErrorResponse,
236
+ 404: NotFoundErrorResponse,
237
+ 500: InternalErrorResponse
238
+ }
239
+ });
240
+ const createCustomModelRoute = createRoute({
241
+ method: "post",
242
+ path: "/llm/custom-models",
243
+ summary: "Create Custom Model",
244
+ description: "Saves a new custom openai-compatible model configuration",
245
+ tags: ["llm"],
246
+ request: {
247
+ body: { content: { "application/json": { schema: CustomModelSchema } } }
248
+ },
249
+ responses: {
250
+ 200: {
251
+ description: "Custom model saved",
252
+ content: {
253
+ "application/json": {
254
+ schema: z.object({
255
+ ok: z.literal(true).describe("Success indicator"),
256
+ model: CustomModelSchema
257
+ })
218
258
  }
219
259
  }
220
- }
221
- });
222
- const createCustomModelRoute = createRoute({
223
- method: "post",
224
- path: "/llm/custom-models",
225
- summary: "Create Custom Model",
226
- description: "Saves a new custom openai-compatible model configuration",
227
- tags: ["llm"],
228
- request: {
229
- body: { content: { "application/json": { schema: CustomModelSchema } } }
230
260
  },
231
- responses: {
232
- 200: {
233
- description: "Custom model saved",
234
- content: {
235
- "application/json": {
236
- schema: z.object({
237
- ok: z.literal(true).describe("Success indicator"),
238
- model: CustomModelSchema
239
- })
240
- }
261
+ 400: BadRequestErrorResponse,
262
+ 404: NotFoundErrorResponse,
263
+ 409: ConflictErrorResponse,
264
+ 500: InternalErrorResponse
265
+ }
266
+ });
267
+ const deleteCustomModelRoute = createRoute({
268
+ method: "delete",
269
+ path: "/llm/custom-models/{name}",
270
+ summary: "Delete Custom Model",
271
+ description: "Deletes a custom model by name",
272
+ tags: ["llm"],
273
+ request: {
274
+ params: z.object({
275
+ name: z.string().min(1).describe("Model name to delete")
276
+ })
277
+ },
278
+ responses: {
279
+ 200: {
280
+ description: "Custom model deleted",
281
+ content: {
282
+ "application/json": {
283
+ schema: z.object({
284
+ ok: z.literal(true).describe("Success indicator"),
285
+ deleted: z.string().describe("Name of the deleted model")
286
+ })
241
287
  }
242
288
  }
243
- }
244
- });
245
- const deleteCustomModelRoute = createRoute({
246
- method: "delete",
247
- path: "/llm/custom-models/{name}",
248
- summary: "Delete Custom Model",
249
- description: "Deletes a custom model by name",
250
- tags: ["llm"],
251
- request: {
252
- params: z.object({
253
- name: z.string().min(1).describe("Model name to delete")
254
- })
255
289
  },
256
- responses: {
257
- 200: {
258
- description: "Custom model deleted",
259
- content: {
260
- "application/json": {
261
- schema: z.object({
262
- ok: z.literal(true).describe("Success indicator"),
263
- deleted: z.string().describe("Name of the deleted model")
264
- })
265
- }
266
- }
267
- },
268
- 404: {
269
- description: "Custom model not found",
270
- content: {
271
- "application/json": {
272
- schema: z.object({
273
- ok: z.literal(false).describe("Failure indicator"),
274
- error: z.string().describe("Error message")
275
- })
276
- }
290
+ 400: BadRequestErrorResponse,
291
+ 404: NotFoundErrorResponse,
292
+ 500: InternalErrorResponse
293
+ }
294
+ });
295
+ const capabilitiesRoute = createRoute({
296
+ method: "get",
297
+ path: "/llm/capabilities",
298
+ summary: "Get Model Capabilities",
299
+ description: "Returns the capabilities (supported file types) for a specific provider/model combination. Handles gateway providers (dexto-nova, openrouter) by resolving to the underlying model capabilities.",
300
+ tags: ["llm"],
301
+ request: {
302
+ query: CapabilitiesQuerySchema
303
+ },
304
+ responses: {
305
+ 200: {
306
+ description: "Model capabilities",
307
+ content: {
308
+ "application/json": {
309
+ schema: z.object({
310
+ provider: z.enum(LLM_PROVIDERS).describe("Provider name"),
311
+ model: z.string().describe("Model name as provided"),
312
+ supportedFileTypes: z.array(z.enum(SUPPORTED_FILE_TYPES)).describe("File types supported by this model"),
313
+ reasoning: z.object({
314
+ capable: z.boolean().describe(
315
+ "Whether Dexto considers this provider/model reasoning-capable (derived from registry metadata plus explicit provider/model rules)"
316
+ ),
317
+ paradigm: z.enum([
318
+ "effort",
319
+ "adaptive-effort",
320
+ "thinking-level",
321
+ "budget",
322
+ "none"
323
+ ]).describe("Reasoning control paradigm for this model"),
324
+ variants: z.array(
325
+ z.object({
326
+ id: z.string().describe(
327
+ "Native reasoning variant identifier"
328
+ ),
329
+ label: z.string().describe(
330
+ "Display label for the native reasoning variant"
331
+ )
332
+ }).strict()
333
+ ).describe("Native reasoning variants exposed to users"),
334
+ supportedVariants: z.array(z.string()).describe(
335
+ "Native reasoning variant IDs supported for this model/provider"
336
+ ),
337
+ defaultVariant: z.string().optional().describe(
338
+ "Default reasoning variant used when no explicit override is set"
339
+ ),
340
+ supportsBudgetTokens: z.boolean().describe(
341
+ "Whether this provider/model supports a budgetTokens-style escape hatch"
342
+ )
343
+ }).strict().describe(
344
+ "Reasoning tuning capabilities derived from registry metadata and explicit provider/model rules"
345
+ )
346
+ })
277
347
  }
278
348
  }
279
- }
280
- });
281
- const capabilitiesRoute = createRoute({
282
- method: "get",
283
- path: "/llm/capabilities",
284
- summary: "Get Model Capabilities",
285
- description: "Returns the capabilities (supported file types) for a specific provider/model combination. Handles gateway providers (dexto-nova, openrouter) by resolving to the underlying model capabilities.",
286
- tags: ["llm"],
287
- request: {
288
- query: z.object({
289
- provider: z.enum(LLM_PROVIDERS).describe("LLM provider name"),
290
- model: z.string().min(1).describe("Model name (supports both native and OpenRouter format)")
291
- })
292
349
  },
293
- responses: {
294
- 200: {
295
- description: "Model capabilities",
296
- content: {
297
- "application/json": {
298
- schema: z.object({
299
- provider: z.enum(LLM_PROVIDERS).describe("Provider name"),
300
- model: z.string().describe("Model name as provided"),
301
- supportedFileTypes: z.array(z.enum(SUPPORTED_FILE_TYPES)).describe("File types supported by this model"),
302
- reasoning: z.object({
303
- capable: z.boolean().describe(
304
- "Whether Dexto considers this provider/model reasoning-capable (derived from registry metadata plus explicit provider/model rules)"
305
- ),
306
- paradigm: z.enum([
307
- "effort",
308
- "adaptive-effort",
309
- "thinking-level",
310
- "budget",
311
- "none"
312
- ]).describe("Reasoning control paradigm for this model"),
313
- variants: z.array(
314
- z.object({
315
- id: z.string().describe(
316
- "Native reasoning variant identifier"
317
- ),
318
- label: z.string().describe(
319
- "Display label for the native reasoning variant"
320
- )
321
- }).strict()
322
- ).describe("Native reasoning variants exposed to users"),
323
- supportedVariants: z.array(z.string()).describe(
324
- "Native reasoning variant IDs supported for this model/provider"
325
- ),
326
- defaultVariant: z.string().optional().describe(
327
- "Default reasoning variant used when no explicit override is set"
328
- ),
329
- supportsBudgetTokens: z.boolean().describe(
330
- "Whether this provider/model supports a budgetTokens-style escape hatch"
331
- )
332
- }).strict().describe(
333
- "Reasoning tuning capabilities derived from registry metadata and explicit provider/model rules"
334
- )
335
- })
336
- }
350
+ 400: BadRequestErrorResponse,
351
+ 404: NotFoundErrorResponse,
352
+ 500: InternalErrorResponse
353
+ }
354
+ });
355
+ const modelPickerStateRoute = createRoute({
356
+ method: "get",
357
+ path: "/llm/model-picker-state",
358
+ summary: "Model Picker State",
359
+ description: "Returns hydrated Featured, Recents, Favorites, and Custom sections for the model picker.",
360
+ tags: ["llm"],
361
+ responses: {
362
+ 200: {
363
+ description: "Hydrated model picker sections",
364
+ content: {
365
+ "application/json": {
366
+ schema: z.object({
367
+ featured: z.array(ModelPickerEntrySchema).describe("Curated featured models"),
368
+ recents: z.array(ModelPickerEntrySchema).describe("Most recently used models"),
369
+ favorites: z.array(ModelPickerEntrySchema).describe("User favorited models"),
370
+ custom: z.array(ModelPickerEntrySchema).describe("User-defined custom models")
371
+ }).strict()
337
372
  }
338
373
  }
339
- }
340
- });
341
- const modelPickerStateRoute = createRoute({
342
- method: "get",
343
- path: "/llm/model-picker-state",
344
- summary: "Model Picker State",
345
- description: "Returns hydrated Featured, Recents, Favorites, and Custom sections for the model picker.",
346
- tags: ["llm"],
347
- responses: {
348
- 200: {
349
- description: "Hydrated model picker sections",
350
- content: {
351
- "application/json": {
352
- schema: z.object({
353
- featured: z.array(ModelPickerEntrySchema).describe("Curated featured models"),
354
- recents: z.array(ModelPickerEntrySchema).describe("Most recently used models"),
355
- favorites: z.array(ModelPickerEntrySchema).describe("User favorited models"),
356
- custom: z.array(ModelPickerEntrySchema).describe("User-defined custom models")
357
- }).strict()
358
- }
374
+ },
375
+ 400: ModelPickerErrorResponses[400],
376
+ 404: ModelPickerErrorResponses[404],
377
+ 500: ModelPickerErrorResponses[500]
378
+ }
379
+ });
380
+ const recordRecentModelRoute = createRoute({
381
+ method: "post",
382
+ path: "/llm/model-picker-state/recents",
383
+ summary: "Record Recent Model",
384
+ description: "Records a model selection in recents.",
385
+ tags: ["llm"],
386
+ request: {
387
+ body: {
388
+ required: true,
389
+ content: {
390
+ "application/json": {
391
+ schema: ModelPickerModelRefSchema
359
392
  }
360
- },
361
- ...ModelPickerErrorResponses
393
+ }
362
394
  }
363
- });
364
- const recordRecentModelRoute = createRoute({
365
- method: "post",
366
- path: "/llm/model-picker-state/recents",
367
- summary: "Record Recent Model",
368
- description: "Records a model selection in recents.",
369
- tags: ["llm"],
370
- request: {
371
- body: {
372
- required: true,
373
- content: {
374
- "application/json": {
375
- schema: ModelPickerModelRefSchema
376
- }
395
+ },
396
+ responses: {
397
+ 200: {
398
+ description: "Recent model recorded",
399
+ content: {
400
+ "application/json": {
401
+ schema: z.object({
402
+ ok: z.literal(true).describe("Success indicator")
403
+ }).strict()
377
404
  }
378
405
  }
379
406
  },
380
- responses: {
381
- 200: {
382
- description: "Recent model recorded",
383
- content: {
384
- "application/json": {
385
- schema: z.object({
386
- ok: z.literal(true).describe("Success indicator")
387
- }).strict()
388
- }
407
+ 400: ModelPickerErrorResponses[400],
408
+ 404: ModelPickerErrorResponses[404],
409
+ 500: ModelPickerErrorResponses[500]
410
+ }
411
+ });
412
+ const toggleFavoriteModelRoute = createRoute({
413
+ method: "post",
414
+ path: "/llm/model-picker-state/favorites/toggle",
415
+ summary: "Toggle Favorite Model",
416
+ description: "Adds or removes a model from favorites.",
417
+ tags: ["llm"],
418
+ request: {
419
+ body: {
420
+ required: true,
421
+ content: {
422
+ "application/json": {
423
+ schema: ModelPickerModelRefSchema
389
424
  }
390
- },
391
- ...ModelPickerErrorResponses
425
+ }
392
426
  }
393
- });
394
- const toggleFavoriteModelRoute = createRoute({
395
- method: "post",
396
- path: "/llm/model-picker-state/favorites/toggle",
397
- summary: "Toggle Favorite Model",
398
- description: "Adds or removes a model from favorites.",
399
- tags: ["llm"],
400
- request: {
401
- body: {
402
- required: true,
403
- content: {
404
- "application/json": {
405
- schema: ModelPickerModelRefSchema
406
- }
427
+ },
428
+ responses: {
429
+ 200: {
430
+ description: "Favorite toggled",
431
+ content: {
432
+ "application/json": {
433
+ schema: z.object({
434
+ ok: z.literal(true).describe("Success indicator"),
435
+ isFavorite: z.boolean().describe("Whether the model is now favorited")
436
+ }).strict()
407
437
  }
408
438
  }
409
439
  },
410
- responses: {
411
- 200: {
412
- description: "Favorite toggled",
413
- content: {
414
- "application/json": {
415
- schema: z.object({
416
- ok: z.literal(true).describe("Success indicator"),
417
- isFavorite: z.boolean().describe("Whether the model is now favorited")
418
- }).strict()
419
- }
440
+ 400: ModelPickerErrorResponses[400],
441
+ 404: ModelPickerErrorResponses[404],
442
+ 500: ModelPickerErrorResponses[500]
443
+ }
444
+ });
445
+ const setFavoritesRoute = createRoute({
446
+ method: "put",
447
+ path: "/llm/model-picker-state/favorites",
448
+ summary: "Set Favorite Models",
449
+ description: "Replaces favorite models list. Used by migration or bulk updates.",
450
+ tags: ["llm"],
451
+ request: {
452
+ body: {
453
+ required: true,
454
+ content: {
455
+ "application/json": {
456
+ schema: SetFavoritesBodySchema
420
457
  }
421
- },
422
- ...ModelPickerErrorResponses
458
+ }
423
459
  }
424
- });
425
- const setFavoritesRoute = createRoute({
426
- method: "put",
427
- path: "/llm/model-picker-state/favorites",
428
- summary: "Set Favorite Models",
429
- description: "Replaces favorite models list. Used by migration or bulk updates.",
430
- tags: ["llm"],
431
- request: {
432
- body: {
433
- required: true,
434
- content: {
435
- "application/json": {
436
- schema: z.object({
437
- favorites: z.array(ModelPickerModelRefSchema).describe("Complete list of favorite model references")
438
- }).strict()
439
- }
460
+ },
461
+ responses: {
462
+ 200: {
463
+ description: "Favorites updated",
464
+ content: {
465
+ "application/json": {
466
+ schema: z.object({
467
+ ok: z.literal(true).describe("Success indicator"),
468
+ count: z.number().int().nonnegative().describe("Number of favorites persisted")
469
+ }).strict()
440
470
  }
441
471
  }
442
472
  },
443
- responses: {
444
- 200: {
445
- description: "Favorites updated",
446
- content: {
447
- "application/json": {
448
- schema: z.object({
449
- ok: z.literal(true).describe("Success indicator"),
450
- count: z.number().int().nonnegative().describe("Number of favorites persisted")
451
- }).strict()
452
- }
453
- }
454
- },
455
- ...ModelPickerErrorResponses
456
- }
457
- });
473
+ 400: ModelPickerErrorResponses[400],
474
+ 404: ModelPickerErrorResponses[404],
475
+ 500: ModelPickerErrorResponses[500]
476
+ }
477
+ });
478
+ function createLlmRouter(getAgent) {
479
+ const app = new OpenAPIHono();
458
480
  const isProviderEnabled = (provider) => provider !== "dexto-nova" || isDextoAuthEnabled();
459
481
  const dedupeEntries = (entries) => {
460
482
  const seen = /* @__PURE__ */ new Set();
@@ -594,16 +616,19 @@ function createLlmRouter(getAgent) {
594
616
  }
595
617
  const { apiKey, ...configWithoutKey } = currentConfig;
596
618
  const viaDexto = isDextoAuthEnabled() && currentConfig.provider === "dexto-nova";
597
- return ctx.json({
598
- config: {
599
- ...configWithoutKey,
600
- hasApiKey: !!apiKey,
601
- ...displayName && { displayName }
619
+ return ctx.json(
620
+ {
621
+ config: {
622
+ ...configWithoutKey,
623
+ hasApiKey: !!apiKey,
624
+ ...displayName && { displayName }
625
+ },
626
+ routing: {
627
+ viaDexto
628
+ }
602
629
  },
603
- routing: {
604
- viaDexto
605
- }
606
- });
630
+ 200
631
+ );
607
632
  }).openapi(catalogRoute, (ctx) => {
608
633
  const queryParams = ctx.req.valid("query");
609
634
  const includeModels = queryParams.includeModels ?? true;
@@ -686,9 +711,9 @@ function createLlmRouter(getAgent) {
686
711
  flat.push({ provider: id, ...model });
687
712
  }
688
713
  }
689
- return ctx.json({ models: flat });
714
+ return ctx.json({ models: flat }, 200);
690
715
  }
691
- return ctx.json({ providers: filtered });
716
+ return ctx.json({ providers: filtered }, 200);
692
717
  }).openapi(switchRoute, async (ctx) => {
693
718
  const agent = await getAgent(ctx);
694
719
  const raw = ctx.req.valid("json");
@@ -703,20 +728,23 @@ function createLlmRouter(getAgent) {
703
728
  } catch {
704
729
  }
705
730
  const { apiKey, ...configWithoutKey } = config;
706
- return ctx.json({
707
- config: {
708
- ...configWithoutKey,
709
- hasApiKey: !!apiKey
731
+ return ctx.json(
732
+ {
733
+ config: {
734
+ ...configWithoutKey,
735
+ hasApiKey: !!apiKey
736
+ },
737
+ sessionId
710
738
  },
711
- sessionId
712
- });
739
+ 200
740
+ );
713
741
  }).openapi(listCustomModelsRoute, async (ctx) => {
714
742
  const models = await loadCustomModels();
715
- return ctx.json({ models });
743
+ return ctx.json({ models }, 200);
716
744
  }).openapi(createCustomModelRoute, async (ctx) => {
717
745
  const model = ctx.req.valid("json");
718
746
  await saveCustomModel(model);
719
- return ctx.json({ ok: true, model });
747
+ return ctx.json({ ok: true, model }, 200);
720
748
  }).openapi(deleteCustomModelRoute, async (ctx) => {
721
749
  const { name: encodedName } = ctx.req.valid("param");
722
750
  const name = decodeURIComponent(encodedName);
@@ -733,27 +761,33 @@ function createLlmRouter(getAgent) {
733
761
  return ctx.json({ ok: true, deleted: name }, 200);
734
762
  }).openapi(modelPickerStateRoute, async (ctx) => {
735
763
  const sections = await buildModelPickerSections();
736
- return ctx.json(sections);
764
+ return ctx.json(sections, 200);
737
765
  }).openapi(recordRecentModelRoute, async (ctx) => {
738
766
  const modelRef = ctx.req.valid("json");
739
767
  await recordRecentModel(modelRef);
740
- return ctx.json({ ok: true });
768
+ return ctx.json({ ok: true }, 200);
741
769
  }).openapi(toggleFavoriteModelRoute, async (ctx) => {
742
770
  const modelRef = ctx.req.valid("json");
743
771
  const result = await toggleFavoriteModel(modelRef);
744
- return ctx.json({
745
- ok: true,
746
- isFavorite: result.isFavorite
747
- });
772
+ return ctx.json(
773
+ {
774
+ ok: true,
775
+ isFavorite: result.isFavorite
776
+ },
777
+ 200
778
+ );
748
779
  }).openapi(setFavoritesRoute, async (ctx) => {
749
780
  const payload = ctx.req.valid("json");
750
781
  const state = await setFavoriteModels({
751
782
  favorites: payload.favorites
752
783
  });
753
- return ctx.json({
754
- ok: true,
755
- count: state.favorites.length
756
- });
784
+ return ctx.json(
785
+ {
786
+ ok: true,
787
+ count: state.favorites.length
788
+ },
789
+ 200
790
+ );
757
791
  }).openapi(capabilitiesRoute, (ctx) => {
758
792
  const { provider, model } = ctx.req.valid("query");
759
793
  let supportedFileTypes;
@@ -764,12 +798,15 @@ function createLlmRouter(getAgent) {
764
798
  supportedFileTypes = providerInfo?.supportedFileTypes ?? [];
765
799
  }
766
800
  const reasoning = getReasoningProfile(provider, model);
767
- return ctx.json({
768
- provider,
769
- model,
770
- supportedFileTypes,
771
- reasoning
772
- });
801
+ return ctx.json(
802
+ {
803
+ provider,
804
+ model,
805
+ supportedFileTypes,
806
+ reasoning
807
+ },
808
+ 200
809
+ );
773
810
  });
774
811
  }
775
812
  export {