@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.
- package/dist/approval/manual-approval-handler.cjs +23 -15
- package/dist/approval/manual-approval-handler.d.ts.map +1 -1
- package/dist/approval/manual-approval-handler.js +23 -15
- package/dist/events/webhook-subscriber.cjs +1 -1
- package/dist/events/webhook-subscriber.d.ts.map +1 -1
- package/dist/events/webhook-subscriber.js +1 -1
- package/dist/hono/__tests__/test-fixtures.cjs +2 -2
- package/dist/hono/__tests__/test-fixtures.d.ts.map +1 -1
- package/dist/hono/__tests__/test-fixtures.js +2 -2
- package/dist/hono/index.cjs +14 -2
- package/dist/hono/index.d.ts +486 -132
- package/dist/hono/index.d.ts.map +1 -1
- package/dist/hono/index.js +17 -2
- package/dist/hono/middleware/error.d.ts.map +1 -1
- package/dist/hono/routes/agents.cjs +8 -10
- package/dist/hono/routes/agents.d.ts +15 -8
- package/dist/hono/routes/agents.d.ts.map +1 -1
- package/dist/hono/routes/agents.js +10 -10
- package/dist/hono/routes/approvals.cjs +52 -1
- package/dist/hono/routes/approvals.d.ts +25 -0
- package/dist/hono/routes/approvals.d.ts.map +1 -1
- package/dist/hono/routes/approvals.js +52 -1
- package/dist/hono/routes/llm.cjs +110 -31
- package/dist/hono/routes/llm.d.ts +89 -37
- package/dist/hono/routes/llm.d.ts.map +1 -1
- package/dist/hono/routes/llm.js +108 -25
- package/dist/hono/routes/mcp.cjs +8 -4
- package/dist/hono/routes/mcp.d.ts +4 -1
- package/dist/hono/routes/mcp.d.ts.map +1 -1
- package/dist/hono/routes/mcp.js +9 -5
- package/dist/hono/routes/memory.d.ts +1 -1
- package/dist/hono/routes/messages.cjs +56 -64
- package/dist/hono/routes/messages.d.ts +101 -57
- package/dist/hono/routes/messages.d.ts.map +1 -1
- package/dist/hono/routes/messages.js +57 -65
- package/dist/hono/routes/prompts.cjs +2 -2
- package/dist/hono/routes/prompts.d.ts +7 -7
- package/dist/hono/routes/prompts.js +2 -2
- package/dist/hono/routes/queue.cjs +202 -0
- package/dist/hono/routes/queue.d.ts +171 -0
- package/dist/hono/routes/queue.d.ts.map +1 -0
- package/dist/hono/routes/queue.js +178 -0
- package/dist/hono/routes/resources.d.ts +1 -1
- package/dist/hono/routes/search.cjs +2 -24
- package/dist/hono/routes/search.d.ts +43 -15
- package/dist/hono/routes/search.d.ts.map +1 -1
- package/dist/hono/routes/search.js +3 -25
- package/dist/hono/routes/sessions.cjs +65 -11
- package/dist/hono/routes/sessions.d.ts +27 -5
- package/dist/hono/routes/sessions.d.ts.map +1 -1
- package/dist/hono/routes/sessions.js +65 -11
- package/dist/hono/routes/static.cjs +77 -0
- package/dist/hono/routes/static.d.ts +41 -0
- package/dist/hono/routes/static.d.ts.map +1 -0
- package/dist/hono/routes/static.js +52 -0
- package/dist/hono/schemas/responses.cjs +67 -25
- package/dist/hono/schemas/responses.d.ts +2076 -354
- package/dist/hono/schemas/responses.d.ts.map +1 -1
- package/dist/hono/schemas/responses.js +69 -35
- package/package.json +3 -3
package/dist/hono/routes/llm.js
CHANGED
|
@@ -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 {
|
|
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 {
|
package/dist/hono/routes/mcp.cjs
CHANGED
|
@@ -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(
|
|
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.
|
|
234
|
-
|
|
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
|
-
|
|
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
|
|
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"}
|
package/dist/hono/routes/mcp.js
CHANGED
|
@@ -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(
|
|
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.
|
|
211
|
-
|
|
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
|
-
|
|
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
|
-
|
|
30
|
-
sessionId: import_zod_openapi.z.string().min(1, "Session ID is required").describe("The session to use for this message")
|
|
31
|
-
|
|
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 {
|
|
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.
|
|
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 {
|
|
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(
|
|
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
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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(
|
|
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.
|
|
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.
|
|
281
|
+
event: event.name,
|
|
290
282
|
data: JSON.stringify(eventData)
|
|
291
283
|
});
|
|
292
284
|
}
|