@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
@@ -14,8 +14,15 @@ import os from "os";
14
14
  import path from "path";
15
15
  import { promises as fs } from "fs";
16
16
  import { DextoValidationError, AgentErrorCode, ErrorScope, ErrorType } from "@dexto/core";
17
- import { AgentRegistryEntrySchema } from "../schemas/responses.js";
18
- const AgentConfigSchemaForOpenAPI = z.record(z.any()).describe(
17
+ import {
18
+ AgentRegistryEntrySchema,
19
+ BadRequestErrorResponse,
20
+ ConflictErrorResponse,
21
+ InternalErrorResponse,
22
+ JsonValueSchema,
23
+ NotFoundErrorResponse
24
+ } from "../schemas/responses.js";
25
+ const AgentConfigSchemaForOpenAPI = z.record(z.string(), JsonValueSchema).describe(
19
26
  "Complete agent configuration. See AgentConfig type documentation for full schema details."
20
27
  );
21
28
  const AgentIdentifierSchema = z.object({
@@ -116,296 +123,341 @@ const SaveConfigResponseSchema = z.object({
116
123
  changesApplied: z.array(z.string()).describe("List of changes that were applied"),
117
124
  message: z.string().describe("Success message")
118
125
  }).strict().describe("Configuration save result");
119
- function createAgentsRouter(getAgent, context, getAgentConfigPath) {
120
- const app = new OpenAPIHono();
121
- const { switchAgentById, switchAgentByPath, resolveAgentInfo, getActiveAgentId } = context;
122
- const resolveAgentConfigPath = async (ctx) => {
123
- const configPath = await getAgentConfigPath(ctx);
124
- if (!configPath) {
125
- throw AgentError.noConfigPath();
126
- }
127
- return configPath;
128
- };
129
- const listRoute = createRoute({
130
- method: "get",
131
- path: "/agents",
132
- summary: "List Agents",
133
- description: "Retrieves all agents (installed, available, and current active agent)",
134
- tags: ["agents"],
135
- responses: {
136
- 200: {
137
- description: "List all agents",
138
- content: { "application/json": { schema: ListAgentsResponseSchema } }
139
- }
140
- }
141
- });
142
- const currentRoute = createRoute({
143
- method: "get",
144
- path: "/agents/current",
145
- summary: "Get Current Agent",
146
- description: "Retrieves the currently active agent",
147
- tags: ["agents"],
148
- responses: {
149
- 200: {
150
- description: "Current agent",
151
- content: { "application/json": { schema: AgentInfoNullableSchema } }
152
- }
153
- }
154
- });
155
- const installRoute = createRoute({
156
- method: "post",
157
- path: "/agents/install",
158
- summary: "Install Agent",
159
- description: "Installs an agent from the registry or from a custom source",
160
- tags: ["agents"],
161
- request: {
162
- body: {
163
- content: {
164
- "application/json": {
165
- schema: z.union([CustomAgentInstallSchema, AgentIdentifierSchema])
166
- }
167
- }
168
- }
126
+ const CreateCustomAgentResponseSchema = z.object({
127
+ created: z.literal(true).describe("Creation success indicator"),
128
+ id: z.string().describe("Agent identifier"),
129
+ name: z.string().describe("Agent name")
130
+ }).strict().describe("Custom agent creation response");
131
+ const ValidateConfigErrorSchema = z.object({
132
+ line: z.number().int().optional().describe("Line number"),
133
+ column: z.number().int().optional().describe("Column number"),
134
+ path: z.string().optional().describe("Configuration path"),
135
+ message: z.string().describe("Error message"),
136
+ code: z.string().describe("Error code")
137
+ }).passthrough().describe("Configuration validation error");
138
+ const ValidateConfigWarningSchema = z.object({
139
+ path: z.string().describe("Configuration path"),
140
+ message: z.string().describe("Warning message"),
141
+ code: z.string().describe("Warning code")
142
+ }).strict().describe("Configuration validation warning");
143
+ const ValidateConfigResponseSchema = z.object({
144
+ valid: z.boolean().describe("Whether configuration is valid"),
145
+ errors: z.array(ValidateConfigErrorSchema).describe("Validation errors"),
146
+ warnings: z.array(ValidateConfigWarningSchema).describe("Configuration warnings")
147
+ }).strict().describe("Configuration validation result");
148
+ const ExportConfigQuerySchema = z.object({
149
+ sessionId: z.string().optional().describe("Session identifier to export session-specific configuration")
150
+ }).describe("Export configuration query");
151
+ const InstallAgentRequestSchema = z.union([CustomAgentInstallSchema, AgentIdentifierSchema]).describe("Agent installation request");
152
+ const listRoute = createRoute({
153
+ method: "get",
154
+ path: "/agents",
155
+ summary: "List Agents",
156
+ description: "Retrieves all agents (installed, available, and current active agent)",
157
+ tags: ["agents"],
158
+ responses: {
159
+ 200: {
160
+ description: "List all agents",
161
+ content: { "application/json": { schema: ListAgentsResponseSchema } }
169
162
  },
170
- responses: {
171
- 201: {
172
- description: "Agent installed",
173
- content: { "application/json": { schema: InstallAgentResponseSchema } }
174
- }
175
- }
176
- });
177
- const switchRoute = createRoute({
178
- method: "post",
179
- path: "/agents/switch",
180
- summary: "Switch Agent",
181
- description: "Switches to a different agent by ID or file path",
182
- tags: ["agents"],
183
- request: {
184
- body: {
185
- content: {
186
- "application/json": {
187
- schema: AgentIdentifierSchema
188
- }
189
- }
190
- }
163
+ 400: BadRequestErrorResponse,
164
+ 500: InternalErrorResponse
165
+ }
166
+ });
167
+ const currentRoute = createRoute({
168
+ method: "get",
169
+ path: "/agents/current",
170
+ summary: "Get Current Agent",
171
+ description: "Retrieves the currently active agent",
172
+ tags: ["agents"],
173
+ responses: {
174
+ 200: {
175
+ description: "Current agent",
176
+ content: { "application/json": { schema: AgentInfoNullableSchema } }
191
177
  },
192
- responses: {
193
- 200: {
194
- description: "Agent switched",
195
- content: { "application/json": { schema: SwitchAgentResponseSchema } }
178
+ 400: BadRequestErrorResponse,
179
+ 500: InternalErrorResponse
180
+ }
181
+ });
182
+ const installRoute = createRoute({
183
+ method: "post",
184
+ path: "/agents/install",
185
+ summary: "Install Agent",
186
+ description: "Installs an agent from the registry or from a custom source",
187
+ tags: ["agents"],
188
+ request: {
189
+ body: {
190
+ content: {
191
+ "application/json": {
192
+ schema: InstallAgentRequestSchema
193
+ }
196
194
  }
197
195
  }
198
- });
199
- const validateNameRoute = createRoute({
200
- method: "post",
201
- path: "/agents/validate-name",
202
- summary: "Validate Agent Name",
203
- description: "Checks if an agent ID conflicts with existing agents",
204
- tags: ["agents"],
205
- request: {
206
- body: {
207
- content: {
208
- "application/json": {
209
- schema: AgentIdentifierSchema
210
- }
196
+ },
197
+ responses: {
198
+ 201: {
199
+ description: "Agent installed",
200
+ content: { "application/json": { schema: InstallAgentResponseSchema } }
201
+ },
202
+ 400: BadRequestErrorResponse,
203
+ 404: NotFoundErrorResponse,
204
+ 409: ConflictErrorResponse,
205
+ 500: InternalErrorResponse
206
+ }
207
+ });
208
+ const switchRoute = createRoute({
209
+ method: "post",
210
+ path: "/agents/switch",
211
+ summary: "Switch Agent",
212
+ description: "Switches to a different agent by ID or file path",
213
+ tags: ["agents"],
214
+ request: {
215
+ body: {
216
+ content: {
217
+ "application/json": {
218
+ schema: AgentIdentifierSchema
211
219
  }
212
220
  }
221
+ }
222
+ },
223
+ responses: {
224
+ 200: {
225
+ description: "Agent switched",
226
+ content: { "application/json": { schema: SwitchAgentResponseSchema } }
213
227
  },
214
- responses: {
215
- 200: {
216
- description: "Name validation result",
217
- content: { "application/json": { schema: ValidateNameResponseSchema } }
228
+ 400: BadRequestErrorResponse,
229
+ 404: NotFoundErrorResponse,
230
+ 409: ConflictErrorResponse,
231
+ 500: InternalErrorResponse
232
+ }
233
+ });
234
+ const validateNameRoute = createRoute({
235
+ method: "post",
236
+ path: "/agents/validate-name",
237
+ summary: "Validate Agent Name",
238
+ description: "Checks if an agent ID conflicts with existing agents",
239
+ tags: ["agents"],
240
+ request: {
241
+ body: {
242
+ content: {
243
+ "application/json": {
244
+ schema: AgentIdentifierSchema
245
+ }
218
246
  }
219
247
  }
220
- });
221
- const uninstallRoute = createRoute({
222
- method: "post",
223
- path: "/agents/uninstall",
224
- summary: "Uninstall Agent",
225
- description: "Removes an agent from the system. Custom agents are removed from registry; builtin agents can be reinstalled",
226
- tags: ["agents"],
227
- request: {
228
- body: {
229
- content: {
230
- "application/json": {
231
- schema: UninstallAgentSchema
232
- }
248
+ },
249
+ responses: {
250
+ 200: {
251
+ description: "Name validation result",
252
+ content: { "application/json": { schema: ValidateNameResponseSchema } }
253
+ },
254
+ 400: BadRequestErrorResponse,
255
+ 404: NotFoundErrorResponse,
256
+ 409: ConflictErrorResponse,
257
+ 500: InternalErrorResponse
258
+ }
259
+ });
260
+ const uninstallRoute = createRoute({
261
+ method: "post",
262
+ path: "/agents/uninstall",
263
+ summary: "Uninstall Agent",
264
+ description: "Removes an agent from the system. Custom agents are removed from registry; builtin agents can be reinstalled",
265
+ tags: ["agents"],
266
+ request: {
267
+ body: {
268
+ content: {
269
+ "application/json": {
270
+ schema: UninstallAgentSchema
233
271
  }
234
272
  }
273
+ }
274
+ },
275
+ responses: {
276
+ 200: {
277
+ description: "Agent uninstalled",
278
+ content: { "application/json": { schema: UninstallAgentResponseSchema } }
235
279
  },
236
- responses: {
237
- 200: {
238
- description: "Agent uninstalled",
239
- content: { "application/json": { schema: UninstallAgentResponseSchema } }
280
+ 400: BadRequestErrorResponse,
281
+ 404: NotFoundErrorResponse,
282
+ 409: ConflictErrorResponse,
283
+ 500: InternalErrorResponse
284
+ }
285
+ });
286
+ const customCreateRoute = createRoute({
287
+ method: "post",
288
+ path: "/agents/custom/create",
289
+ summary: "Create Custom Agent",
290
+ description: "Creates a new custom agent from scratch via the UI/API",
291
+ tags: ["agents"],
292
+ request: {
293
+ body: {
294
+ content: {
295
+ "application/json": {
296
+ schema: CustomAgentCreateSchema
297
+ }
240
298
  }
241
299
  }
242
- });
243
- const customCreateRoute = createRoute({
244
- method: "post",
245
- path: "/agents/custom/create",
246
- summary: "Create Custom Agent",
247
- description: "Creates a new custom agent from scratch via the UI/API",
248
- tags: ["agents"],
249
- request: {
250
- body: {
251
- content: {
252
- "application/json": {
253
- schema: CustomAgentCreateSchema
254
- }
300
+ },
301
+ responses: {
302
+ 201: {
303
+ description: "Custom agent created",
304
+ content: {
305
+ "application/json": {
306
+ schema: CreateCustomAgentResponseSchema
255
307
  }
256
308
  }
257
309
  },
258
- responses: {
259
- 201: {
260
- description: "Custom agent created",
261
- content: {
262
- "application/json": {
263
- schema: z.object({
264
- created: z.literal(true).describe("Creation success indicator"),
265
- id: z.string().describe("Agent identifier"),
266
- name: z.string().describe("Agent name")
267
- }).strict()
268
- }
310
+ 400: BadRequestErrorResponse,
311
+ 404: NotFoundErrorResponse,
312
+ 409: ConflictErrorResponse,
313
+ 500: InternalErrorResponse
314
+ }
315
+ });
316
+ const getPathRoute = createRoute({
317
+ method: "get",
318
+ path: "/agent/path",
319
+ summary: "Get Agent File Path",
320
+ description: "Retrieves the file path of the currently active agent configuration",
321
+ tags: ["agent"],
322
+ responses: {
323
+ 200: {
324
+ description: "Agent file path",
325
+ content: {
326
+ "application/json": {
327
+ schema: AgentPathResponseSchema
269
328
  }
270
329
  }
271
- }
272
- });
273
- const getPathRoute = createRoute({
274
- method: "get",
275
- path: "/agent/path",
276
- summary: "Get Agent File Path",
277
- description: "Retrieves the file path of the currently active agent configuration",
278
- tags: ["agent"],
279
- responses: {
280
- 200: {
281
- description: "Agent file path",
282
- content: {
283
- "application/json": {
284
- schema: AgentPathResponseSchema
285
- }
330
+ },
331
+ 400: BadRequestErrorResponse,
332
+ 404: NotFoundErrorResponse,
333
+ 409: ConflictErrorResponse,
334
+ 500: InternalErrorResponse
335
+ }
336
+ });
337
+ const getConfigRoute = createRoute({
338
+ method: "get",
339
+ path: "/agent/config",
340
+ summary: "Get Agent Configuration",
341
+ description: "Retrieves the raw YAML configuration of the currently active agent",
342
+ tags: ["agent"],
343
+ responses: {
344
+ 200: {
345
+ description: "Agent configuration",
346
+ content: {
347
+ "application/json": {
348
+ schema: AgentConfigResponseSchema
286
349
  }
287
350
  }
288
- }
289
- });
290
- const getConfigRoute = createRoute({
291
- method: "get",
292
- path: "/agent/config",
293
- summary: "Get Agent Configuration",
294
- description: "Retrieves the raw YAML configuration of the currently active agent",
295
- tags: ["agent"],
296
- responses: {
297
- 200: {
298
- description: "Agent configuration",
299
- content: {
300
- "application/json": {
301
- schema: AgentConfigResponseSchema
302
- }
351
+ },
352
+ 400: BadRequestErrorResponse,
353
+ 404: NotFoundErrorResponse,
354
+ 409: ConflictErrorResponse,
355
+ 500: InternalErrorResponse
356
+ }
357
+ });
358
+ const validateConfigRoute = createRoute({
359
+ method: "post",
360
+ path: "/agent/validate",
361
+ summary: "Validate Agent Configuration",
362
+ description: "Validates YAML agent configuration without saving it",
363
+ tags: ["agent"],
364
+ request: {
365
+ body: {
366
+ content: {
367
+ "application/json": {
368
+ schema: AgentConfigValidateSchema
303
369
  }
304
370
  }
305
371
  }
306
- });
307
- const validateConfigRoute = createRoute({
308
- method: "post",
309
- path: "/agent/validate",
310
- summary: "Validate Agent Configuration",
311
- description: "Validates YAML agent configuration without saving it",
312
- tags: ["agent"],
313
- request: {
314
- body: {
315
- content: {
316
- "application/json": {
317
- schema: AgentConfigValidateSchema
318
- }
372
+ },
373
+ responses: {
374
+ 200: {
375
+ description: "Validation result",
376
+ content: {
377
+ "application/json": {
378
+ schema: ValidateConfigResponseSchema
319
379
  }
320
380
  }
321
381
  },
322
- responses: {
323
- 200: {
324
- description: "Validation result",
325
- content: {
326
- "application/json": {
327
- schema: z.object({
328
- valid: z.boolean().describe("Whether configuration is valid"),
329
- errors: z.array(
330
- z.object({
331
- line: z.number().int().optional().describe("Line number"),
332
- column: z.number().int().optional().describe("Column number"),
333
- path: z.string().optional().describe("Configuration path"),
334
- message: z.string().describe("Error message"),
335
- code: z.string().describe("Error code")
336
- }).passthrough()
337
- ).describe("Validation errors"),
338
- warnings: z.array(
339
- z.object({
340
- path: z.string().describe("Configuration path"),
341
- message: z.string().describe("Warning message"),
342
- code: z.string().describe("Warning code")
343
- }).strict()
344
- ).describe("Configuration warnings")
345
- }).strict()
346
- }
382
+ 400: BadRequestErrorResponse,
383
+ 404: NotFoundErrorResponse,
384
+ 409: ConflictErrorResponse,
385
+ 500: InternalErrorResponse
386
+ }
387
+ });
388
+ const saveConfigRoute = createRoute({
389
+ method: "post",
390
+ path: "/agent/config",
391
+ summary: "Save Agent Configuration",
392
+ description: "Saves and applies YAML agent configuration. Creates backup before saving",
393
+ tags: ["agent"],
394
+ request: {
395
+ body: {
396
+ content: {
397
+ "application/json": {
398
+ schema: AgentConfigSaveSchema
347
399
  }
348
400
  }
349
401
  }
350
- });
351
- const saveConfigRoute = createRoute({
352
- method: "post",
353
- path: "/agent/config",
354
- summary: "Save Agent Configuration",
355
- description: "Saves and applies YAML agent configuration. Creates backup before saving",
356
- tags: ["agent"],
357
- request: {
358
- body: {
359
- content: {
360
- "application/json": {
361
- schema: AgentConfigSaveSchema
362
- }
402
+ },
403
+ responses: {
404
+ 200: {
405
+ description: "Configuration saved",
406
+ content: {
407
+ "application/json": {
408
+ schema: SaveConfigResponseSchema
363
409
  }
364
410
  }
365
411
  },
366
- responses: {
367
- 200: {
368
- description: "Configuration saved",
369
- content: {
370
- "application/json": {
371
- schema: SaveConfigResponseSchema
372
- }
373
- }
374
- }
412
+ 400: BadRequestErrorResponse,
413
+ 404: NotFoundErrorResponse,
414
+ 409: ConflictErrorResponse,
415
+ 500: InternalErrorResponse
416
+ }
417
+ });
418
+ const exportConfigRoute = createRoute({
419
+ method: "get",
420
+ path: "/agent/config/export",
421
+ summary: "Export Agent Configuration",
422
+ description: "Exports the effective agent configuration with sensitive values redacted",
423
+ tags: ["agent"],
424
+ request: {
425
+ query: ExportConfigQuerySchema
426
+ },
427
+ responses: {
428
+ 200: {
429
+ description: "Exported configuration",
430
+ content: { "application/x-yaml": { schema: z.string() } }
375
431
  }
376
- });
377
- const exportConfigRoute = createRoute({
378
- method: "get",
379
- path: "/agent/config/export",
380
- summary: "Export Agent Configuration",
381
- description: "Exports the effective agent configuration with sensitive values redacted",
382
- tags: ["agent"],
383
- request: {
384
- query: z.object({
385
- sessionId: z.string().optional().describe("Session identifier to export session-specific configuration")
386
- })
387
- },
388
- responses: {
389
- 200: {
390
- description: "Exported configuration",
391
- content: { "application/x-yaml": { schema: z.string() } }
392
- }
432
+ }
433
+ });
434
+ function createAgentsRouter(getAgent, context, getAgentConfigPath) {
435
+ const app = new OpenAPIHono();
436
+ const { switchAgentById, switchAgentByPath, resolveAgentInfo, getActiveAgentId } = context;
437
+ const resolveAgentConfigPath = async (ctx) => {
438
+ const configPath = await getAgentConfigPath(ctx);
439
+ if (!configPath) {
440
+ throw AgentError.noConfigPath();
393
441
  }
394
- });
442
+ return configPath;
443
+ };
395
444
  return app.openapi(listRoute, async (ctx) => {
396
445
  const agents = await AgentFactory.listAgents();
397
446
  const currentId = getActiveAgentId() ?? null;
398
- return ctx.json({
399
- installed: agents.installed,
400
- available: agents.available,
401
- current: currentId ? await resolveAgentInfo(currentId) : { id: null, name: null }
402
- });
447
+ return ctx.json(
448
+ {
449
+ installed: agents.installed,
450
+ available: agents.available,
451
+ current: currentId ? await resolveAgentInfo(currentId) : { id: null, name: null }
452
+ },
453
+ 200
454
+ );
403
455
  }).openapi(currentRoute, async (ctx) => {
404
456
  const currentId = getActiveAgentId() ?? null;
405
457
  if (!currentId) {
406
- return ctx.json({ id: null, name: null });
458
+ return ctx.json({ id: null, name: null }, 200);
407
459
  }
408
- return ctx.json(await resolveAgentInfo(currentId));
460
+ return ctx.json(await resolveAgentInfo(currentId), 200);
409
461
  }).openapi(installRoute, async (ctx) => {
410
462
  const body = ctx.req.valid("json");
411
463
  if ("sourcePath" in body && "metadata" in body) {
@@ -436,53 +488,68 @@ function createAgentsRouter(getAgent, context, getAgentConfigPath) {
436
488
  }).openapi(switchRoute, async (ctx) => {
437
489
  const { id, path: filePath } = ctx.req.valid("json");
438
490
  const result = filePath ? await switchAgentByPath(filePath) : await switchAgentById(id);
439
- return ctx.json({ switched: true, ...result });
491
+ return ctx.json({ switched: true, ...result }, 200);
440
492
  }).openapi(validateNameRoute, async (ctx) => {
441
493
  const { id } = ctx.req.valid("json");
442
494
  const agents = await AgentFactory.listAgents();
443
495
  const installedAgent = agents.installed.find((a) => a.id === id);
444
496
  if (installedAgent) {
445
- return ctx.json({
446
- valid: false,
447
- conflict: installedAgent.type,
448
- message: `Agent id '${id}' already exists (${installedAgent.type})`
449
- });
497
+ return ctx.json(
498
+ {
499
+ valid: false,
500
+ conflict: installedAgent.type,
501
+ message: `Agent id '${id}' already exists (${installedAgent.type})`
502
+ },
503
+ 200
504
+ );
450
505
  }
451
506
  const availableAgent = agents.available.find((a) => a.id === id);
452
507
  if (availableAgent) {
453
- return ctx.json({
454
- valid: false,
455
- conflict: availableAgent.type,
456
- message: `Agent id '${id}' conflicts with ${availableAgent.type} agent`
457
- });
508
+ return ctx.json(
509
+ {
510
+ valid: false,
511
+ conflict: availableAgent.type,
512
+ message: `Agent id '${id}' conflicts with ${availableAgent.type} agent`
513
+ },
514
+ 200
515
+ );
458
516
  }
459
- return ctx.json({ valid: true });
517
+ return ctx.json({ valid: true }, 200);
460
518
  }).openapi(uninstallRoute, async (ctx) => {
461
519
  const { id, force } = ctx.req.valid("json");
462
520
  await AgentFactory.uninstallAgent(id, force);
463
- return ctx.json({ uninstalled: true, id });
521
+ return ctx.json({ uninstalled: true, id }, 200);
464
522
  }).openapi(customCreateRoute, async (ctx) => {
465
523
  const { id, name, description, author, tags, config } = ctx.req.valid("json");
466
- const provider = config.llm.provider;
467
- let agentConfig = config;
468
- if (config.llm.apiKey && !config.llm.apiKey.startsWith("$")) {
469
- const meta = await saveProviderApiKey(provider, config.llm.apiKey, process.cwd());
524
+ const configResult = AgentConfigSchema.safeParse(config);
525
+ if (!configResult.success) {
526
+ throw new DextoValidationError(zodToIssues(configResult.error));
527
+ }
528
+ const validatedConfig = configResult.data;
529
+ const provider = validatedConfig.llm.provider;
530
+ let agentConfig = validatedConfig;
531
+ if (validatedConfig.llm.apiKey && !validatedConfig.llm.apiKey.startsWith("$")) {
532
+ const meta = await saveProviderApiKey(
533
+ provider,
534
+ validatedConfig.llm.apiKey,
535
+ process.cwd()
536
+ );
470
537
  const apiKeyRef = `$${meta.envVar}`;
471
538
  logger.info(
472
539
  `Stored API key securely for ${provider}, using env var: ${meta.envVar}`
473
540
  );
474
541
  agentConfig = {
475
- ...config,
542
+ ...validatedConfig,
476
543
  llm: {
477
- ...config.llm,
544
+ ...validatedConfig.llm,
478
545
  apiKey: apiKeyRef
479
546
  }
480
547
  };
481
- } else if (!config.llm.apiKey) {
548
+ } else if (!validatedConfig.llm.apiKey) {
482
549
  agentConfig = {
483
- ...config,
550
+ ...validatedConfig,
484
551
  llm: {
485
- ...config.llm,
552
+ ...validatedConfig.llm,
486
553
  apiKey: `$${getPrimaryApiKeyEnvVar(provider)}`
487
554
  }
488
555
  };
@@ -514,26 +581,32 @@ function createAgentsRouter(getAgent, context, getAgentConfigPath) {
514
581
  const relativePath = path.basename(agentPath);
515
582
  const ext = path.extname(agentPath);
516
583
  const name = path.basename(agentPath, ext);
517
- return ctx.json({
518
- path: agentPath,
519
- relativePath,
520
- name,
521
- isDefault: name === "coding-agent"
522
- });
584
+ return ctx.json(
585
+ {
586
+ path: agentPath,
587
+ relativePath,
588
+ name,
589
+ isDefault: name === "coding-agent"
590
+ },
591
+ 200
592
+ );
523
593
  }).openapi(getConfigRoute, async (ctx) => {
524
594
  const agentPath = await resolveAgentConfigPath(ctx);
525
595
  const yamlContent = await fs.readFile(agentPath, "utf-8");
526
596
  const stats = await fs.stat(agentPath);
527
- return ctx.json({
528
- yaml: yamlContent,
529
- path: agentPath,
530
- relativePath: path.basename(agentPath),
531
- lastModified: stats.mtime,
532
- warnings: [
533
- "Environment variables ($VAR) will be resolved at runtime",
534
- "API keys should use environment variables"
535
- ]
536
- });
597
+ return ctx.json(
598
+ {
599
+ yaml: yamlContent,
600
+ path: agentPath,
601
+ relativePath: path.basename(agentPath),
602
+ lastModified: stats.mtime,
603
+ warnings: [
604
+ "Environment variables ($VAR) will be resolved at runtime",
605
+ "API keys should use environment variables"
606
+ ]
607
+ },
608
+ 200
609
+ );
537
610
  }).openapi(validateConfigRoute, async (ctx) => {
538
611
  const { yaml } = ctx.req.valid("json");
539
612
  let parsed;
@@ -542,32 +615,38 @@ function createAgentsRouter(getAgent, context, getAgentConfigPath) {
542
615
  } catch (parseError) {
543
616
  const message = parseError instanceof Error ? parseError.message : String(parseError);
544
617
  const linePos = typeof parseError === "object" && parseError !== null && "linePos" in parseError ? parseError.linePos : void 0;
545
- return ctx.json({
546
- valid: false,
547
- errors: [
548
- {
549
- line: linePos?.[0]?.line ?? 1,
550
- column: linePos?.[0]?.col ?? 1,
551
- message,
552
- code: "YAML_PARSE_ERROR"
553
- }
554
- ],
555
- warnings: []
556
- });
618
+ return ctx.json(
619
+ {
620
+ valid: false,
621
+ errors: [
622
+ {
623
+ line: linePos?.[0]?.line ?? 1,
624
+ column: linePos?.[0]?.col ?? 1,
625
+ message,
626
+ code: "YAML_PARSE_ERROR"
627
+ }
628
+ ],
629
+ warnings: []
630
+ },
631
+ 200
632
+ );
557
633
  }
558
634
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
559
- return ctx.json({
560
- valid: false,
561
- errors: [
562
- {
563
- line: 1,
564
- column: 1,
565
- message: "Configuration must be a valid YAML object",
566
- code: "INVALID_CONFIG_TYPE"
567
- }
568
- ],
569
- warnings: []
570
- });
635
+ return ctx.json(
636
+ {
637
+ valid: false,
638
+ errors: [
639
+ {
640
+ line: 1,
641
+ column: 1,
642
+ message: "Configuration must be a valid YAML object",
643
+ code: "INVALID_CONFIG_TYPE"
644
+ }
645
+ ],
646
+ warnings: []
647
+ },
648
+ 200
649
+ );
571
650
  }
572
651
  const enriched = enrichAgentConfig(parsed, void 0);
573
652
  const result = AgentConfigSchema.safeParse(enriched);
@@ -578,11 +657,14 @@ function createAgentsRouter(getAgent, context, getAgentConfigPath) {
578
657
  message: issue.message,
579
658
  code: "SCHEMA_VALIDATION_ERROR"
580
659
  }));
581
- return ctx.json({
582
- valid: false,
583
- errors,
584
- warnings: []
585
- });
660
+ return ctx.json(
661
+ {
662
+ valid: false,
663
+ errors,
664
+ warnings: []
665
+ },
666
+ 200
667
+ );
586
668
  }
587
669
  const warnings = [];
588
670
  if (parsed.llm?.apiKey && !parsed.llm.apiKey.startsWith("$")) {
@@ -592,11 +674,14 @@ function createAgentsRouter(getAgent, context, getAgentConfigPath) {
592
674
  code: "SECURITY_WARNING"
593
675
  });
594
676
  }
595
- return ctx.json({
596
- valid: true,
597
- errors: [],
598
- warnings
599
- });
677
+ return ctx.json(
678
+ {
679
+ valid: true,
680
+ errors: [],
681
+ warnings
682
+ },
683
+ 200
684
+ );
600
685
  }).openapi(saveConfigRoute, async (ctx) => {
601
686
  const { yaml } = ctx.req.valid("json");
602
687
  let parsed;
@@ -647,14 +732,17 @@ function createAgentsRouter(getAgent, context, getAgentConfigPath) {
647
732
  await fs.unlink(backupPath).catch(() => {
648
733
  });
649
734
  logger.info(`Agent configuration saved and applied: ${agentPath}`);
650
- return ctx.json({
651
- ok: true,
652
- path: agentPath,
653
- reloaded: true,
654
- restarted: true,
655
- changesApplied: ["restart"],
656
- message: "Configuration saved and applied successfully (agent restarted)"
657
- });
735
+ return ctx.json(
736
+ {
737
+ ok: true,
738
+ path: agentPath,
739
+ reloaded: true,
740
+ restarted: true,
741
+ changesApplied: ["restart"],
742
+ message: "Configuration saved and applied successfully (agent restarted)"
743
+ },
744
+ 200
745
+ );
658
746
  } catch (error) {
659
747
  await fs.copyFile(backupPath, agentPath).catch(() => {
660
748
  });