@swarmclawai/swarmclaw 1.2.4 → 1.2.6

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 (262) hide show
  1. package/README.md +14 -0
  2. package/bin/daemon-cmd.js +169 -0
  3. package/bin/server-cmd.js +3 -0
  4. package/bin/swarmclaw.js +11 -0
  5. package/package.json +17 -16
  6. package/src/app/api/agents/[id]/clone/route.ts +3 -32
  7. package/src/app/api/agents/[id]/route.ts +6 -158
  8. package/src/app/api/agents/[id]/status/route.ts +2 -3
  9. package/src/app/api/agents/[id]/thread/route.ts +4 -17
  10. package/src/app/api/agents/bulk/route.ts +5 -47
  11. package/src/app/api/agents/route.ts +5 -119
  12. package/src/app/api/agents/trash/route.ts +13 -24
  13. package/src/app/api/auth/route.ts +3 -9
  14. package/src/app/api/autonomy/estop/route.ts +5 -5
  15. package/src/app/api/chatrooms/[id]/chat/route.ts +11 -5
  16. package/src/app/api/chatrooms/[id]/route.ts +23 -2
  17. package/src/app/api/chatrooms/route.ts +13 -2
  18. package/src/app/api/chats/[id]/clear/route.ts +2 -13
  19. package/src/app/api/chats/[id]/deploy/route.ts +2 -3
  20. package/src/app/api/chats/[id]/edit-resend/route.ts +7 -13
  21. package/src/app/api/chats/[id]/mailbox/route.ts +6 -8
  22. package/src/app/api/chats/[id]/queue/route.ts +17 -64
  23. package/src/app/api/chats/[id]/retry/route.ts +4 -22
  24. package/src/app/api/chats/[id]/route.ts +10 -138
  25. package/src/app/api/chats/heartbeat/route.ts +2 -1
  26. package/src/app/api/chats/migrate-messages/route.ts +7 -0
  27. package/src/app/api/chats/route.ts +13 -134
  28. package/src/app/api/connectors/[id]/access/route.ts +12 -229
  29. package/src/app/api/connectors/[id]/doctor/route.ts +1 -1
  30. package/src/app/api/connectors/[id]/health/route.ts +12 -39
  31. package/src/app/api/connectors/[id]/route.ts +14 -122
  32. package/src/app/api/connectors/[id]/webhook/route.ts +1 -1
  33. package/src/app/api/connectors/doctor/route.ts +1 -1
  34. package/src/app/api/connectors/route.ts +12 -70
  35. package/src/app/api/credentials/[id]/route.ts +2 -4
  36. package/src/app/api/credentials/route.ts +10 -19
  37. package/src/app/api/daemon/health-check/route.ts +3 -4
  38. package/src/app/api/daemon/route.ts +10 -8
  39. package/src/app/api/documents/route.ts +11 -10
  40. package/src/app/api/external-agents/route.ts +3 -3
  41. package/src/app/api/gateways/[id]/health/route.ts +2 -3
  42. package/src/app/api/gateways/[id]/route.ts +7 -122
  43. package/src/app/api/gateways/route.ts +3 -103
  44. package/src/app/api/mcp-servers/[id]/tools/route.ts +5 -5
  45. package/src/app/api/openclaw/dashboard-url/route.ts +8 -16
  46. package/src/app/api/openclaw/directory/route.ts +2 -2
  47. package/src/app/api/openclaw/history/route.ts +3 -5
  48. package/src/app/api/providers/[id]/route.test.ts +49 -0
  49. package/src/app/api/providers/ollama/route.ts +6 -5
  50. package/src/app/api/schedules/[id]/route.ts +14 -108
  51. package/src/app/api/schedules/[id]/run/route.ts +6 -67
  52. package/src/app/api/schedules/route.ts +9 -51
  53. package/src/app/api/settings/route.ts +4 -3
  54. package/src/app/api/setup/check-provider/route.ts +23 -1
  55. package/src/app/api/setup/openclaw-device/route.ts +2 -2
  56. package/src/app/api/system/status/route.ts +2 -2
  57. package/src/app/api/tasks/[id]/route.ts +16 -202
  58. package/src/app/api/tasks/bulk/route.ts +5 -86
  59. package/src/app/api/tasks/metrics/route.ts +2 -1
  60. package/src/app/api/tasks/route.ts +11 -171
  61. package/src/app/api/upload/route.ts +1 -1
  62. package/src/app/api/uploads/[filename]/route.ts +1 -1
  63. package/src/app/api/uploads/route.ts +1 -1
  64. package/src/app/api/webhooks/[id]/history/route.ts +2 -2
  65. package/src/app/layout.tsx +9 -6
  66. package/src/app/protocols/page.tsx +71 -89
  67. package/src/app/tasks/page.tsx +32 -32
  68. package/src/cli/index.js +1 -0
  69. package/src/cli/spec.js +1 -0
  70. package/src/components/agents/agent-sheet.tsx +5 -5
  71. package/src/components/auth/setup-wizard/index.tsx +4 -4
  72. package/src/components/auth/setup-wizard/step-agents.tsx +1 -1
  73. package/src/components/auth/setup-wizard/step-connect.tsx +1 -1
  74. package/src/components/auth/setup-wizard/utils.ts +1 -1
  75. package/src/components/chatrooms/chatroom-sheet.tsx +16 -276
  76. package/src/components/connectors/connector-list.tsx +26 -40
  77. package/src/components/connectors/connector-sheet.tsx +95 -149
  78. package/src/components/gateways/gateway-sheet.tsx +61 -110
  79. package/src/components/layout/live-query-sync.tsx +121 -0
  80. package/src/components/protocols/structured-session-launcher.tsx +24 -45
  81. package/src/components/providers/app-query-provider.tsx +17 -0
  82. package/src/components/providers/provider-list.tsx +60 -61
  83. package/src/components/providers/provider-sheet.tsx +74 -56
  84. package/src/components/skills/skill-list.tsx +5 -18
  85. package/src/components/skills/skill-sheet.tsx +21 -20
  86. package/src/components/skills/skills-workspace.tsx +48 -87
  87. package/src/components/tasks/task-card.tsx +20 -13
  88. package/src/components/tasks/task-column.tsx +22 -7
  89. package/src/components/tasks/task-list.tsx +8 -11
  90. package/src/components/tasks/task-sheet.tsx +111 -103
  91. package/src/features/agents/queries.ts +20 -0
  92. package/src/features/chatrooms/queries.ts +20 -0
  93. package/src/features/chats/queries.ts +27 -0
  94. package/src/features/connectors/queries.ts +145 -0
  95. package/src/features/credentials/queries.ts +37 -0
  96. package/src/features/extensions/queries.ts +26 -0
  97. package/src/features/external-agents/queries.ts +36 -0
  98. package/src/features/gateways/queries.ts +274 -0
  99. package/src/features/missions/queries.ts +23 -0
  100. package/src/features/projects/queries.ts +20 -0
  101. package/src/features/protocols/queries.ts +149 -0
  102. package/src/features/providers/queries.ts +142 -0
  103. package/src/features/settings/queries.ts +20 -0
  104. package/src/features/skills/queries.ts +182 -0
  105. package/src/features/tasks/queries.ts +189 -0
  106. package/src/hooks/use-ws.ts +3 -2
  107. package/src/lib/app/api-client.ts +2 -2
  108. package/src/lib/providers/index.test.ts +108 -0
  109. package/src/lib/providers/index.ts +38 -15
  110. package/src/lib/query/client.ts +17 -0
  111. package/src/lib/server/agents/agent-runtime-config.ts +1 -1
  112. package/src/lib/server/agents/agent-service.ts +429 -0
  113. package/src/lib/server/agents/agent-thread-session.ts +6 -5
  114. package/src/lib/server/agents/autonomy-contract.ts +1 -4
  115. package/src/lib/server/agents/delegation-advisory.test.ts +206 -0
  116. package/src/lib/server/agents/delegation-advisory.ts +251 -0
  117. package/src/lib/server/agents/main-agent-loop.ts +98 -40
  118. package/src/lib/server/agents/subagent-runtime.ts +12 -0
  119. package/src/lib/server/autonomy/supervisor-reflection.test.ts +20 -1
  120. package/src/lib/server/autonomy/supervisor-reflection.ts +39 -19
  121. package/src/lib/server/build-llm.ts +7 -15
  122. package/src/lib/server/capability-router.test.ts +70 -1
  123. package/src/lib/server/capability-router.ts +24 -99
  124. package/src/lib/server/chat-execution/chat-execution-utils.ts +0 -15
  125. package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -4
  126. package/src/lib/server/chat-execution/chat-turn-finalization.ts +77 -12
  127. package/src/lib/server/chat-execution/chat-turn-partial-persistence.ts +4 -4
  128. package/src/lib/server/chat-execution/chat-turn-preflight.ts +2 -2
  129. package/src/lib/server/chat-execution/chat-turn-preparation.ts +41 -17
  130. package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -2
  131. package/src/lib/server/chat-execution/chat-turn-tool-routing.test.ts +45 -0
  132. package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +48 -17
  133. package/src/lib/server/chat-execution/continuation-evaluator.ts +4 -1
  134. package/src/lib/server/chat-execution/direct-memory-intent.test.ts +9 -0
  135. package/src/lib/server/chat-execution/direct-memory-intent.ts +12 -2
  136. package/src/lib/server/chat-execution/message-classifier.test.ts +35 -23
  137. package/src/lib/server/chat-execution/message-classifier.ts +74 -32
  138. package/src/lib/server/chat-execution/prompt-builder.test.ts +29 -0
  139. package/src/lib/server/chat-execution/prompt-builder.ts +37 -2
  140. package/src/lib/server/chat-execution/prompt-sections.test.ts +56 -0
  141. package/src/lib/server/chat-execution/prompt-sections.ts +193 -0
  142. package/src/lib/server/chat-execution/stream-agent-chat.ts +63 -7
  143. package/src/lib/server/chat-execution/stream-continuation.test.ts +36 -0
  144. package/src/lib/server/chat-execution/stream-continuation.ts +28 -13
  145. package/src/lib/server/chatrooms/chatroom-agent-signals.ts +26 -18
  146. package/src/lib/server/chatrooms/chatroom-helpers.ts +19 -18
  147. package/src/lib/server/chatrooms/chatroom-repository.ts +16 -0
  148. package/src/lib/server/chatrooms/chatroom-routing.test.ts +96 -0
  149. package/src/lib/server/chatrooms/chatroom-routing.ts +207 -53
  150. package/src/lib/server/chatrooms/mailbox-utils.ts +4 -2
  151. package/src/lib/server/chatrooms/session-mailbox.ts +50 -40
  152. package/src/lib/server/chats/chat-session-service.ts +410 -0
  153. package/src/lib/server/connectors/access.ts +1 -1
  154. package/src/lib/server/connectors/commands.ts +7 -6
  155. package/src/lib/server/connectors/connector-inbound.ts +14 -7
  156. package/src/lib/server/connectors/connector-outbound.ts +16 -11
  157. package/src/lib/server/connectors/connector-service.ts +453 -0
  158. package/src/lib/server/connectors/delivery.ts +17 -12
  159. package/src/lib/server/connectors/inbound-audio-transcription.ts +5 -14
  160. package/src/lib/server/connectors/media.ts +1 -1
  161. package/src/lib/server/connectors/response-media.ts +1 -1
  162. package/src/lib/server/connectors/session-consolidation.ts +11 -7
  163. package/src/lib/server/connectors/session.ts +9 -7
  164. package/src/lib/server/connectors/voice-note.ts +2 -1
  165. package/src/lib/server/context-manager.ts +20 -1
  166. package/src/lib/server/cost.ts +2 -3
  167. package/src/lib/server/credentials/credential-repository.ts +43 -4
  168. package/src/lib/server/credentials/credential-service.ts +112 -0
  169. package/src/lib/server/daemon/admin-metadata.ts +64 -0
  170. package/src/lib/server/daemon/controller.ts +577 -0
  171. package/src/lib/server/daemon/daemon-runtime.ts +352 -0
  172. package/src/lib/server/daemon/daemon-status-repository.ts +63 -0
  173. package/src/lib/server/daemon/types.ts +101 -0
  174. package/src/lib/server/embeddings.ts +3 -9
  175. package/src/lib/server/eval/agent-regression.ts +3 -2
  176. package/src/lib/server/eval/runner.ts +2 -2
  177. package/src/lib/server/execution-brief.test.ts +167 -0
  178. package/src/lib/server/execution-brief.ts +295 -0
  179. package/src/lib/server/execution-engine/chat-turn.ts +9 -0
  180. package/src/lib/server/execution-engine/import-boundary.test.ts +44 -0
  181. package/src/lib/server/execution-engine/index.ts +35 -0
  182. package/src/lib/server/execution-engine/task-attempt.ts +303 -0
  183. package/src/lib/server/execution-engine/types.ts +33 -0
  184. package/src/lib/server/gateways/gateway-profile-repository.ts +47 -3
  185. package/src/lib/server/gateways/gateway-profile-service.ts +200 -0
  186. package/src/lib/server/memory/session-archive-memory.ts +12 -10
  187. package/src/lib/server/messages/message-repository.ts +330 -0
  188. package/src/lib/server/missions/mission-service/core.ts +8 -6
  189. package/src/lib/server/openclaw/agent-resolver.ts +2 -3
  190. package/src/lib/server/openclaw/doctor.ts +1 -1
  191. package/src/lib/server/openclaw/gateway.test.ts +10 -1
  192. package/src/lib/server/openclaw/gateway.ts +5 -14
  193. package/src/lib/server/openclaw/health.ts +3 -11
  194. package/src/lib/server/openclaw/sync.ts +8 -6
  195. package/src/lib/server/persistence/storage-context.ts +3 -0
  196. package/src/lib/server/protocols/protocol-agent-turn.ts +25 -17
  197. package/src/lib/server/protocols/protocol-normalization.ts +1 -1
  198. package/src/lib/server/protocols/protocol-queries.ts +13 -7
  199. package/src/lib/server/protocols/protocol-run-lifecycle.ts +16 -20
  200. package/src/lib/server/protocols/protocol-run-repository.ts +81 -0
  201. package/src/lib/server/protocols/protocol-step-processors.ts +23 -31
  202. package/src/lib/server/protocols/protocol-swarm.ts +8 -8
  203. package/src/lib/server/protocols/protocol-template-repository.ts +42 -0
  204. package/src/lib/server/protocols/protocol-templates.ts +4 -2
  205. package/src/lib/server/protocols/protocol-types.ts +10 -7
  206. package/src/lib/server/provider-endpoint.ts +7 -12
  207. package/src/lib/server/provider-model-discovery.ts +2 -11
  208. package/src/lib/server/query-expansion.ts +5 -6
  209. package/src/lib/server/run-context.test.ts +365 -0
  210. package/src/lib/server/run-context.ts +367 -0
  211. package/src/lib/server/runtime/heartbeat-service.ts +7 -5
  212. package/src/lib/server/runtime/queue/core.ts +61 -190
  213. package/src/lib/server/runtime/run-ledger.ts +8 -0
  214. package/src/lib/server/runtime/session-run-manager/drain.ts +2 -2
  215. package/src/lib/server/runtime/session-run-manager/enqueue.ts +6 -0
  216. package/src/lib/server/runtime/session-run-manager/state.ts +4 -0
  217. package/src/lib/server/schedules/schedule-route-service.ts +230 -0
  218. package/src/lib/server/service-result.ts +16 -0
  219. package/src/lib/server/session-note.ts +2 -3
  220. package/src/lib/server/session-reset-policy.ts +4 -3
  221. package/src/lib/server/session-tools/connector.ts +9 -6
  222. package/src/lib/server/session-tools/context-mgmt.ts +58 -9
  223. package/src/lib/server/session-tools/crud.ts +162 -10
  224. package/src/lib/server/session-tools/delegate.ts +1 -1
  225. package/src/lib/server/session-tools/manage-tasks.test.ts +152 -0
  226. package/src/lib/server/session-tools/memory.ts +6 -4
  227. package/src/lib/server/session-tools/session-info.test.ts +56 -0
  228. package/src/lib/server/session-tools/session-info.ts +119 -12
  229. package/src/lib/server/session-tools/skill-runtime.ts +3 -1
  230. package/src/lib/server/session-tools/skills.ts +15 -15
  231. package/src/lib/server/session-tools/subagent.test.ts +115 -1
  232. package/src/lib/server/session-tools/subagent.ts +125 -7
  233. package/src/lib/server/session-tools/team-context.ts +4 -3
  234. package/src/lib/server/session-tools/wallet.ts +0 -58
  235. package/src/lib/server/sessions/session-lineage.ts +55 -0
  236. package/src/lib/server/sessions/session-repository.ts +2 -2
  237. package/src/lib/server/skills/learned-skills.ts +24 -23
  238. package/src/lib/server/skills/runtime-skill-resolver.ts +2 -1
  239. package/src/lib/server/skills/skill-repository.ts +136 -13
  240. package/src/lib/server/skills/skill-suggestions.ts +25 -28
  241. package/src/lib/server/storage-normalization.test.ts +44 -267
  242. package/src/lib/server/storage-normalization.ts +75 -0
  243. package/src/lib/server/storage.ts +19 -0
  244. package/src/lib/server/structured-extract.ts +3 -14
  245. package/src/lib/server/tasks/task-followups.ts +16 -11
  246. package/src/lib/server/tasks/task-result.test.ts +25 -29
  247. package/src/lib/server/tasks/task-result.ts +5 -9
  248. package/src/lib/server/tasks/task-route-service.ts +449 -0
  249. package/src/lib/server/text-normalization.ts +41 -0
  250. package/src/lib/server/tool-planning.ts +6 -42
  251. package/src/lib/server/upload-path.ts +5 -0
  252. package/src/lib/server/working-state/extraction.ts +614 -0
  253. package/src/lib/server/working-state/normalization.ts +866 -0
  254. package/src/lib/server/working-state/prompt.ts +60 -0
  255. package/src/lib/server/working-state/repository.ts +38 -0
  256. package/src/lib/server/working-state/service.test.ts +253 -0
  257. package/src/lib/server/working-state/service.ts +293 -0
  258. package/src/lib/validation/schemas.ts +1 -0
  259. package/src/lib/ws-client.ts +3 -3
  260. package/src/stores/slices/task-slice.ts +1 -4
  261. package/src/stores/use-chatroom-store.ts +2 -2
  262. package/src/types/index.ts +277 -12
@@ -1,45 +1,16 @@
1
1
  import { NextResponse } from 'next/server'
2
- import { genId } from '@/lib/id'
3
2
  import { perf } from '@/lib/server/runtime/perf'
4
- import { loadAgents, loadSessions, loadUsage, logActivity, upsertStoredItem } from '@/lib/server/storage'
5
- import { normalizeProviderEndpoint } from '@/lib/openclaw/openclaw-endpoint'
6
- import { notify } from '@/lib/server/ws-hub'
7
- import { getAgentSpendWindows } from '@/lib/server/cost'
8
- import { resolveAgentToolSelection } from '@/lib/agent-default-tools'
9
- import { normalizeAgentSandboxConfig } from '@/lib/agent-sandbox-defaults'
10
- import { normalizeOrchestratorConfig } from '@/lib/orchestrator-config'
3
+ import { listAgentsForApi, createAgent } from '@/lib/server/agents/agent-service'
11
4
  import { AgentCreateSchema, formatZodError } from '@/lib/validation/schemas'
5
+ import { ensureDaemonProcessRunning } from '@/lib/server/daemon/controller'
12
6
  import { z } from 'zod'
13
7
  import { safeParseBody } from '@/lib/server/safe-parse-body'
14
8
  export const dynamic = 'force-dynamic'
15
9
 
16
- async function ensureDaemonIfNeeded(source: string) {
17
- const { ensureDaemonStarted } = await import('@/lib/server/runtime/daemon-state')
18
- ensureDaemonStarted(source)
19
- }
20
-
21
10
 
22
11
  export async function GET(req: Request) {
23
12
  const endPerf = perf.start('api', 'GET /api/agents')
24
- const agents = loadAgents()
25
- const sessions = loadSessions()
26
- const usage = loadUsage()
27
- // Enrich agents that have spend limits with current spend windows
28
- for (const agent of Object.values(agents)) {
29
- if (
30
- (typeof agent.monthlyBudget === 'number' && agent.monthlyBudget > 0)
31
- || (typeof agent.dailyBudget === 'number' && agent.dailyBudget > 0)
32
- || (typeof agent.hourlyBudget === 'number' && agent.hourlyBudget > 0)
33
- ) {
34
- const spend = getAgentSpendWindows(agent.id, Date.now(), {
35
- sessions: sessions as unknown as Record<string, Record<string, unknown>>,
36
- usage,
37
- })
38
- if (typeof agent.monthlyBudget === 'number' && agent.monthlyBudget > 0) agent.monthlySpend = spend.monthly
39
- if (typeof agent.dailyBudget === 'number' && agent.dailyBudget > 0) agent.dailySpend = spend.daily
40
- if (typeof agent.hourlyBudget === 'number' && agent.hourlyBudget > 0) agent.hourlySpend = spend.hourly
41
- }
42
- }
13
+ const agents = listAgentsForApi()
43
14
 
44
15
  const { searchParams } = new URL(req.url)
45
16
  const limitParam = searchParams.get('limit')
@@ -57,7 +28,7 @@ export async function GET(req: Request) {
57
28
  }
58
29
 
59
30
  export async function POST(req: Request) {
60
- await ensureDaemonIfNeeded('api/agents:post')
31
+ await ensureDaemonProcessRunning('api/agents:post')
61
32
  const { data: raw, error } = await safeParseBody(req)
62
33
  if (error) return error
63
34
  const rawRecord = raw && typeof raw === 'object' ? raw as Record<string, unknown> : null
@@ -65,91 +36,6 @@ export async function POST(req: Request) {
65
36
  if (!parsed.success) {
66
37
  return NextResponse.json(formatZodError(parsed.error as z.ZodError), { status: 400 })
67
38
  }
68
- const body = parsed.data
69
- const orchestratorConfig = normalizeOrchestratorConfig({
70
- provider: body.provider,
71
- orchestratorEnabled: body.orchestratorEnabled,
72
- orchestratorMission: body.orchestratorMission,
73
- orchestratorWakeInterval: body.orchestratorWakeInterval,
74
- orchestratorGovernance: body.orchestratorGovernance,
75
- orchestratorMaxCyclesPerDay: body.orchestratorMaxCyclesPerDay,
76
- })
77
- const capabilitySelection = resolveAgentToolSelection({
78
- hasExplicitTools: Boolean(rawRecord && Object.prototype.hasOwnProperty.call(rawRecord, 'tools')),
79
- hasExplicitExtensions: Boolean(rawRecord && Object.prototype.hasOwnProperty.call(rawRecord, 'extensions')),
80
- tools: body.tools,
81
- extensions: body.extensions,
82
- })
83
- const id = genId()
84
- const now = Date.now()
85
- const agent = {
86
- id,
87
- name: body.name,
88
- description: body.description,
89
- soul: body.soul || undefined,
90
- systemPrompt: body.systemPrompt,
91
- provider: body.provider,
92
- model: body.model,
93
- ollamaMode: body.provider === 'ollama' ? (body.ollamaMode || 'local') : null,
94
- credentialId: body.credentialId,
95
- fallbackCredentialIds: body.fallbackCredentialIds,
96
- apiEndpoint: normalizeProviderEndpoint(body.provider, body.apiEndpoint || null),
97
- gatewayProfileId: body.gatewayProfileId,
98
- preferredGatewayTags: body.preferredGatewayTags,
99
- preferredGatewayUseCase: body.preferredGatewayUseCase,
100
- routingStrategy: body.routingStrategy,
101
- routingTargets: body.routingTargets?.map((target) => ({
102
- ...target,
103
- ollamaMode: target.provider === 'ollama' ? (target.ollamaMode || 'local') : null,
104
- apiEndpoint: normalizeProviderEndpoint(target.provider, target.apiEndpoint || null),
105
- })),
106
- delegationEnabled: body.delegationEnabled ?? false,
107
- delegationTargetMode: body.delegationTargetMode ?? 'all',
108
- delegationTargetAgentIds: (body.delegationTargetMode === 'selected' ? body.delegationTargetAgentIds : []).filter(Boolean),
109
- tools: capabilitySelection.tools,
110
- extensions: capabilitySelection.extensions,
111
- skills: body.skills,
112
- skillIds: body.skillIds,
113
- mcpServerIds: body.mcpServerIds,
114
- mcpDisabledTools: body.mcpDisabledTools?.length ? body.mcpDisabledTools : undefined,
115
- capabilities: body.capabilities,
116
- thinkingLevel: body.thinkingLevel || undefined,
117
- autoRecovery: body.autoRecovery || false,
118
- disabled: body.disabled || false,
119
- heartbeatEnabled: body.heartbeatEnabled ?? true,
120
- heartbeatInterval: body.heartbeatInterval,
121
- heartbeatIntervalSec: body.heartbeatIntervalSec,
122
- heartbeatModel: body.heartbeatModel,
123
- heartbeatPrompt: body.heartbeatPrompt,
124
- orchestratorEnabled: orchestratorConfig.orchestratorEnabled,
125
- orchestratorMission: orchestratorConfig.orchestratorMission,
126
- orchestratorWakeInterval: orchestratorConfig.orchestratorWakeInterval,
127
- orchestratorGovernance: orchestratorConfig.orchestratorGovernance,
128
- orchestratorMaxCyclesPerDay: orchestratorConfig.orchestratorMaxCyclesPerDay,
129
- elevenLabsVoiceId: body.elevenLabsVoiceId,
130
- monthlyBudget: body.monthlyBudget ?? null,
131
- dailyBudget: body.dailyBudget ?? null,
132
- hourlyBudget: body.hourlyBudget ?? null,
133
- budgetAction: body.budgetAction || 'warn',
134
- identityState: body.identityState ?? null,
135
- memoryScopeMode: body.memoryScopeMode,
136
- memoryTierPreference: body.memoryTierPreference,
137
- proactiveMemory: body.proactiveMemory ?? true,
138
- autoDraftSkillSuggestions: body.autoDraftSkillSuggestions,
139
- projectId: body.projectId,
140
- avatarSeed: body.avatarSeed,
141
- avatarUrl: body.avatarUrl,
142
- sessionResetMode: body.sessionResetMode ?? null,
143
- sessionIdleTimeoutSec: body.sessionIdleTimeoutSec ?? null,
144
- sessionMaxAgeSec: body.sessionMaxAgeSec ?? null,
145
- sessionDailyResetAt: body.sessionDailyResetAt ?? null,
146
- sessionResetTimezone: body.sessionResetTimezone ?? null,
147
- sandboxConfig: normalizeAgentSandboxConfig(body.sandboxConfig),
148
- createdAt: now,
149
- updatedAt: now,
150
- }
151
- upsertStoredItem('agents', id, agent)
152
- logActivity({ entityType: 'agent', entityId: id, action: 'created', actor: 'user', summary: `Agent created: "${agent.name}"` })
153
- notify('agents')
39
+ const agent = createAgent({ body: parsed.data as unknown as Record<string, unknown>, rawRecord })
154
40
  return NextResponse.json(agent)
155
41
  }
@@ -1,13 +1,16 @@
1
1
  import { NextResponse } from 'next/server'
2
- import { loadTrashedAgents, loadAgents, saveAgents, deleteAgent } from '@/lib/server/storage'
2
+ import {
3
+ listTrashedAgentsForApi,
4
+ permanentlyDeleteTrashedAgent,
5
+ restoreTrashedAgent,
6
+ } from '@/lib/server/agents/agent-service'
3
7
  import { notify } from '@/lib/server/ws-hub'
4
8
  import { badRequest, notFound } from '@/lib/server/collection-helpers'
5
- import { purgeAgentReferences, restoreAgentSchedules } from '@/lib/server/agents/agent-cascade'
6
9
  import { safeParseBody } from '@/lib/server/safe-parse-body'
7
10
 
8
11
  /** GET — list trashed agents */
9
12
  export async function GET() {
10
- return NextResponse.json(loadTrashedAgents())
13
+ return NextResponse.json(listTrashedAgentsForApi())
11
14
  }
12
15
 
13
16
  /** POST { id } — restore a trashed agent */
@@ -17,21 +20,9 @@ export async function POST(req: Request) {
17
20
  const id = body?.id as string | undefined
18
21
  if (!id) return badRequest('Missing agent id')
19
22
 
20
- const all = loadAgents({ includeTrashed: true })
21
- const agent = all[id]
23
+ const agent = restoreTrashedAgent(id)
22
24
  if (!agent) return notFound()
23
- if (!agent.trashedAt) return badRequest('Agent is not trashed')
24
-
25
- delete agent.trashedAt
26
- agent.updatedAt = Date.now()
27
- all[id] = agent
28
- saveAgents(all)
29
25
  notify('agents')
30
-
31
- // Re-enable schedules that were paused when the agent was trashed
32
- const restoredSchedules = restoreAgentSchedules(id)
33
- if (restoredSchedules) notify('schedules')
34
-
35
26
  return NextResponse.json(agent)
36
27
  }
37
28
 
@@ -42,14 +33,12 @@ export async function DELETE(req: Request) {
42
33
  const id = body?.id as string | undefined
43
34
  if (!id) return badRequest('Missing agent id')
44
35
 
45
- const all = loadAgents({ includeTrashed: true })
46
- const agent = all[id]
47
- if (!agent) return notFound()
48
- if (!agent.trashedAt) return badRequest('Agent must be trashed before permanent deletion')
49
-
50
- // Hard-delete all referencing entities before removing the agent record
51
- const purged = purgeAgentReferences(id)
52
- deleteAgent(id)
36
+ const result = permanentlyDeleteTrashedAgent(id)
37
+ if (!result.ok) {
38
+ if (result.reason === 'not_found') return notFound()
39
+ return badRequest('Agent must be trashed before permanent deletion')
40
+ }
41
+ const purged = result.purged
53
42
  notify('agents')
54
43
  if (purged.tasks) notify('tasks')
55
44
  if (purged.schedules) notify('schedules')
@@ -4,6 +4,7 @@ import { log } from '@/lib/server/logger'
4
4
  import { getAccessKey, validateAccessKey, isFirstTimeSetup, markSetupComplete, replaceAccessKey } from '@/lib/server/storage-auth'
5
5
  import { AUTH_COOKIE_NAME, getCookieValue } from '@/lib/auth'
6
6
  import { isProductionRuntime } from '@/lib/runtime/runtime-env'
7
+ import { ensureDaemonProcessRunning } from '@/lib/server/daemon/controller'
7
8
  import { hmrSingleton } from '@/lib/shared-utils'
8
9
  export const dynamic = 'force-dynamic'
9
10
 
@@ -79,16 +80,9 @@ function pruneExpiredEntries() {
79
80
  }
80
81
 
81
82
  function startDaemonAfterAuth() {
82
- void import('@/lib/server/runtime/daemon-state')
83
- .then(({ ensureDaemonStarted }) => {
84
- try {
85
- ensureDaemonStarted('api/auth:post')
86
- } catch (err: unknown) {
87
- log.error(TAG, 'Deferred daemon start failed', err)
88
- }
89
- })
83
+ void ensureDaemonProcessRunning('api/auth:post')
90
84
  .catch((err: unknown) => {
91
- log.error(TAG, 'Failed to load daemon-state for deferred start', err)
85
+ log.error(TAG, 'Deferred daemon start failed', err)
92
86
  })
93
87
  }
94
88
 
@@ -1,6 +1,6 @@
1
1
  import { NextResponse } from 'next/server'
2
2
  import { cancelAllRuns } from '@/lib/server/runtime/session-run-manager'
3
- import { startDaemon, stopDaemon } from '@/lib/server/runtime/daemon-state'
3
+ import { ensureDaemonProcessRunning, stopDaemonProcess } from '@/lib/server/daemon/controller'
4
4
  import {
5
5
  areEstopResumeApprovalsEnabled,
6
6
  engageEstop,
@@ -37,7 +37,7 @@ export async function POST(req: Request) {
37
37
  reason: typeof body.reason === 'string' ? body.reason : null,
38
38
  engagedBy: typeof body.engagedBy === 'string' ? body.engagedBy : 'user',
39
39
  })
40
- await stopDaemon({ source: `api/autonomy/estop:${level}` })
40
+ await stopDaemonProcess({ source: `api/autonomy/estop:${level}` })
41
41
  const cancelled = level === 'all'
42
42
  ? cancelAllRuns('Cancelled because all estop is engaged.')
43
43
  : { cancelledQueued: 0, abortedRunning: 0 }
@@ -55,7 +55,7 @@ export async function POST(req: Request) {
55
55
 
56
56
  if (!requiresApproval) {
57
57
  const resumed = resumeEstop({ bypassApproval: true })
58
- startDaemon({ source: 'api/autonomy/estop:resume', manualStart: true })
58
+ await ensureDaemonProcessRunning('api/autonomy/estop:resume', { manualStart: true })
59
59
  return NextResponse.json({ ok: true, state: buildStateResponse(resumed) })
60
60
  }
61
61
 
@@ -63,7 +63,7 @@ export async function POST(req: Request) {
63
63
  const existingApproval = state.resumeApprovalId ? findEstopResumeApproval(state.resumeApprovalId) : null
64
64
  if (existingApproval?.status === 'approved') {
65
65
  const resumed = resumeEstop({ approvalId: existingApproval.id })
66
- startDaemon({ source: 'api/autonomy/estop:resume', manualStart: true })
66
+ await ensureDaemonProcessRunning('api/autonomy/estop:resume', { manualStart: true })
67
67
  return NextResponse.json({ ok: true, state: buildStateResponse(resumed) })
68
68
  }
69
69
 
@@ -79,7 +79,7 @@ export async function POST(req: Request) {
79
79
  }
80
80
 
81
81
  const resumed = resumeEstop({ approvalId })
82
- startDaemon({ source: 'api/autonomy/estop:resume', manualStart: true })
82
+ await ensureDaemonProcessRunning('api/autonomy/estop:resume', { manualStart: true })
83
83
  return NextResponse.json({ ok: true, state: buildStateResponse(resumed) })
84
84
  }
85
85
 
@@ -20,7 +20,10 @@ import {
20
20
  isMuted,
21
21
  } from '@/lib/server/chatrooms/chatroom-helpers'
22
22
  import { filterHealthyChatroomAgents } from '@/lib/server/chatrooms/chatroom-health'
23
- import { evaluateRoutingRules } from '@/lib/server/chatrooms/chatroom-routing'
23
+ import {
24
+ ensureChatroomRoutingGuidance,
25
+ selectChatroomRecipients,
26
+ } from '@/lib/server/chatrooms/chatroom-routing'
24
27
  import { markProviderFailure, markProviderSuccess } from '@/lib/server/provider-health'
25
28
  import { applyAgentReactionsFromText } from '@/lib/server/chatrooms/chatroom-agent-signals'
26
29
  import { resolvePrimaryAgentRoute } from '@/lib/server/agents/agent-runtime-config'
@@ -60,11 +63,14 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
60
63
  // Persist incoming message
61
64
  const senderName = senderId === 'user' ? 'You' : (agents[senderId]?.name || senderId)
62
65
  const replyTargetAgentId = resolveReplyTargetAgentId(replyToId, chatroom.messages, chatroom.agentIds)
66
+ ensureChatroomRoutingGuidance(chatroom, agents)
63
67
  let mentions = parseMentions(text, agents, chatroom.agentIds, { replyTargetAgentId, senderId: senderId !== 'user' ? senderId : null })
64
- // Routing rules: if no explicit mentions, evaluate keyword/capability rules
65
- if (mentions.length === 0 && chatroom.routingRules?.length) {
66
- const agentList = chatroom.agentIds.map((aid) => agents[aid]).filter(Boolean)
67
- mentions = evaluateRoutingRules(text, chatroom.routingRules, agentList)
68
+ if (mentions.length === 0 && !chatroom.autoAddress) {
69
+ mentions = await selectChatroomRecipients({
70
+ text,
71
+ chatroom,
72
+ agentsById: agents,
73
+ })
68
74
  }
69
75
  // Auto-address: if enabled and still no mentions, address all agents
70
76
  if (chatroom.autoAddress && mentions.length === 0) {
@@ -5,12 +5,21 @@ import { notFound } from '@/lib/server/collection-helpers'
5
5
  import { safeParseBody } from '@/lib/server/safe-parse-body'
6
6
  import { genId } from '@/lib/id'
7
7
  import { isWorkerOnlyAgent } from '@/lib/server/agents/agent-availability'
8
+ import {
9
+ ensureChatroomRoutingGuidance,
10
+ synthesizeRoutingGuidanceFromRules,
11
+ } from '@/lib/server/chatrooms/chatroom-routing'
8
12
 
9
13
  export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
10
14
  const { id } = await params
11
15
  const chatrooms = loadChatrooms()
12
16
  const chatroom = chatrooms[id]
13
17
  if (!chatroom) return notFound()
18
+ const agents = loadAgents()
19
+ if (ensureChatroomRoutingGuidance(chatroom, agents)) {
20
+ chatrooms[id] = chatroom
21
+ saveChatrooms(chatrooms)
22
+ }
14
23
  return NextResponse.json(chatroom)
15
24
  }
16
25
 
@@ -30,8 +39,16 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
30
39
  if (body.autoAddress !== undefined) {
31
40
  chatroom.autoAddress = Boolean(body.autoAddress)
32
41
  }
33
- if (body.routingRules !== undefined) {
34
- chatroom.routingRules = Array.isArray(body.routingRules) ? body.routingRules : undefined
42
+ if (body.routingGuidance !== undefined || body.routingRules !== undefined) {
43
+ const agents = loadAgents()
44
+ const routingGuidance = (typeof body.routingGuidance === 'string' && body.routingGuidance.trim())
45
+ ? body.routingGuidance.trim()
46
+ : synthesizeRoutingGuidanceFromRules(
47
+ Array.isArray(body.routingRules) ? body.routingRules : undefined,
48
+ agents,
49
+ )
50
+ chatroom.routingGuidance = routingGuidance
51
+ delete chatroom.routingRules
35
52
  }
36
53
 
37
54
  // Diff agentIds and inject join/leave system messages
@@ -99,6 +116,10 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
99
116
  chatroom.agentIds = agentIds
100
117
  }
101
118
 
119
+ if (body.routingGuidance === undefined && body.routingRules === undefined) {
120
+ ensureChatroomRoutingGuidance(chatroom, loadAgents())
121
+ }
122
+
102
123
  chatroom.updatedAt = Date.now()
103
124
 
104
125
  chatrooms[id] = chatroom
@@ -7,6 +7,10 @@ import { safeParseBody } from '@/lib/server/safe-parse-body'
7
7
  import { z } from 'zod'
8
8
  import type { Chatroom, ChatroomMessage } from '@/types'
9
9
  import { isWorkerOnlyAgent } from '@/lib/server/agents/agent-availability'
10
+ import {
11
+ ensureChatroomRoutingGuidance,
12
+ synthesizeRoutingGuidanceFromRules,
13
+ } from '@/lib/server/chatrooms/chatroom-routing'
10
14
 
11
15
  export const dynamic = 'force-dynamic'
12
16
 
@@ -14,9 +18,12 @@ export async function GET(req: Request) {
14
18
  const { searchParams } = new URL(req.url)
15
19
  const filter = searchParams.get('filter') || 'chatrooms'
16
20
  const chatrooms = loadChatrooms()
21
+ const agents = loadAgents()
17
22
  const filtered: typeof chatrooms = {}
23
+ let migrated = false
18
24
  for (const [id, chatroom] of Object.entries(chatrooms)) {
19
25
  if (chatroom.archivedAt) continue
26
+ if (ensureChatroomRoutingGuidance(chatroom, agents)) migrated = true
20
27
  if (filter === 'chatrooms') {
21
28
  // Default: exclude hidden (protocol transcript rooms)
22
29
  if (chatroom.hidden === true) continue
@@ -27,6 +34,7 @@ export async function GET(req: Request) {
27
34
  // filter === 'all': include everything except archived
28
35
  filtered[id] = chatroom
29
36
  }
37
+ if (migrated) saveChatrooms(chatrooms)
30
38
  return NextResponse.json(filtered)
31
39
  }
32
40
 
@@ -63,6 +71,9 @@ export async function POST(req: Request) {
63
71
  const chatMode = body.chatMode === 'parallel' ? 'parallel' : 'sequential'
64
72
  const autoAddress = Boolean(body.autoAddress)
65
73
  const now = Date.now()
74
+ const routingGuidance = (typeof body.routingGuidance === 'string' && body.routingGuidance.trim())
75
+ ? body.routingGuidance.trim()
76
+ : synthesizeRoutingGuidanceFromRules(body.routingRules, knownAgents)
66
77
 
67
78
  // Generate join messages for initial agents
68
79
  const agents = agentIds.length > 0 ? knownAgents : {}
@@ -85,8 +96,8 @@ export async function POST(req: Request) {
85
96
  messages: joinMessages,
86
97
  chatMode,
87
98
  autoAddress,
88
- ...(Array.isArray(body.routingRules) && body.routingRules.length > 0
89
- ? { routingRules: body.routingRules }
99
+ ...(routingGuidance
100
+ ? { routingGuidance }
90
101
  : {}),
91
102
  createdAt: now,
92
103
  updatedAt: now,
@@ -1,20 +1,9 @@
1
1
  import { NextResponse } from 'next/server'
2
- import { loadSession, upsertSession } from '@/lib/server/storage'
2
+ import { clearChatMessages } from '@/lib/server/chats/chat-session-service'
3
3
  import { notFound } from '@/lib/server/collection-helpers'
4
4
 
5
5
  export async function POST(_req: Request, { params }: { params: Promise<{ id: string }> }) {
6
6
  const { id } = await params
7
- const session = loadSession(id)
8
- if (!session) return notFound()
9
- session.messages = []
10
- session.claudeSessionId = null
11
- session.codexThreadId = null
12
- session.opencodeSessionId = null
13
- session.delegateResumeIds = {
14
- claudeCode: null,
15
- codex: null,
16
- opencode: null,
17
- }
18
- upsertSession(id, session)
7
+ if (!clearChatMessages(id)) return notFound()
19
8
  return new NextResponse('OK')
20
9
  }
@@ -1,16 +1,15 @@
1
1
  import { NextResponse } from 'next/server'
2
2
  import { execSync } from 'child_process'
3
- import { loadSessions } from '@/lib/server/storage'
4
3
  import { notFound } from '@/lib/server/collection-helpers'
5
4
  import { safeParseBody } from '@/lib/server/safe-parse-body'
6
5
  import { log } from '@/lib/server/logger'
6
+ import { getSession } from '@/lib/server/sessions/session-repository'
7
7
 
8
8
  const TAG = 'api-deploy'
9
9
 
10
10
  export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
11
11
  const { id } = await params
12
- const sessions = loadSessions()
13
- const session = sessions[id]
12
+ const session = getSession(id)
14
13
  if (!session) return notFound()
15
14
 
16
15
  const { data: body, error } = await safeParseBody<{ message?: string }>(req)
@@ -1,21 +1,15 @@
1
1
  import { NextResponse } from 'next/server'
2
- import { loadSession, upsertSession } from '@/lib/server/storage'
2
+ import { editAndResendChatTurn } from '@/lib/server/chats/chat-session-service'
3
3
  import { notFound } from '@/lib/server/collection-helpers'
4
4
 
5
5
  export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
6
6
  const { id } = await params
7
7
  const body = await req.json().catch(() => ({})) as { messageIndex: number; newText: string }
8
- const session = loadSession(id)
9
- if (!session) return notFound()
10
-
11
- const { messageIndex, newText } = body
12
- if (typeof messageIndex !== 'number' || messageIndex < 0 || messageIndex >= session.messages.length) {
13
- return NextResponse.json({ error: 'Invalid message index' }, { status: 400 })
8
+ const result = editAndResendChatTurn(id, body.messageIndex, body.newText)
9
+ if (!result.ok) {
10
+ return result.status === 404
11
+ ? notFound()
12
+ : NextResponse.json(result.payload, { status: result.status })
14
13
  }
15
-
16
- // Truncate messages to messageIndex (discard that msg + everything after)
17
- session.messages = session.messages.slice(0, messageIndex)
18
- upsertSession(id, session)
19
-
20
- return NextResponse.json({ message: newText })
14
+ return NextResponse.json(result.payload)
21
15
  }
@@ -1,6 +1,6 @@
1
1
  import { NextResponse } from 'next/server'
2
2
  import { ackMailboxEnvelope, clearMailbox, listMailbox, sendMailboxEnvelope } from '@/lib/server/chatrooms/session-mailbox'
3
- import { loadSessions } from '@/lib/server/storage'
3
+ import { getSession } from '@/lib/server/sessions/session-repository'
4
4
 
5
5
  function parseIntParam(value: string | null, fallback: number, min: number, max: number): number {
6
6
  const parsed = value ? Number.parseInt(value, 10) : Number.NaN
@@ -16,8 +16,8 @@ export async function GET(req: Request, { params }: { params: Promise<{ id: stri
16
16
  try {
17
17
  const envelopes = listMailbox(id, { limit, includeAcked })
18
18
  return NextResponse.json({ sessionId: id, count: envelopes.length, envelopes })
19
- } catch (err: any) {
20
- return NextResponse.json({ error: err?.message || 'Failed to load mailbox.' }, { status: 404 })
19
+ } catch (err: unknown) {
20
+ return NextResponse.json({ error: err instanceof Error ? err.message : 'Failed to load mailbox.' }, { status: 404 })
21
21
  }
22
22
  }
23
23
 
@@ -25,8 +25,7 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
25
25
  const { id } = await params
26
26
  const body = await req.json().catch(() => ({}))
27
27
  const action = typeof body?.action === 'string' ? body.action : 'send'
28
- const sessions = loadSessions()
29
- const current = sessions[id]
28
+ const current = getSession(id)
30
29
  if (!current) return NextResponse.json({ error: `Session not found: ${id}` }, { status: 404 })
31
30
 
32
31
  try {
@@ -63,8 +62,7 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
63
62
  }
64
63
 
65
64
  return NextResponse.json({ error: `Unsupported action: ${action}` }, { status: 400 })
66
- } catch (err: any) {
67
- return NextResponse.json({ error: err?.message || 'Mailbox operation failed.' }, { status: 500 })
65
+ } catch (err: unknown) {
66
+ return NextResponse.json({ error: err instanceof Error ? err.message : 'Mailbox operation failed.' }, { status: 500 })
68
67
  }
69
68
  }
70
-
@@ -1,84 +1,37 @@
1
1
  import { NextResponse } from 'next/server'
2
2
  import { notFound } from '@/lib/server/collection-helpers'
3
- import { loadSession } from '@/lib/server/storage'
4
3
  import {
5
- cancelQueuedRunById,
6
- cancelQueuedRunsForSession,
7
- enqueueSessionRun,
8
- getSessionQueueSnapshot,
9
- } from '@/lib/server/runtime/session-run-manager'
4
+ cancelQueuedChatMessages,
5
+ getQueueSnapshot,
6
+ queueChatMessage,
7
+ } from '@/lib/server/chats/chat-session-service'
10
8
 
11
9
  export const dynamic = 'force-dynamic'
12
10
 
13
11
  export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
14
12
  const { id } = await params
15
- const session = loadSession(id)
16
- if (!session) return notFound()
17
- return NextResponse.json(getSessionQueueSnapshot(id))
13
+ const snapshot = getQueueSnapshot(id)
14
+ if (!snapshot) return notFound()
15
+ return NextResponse.json(snapshot)
18
16
  }
19
17
 
20
18
  export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
21
19
  const { id } = await params
22
- const session = loadSession(id)
23
- if (!session) return notFound()
24
-
25
20
  const body = await req.json().catch(() => ({}))
26
- const message = typeof body.message === 'string' ? body.message : ''
27
- const imagePath = typeof body.imagePath === 'string' ? body.imagePath : undefined
28
- const imageUrl = typeof body.imageUrl === 'string' ? body.imageUrl : undefined
29
- const attachedFiles = Array.isArray(body.attachedFiles)
30
- ? body.attachedFiles.filter((file: unknown): file is string => typeof file === 'string' && file.trim().length > 0)
31
- : undefined
32
- const replyToId = typeof body.replyToId === 'string' ? body.replyToId : undefined
33
- const hasFiles = !!(imagePath || imageUrl || attachedFiles?.length)
34
-
35
- if (!message.trim() && !hasFiles) {
36
- return NextResponse.json({ error: 'message or file is required' }, { status: 400 })
21
+ const result = queueChatMessage(id, body as Record<string, unknown>)
22
+ if (!result.ok) {
23
+ return result.status === 404
24
+ ? notFound()
25
+ : NextResponse.json(result.payload, { status: result.status })
37
26
  }
38
-
39
- const queued = enqueueSessionRun({
40
- sessionId: id,
41
- missionId: session.missionId || null,
42
- message,
43
- imagePath,
44
- imageUrl,
45
- attachedFiles,
46
- source: 'chat',
47
- mode: 'followup',
48
- replyToId,
49
- })
50
-
51
- return NextResponse.json({
52
- queued: {
53
- runId: queued.runId,
54
- position: queued.position,
55
- },
56
- snapshot: getSessionQueueSnapshot(id),
57
- }, { status: 202 })
27
+ return NextResponse.json(result.payload)
58
28
  }
59
29
 
60
30
  export async function DELETE(req: Request, { params }: { params: Promise<{ id: string }> }) {
61
31
  const { id } = await params
62
- const session = loadSession(id)
63
- if (!session) return notFound()
64
-
65
32
  const body = await req.json().catch(() => ({}))
66
- const runId = typeof body.runId === 'string' ? body.runId.trim() : ''
67
- if (runId) {
68
- const snapshot = getSessionQueueSnapshot(id)
69
- if (!snapshot.items.some((item) => item.runId === runId)) {
70
- return NextResponse.json({ error: 'Queued run not found' }, { status: 404 })
71
- }
72
- cancelQueuedRunById(runId, 'Removed from queue')
73
- return NextResponse.json({
74
- cancelled: 1,
75
- snapshot: getSessionQueueSnapshot(id),
76
- })
77
- }
78
-
79
- const cancelled = cancelQueuedRunsForSession(id, 'Cleared queued messages')
80
- return NextResponse.json({
81
- cancelled,
82
- snapshot: getSessionQueueSnapshot(id),
83
- })
33
+ const result = cancelQueuedChatMessages(id, typeof body.runId === 'string' ? body.runId : '')
34
+ if (!result) return notFound()
35
+ if (!result.ok) return NextResponse.json(result.payload, { status: result.status })
36
+ return NextResponse.json(result.payload)
84
37
  }