@lobu/gateway 3.0.8 → 3.0.12

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 (219) hide show
  1. package/dist/api/platform.d.ts.map +1 -1
  2. package/dist/api/platform.js +8 -26
  3. package/dist/api/platform.js.map +1 -1
  4. package/dist/auth/mcp/proxy.d.ts +14 -0
  5. package/dist/auth/mcp/proxy.d.ts.map +1 -1
  6. package/dist/auth/mcp/proxy.js +149 -13
  7. package/dist/auth/mcp/proxy.js.map +1 -1
  8. package/dist/cli/gateway.d.ts.map +1 -1
  9. package/dist/cli/gateway.js +29 -0
  10. package/dist/cli/gateway.js.map +1 -1
  11. package/dist/cli/index.js +2 -2
  12. package/dist/cli/index.js.map +1 -1
  13. package/dist/connections/chat-instance-manager.d.ts.map +1 -1
  14. package/dist/connections/chat-instance-manager.js +2 -1
  15. package/dist/connections/chat-instance-manager.js.map +1 -1
  16. package/dist/connections/interaction-bridge.d.ts +9 -2
  17. package/dist/connections/interaction-bridge.d.ts.map +1 -1
  18. package/dist/connections/interaction-bridge.js +132 -230
  19. package/dist/connections/interaction-bridge.js.map +1 -1
  20. package/dist/connections/message-handler-bridge.d.ts.map +1 -1
  21. package/dist/connections/message-handler-bridge.js +44 -26
  22. package/dist/connections/message-handler-bridge.js.map +1 -1
  23. package/dist/interactions.d.ts +9 -43
  24. package/dist/interactions.d.ts.map +1 -1
  25. package/dist/interactions.js +10 -52
  26. package/dist/interactions.js.map +1 -1
  27. package/dist/orchestration/base-deployment-manager.js +7 -7
  28. package/dist/orchestration/base-deployment-manager.js.map +1 -1
  29. package/dist/platform/unified-thread-consumer.d.ts.map +1 -1
  30. package/dist/platform/unified-thread-consumer.js +38 -34
  31. package/dist/platform/unified-thread-consumer.js.map +1 -1
  32. package/dist/routes/public/agent.d.ts +4 -0
  33. package/dist/routes/public/agent.d.ts.map +1 -1
  34. package/dist/routes/public/agent.js +21 -0
  35. package/dist/routes/public/agent.js.map +1 -1
  36. package/dist/services/core-services.d.ts.map +1 -1
  37. package/dist/services/core-services.js +4 -0
  38. package/dist/services/core-services.js.map +1 -1
  39. package/package.json +2 -2
  40. package/src/__tests__/agent-config-routes.test.ts +0 -254
  41. package/src/__tests__/agent-history-routes.test.ts +0 -72
  42. package/src/__tests__/agent-routes.test.ts +0 -68
  43. package/src/__tests__/agent-schedules-routes.test.ts +0 -59
  44. package/src/__tests__/agent-settings-store.test.ts +0 -323
  45. package/src/__tests__/bedrock-model-catalog.test.ts +0 -40
  46. package/src/__tests__/bedrock-openai-service.test.ts +0 -157
  47. package/src/__tests__/bedrock-provider-module.test.ts +0 -56
  48. package/src/__tests__/chat-instance-manager-slack.test.ts +0 -204
  49. package/src/__tests__/chat-response-bridge.test.ts +0 -131
  50. package/src/__tests__/config-memory-plugins.test.ts +0 -92
  51. package/src/__tests__/config-request-store.test.ts +0 -127
  52. package/src/__tests__/connection-routes.test.ts +0 -144
  53. package/src/__tests__/core-services-store-selection.test.ts +0 -92
  54. package/src/__tests__/docker-deployment.test.ts +0 -1211
  55. package/src/__tests__/embedded-deployment.test.ts +0 -342
  56. package/src/__tests__/grant-store.test.ts +0 -148
  57. package/src/__tests__/http-proxy.test.ts +0 -281
  58. package/src/__tests__/instruction-service.test.ts +0 -37
  59. package/src/__tests__/link-buttons.test.ts +0 -112
  60. package/src/__tests__/lobu.test.ts +0 -32
  61. package/src/__tests__/mcp-config-service.test.ts +0 -347
  62. package/src/__tests__/mcp-proxy.test.ts +0 -694
  63. package/src/__tests__/message-handler-bridge.test.ts +0 -17
  64. package/src/__tests__/model-selection.test.ts +0 -172
  65. package/src/__tests__/oauth-templates.test.ts +0 -39
  66. package/src/__tests__/platform-adapter-slack-send.test.ts +0 -114
  67. package/src/__tests__/platform-helpers-model-resolution.test.ts +0 -253
  68. package/src/__tests__/provider-inheritance.test.ts +0 -212
  69. package/src/__tests__/routes/cli-auth.test.ts +0 -337
  70. package/src/__tests__/routes/interactions.test.ts +0 -121
  71. package/src/__tests__/secret-proxy.test.ts +0 -85
  72. package/src/__tests__/session-manager.test.ts +0 -572
  73. package/src/__tests__/setup.ts +0 -133
  74. package/src/__tests__/skill-and-mcp-registry.test.ts +0 -203
  75. package/src/__tests__/slack-routes.test.ts +0 -161
  76. package/src/__tests__/system-config-resolver.test.ts +0 -75
  77. package/src/__tests__/system-message-limiter.test.ts +0 -89
  78. package/src/__tests__/system-skills-service.test.ts +0 -362
  79. package/src/__tests__/transcription-service.test.ts +0 -222
  80. package/src/__tests__/utils/rate-limiter.test.ts +0 -102
  81. package/src/__tests__/worker-connection-manager.test.ts +0 -497
  82. package/src/__tests__/worker-job-router.test.ts +0 -722
  83. package/src/api/index.ts +0 -1
  84. package/src/api/platform.ts +0 -292
  85. package/src/api/response-renderer.ts +0 -157
  86. package/src/auth/agent-metadata-store.ts +0 -168
  87. package/src/auth/api-auth-middleware.ts +0 -69
  88. package/src/auth/api-key-provider-module.ts +0 -213
  89. package/src/auth/base-provider-module.ts +0 -201
  90. package/src/auth/bedrock/provider-module.ts +0 -110
  91. package/src/auth/chatgpt/chatgpt-oauth-module.ts +0 -185
  92. package/src/auth/chatgpt/device-code-client.ts +0 -218
  93. package/src/auth/chatgpt/index.ts +0 -1
  94. package/src/auth/claude/oauth-module.ts +0 -280
  95. package/src/auth/cli/token-service.ts +0 -249
  96. package/src/auth/external/client.ts +0 -560
  97. package/src/auth/external/device-code-client.ts +0 -235
  98. package/src/auth/mcp/config-service.ts +0 -420
  99. package/src/auth/mcp/proxy.ts +0 -1086
  100. package/src/auth/mcp/string-substitution.ts +0 -17
  101. package/src/auth/mcp/tool-cache.ts +0 -90
  102. package/src/auth/oauth/base-client.ts +0 -267
  103. package/src/auth/oauth/client.ts +0 -153
  104. package/src/auth/oauth/credentials.ts +0 -7
  105. package/src/auth/oauth/providers.ts +0 -69
  106. package/src/auth/oauth/state-store.ts +0 -150
  107. package/src/auth/oauth-templates.ts +0 -179
  108. package/src/auth/provider-catalog.ts +0 -220
  109. package/src/auth/provider-model-options.ts +0 -41
  110. package/src/auth/settings/agent-settings-store.ts +0 -565
  111. package/src/auth/settings/auth-profiles-manager.ts +0 -216
  112. package/src/auth/settings/index.ts +0 -12
  113. package/src/auth/settings/model-preference-store.ts +0 -52
  114. package/src/auth/settings/model-selection.ts +0 -135
  115. package/src/auth/settings/resolved-settings-view.ts +0 -298
  116. package/src/auth/settings/template-utils.ts +0 -44
  117. package/src/auth/settings/token-service.ts +0 -88
  118. package/src/auth/system-env-store.ts +0 -98
  119. package/src/auth/user-agents-store.ts +0 -68
  120. package/src/channels/binding-service.ts +0 -214
  121. package/src/channels/index.ts +0 -4
  122. package/src/cli/gateway.ts +0 -1312
  123. package/src/cli/index.ts +0 -74
  124. package/src/commands/built-in-commands.ts +0 -80
  125. package/src/commands/command-dispatcher.ts +0 -94
  126. package/src/commands/command-reply-adapters.ts +0 -27
  127. package/src/config/file-loader.ts +0 -618
  128. package/src/config/index.ts +0 -588
  129. package/src/config/network-allowlist.ts +0 -71
  130. package/src/connections/chat-instance-manager.ts +0 -1284
  131. package/src/connections/chat-response-bridge.ts +0 -618
  132. package/src/connections/index.ts +0 -7
  133. package/src/connections/interaction-bridge.ts +0 -831
  134. package/src/connections/message-handler-bridge.ts +0 -415
  135. package/src/connections/platform-auth-methods.ts +0 -15
  136. package/src/connections/types.ts +0 -84
  137. package/src/gateway/connection-manager.ts +0 -291
  138. package/src/gateway/index.ts +0 -698
  139. package/src/gateway/job-router.ts +0 -201
  140. package/src/gateway-main.ts +0 -200
  141. package/src/index.ts +0 -41
  142. package/src/infrastructure/queue/index.ts +0 -12
  143. package/src/infrastructure/queue/queue-producer.ts +0 -148
  144. package/src/infrastructure/queue/redis-queue.ts +0 -361
  145. package/src/infrastructure/queue/types.ts +0 -133
  146. package/src/infrastructure/redis/system-message-limiter.ts +0 -94
  147. package/src/interactions/config-request-store.ts +0 -198
  148. package/src/interactions.ts +0 -363
  149. package/src/lobu.ts +0 -311
  150. package/src/metrics/prometheus.ts +0 -159
  151. package/src/modules/module-system.ts +0 -179
  152. package/src/orchestration/base-deployment-manager.ts +0 -900
  153. package/src/orchestration/deployment-utils.ts +0 -98
  154. package/src/orchestration/impl/docker-deployment.ts +0 -620
  155. package/src/orchestration/impl/embedded-deployment.ts +0 -268
  156. package/src/orchestration/impl/index.ts +0 -8
  157. package/src/orchestration/impl/k8s/deployment.ts +0 -1061
  158. package/src/orchestration/impl/k8s/helpers.ts +0 -610
  159. package/src/orchestration/impl/k8s/index.ts +0 -1
  160. package/src/orchestration/index.ts +0 -333
  161. package/src/orchestration/message-consumer.ts +0 -584
  162. package/src/orchestration/scheduled-wakeup.ts +0 -704
  163. package/src/permissions/approval-policy.ts +0 -36
  164. package/src/permissions/grant-store.ts +0 -219
  165. package/src/platform/file-handler.ts +0 -66
  166. package/src/platform/link-buttons.ts +0 -57
  167. package/src/platform/renderer-utils.ts +0 -44
  168. package/src/platform/response-renderer.ts +0 -84
  169. package/src/platform/unified-thread-consumer.ts +0 -187
  170. package/src/platform.ts +0 -318
  171. package/src/proxy/http-proxy.ts +0 -752
  172. package/src/proxy/proxy-manager.ts +0 -81
  173. package/src/proxy/secret-proxy.ts +0 -402
  174. package/src/proxy/token-refresh-job.ts +0 -143
  175. package/src/routes/internal/audio.ts +0 -141
  176. package/src/routes/internal/device-auth.ts +0 -652
  177. package/src/routes/internal/files.ts +0 -226
  178. package/src/routes/internal/history.ts +0 -69
  179. package/src/routes/internal/images.ts +0 -127
  180. package/src/routes/internal/interactions.ts +0 -84
  181. package/src/routes/internal/middleware.ts +0 -23
  182. package/src/routes/internal/schedule.ts +0 -226
  183. package/src/routes/internal/types.ts +0 -22
  184. package/src/routes/openapi-auto.ts +0 -239
  185. package/src/routes/public/agent-access.ts +0 -23
  186. package/src/routes/public/agent-config.ts +0 -675
  187. package/src/routes/public/agent-history.ts +0 -422
  188. package/src/routes/public/agent-schedules.ts +0 -296
  189. package/src/routes/public/agent.ts +0 -1086
  190. package/src/routes/public/agents.ts +0 -373
  191. package/src/routes/public/channels.ts +0 -191
  192. package/src/routes/public/cli-auth.ts +0 -896
  193. package/src/routes/public/connections.ts +0 -574
  194. package/src/routes/public/landing.ts +0 -16
  195. package/src/routes/public/oauth.ts +0 -147
  196. package/src/routes/public/settings-auth.ts +0 -104
  197. package/src/routes/public/slack.ts +0 -173
  198. package/src/routes/shared/agent-ownership.ts +0 -101
  199. package/src/routes/shared/token-verifier.ts +0 -34
  200. package/src/services/bedrock-model-catalog.ts +0 -217
  201. package/src/services/bedrock-openai-service.ts +0 -658
  202. package/src/services/core-services.ts +0 -1072
  203. package/src/services/image-generation-service.ts +0 -257
  204. package/src/services/instruction-service.ts +0 -318
  205. package/src/services/mcp-registry.ts +0 -94
  206. package/src/services/platform-helpers.ts +0 -287
  207. package/src/services/session-manager.ts +0 -262
  208. package/src/services/settings-resolver.ts +0 -74
  209. package/src/services/system-config-resolver.ts +0 -89
  210. package/src/services/system-skills-service.ts +0 -229
  211. package/src/services/transcription-service.ts +0 -684
  212. package/src/session.ts +0 -110
  213. package/src/spaces/index.ts +0 -1
  214. package/src/spaces/space-resolver.ts +0 -17
  215. package/src/stores/in-memory-agent-store.ts +0 -403
  216. package/src/stores/redis-agent-store.ts +0 -279
  217. package/src/utils/public-url.ts +0 -44
  218. package/src/utils/rate-limiter.ts +0 -94
  219. package/tsconfig.json +0 -33
@@ -1,1086 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import { createRoute, OpenAPIHono } from "@hono/zod-openapi";
3
- import {
4
- type AgentConfigStore,
5
- createLogger,
6
- createRootSpan,
7
- findTemplateAgentId,
8
- generateWorkerToken,
9
- type InstalledProvider,
10
- type McpServerConfig,
11
- type NetworkConfig,
12
- verifyWorkerToken,
13
- } from "@lobu/core";
14
- import { streamSSE } from "hono/streaming";
15
- import { z } from "zod";
16
- import {
17
- createApiAuthMiddleware,
18
- TOKEN_EXPIRATION_MS,
19
- } from "../../auth/api-auth-middleware";
20
- import type { CliTokenService } from "../../auth/cli/token-service";
21
- import type { ExternalAuthClient } from "../../auth/external/client";
22
- import type { AgentSettingsStore } from "../../auth/settings/agent-settings-store";
23
- import type { QueueProducer } from "../../infrastructure/queue/queue-producer";
24
- import { getModelProviderModules } from "../../modules/module-system";
25
- import type { PlatformRegistry } from "../../platform";
26
- import { resolveAgentOptions } from "../../services/platform-helpers";
27
- import type { ISessionManager, ThreadSession } from "../../session";
28
-
29
- const logger = createLogger("agent-api");
30
-
31
- // =============================================================================
32
- // Constants
33
- // =============================================================================
34
-
35
- const MAX_CONNECTIONS_PER_AGENT = 5;
36
- const MAX_TOTAL_CONNECTIONS = 1000;
37
-
38
- // SSE connection tracking
39
- const sseConnections = new Map<string, Set<any>>();
40
-
41
- // =============================================================================
42
- // Zod Schemas
43
- // =============================================================================
44
-
45
- const NetworkConfigSchema = z.object({
46
- allowedDomains: z.array(z.string()).optional(),
47
- deniedDomains: z.array(z.string()).optional(),
48
- });
49
-
50
- const McpServerConfigSchema = z.object({
51
- url: z.string().optional(),
52
- type: z.enum(["sse", "stdio"]).optional(),
53
- command: z.string().optional(),
54
- args: z.array(z.string()).optional(),
55
- env: z.record(z.string(), z.string()).optional(),
56
- headers: z.record(z.string(), z.string()).optional(),
57
- description: z.string().optional(),
58
- });
59
-
60
- const NixConfigSchema = z.object({
61
- flakeUrl: z.string().optional(),
62
- packages: z.array(z.string()).optional(),
63
- });
64
-
65
- const CreateAgentRequestSchema = z.object({
66
- provider: z.string().default("claude").optional(),
67
- model: z.string().optional(),
68
- agentId: z.string().min(1).optional(),
69
- userId: z.string().min(1).optional(),
70
- thread: z.string().optional(),
71
- forceNew: z.boolean().optional(),
72
- dryRun: z.boolean().optional(),
73
- networkConfig: NetworkConfigSchema.optional(),
74
- mcpServers: z.record(z.string(), McpServerConfigSchema).optional(),
75
- nix: NixConfigSchema.optional(),
76
- });
77
-
78
- const CreateAgentResponseSchema = z.object({
79
- success: z.boolean(),
80
- agentId: z.string(),
81
- token: z.string(),
82
- expiresAt: z.number(),
83
- sseUrl: z.string(),
84
- messagesUrl: z.string(),
85
- });
86
-
87
- const SlackRoutingInfoSchema = z.object({
88
- channel: z.string().describe("Slack channel ID"),
89
- thread: z.string().optional().describe("Thread timestamp for replies"),
90
- team: z.string().optional().describe("Slack team ID"),
91
- });
92
-
93
- const SendMessageRequestSchema = z
94
- .object({
95
- content: z.string().optional().describe("Message content"),
96
- message: z
97
- .string()
98
- .optional()
99
- .describe("Message content (alias for content)"),
100
- messageId: z.string().optional(),
101
- platform: z
102
- .string()
103
- .optional()
104
- .describe("Target platform (api, slack, telegram)"),
105
- slack: SlackRoutingInfoSchema.optional().describe(
106
- "Slack-specific routing info (required when platform=slack)"
107
- ),
108
- })
109
- .passthrough();
110
-
111
- const SendMessageResponseSchema = z.object({
112
- success: z.boolean(),
113
- messageId: z.string(),
114
- agentId: z.string().optional(),
115
- jobId: z.string().optional(),
116
- eventsUrl: z.string().optional(),
117
- queued: z.boolean(),
118
- traceparent: z.string().optional(),
119
- });
120
-
121
- const AgentStatusResponseSchema = z.object({
122
- success: z.boolean(),
123
- agent: z.object({
124
- agentId: z.string(),
125
- userId: z.string(),
126
- status: z.string(),
127
- createdAt: z.number(),
128
- lastActivity: z.number(),
129
- hasActiveConnection: z.boolean(),
130
- }),
131
- });
132
-
133
- const ErrorResponseSchema = z.object({
134
- success: z.boolean(),
135
- error: z.string(),
136
- details: z.string().optional(),
137
- });
138
-
139
- const SuccessResponseSchema = z.object({
140
- success: z.boolean(),
141
- message: z.string().optional(),
142
- agentId: z.string().optional(),
143
- });
144
-
145
- // Path parameters
146
- const AgentIdParamSchema = z.object({
147
- agentId: z.string(),
148
- });
149
-
150
- // =============================================================================
151
- // Validation Helpers
152
- // =============================================================================
153
-
154
- function validateDomainPattern(pattern: string): string | null {
155
- if (!pattern || typeof pattern !== "string") {
156
- return "Domain pattern must be a non-empty string";
157
- }
158
- const trimmed = pattern.trim().toLowerCase();
159
- if (trimmed === "*") return "Bare wildcard '*' is not allowed";
160
- if (trimmed.includes("://"))
161
- return `Domain pattern cannot contain protocol: ${pattern}`;
162
- if (trimmed.includes("/"))
163
- return `Domain pattern cannot contain path: ${pattern}`;
164
- if (trimmed.includes(":") && !trimmed.includes("[")) {
165
- return `Domain pattern cannot contain port: ${pattern}`;
166
- }
167
- if (trimmed.startsWith("*.") || trimmed.startsWith(".")) {
168
- const domain = trimmed.startsWith("*.")
169
- ? trimmed.substring(2)
170
- : trimmed.substring(1);
171
- if (!domain.includes(".")) {
172
- return `Wildcard pattern too broad: ${pattern}`;
173
- }
174
- } else if (!trimmed.includes(".")) {
175
- return `Invalid domain pattern: ${pattern}`;
176
- }
177
- return null;
178
- }
179
-
180
- function validateNetworkConfig(config: NetworkConfig): string | null {
181
- for (const domains of [config.allowedDomains, config.deniedDomains]) {
182
- if (domains) {
183
- for (const domain of domains) {
184
- const error = validateDomainPattern(domain);
185
- if (error) return error;
186
- }
187
- }
188
- }
189
- return null;
190
- }
191
-
192
- function validateMcpServerConfig(
193
- id: string,
194
- config: McpServerConfig
195
- ): string | null {
196
- if (!config.url && !config.command) {
197
- return `MCP ${id}: must specify either 'url' or 'command'`;
198
- }
199
- if (
200
- config.url &&
201
- !config.url.startsWith("http://") &&
202
- !config.url.startsWith("https://")
203
- ) {
204
- return `MCP ${id}: url must be http:// or https://`;
205
- }
206
- if (config.command) {
207
- const dangerousCommands = [
208
- "rm",
209
- "sudo",
210
- "curl",
211
- "wget",
212
- "sh",
213
- "bash",
214
- "zsh",
215
- "kill",
216
- ];
217
- const baseCommand = config.command.split("/").pop()?.split(" ")[0] || "";
218
- if (dangerousCommands.includes(baseCommand)) {
219
- return `MCP ${id}: command '${baseCommand}' is not allowed`;
220
- }
221
- }
222
- return null;
223
- }
224
-
225
- function validateMcpConfig(
226
- mcpServers: Record<string, McpServerConfig>
227
- ): string | null {
228
- for (const [id, config] of Object.entries(mcpServers)) {
229
- if (!/^[a-zA-Z0-9_-]+$/.test(id)) {
230
- return `MCP ID '${id}' is invalid`;
231
- }
232
- const error = validateMcpServerConfig(id, config);
233
- if (error) return error;
234
- }
235
- return null;
236
- }
237
-
238
- // =============================================================================
239
- // Broadcast Functions (exported for use by other modules)
240
- // =============================================================================
241
-
242
- export function broadcastToAgent(
243
- agentId: string,
244
- event: string,
245
- data: unknown
246
- ): void {
247
- const connections = sseConnections.get(agentId);
248
- if (!connections || connections.size === 0) return;
249
-
250
- const deadConnections = new Set<any>();
251
-
252
- for (const res of connections) {
253
- try {
254
- if (res.closed || res.destroyed || res.writableEnded) {
255
- deadConnections.add(res);
256
- continue;
257
- }
258
- if (typeof res.writeSSE === "function") {
259
- res.writeSSE({ event, data: JSON.stringify(data) });
260
- } else if (typeof res.write === "function") {
261
- const message = `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
262
- res.write(message);
263
- }
264
- } catch {
265
- deadConnections.add(res);
266
- }
267
- }
268
-
269
- for (const deadRes of deadConnections) {
270
- connections.delete(deadRes);
271
- }
272
- if (connections.size === 0) {
273
- sseConnections.delete(agentId);
274
- }
275
- }
276
-
277
- // =============================================================================
278
- // OpenAPI Route Definitions
279
- // =============================================================================
280
-
281
- const createAgentRoute = createRoute({
282
- method: "post",
283
- path: "/api/v1/agents",
284
- tags: ["Agents"],
285
- summary: "Create a new agent",
286
- security: [{ bearerAuth: [] }],
287
- description:
288
- "Creates a new agent session and returns authentication credentials",
289
- request: {
290
- body: {
291
- content: { "application/json": { schema: CreateAgentRequestSchema } },
292
- },
293
- },
294
- responses: {
295
- 201: {
296
- description: "Agent created",
297
- content: { "application/json": { schema: CreateAgentResponseSchema } },
298
- },
299
- 400: {
300
- description: "Invalid request",
301
- content: { "application/json": { schema: ErrorResponseSchema } },
302
- },
303
- 401: {
304
- description: "Unauthorized",
305
- content: { "application/json": { schema: ErrorResponseSchema } },
306
- },
307
- },
308
- });
309
-
310
- const getAgentRoute = createRoute({
311
- method: "get",
312
- path: "/api/v1/agents/{agentId}",
313
- tags: ["Agents"],
314
- summary: "Get agent status",
315
- security: [{ bearerAuth: [] }],
316
- request: { params: AgentIdParamSchema },
317
- responses: {
318
- 200: {
319
- description: "Agent status",
320
- content: { "application/json": { schema: AgentStatusResponseSchema } },
321
- },
322
- 401: {
323
- description: "Unauthorized",
324
- content: { "application/json": { schema: ErrorResponseSchema } },
325
- },
326
- 404: {
327
- description: "Not found",
328
- content: { "application/json": { schema: ErrorResponseSchema } },
329
- },
330
- },
331
- });
332
-
333
- const deleteAgentRoute = createRoute({
334
- method: "delete",
335
- path: "/api/v1/agents/{agentId}",
336
- tags: ["Agents"],
337
- summary: "Delete an agent",
338
- security: [{ bearerAuth: [] }],
339
- request: { params: AgentIdParamSchema },
340
- responses: {
341
- 200: {
342
- description: "Agent deleted",
343
- content: { "application/json": { schema: SuccessResponseSchema } },
344
- },
345
- 401: {
346
- description: "Unauthorized",
347
- content: { "application/json": { schema: ErrorResponseSchema } },
348
- },
349
- 404: {
350
- description: "Not found",
351
- content: { "application/json": { schema: ErrorResponseSchema } },
352
- },
353
- },
354
- });
355
-
356
- const getAgentEventsRoute = createRoute({
357
- method: "get",
358
- path: "/api/v1/agents/{agentId}/events",
359
- tags: ["Messages"],
360
- summary: "Subscribe to agent events (SSE)",
361
- description: "Server-Sent Events stream for real-time agent updates",
362
- security: [{ bearerAuth: [] }],
363
- request: { params: AgentIdParamSchema },
364
- responses: {
365
- 200: {
366
- description: "SSE stream",
367
- content: { "text/event-stream": { schema: z.string() } },
368
- },
369
- 401: {
370
- description: "Unauthorized",
371
- content: { "application/json": { schema: ErrorResponseSchema } },
372
- },
373
- 429: {
374
- description: "Too many connections",
375
- content: { "application/json": { schema: ErrorResponseSchema } },
376
- },
377
- },
378
- });
379
-
380
- const sendMessageRoute = createRoute({
381
- method: "post",
382
- path: "/api/v1/agents/{agentId}/messages",
383
- tags: ["Messages"],
384
- summary: "Send a message to the agent",
385
- description:
386
- "Send a message to an agent. Supports JSON body or multipart form data for file uploads. " +
387
- "When platform is specified, the message is routed through the platform adapter.",
388
- security: [{ bearerAuth: [] }],
389
- request: {
390
- params: AgentIdParamSchema,
391
- body: {
392
- content: {
393
- "application/json": { schema: SendMessageRequestSchema },
394
- },
395
- },
396
- },
397
- responses: {
398
- 200: {
399
- description: "Message queued",
400
- content: { "application/json": { schema: SendMessageResponseSchema } },
401
- },
402
- 400: {
403
- description: "Invalid request",
404
- content: { "application/json": { schema: ErrorResponseSchema } },
405
- },
406
- 401: {
407
- description: "Unauthorized",
408
- content: { "application/json": { schema: ErrorResponseSchema } },
409
- },
410
- 403: {
411
- description: "Forbidden - worker tokens cannot route to platforms",
412
- content: { "application/json": { schema: ErrorResponseSchema } },
413
- },
414
- 404: {
415
- description: "Agent not found",
416
- content: { "application/json": { schema: ErrorResponseSchema } },
417
- },
418
- },
419
- });
420
-
421
- // =============================================================================
422
- // Create OpenAPI Hono App
423
- // =============================================================================
424
-
425
- export interface AgentApiConfig {
426
- queueProducer: QueueProducer;
427
- sessionManager: ISessionManager;
428
- publicGatewayUrl: string;
429
- adminPassword?: string;
430
- cliTokenService?: CliTokenService;
431
- externalAuthClient?: ExternalAuthClient;
432
- agentSettingsStore?: AgentSettingsStore;
433
- agentConfigStore?: Pick<AgentConfigStore, "getSettings" | "listAgents">;
434
- platformRegistry?: PlatformRegistry;
435
- }
436
-
437
- export function createAgentApi(config: AgentApiConfig): OpenAPIHono;
438
- export function createAgentApi(
439
- queueProducer: QueueProducer,
440
- sessionManager: ISessionManager,
441
- publicGatewayUrl: string
442
- ): OpenAPIHono;
443
- export function createAgentApi(
444
- configOrQueue: AgentApiConfig | QueueProducer,
445
- sessionManager?: ISessionManager,
446
- publicGatewayUrl?: string
447
- ): OpenAPIHono {
448
- const config: AgentApiConfig =
449
- configOrQueue instanceof Object && "queueProducer" in configOrQueue
450
- ? configOrQueue
451
- : {
452
- queueProducer: configOrQueue as QueueProducer,
453
- sessionManager: sessionManager!,
454
- publicGatewayUrl: publicGatewayUrl!,
455
- };
456
-
457
- const {
458
- queueProducer,
459
- adminPassword,
460
- cliTokenService,
461
- agentSettingsStore,
462
- agentConfigStore,
463
- platformRegistry,
464
- } = config;
465
- const sessMgr = config.sessionManager;
466
- const pubUrl = config.publicGatewayUrl;
467
- const app = new OpenAPIHono();
468
-
469
- // Unified auth middleware for all agent API routes
470
- app.use(
471
- "/api/v1/agents/*",
472
- createApiAuthMiddleware({
473
- adminPassword,
474
- cliTokenService,
475
- externalAuthClient: config.externalAuthClient,
476
- allowSettingsSession: true,
477
- })
478
- );
479
-
480
- // =============================================================================
481
- // Route Handlers
482
- // =============================================================================
483
-
484
- // POST /api/v1/agents - Create agent
485
- app.openapi(createAgentRoute, async (c): Promise<any> => {
486
- const body = c.req.valid("json");
487
- const {
488
- provider = "claude",
489
- model,
490
- agentId: requestedAgentId,
491
- userId: requestedUserId,
492
- thread,
493
- forceNew,
494
- dryRun,
495
- networkConfig,
496
- mcpServers,
497
- nix: nixConfig,
498
- } = body;
499
-
500
- // Validate provider
501
- if (provider && !["claude"].includes(provider)) {
502
- return c.json(
503
- { success: false, error: "Invalid provider. Supported: claude" },
504
- 400
505
- );
506
- }
507
-
508
- // Validate network config
509
- if (networkConfig) {
510
- const error = validateNetworkConfig(networkConfig as NetworkConfig);
511
- if (error) return c.json({ success: false, error }, 400);
512
- }
513
-
514
- // Validate MCP config
515
- if (mcpServers) {
516
- const error = validateMcpConfig(
517
- mcpServers as Record<string, McpServerConfig>
518
- );
519
- if (error) return c.json({ success: false, error }, 400);
520
- }
521
-
522
- const isEphemeral = !requestedAgentId?.trim();
523
- const agentId = requestedAgentId?.trim() || randomUUID();
524
-
525
- // For ephemeral agents, auto-provision settings so the worker gets provider config
526
- if (isEphemeral && agentSettingsStore) {
527
- // Try system-key providers first (env var based API keys)
528
- const providerModules = getModelProviderModules();
529
- const systemProviders: InstalledProvider[] = providerModules
530
- .filter((m) => m.hasSystemKey())
531
- .map((m) => ({
532
- providerId: m.providerId,
533
- installedAt: Date.now(),
534
- }));
535
-
536
- if (systemProviders.length > 0) {
537
- // Also inherit pluginsConfig from template agent if available
538
- const templateId = agentConfigStore
539
- ? await findTemplateAgentId(agentConfigStore)
540
- : await agentSettingsStore.findTemplateAgentId();
541
- const templateSettings = templateId
542
- ? await (agentConfigStore?.getSettings(templateId) ??
543
- agentSettingsStore.getSettings(templateId))
544
- : null;
545
- await agentSettingsStore.saveSettings(agentId, {
546
- installedProviders: systemProviders,
547
- pluginsConfig: templateSettings?.pluginsConfig,
548
- });
549
- logger.info(
550
- `Ephemeral agent ${agentId}: provisioned system providers [${systemProviders.map((p) => p.providerId).join(", ")}]`
551
- );
552
- } else {
553
- // Fall back to using an existing agent as template (inherits its providers)
554
- const templateId = agentConfigStore
555
- ? await findTemplateAgentId(agentConfigStore)
556
- : await agentSettingsStore.findTemplateAgentId();
557
- if (templateId) {
558
- const templateSettings = await (agentConfigStore?.getSettings(
559
- templateId
560
- ) ?? agentSettingsStore.getSettings(templateId));
561
- await agentSettingsStore.saveSettings(agentId, {
562
- templateAgentId: templateId,
563
- pluginsConfig: templateSettings?.pluginsConfig,
564
- });
565
- logger.info(
566
- `Ephemeral agent ${agentId}: using template ${templateId}`
567
- );
568
- }
569
- }
570
- }
571
-
572
- const userId = requestedUserId || agentId;
573
-
574
- // Build composite conversationId for user-specific sessions
575
- // Uses _ separator (colons not allowed in BullMQ custom IDs)
576
- const conversationId = thread
577
- ? `${agentId}_${userId}_${thread}`
578
- : `${agentId}_${userId}`;
579
- const channelId = `api_${userId}`;
580
- const deploymentName = `api-${agentId.slice(0, 8)}`;
581
-
582
- // Try to resume existing session (unless forceNew is requested)
583
- if (!forceNew) {
584
- const existing = await sessMgr.getSession(conversationId);
585
- if (existing) {
586
- // Reuse existing session — touch lastActivity and return existing token
587
- await sessMgr.touchSession(conversationId);
588
-
589
- const token = generateWorkerToken(
590
- agentId,
591
- conversationId,
592
- deploymentName,
593
- {
594
- channelId,
595
- agentId,
596
- platform: "api",
597
- sessionKey: userId,
598
- }
599
- );
600
-
601
- const expiresAt = Date.now() + TOKEN_EXPIRATION_MS;
602
- const baseUrl = pubUrl || "http://localhost:8080";
603
-
604
- logger.info(
605
- `Resumed API session: ${conversationId} (agent=${agentId})`
606
- );
607
-
608
- return c.json(
609
- {
610
- success: true,
611
- agentId: conversationId,
612
- token,
613
- expiresAt,
614
- sseUrl: `${baseUrl}/api/v1/agents/${conversationId}/events`,
615
- messagesUrl: `${baseUrl}/api/v1/agents/${conversationId}/messages`,
616
- },
617
- 201
618
- );
619
- }
620
- }
621
-
622
- const token = generateWorkerToken(agentId, conversationId, deploymentName, {
623
- channelId,
624
- agentId,
625
- platform: "api",
626
- sessionKey: userId,
627
- });
628
-
629
- const expiresAt = Date.now() + TOKEN_EXPIRATION_MS;
630
-
631
- const session: ThreadSession = {
632
- conversationId,
633
- channelId,
634
- userId,
635
- threadCreator: userId,
636
- lastActivity: Date.now(),
637
- createdAt: Date.now(),
638
- status: "created",
639
- provider,
640
- model,
641
- networkConfig: networkConfig as NetworkConfig | undefined,
642
- mcpConfig: mcpServers
643
- ? { mcpServers: mcpServers as Record<string, McpServerConfig> }
644
- : undefined,
645
- nixConfig,
646
- agentId,
647
- dryRun: dryRun || false,
648
- };
649
- await sessMgr.setSession(session);
650
-
651
- logger.info(`Created API agent: ${conversationId} (agent=${agentId})`);
652
-
653
- const baseUrl = pubUrl || "http://localhost:8080";
654
- return c.json(
655
- {
656
- success: true,
657
- agentId: conversationId,
658
- token,
659
- expiresAt,
660
- sseUrl: `${baseUrl}/api/v1/agents/${conversationId}/events`,
661
- messagesUrl: `${baseUrl}/api/v1/agents/${conversationId}/messages`,
662
- },
663
- 201
664
- );
665
- });
666
-
667
- // GET /api/v1/agents/:agentId - Get status
668
- app.openapi(getAgentRoute, async (c): Promise<any> => {
669
- const { agentId: sessionKey } = c.req.valid("param");
670
-
671
- const session = await sessMgr.getSession(sessionKey);
672
- if (!session) {
673
- return c.json({ success: false, error: "Agent not found" }, 404);
674
- }
675
-
676
- const hasActiveConnection =
677
- sseConnections.has(sessionKey) &&
678
- (sseConnections.get(sessionKey)?.size ?? 0) > 0;
679
-
680
- return c.json({
681
- success: true,
682
- agent: {
683
- agentId: session.conversationId,
684
- userId: session.userId,
685
- status: session.status || "active",
686
- createdAt: session.createdAt,
687
- lastActivity: session.lastActivity,
688
- hasActiveConnection,
689
- },
690
- });
691
- });
692
-
693
- // DELETE /api/v1/agents/:agentId
694
- app.openapi(deleteAgentRoute, async (c): Promise<any> => {
695
- const { agentId: sessionKey } = c.req.valid("param");
696
-
697
- const connections = sseConnections.get(sessionKey);
698
- if (connections) {
699
- for (const connection of connections) {
700
- try {
701
- if (typeof connection.writeSSE === "function") {
702
- connection.writeSSE({
703
- event: "closed",
704
- data: JSON.stringify({ reason: "agent_deleted" }),
705
- });
706
- } else if (typeof connection.write === "function") {
707
- connection.write(
708
- `event: closed\ndata: ${JSON.stringify({ reason: "agent_deleted" })}\n\n`
709
- );
710
- }
711
- connection.close?.();
712
- connection.end?.();
713
- } catch {
714
- // Ignore
715
- }
716
- }
717
- sseConnections.delete(sessionKey);
718
- }
719
-
720
- // Get real agentId from session before deleting
721
- const session = await sessMgr.getSession(sessionKey);
722
- const realAgentId = session?.agentId || sessionKey;
723
-
724
- await sessMgr.deleteSession(sessionKey);
725
- // Clean up ephemeral agent settings
726
- if (agentSettingsStore) {
727
- await agentSettingsStore.deleteSettings(realAgentId).catch(() => {
728
- /* best-effort cleanup */
729
- });
730
- }
731
- logger.info(`Deleted agent ${sessionKey}`);
732
-
733
- return c.json({
734
- success: true,
735
- message: "Agent deleted",
736
- agentId: sessionKey,
737
- });
738
- });
739
-
740
- // GET /api/v1/agents/:agentId/events - SSE stream
741
- app.openapi(getAgentEventsRoute, async (c): Promise<any> => {
742
- const { agentId: sessionKey } = c.req.valid("param");
743
-
744
- const session = await sessMgr.getSession(sessionKey);
745
- if (!session) {
746
- return c.json({ success: false, error: "Agent not found" }, 404);
747
- }
748
-
749
- // Check connection limits
750
- const totalConnections = Array.from(sseConnections.values()).reduce(
751
- (acc, set) => acc + set.size,
752
- 0
753
- );
754
- if (totalConnections >= MAX_TOTAL_CONNECTIONS) {
755
- return c.json(
756
- { success: false, error: "Server connection limit reached" },
757
- 429
758
- );
759
- }
760
-
761
- // Use conversationId as the SSE connection key (matches broadcastToAgent calls)
762
- const sseKey = session.conversationId;
763
- if (!sseConnections.has(sseKey)) {
764
- sseConnections.set(sseKey, new Set());
765
- }
766
- const agentConnections = sseConnections.get(sseKey)!;
767
- if (agentConnections.size >= MAX_CONNECTIONS_PER_AGENT) {
768
- return c.json(
769
- {
770
- success: false,
771
- error: `Maximum ${MAX_CONNECTIONS_PER_AGENT} connections`,
772
- },
773
- 429
774
- );
775
- }
776
-
777
- // Return SSE stream
778
- return streamSSE(c, async (stream) => {
779
- agentConnections.add(stream);
780
-
781
- await stream.writeSSE({
782
- event: "connected",
783
- data: JSON.stringify({
784
- agentId: session.agentId || sessionKey,
785
- timestamp: Date.now(),
786
- }),
787
- });
788
-
789
- const heartbeatInterval = setInterval(async () => {
790
- try {
791
- await stream.writeSSE({
792
- event: "ping",
793
- data: JSON.stringify({ timestamp: Date.now() }),
794
- });
795
- } catch {
796
- clearInterval(heartbeatInterval);
797
- }
798
- }, 30000);
799
-
800
- stream.onAbort(() => {
801
- clearInterval(heartbeatInterval);
802
- agentConnections.delete(stream);
803
- if (agentConnections.size === 0) {
804
- sseConnections.delete(sseKey);
805
- }
806
- logger.info(`SSE connection closed for session ${sseKey}`);
807
- });
808
-
809
- while (true) {
810
- await stream.sleep(1000);
811
- }
812
- });
813
- });
814
-
815
- // POST /api/v1/agents/:agentId/messages - Send message
816
- // Supports two paths:
817
- // 1. Direct API (no platform field): requires pre-created session, enqueues directly
818
- // 2. Platform-routed (platform field present): delegates to platform adapter
819
- app.openapi(sendMessageRoute, async (c): Promise<any> => {
820
- const { agentId } = c.req.valid("param");
821
-
822
- // Parse body — multipart for file uploads, JSON otherwise
823
- const contentType = c.req.header("content-type") || "";
824
- let body: Record<string, any>;
825
- let files: Array<{ buffer: Buffer; filename: string }> | undefined;
826
-
827
- if (contentType.includes("multipart/form-data")) {
828
- const formData = await c.req.formData();
829
- body = {
830
- content: formData.get("content") as string | null,
831
- message: formData.get("message") as string | null,
832
- messageId: formData.get("messageId") as string | null,
833
- platform: formData.get("platform") as string | null,
834
- };
835
-
836
- // Extract nested platform routing from form fields
837
- const slackChannel = formData.get("slack.channel") as string;
838
- if (slackChannel) {
839
- body.slack = {
840
- channel: slackChannel,
841
- thread: formData.get("slack.thread") as string | undefined,
842
- team: formData.get("slack.team") as string | undefined,
843
- };
844
- }
845
- const whatsappChat = formData.get("whatsapp.chat") as string;
846
- if (whatsappChat) {
847
- body.whatsapp = { chat: whatsappChat };
848
- }
849
- const telegramChatId = formData.get("telegram.chatId") as string;
850
- if (telegramChatId) {
851
- body.telegram = { chatId: telegramChatId };
852
- }
853
-
854
- // Extract files with size validation
855
- const MAX_FILE_SIZE = 50 * 1024 * 1024;
856
- const MAX_TOTAL_SIZE = 100 * 1024 * 1024;
857
- const MAX_FILE_COUNT = 10;
858
- const fileEntries = formData.getAll("files");
859
- if (fileEntries.length > MAX_FILE_COUNT) {
860
- return c.json(
861
- {
862
- success: false,
863
- error: `Too many files: ${fileEntries.length} (max ${MAX_FILE_COUNT})`,
864
- },
865
- 400
866
- );
867
- }
868
- if (fileEntries.length > 0) {
869
- const fileResults: Array<{ buffer: Buffer; filename: string }> = [];
870
- let totalSize = 0;
871
- for (const entry of fileEntries) {
872
- if (entry instanceof File) {
873
- if (entry.size > MAX_FILE_SIZE) {
874
- return c.json(
875
- {
876
- success: false,
877
- error: `File "${entry.name}" exceeds maximum size of ${MAX_FILE_SIZE / 1024 / 1024}MB`,
878
- },
879
- 400
880
- );
881
- }
882
- totalSize += entry.size;
883
- if (totalSize > MAX_TOTAL_SIZE) {
884
- return c.json(
885
- {
886
- success: false,
887
- error: `Total upload size exceeds maximum of ${MAX_TOTAL_SIZE / 1024 / 1024}MB`,
888
- },
889
- 400
890
- );
891
- }
892
- const arrayBuffer = await entry.arrayBuffer();
893
- fileResults.push({
894
- buffer: Buffer.from(arrayBuffer),
895
- filename: entry.name,
896
- });
897
- }
898
- }
899
- if (fileResults.length > 0) files = fileResults;
900
- }
901
- } else {
902
- body = c.req.valid("json");
903
- }
904
-
905
- const messageContent = body.content || body.message;
906
- const messageId = body.messageId || randomUUID();
907
-
908
- if (!messageContent || typeof messageContent !== "string") {
909
- return c.json({ success: false, error: "content is required" }, 400);
910
- }
911
-
912
- const platform = body.platform as string | undefined;
913
-
914
- // ── Platform-routed path ──────────────────────────────────────────────────
915
- // When platform is specified, delegate to the platform adapter which handles
916
- // session creation, routing, and file delivery.
917
- if (platform) {
918
- // Worker tokens cannot route to user-facing platform connections
919
- const authHeader = c.req.header("Authorization");
920
- const rawToken = authHeader?.startsWith("Bearer ")
921
- ? authHeader.substring(7)
922
- : "";
923
- if (verifyWorkerToken(rawToken)) {
924
- return c.json(
925
- { success: false, error: "Worker tokens cannot route to platforms" },
926
- 403
927
- );
928
- }
929
-
930
- if (!platformRegistry) {
931
- return c.json(
932
- { success: false, error: "Platform routing not available" },
933
- 501
934
- );
935
- }
936
-
937
- const adapter = platformRegistry.get(platform);
938
- if (!adapter) {
939
- return c.json(
940
- {
941
- success: false,
942
- error: `Platform "${platform}" not found`,
943
- details: `Available: ${platformRegistry.getAvailablePlatforms().join(", ")}`,
944
- },
945
- 404
946
- );
947
- }
948
-
949
- if (!adapter.sendMessage) {
950
- return c.json(
951
- {
952
- success: false,
953
- error: `Platform "${platform}" does not support sendMessage`,
954
- },
955
- 501
956
- );
957
- }
958
-
959
- // Extract platform-specific routing info
960
- let channelId = agentId;
961
- let conversationId: string | undefined =
962
- platform === "api" ? agentId : undefined;
963
- let teamId = "api";
964
-
965
- if (adapter.extractRoutingInfo) {
966
- const routingInfo = adapter.extractRoutingInfo(
967
- body as Record<string, unknown>
968
- );
969
- if (routingInfo) {
970
- channelId = routingInfo.channelId;
971
- conversationId = routingInfo.conversationId || conversationId;
972
- teamId = routingInfo.teamId || "api";
973
- } else if (platform !== "api") {
974
- return c.json(
975
- {
976
- success: false,
977
- error: `Platform-specific routing info required for ${platform}`,
978
- },
979
- 400
980
- );
981
- }
982
- }
983
-
984
- logger.info(
985
- `Sending message via ${platform}: agentId=${agentId}, channelId=${channelId}${files?.length ? `, files=${files.length}` : ""}`
986
- );
987
-
988
- try {
989
- const result = await adapter.sendMessage(rawToken, messageContent, {
990
- agentId,
991
- channelId,
992
- conversationId,
993
- teamId,
994
- files,
995
- });
996
-
997
- return c.json({
998
- success: true,
999
- agentId,
1000
- messageId: result.messageId,
1001
- eventsUrl: result.eventsUrl,
1002
- queued: result.queued || false,
1003
- });
1004
- } catch (error) {
1005
- logger.error("Failed to send platform message", { error });
1006
- return c.json({ success: false, error: "Internal server error" }, 500);
1007
- }
1008
- }
1009
-
1010
- // ── Direct API path ───────────────────────────────────────────────────────
1011
- // No platform field: use existing session-based direct enqueue
1012
- const session = await sessMgr.getSession(agentId);
1013
- if (!session) {
1014
- return c.json({ success: false, error: "Agent not found" }, 404);
1015
- }
1016
-
1017
- await sessMgr.touchSession(agentId);
1018
-
1019
- const realAgentId = session.agentId || agentId;
1020
-
1021
- const { span: rootSpan, traceparent } = createRootSpan("message_received", {
1022
- "lobu.agent_id": realAgentId,
1023
- "lobu.message_id": messageId,
1024
- });
1025
-
1026
- try {
1027
- const channelId = session.channelId || `api_${session.userId}`;
1028
-
1029
- const baseOptions: Record<string, any> = {
1030
- provider: session.provider || "claude",
1031
- model: session.model,
1032
- };
1033
- const agentOptions = await resolveAgentOptions(
1034
- realAgentId,
1035
- baseOptions,
1036
- agentSettingsStore
1037
- );
1038
-
1039
- const {
1040
- networkConfig: settingsNetwork,
1041
- mcpServers: settingsMcpServers,
1042
- ...remainingOptions
1043
- } = agentOptions;
1044
-
1045
- const jobId = await queueProducer.enqueueMessage({
1046
- userId: session.userId,
1047
- conversationId: session.conversationId || agentId,
1048
- messageId,
1049
- channelId,
1050
- teamId: "api",
1051
- agentId: realAgentId,
1052
- botId: "lobu-api",
1053
- platform: "api",
1054
- messageText: messageContent,
1055
- platformMetadata: {
1056
- agentId: realAgentId,
1057
- source: "direct-api",
1058
- traceparent: traceparent || undefined,
1059
- dryRun: session.dryRun || false,
1060
- },
1061
- agentOptions: remainingOptions,
1062
- networkConfig: session.networkConfig || settingsNetwork,
1063
- mcpConfig:
1064
- session.mcpConfig ||
1065
- (settingsMcpServers ? { mcpServers: settingsMcpServers } : undefined),
1066
- });
1067
-
1068
- rootSpan?.end();
1069
-
1070
- return c.json({
1071
- success: true,
1072
- messageId,
1073
- jobId,
1074
- queued: true,
1075
- traceparent: traceparent || undefined,
1076
- });
1077
- } catch (error) {
1078
- rootSpan?.end();
1079
- throw error;
1080
- }
1081
- });
1082
-
1083
- logger.debug("Hono Agent API routes registered");
1084
-
1085
- return app;
1086
- }