@dexto/server 1.2.6 → 1.4.0

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 (60) hide show
  1. package/dist/approval/manual-approval-handler.cjs +23 -15
  2. package/dist/approval/manual-approval-handler.d.ts.map +1 -1
  3. package/dist/approval/manual-approval-handler.js +23 -15
  4. package/dist/events/webhook-subscriber.cjs +1 -1
  5. package/dist/events/webhook-subscriber.d.ts.map +1 -1
  6. package/dist/events/webhook-subscriber.js +1 -1
  7. package/dist/hono/__tests__/test-fixtures.cjs +2 -2
  8. package/dist/hono/__tests__/test-fixtures.d.ts.map +1 -1
  9. package/dist/hono/__tests__/test-fixtures.js +2 -2
  10. package/dist/hono/index.cjs +14 -2
  11. package/dist/hono/index.d.ts +486 -132
  12. package/dist/hono/index.d.ts.map +1 -1
  13. package/dist/hono/index.js +17 -2
  14. package/dist/hono/middleware/error.d.ts.map +1 -1
  15. package/dist/hono/routes/agents.cjs +8 -10
  16. package/dist/hono/routes/agents.d.ts +15 -8
  17. package/dist/hono/routes/agents.d.ts.map +1 -1
  18. package/dist/hono/routes/agents.js +10 -10
  19. package/dist/hono/routes/approvals.cjs +52 -1
  20. package/dist/hono/routes/approvals.d.ts +25 -0
  21. package/dist/hono/routes/approvals.d.ts.map +1 -1
  22. package/dist/hono/routes/approvals.js +52 -1
  23. package/dist/hono/routes/llm.cjs +110 -31
  24. package/dist/hono/routes/llm.d.ts +89 -37
  25. package/dist/hono/routes/llm.d.ts.map +1 -1
  26. package/dist/hono/routes/llm.js +108 -25
  27. package/dist/hono/routes/mcp.cjs +8 -4
  28. package/dist/hono/routes/mcp.d.ts +4 -1
  29. package/dist/hono/routes/mcp.d.ts.map +1 -1
  30. package/dist/hono/routes/mcp.js +9 -5
  31. package/dist/hono/routes/memory.d.ts +1 -1
  32. package/dist/hono/routes/messages.cjs +56 -64
  33. package/dist/hono/routes/messages.d.ts +101 -57
  34. package/dist/hono/routes/messages.d.ts.map +1 -1
  35. package/dist/hono/routes/messages.js +57 -65
  36. package/dist/hono/routes/prompts.cjs +2 -2
  37. package/dist/hono/routes/prompts.d.ts +7 -7
  38. package/dist/hono/routes/prompts.js +2 -2
  39. package/dist/hono/routes/queue.cjs +202 -0
  40. package/dist/hono/routes/queue.d.ts +171 -0
  41. package/dist/hono/routes/queue.d.ts.map +1 -0
  42. package/dist/hono/routes/queue.js +178 -0
  43. package/dist/hono/routes/resources.d.ts +1 -1
  44. package/dist/hono/routes/search.cjs +2 -24
  45. package/dist/hono/routes/search.d.ts +43 -15
  46. package/dist/hono/routes/search.d.ts.map +1 -1
  47. package/dist/hono/routes/search.js +3 -25
  48. package/dist/hono/routes/sessions.cjs +65 -11
  49. package/dist/hono/routes/sessions.d.ts +27 -5
  50. package/dist/hono/routes/sessions.d.ts.map +1 -1
  51. package/dist/hono/routes/sessions.js +65 -11
  52. package/dist/hono/routes/static.cjs +77 -0
  53. package/dist/hono/routes/static.d.ts +41 -0
  54. package/dist/hono/routes/static.d.ts.map +1 -0
  55. package/dist/hono/routes/static.js +52 -0
  56. package/dist/hono/schemas/responses.cjs +67 -25
  57. package/dist/hono/schemas/responses.d.ts +2076 -354
  58. package/dist/hono/schemas/responses.d.ts.map +1 -1
  59. package/dist/hono/schemas/responses.js +69 -35
  60. package/package.json +3 -3
@@ -1,15 +1,20 @@
1
1
  import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
2
+ import { DextoRuntimeError, ErrorScope, ErrorType } from "@dexto/core";
2
3
  import {
3
4
  LLM_REGISTRY,
4
5
  LLM_PROVIDERS,
5
- LLM_ROUTERS,
6
6
  SUPPORTED_FILE_TYPES,
7
- getSupportedRoutersForProvider,
8
7
  supportsBaseURL,
9
- isRouterSupportedForModel,
10
8
  LLMUpdatesSchema
11
9
  } from "@dexto/core";
12
- import { getProviderKeyStatus, saveProviderApiKey } from "@dexto/agent-management";
10
+ import {
11
+ getProviderKeyStatus,
12
+ saveProviderApiKey,
13
+ loadCustomModels,
14
+ saveCustomModel,
15
+ deleteCustomModel,
16
+ CustomModelSchema
17
+ } from "@dexto/agent-management";
13
18
  import {
14
19
  ProviderCatalogSchema,
15
20
  ModelFlatSchema,
@@ -25,7 +30,6 @@ const CatalogQuerySchema = z.object({
25
30
  hasKey: z.union([z.literal("true"), z.literal("false"), z.literal("1"), z.literal("0")]).optional().transform(
26
31
  (raw) => raw === "true" || raw === "1" ? true : raw === "false" || raw === "0" ? false : void 0
27
32
  ).describe("Filter by API key presence (true or false)"),
28
- router: z.enum(LLM_ROUTERS).optional().describe("Filter by router type (vercel or in-built)"),
29
33
  fileType: z.enum(SUPPORTED_FILE_TYPES).optional().describe("Filter by supported file type (audio, pdf, or image)"),
30
34
  defaultOnly: z.union([z.literal("true"), z.literal("false"), z.literal("1"), z.literal("0")]).optional().transform(
31
35
  (raw) => raw === "true" || raw === "1" ? true : raw === "false" || raw === "0" ? false : void 0
@@ -57,8 +61,7 @@ function createLlmRouter(getAgent) {
57
61
  "application/json": {
58
62
  schema: z.object({
59
63
  config: LLMConfigResponseSchema.partial({
60
- maxIterations: true,
61
- router: true
64
+ maxIterations: true
62
65
  }).extend({
63
66
  displayName: z.string().optional().describe("Human-readable model display name")
64
67
  })
@@ -152,6 +155,84 @@ function createLlmRouter(getAgent) {
152
155
  }
153
156
  }
154
157
  });
158
+ const listCustomModelsRoute = createRoute({
159
+ method: "get",
160
+ path: "/llm/custom-models",
161
+ summary: "List Custom Models",
162
+ description: "Returns all saved custom openai-compatible model configurations",
163
+ tags: ["llm"],
164
+ responses: {
165
+ 200: {
166
+ description: "List of custom models",
167
+ content: {
168
+ "application/json": {
169
+ schema: z.object({
170
+ models: z.array(CustomModelSchema).describe("List of custom models")
171
+ })
172
+ }
173
+ }
174
+ }
175
+ }
176
+ });
177
+ const createCustomModelRoute = createRoute({
178
+ method: "post",
179
+ path: "/llm/custom-models",
180
+ summary: "Create Custom Model",
181
+ description: "Saves a new custom openai-compatible model configuration",
182
+ tags: ["llm"],
183
+ request: {
184
+ body: { content: { "application/json": { schema: CustomModelSchema } } }
185
+ },
186
+ responses: {
187
+ 200: {
188
+ description: "Custom model saved",
189
+ content: {
190
+ "application/json": {
191
+ schema: z.object({
192
+ ok: z.literal(true).describe("Success indicator"),
193
+ model: CustomModelSchema
194
+ })
195
+ }
196
+ }
197
+ }
198
+ }
199
+ });
200
+ const deleteCustomModelRoute = createRoute({
201
+ method: "delete",
202
+ path: "/llm/custom-models/{name}",
203
+ summary: "Delete Custom Model",
204
+ description: "Deletes a custom model by name",
205
+ tags: ["llm"],
206
+ request: {
207
+ params: z.object({
208
+ name: z.string().min(1).describe("Model name to delete")
209
+ })
210
+ },
211
+ responses: {
212
+ 200: {
213
+ description: "Custom model deleted",
214
+ content: {
215
+ "application/json": {
216
+ schema: z.object({
217
+ ok: z.literal(true).describe("Success indicator"),
218
+ deleted: z.string().describe("Name of the deleted model")
219
+ })
220
+ }
221
+ }
222
+ },
223
+ 404: {
224
+ description: "Custom model not found",
225
+ content: {
226
+ "application/json": {
227
+ schema: z.object({
228
+ ok: z.literal(false).describe("Failure indicator"),
229
+ error: z.string().describe("Error message")
230
+ })
231
+ }
232
+ }
233
+ }
234
+ }
235
+ });
155
236
  return app.openapi(currentRoute, (ctx) => {
156
237
  const agent = getAgent();
157
238
  const { sessionId } = ctx.req.valid("query");
@@ -183,7 +264,6 @@ function createLlmRouter(getAgent) {
183
264
  name: displayName,
184
265
  hasApiKey: keyStatus.hasApiKey,
185
266
  primaryEnvVar: keyStatus.envVar,
186
- supportedRouters: getSupportedRoutersForProvider(provider),
187
267
  supportsBaseURL: supportsBaseURL(provider),
188
268
  models: info.models,
189
269
  supportedFileTypes: info.supportedFileTypes
@@ -213,23 +293,6 @@ function createLlmRouter(getAgent) {
213
293
  }
214
294
  filtered = byKey;
215
295
  }
216
- if (queryParams.router) {
217
- const byRouter = {};
218
- for (const [id, catalog] of Object.entries(filtered)) {
219
- if (!catalog.supportedRouters.includes(queryParams.router)) continue;
220
- const models = catalog.models.filter(
221
- (model) => isRouterSupportedForModel(
222
- id,
223
- model.name,
224
- queryParams.router
225
- )
226
- );
227
- if (models.length > 0) {
228
- byRouter[id] = { ...catalog, models };
229
- }
230
- }
231
- filtered = byRouter;
232
- }
233
296
  if (queryParams.fileType) {
234
297
  const byFileType = {};
235
298
  for (const [id, catalog] of Object.entries(filtered)) {
@@ -280,6 +343,26 @@ function createLlmRouter(getAgent) {
280
343
  },
281
344
  sessionId
282
345
  });
346
+ }).openapi(listCustomModelsRoute, async (ctx) => {
347
+ const models = await loadCustomModels();
348
+ return ctx.json({ models });
349
+ }).openapi(createCustomModelRoute, async (ctx) => {
350
+ const model = ctx.req.valid("json");
351
+ await saveCustomModel(model);
352
+ return ctx.json({ ok: true, model });
353
+ }).openapi(deleteCustomModelRoute, async (ctx) => {
354
+ const { name } = ctx.req.valid("param");
355
+ const deleted = await deleteCustomModel(name);
356
+ if (!deleted) {
357
+ throw new DextoRuntimeError(
358
+ "custom_model_not_found",
359
+ ErrorScope.LLM,
360
+ ErrorType.NOT_FOUND,
361
+ `Custom model '${name}' not found`,
362
+ { modelName: name }
363
+ );
364
+ }
365
+ return ctx.json({ ok: true, deleted: name }, 200);
283
366
  });
284
367
  }
285
368
  export {
@@ -40,7 +40,7 @@ const ServerStatusResponseSchema = import_zod_openapi.z.object({
40
40
  const ServerInfoSchema = import_zod_openapi.z.object({
41
41
  id: import_zod_openapi.z.string().describe("Server identifier"),
42
42
  name: import_zod_openapi.z.string().describe("Server name"),
43
- status: import_zod_openapi.z.enum(["connected", "error", "disconnected"]).describe("Server status")
43
+ status: import_zod_openapi.z.enum(import_core.MCP_CONNECTION_STATUSES).describe("Server status")
44
44
  }).strict().describe("MCP server information");
45
45
  const ServersListResponseSchema = import_zod_openapi.z.object({
46
46
  servers: import_zod_openapi.z.array(ServerInfoSchema).describe("Array of server information")
@@ -230,8 +230,11 @@ function createMcpRouter(getAgent) {
230
230
  return app.openapi(addServerRoute, async (ctx) => {
231
231
  const agent = getAgent();
232
232
  const { name, config, persistToAgent } = ctx.req.valid("json");
233
- await agent.connectMcpServer(name, config);
234
- import_core.logger.info(`Successfully connected to new server '${name}' via API request.`);
233
+ await agent.addMcpServer(name, config);
234
+ const isConnected = config.enabled !== false;
235
+ import_core.logger.info(
236
+ isConnected ? `Successfully connected to new server '${name}' via API request.` : `Registered server '${name}' (disabled) via API request.`
237
+ );
235
238
  if (persistToAgent === true) {
236
239
  try {
237
240
  const currentConfig = agent.getEffectiveConfig();
@@ -262,7 +265,8 @@ function createMcpRouter(getAgent) {
262
265
  );
263
266
  }
264
267
  }
265
- return ctx.json({ status: "connected", name }, 200);
268
+ const status = isConnected ? "connected" : "registered";
269
+ return ctx.json({ status, name }, 200);
266
270
  }).openapi(listServersRoute, async (ctx) => {
267
271
  const agent = getAgent();
268
272
  const clientsMap = agent.getMcpClients();
@@ -9,6 +9,7 @@ export declare function createMcpRouter(getAgent: () => DextoAgent): OpenAPIHono
9
9
  type: "stdio";
10
10
  command: string;
11
11
  timeout?: number | undefined;
12
+ enabled?: boolean | undefined;
12
13
  args?: string[] | undefined;
13
14
  env?: Record<string, string> | undefined;
14
15
  connectionMode?: "strict" | "lenient" | undefined;
@@ -16,12 +17,14 @@ export declare function createMcpRouter(getAgent: () => DextoAgent): OpenAPIHono
16
17
  type: "sse";
17
18
  url: string;
18
19
  timeout?: number | undefined;
20
+ enabled?: boolean | undefined;
19
21
  connectionMode?: "strict" | "lenient" | undefined;
20
22
  headers?: Record<string, string> | undefined;
21
23
  } | {
22
24
  type: "http";
23
25
  url: string;
24
26
  timeout?: number | undefined;
27
+ enabled?: boolean | undefined;
25
28
  connectionMode?: "strict" | "lenient" | undefined;
26
29
  headers?: Record<string, string> | undefined;
27
30
  };
@@ -200,10 +203,10 @@ export declare function createMcpRouter(getAgent: () => DextoAgent): OpenAPIHono
200
203
  source: "mcp" | "internal";
201
204
  description?: string | undefined;
202
205
  mimeType?: string | undefined;
203
- name?: string | undefined;
204
206
  metadata?: {
205
207
  [x: string]: import("hono/utils/types").JSONValue;
206
208
  } | undefined;
209
+ name?: string | undefined;
207
210
  serverName?: string | undefined;
208
211
  size?: number | undefined;
209
212
  lastModified?: string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../../src/hono/routes/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkB,MAAM,mBAAmB,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA0I9C,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA4SzD"}
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../../src/hono/routes/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkB,MAAM,mBAAmB,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA0I9C,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAkTzD"}
@@ -1,5 +1,5 @@
1
1
  import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
2
- import { logger, McpServerConfigSchema } from "@dexto/core";
2
+ import { logger, McpServerConfigSchema, MCP_CONNECTION_STATUSES } from "@dexto/core";
3
3
  import { updateAgentConfigFile } from "@dexto/agent-management";
4
4
  import { ResourceSchema } from "../schemas/responses.js";
5
5
  const McpServerRequestSchema = z.object({
@@ -17,7 +17,7 @@ const ServerStatusResponseSchema = z.object({
17
17
  const ServerInfoSchema = z.object({
18
18
  id: z.string().describe("Server identifier"),
19
19
  name: z.string().describe("Server name"),
20
- status: z.enum(["connected", "error", "disconnected"]).describe("Server status")
20
+ status: z.enum(MCP_CONNECTION_STATUSES).describe("Server status")
21
21
  }).strict().describe("MCP server information");
22
22
  const ServersListResponseSchema = z.object({
23
23
  servers: z.array(ServerInfoSchema).describe("Array of server information")
@@ -207,8 +207,11 @@ function createMcpRouter(getAgent) {
207
207
  return app.openapi(addServerRoute, async (ctx) => {
208
208
  const agent = getAgent();
209
209
  const { name, config, persistToAgent } = ctx.req.valid("json");
210
- await agent.connectMcpServer(name, config);
211
- logger.info(`Successfully connected to new server '${name}' via API request.`);
210
+ await agent.addMcpServer(name, config);
211
+ const isConnected = config.enabled !== false;
212
+ logger.info(
213
+ isConnected ? `Successfully connected to new server '${name}' via API request.` : `Registered server '${name}' (disabled) via API request.`
214
+ );
212
215
  if (persistToAgent === true) {
213
216
  try {
214
217
  const currentConfig = agent.getEffectiveConfig();
@@ -239,7 +242,8 @@ function createMcpRouter(getAgent) {
239
242
  );
240
243
  }
241
244
  }
242
- return ctx.json({ status: "connected", name }, 200);
245
+ const status = isConnected ? "connected" : "registered";
246
+ return ctx.json({ status, name }, 200);
243
247
  }).openapi(listServersRoute, async (ctx) => {
244
248
  const agent = getAgent();
245
249
  const clientsMap = agent.getMcpClients();
@@ -99,11 +99,11 @@ export declare function createMemoryRouter(getAgent: () => DextoAgent): OpenAPIH
99
99
  };
100
100
  } & {
101
101
  json: {
102
+ content?: string | undefined;
102
103
  metadata?: z.objectInputType<{
103
104
  source: z.ZodOptional<z.ZodEnum<["user", "system"]>>;
104
105
  pinned: z.ZodOptional<z.ZodBoolean>;
105
106
  }, z.ZodTypeAny, "passthrough"> | undefined;
106
- content?: string | undefined;
107
107
  tags?: string[] | undefined;
108
108
  };
109
109
  };
@@ -25,25 +25,26 @@ var import_zod_openapi = require("@hono/zod-openapi");
25
25
  var import_streaming = require("hono/streaming");
26
26
  var import_core = require("@dexto/core");
27
27
  var import_responses = require("../schemas/responses.js");
28
+ const TextPartSchema = import_zod_openapi.z.object({
29
+ type: import_zod_openapi.z.literal("text").describe("Content type identifier"),
30
+ text: import_zod_openapi.z.string().describe("Text content")
31
+ }).describe("Text content part");
32
+ const ImagePartSchema = import_zod_openapi.z.object({
33
+ type: import_zod_openapi.z.literal("image").describe("Content type identifier"),
34
+ image: import_zod_openapi.z.string().describe("Base64-encoded image data or URL"),
35
+ mimeType: import_zod_openapi.z.string().optional().describe("MIME type (e.g., image/png)")
36
+ }).describe("Image content part");
37
+ const FilePartSchema = import_zod_openapi.z.object({
38
+ type: import_zod_openapi.z.literal("file").describe("Content type identifier"),
39
+ data: import_zod_openapi.z.string().describe("Base64-encoded file data or URL"),
40
+ mimeType: import_zod_openapi.z.string().describe("MIME type (e.g., application/pdf)"),
41
+ filename: import_zod_openapi.z.string().optional().describe("Optional filename")
42
+ }).describe("File content part");
43
+ const ContentPartSchema = import_zod_openapi.z.discriminatedUnion("type", [TextPartSchema, ImagePartSchema, FilePartSchema]).describe("Content part - text, image, or file");
28
44
  const MessageBodySchema = import_zod_openapi.z.object({
29
- message: import_zod_openapi.z.string().optional().describe("The user message text"),
30
- sessionId: import_zod_openapi.z.string().min(1, "Session ID is required").describe("The session to use for this message"),
31
- imageData: import_zod_openapi.z.object({
32
- base64: import_zod_openapi.z.string().describe("Base64-encoded image data"),
33
- mimeType: import_zod_openapi.z.string().describe("The MIME type of the image (e.g., image/png)")
34
- }).optional().describe("Optional image data to include with the message"),
35
- fileData: import_zod_openapi.z.object({
36
- base64: import_zod_openapi.z.string().describe("Base64-encoded file data"),
37
- mimeType: import_zod_openapi.z.string().describe("The MIME type of the file (e.g., application/pdf)"),
38
- filename: import_zod_openapi.z.string().optional().describe("The filename")
39
- }).optional().describe("Optional file data to include with the message")
40
- }).refine(
41
- (data) => {
42
- const msg = (data.message ?? "").trim();
43
- return msg.length > 0 || !!data.imageData || !!data.fileData;
44
- },
45
- { message: "Must provide either message text, image data, or file data" }
46
- ).describe("Request body for sending a message to the agent");
45
+ content: import_zod_openapi.z.union([import_zod_openapi.z.string(), import_zod_openapi.z.array(ContentPartSchema)]).describe("Message content - string for text, or ContentPart[] for multimodal"),
46
+ sessionId: import_zod_openapi.z.string().min(1, "Session ID is required").describe("The session to use for this message")
47
+ }).describe("Request body for sending a message to the agent");
47
48
  const ResetBodySchema = import_zod_openapi.z.object({
48
49
  sessionId: import_zod_openapi.z.string().min(1, "Session ID is required").describe("The ID of the session to reset")
49
50
  }).describe("Request body for resetting a conversation");
@@ -95,8 +96,7 @@ function createMessagesRouter(getAgent, approvalCoordinator) {
95
96
  tokenUsage: import_responses.TokenUsageSchema.optional().describe("Token usage statistics"),
96
97
  reasoning: import_zod_openapi.z.string().optional().describe("Extended thinking content from reasoning models"),
97
98
  model: import_zod_openapi.z.string().optional().describe("Model used for this response"),
98
- provider: import_zod_openapi.z.enum(import_core.LLM_PROVIDERS).optional().describe("LLM provider"),
99
- router: import_zod_openapi.z.enum(import_core.LLM_ROUTERS).optional().describe("Router used (e.g., vercel)")
99
+ provider: import_zod_openapi.z.enum(import_core.LLM_PROVIDERS).optional().describe("LLM provider")
100
100
  }).strict()
101
101
  }
102
102
  }
@@ -131,7 +131,7 @@ function createMessagesRouter(getAgent, approvalCoordinator) {
131
131
  method: "post",
132
132
  path: "/message-stream",
133
133
  summary: "Stream message response",
134
- description: "Sends a message and streams the response via Server-Sent Events (SSE). Returns SSE stream directly in response. Events include llm:thinking, llm:chunk, llm:tool-call, llm:tool-result, llm:response, and llm:error.",
134
+ description: "Sends a message and streams the response via Server-Sent Events (SSE). Returns SSE stream directly in response. Events include llm:thinking, llm:chunk, llm:tool-call, llm:tool-result, llm:response, and llm:error. If the session is busy processing another message, returns 202 with queue information.",
135
135
  tags: ["messages"],
136
136
  request: {
137
137
  body: {
@@ -167,23 +167,28 @@ function createMessagesRouter(getAgent, approvalCoordinator) {
167
167
  }
168
168
  }
169
169
  },
170
+ 202: {
171
+ description: "Session is busy processing another message. Use the queue endpoints to manage pending messages.",
172
+ content: {
173
+ "application/json": {
174
+ schema: import_zod_openapi.z.object({
175
+ busy: import_zod_openapi.z.literal(true).describe("Indicates session is busy"),
176
+ sessionId: import_zod_openapi.z.string().describe("The session ID"),
177
+ queueLength: import_zod_openapi.z.number().describe("Current number of messages in queue"),
178
+ hint: import_zod_openapi.z.string().describe("Instructions for the client")
179
+ }).strict()
180
+ }
181
+ }
182
+ },
170
183
  400: { description: "Validation error" }
171
184
  }
172
185
  });
173
186
  return app.openapi(messageRoute, async (ctx) => {
174
187
  const agent = getAgent();
175
188
  agent.logger.info("Received message via POST /api/message");
176
- const { message, sessionId, imageData, fileData } = ctx.req.valid("json");
177
- const imageDataInput = imageData ? { image: imageData.base64, mimeType: imageData.mimeType } : void 0;
178
- const fileDataInput = fileData ? {
179
- data: fileData.base64,
180
- mimeType: fileData.mimeType,
181
- ...fileData.filename && { filename: fileData.filename }
182
- } : void 0;
183
- if (imageDataInput) agent.logger.info("Image data included in message.");
184
- if (fileDataInput) agent.logger.info("File data included in message.");
189
+ const { content, sessionId } = ctx.req.valid("json");
185
190
  agent.logger.info(`Message for session: ${sessionId}`);
186
- agent.run(message || "", imageDataInput, fileDataInput, sessionId, false).catch((error) => {
191
+ agent.generate(content, sessionId).catch((error) => {
187
192
  agent.logger.error(
188
193
  `Error in async message processing: ${error instanceof Error ? error.message : String(error)}`
189
194
  );
@@ -192,21 +197,9 @@ function createMessagesRouter(getAgent, approvalCoordinator) {
192
197
  }).openapi(messageSyncRoute, async (ctx) => {
193
198
  const agent = getAgent();
194
199
  agent.logger.info("Received message via POST /api/message-sync");
195
- const { message, sessionId, imageData, fileData } = ctx.req.valid("json");
196
- const imageDataInput = imageData ? { image: imageData.base64, mimeType: imageData.mimeType } : void 0;
197
- const fileDataInput = fileData ? {
198
- data: fileData.base64,
199
- mimeType: fileData.mimeType,
200
- ...fileData.filename && { filename: fileData.filename }
201
- } : void 0;
202
- if (imageDataInput) agent.logger.info("Image data included in message.");
203
- if (fileDataInput) agent.logger.info("File data included in message.");
200
+ const { content, sessionId } = ctx.req.valid("json");
204
201
  agent.logger.info(`Message for session: ${sessionId}`);
205
- const result = await agent.generate(message || "", {
206
- sessionId,
207
- imageData: imageDataInput,
208
- fileData: fileDataInput
209
- });
202
+ const result = await agent.generate(content, sessionId);
210
203
  const llmConfig = agent.stateManager.getLLMConfig(sessionId);
211
204
  return ctx.json({
212
205
  response: result.content,
@@ -214,9 +207,7 @@ function createMessagesRouter(getAgent, approvalCoordinator) {
214
207
  tokenUsage: result.usage,
215
208
  reasoning: result.reasoning,
216
209
  model: llmConfig.model,
217
- provider: llmConfig.provider,
218
- router: "vercel"
219
- // Hardcoded for now since we only use Vercel AI SDK
210
+ provider: llmConfig.provider
220
211
  });
221
212
  }).openapi(resetRoute, async (ctx) => {
222
213
  const agent = getAgent();
@@ -226,22 +217,23 @@ function createMessagesRouter(getAgent, approvalCoordinator) {
226
217
  return ctx.json({ status: "reset initiated", sessionId });
227
218
  }).openapi(messageStreamRoute, async (ctx) => {
228
219
  const agent = getAgent();
229
- const body = ctx.req.valid("json");
230
- const { message = "", sessionId, imageData, fileData } = body;
231
- const imageDataInput = imageData ? { image: imageData.base64, mimeType: imageData.mimeType } : void 0;
232
- const fileDataInput = fileData ? {
233
- data: fileData.base64,
234
- mimeType: fileData.mimeType,
235
- ...fileData.filename && { filename: fileData.filename }
236
- } : void 0;
220
+ const { content, sessionId } = ctx.req.valid("json");
221
+ const isBusy = await agent.isSessionBusy(sessionId);
222
+ if (isBusy) {
223
+ const queuedMessages = await agent.getQueuedMessages(sessionId);
224
+ return ctx.json(
225
+ {
226
+ busy: true,
227
+ sessionId,
228
+ queueLength: queuedMessages.length,
229
+ hint: "Use POST /api/queue/{sessionId} to queue this message, or wait for the current request to complete."
230
+ },
231
+ 202
232
+ );
233
+ }
237
234
  const abortController = new AbortController();
238
235
  const { signal } = abortController;
239
- const iterator = await agent.stream(message, {
240
- sessionId,
241
- imageData: imageDataInput,
242
- fileData: fileDataInput,
243
- signal
244
- });
236
+ const iterator = await agent.stream(content, sessionId, { signal });
245
237
  return (0, import_streaming.streamSSE)(ctx, async (stream) => {
246
238
  const pendingApprovalEvents = [];
247
239
  if (approvalCoordinator) {
@@ -277,7 +269,7 @@ function createMessagesRouter(getAgent, approvalCoordinator) {
277
269
  data: JSON.stringify(approvalEvent.data)
278
270
  });
279
271
  }
280
- const eventData = event.type === "llm:error" && event.error instanceof Error ? {
272
+ const eventData = event.name === "llm:error" && event.error instanceof Error ? {
281
273
  ...event,
282
274
  error: {
283
275
  message: event.error.message,
@@ -286,7 +278,7 @@ function createMessagesRouter(getAgent, approvalCoordinator) {
286
278
  }
287
279
  } : event;
288
280
  await stream.writeSSE({
289
- event: event.type,
281
+ event: event.name,
290
282
  data: JSON.stringify(eventData)
291
283
  });
292
284
  }