@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,35 +1,19 @@
1
1
  import { NextResponse } from 'next/server'
2
2
  import { safeParseBody } from '@/lib/server/safe-parse-body'
3
- import { genId } from '@/lib/id'
4
3
  import { perf } from '@/lib/server/runtime/perf'
5
- import { deleteTask, loadAgents, loadSettings, loadTasks, logActivity, upsertTask } from '@/lib/server/storage'
6
4
  import { TaskCreateSchema, formatZodError } from '@/lib/validation/schemas'
7
5
  import { z } from 'zod'
8
- import { enqueueTask, recoverStalledRunningTasks, validateCompletedTasksQueue } from '@/lib/server/runtime/queue'
9
- import { pushMainLoopEventToMainSessions } from '@/lib/server/agents/main-agent-loop'
10
- import { notify } from '@/lib/server/ws-hub'
11
- import { resolveTaskAgentFromDescription } from '@/lib/server/tasks/task-mention'
12
- import { validateDag } from '@/lib/server/dag-validation'
13
- import { getExtensionManager } from '@/lib/server/extensions'
14
- import { getEnabledCapabilityIds } from '@/lib/capability-selection'
15
6
  import {
16
- prepareTaskCreation,
17
- } from '@/lib/server/tasks/task-service'
18
- import { ensureMissionForTask, enrichTaskWithMissionSummary } from '@/lib/server/missions/mission-service'
19
- import '@/lib/server/builtin-extensions'
7
+ createTaskFromRoute,
8
+ deleteTasksByFilter,
9
+ prepareTasksForListing,
10
+ } from '@/lib/server/tasks/task-route-service'
20
11
 
21
12
  export async function GET(req: Request) {
22
13
  const endPerf = perf.start('api', 'GET /api/tasks')
23
- // Keep completed queue integrity even if daemon is not running.
24
- validateCompletedTasksQueue()
25
- recoverStalledRunningTasks()
26
-
27
14
  const { searchParams } = new URL(req.url)
28
15
  const includeArchived = searchParams.get('includeArchived') === 'true'
29
- const allTasks = loadTasks()
30
- const missionTasks = Object.fromEntries(
31
- Object.entries(allTasks).map(([id, task]) => [id, enrichTaskWithMissionSummary(task)]),
32
- )
16
+ const missionTasks = prepareTasksForListing()
33
17
 
34
18
  if (includeArchived) {
35
19
  endPerf({ count: Object.keys(missionTasks).length })
@@ -37,7 +21,7 @@ export async function GET(req: Request) {
37
21
  }
38
22
 
39
23
  // Exclude archived tasks by default
40
- const filtered: Record<string, typeof allTasks[string]> = {}
24
+ const filtered: Record<string, (typeof missionTasks)[string]> = {}
41
25
  for (const [id, task] of Object.entries(missionTasks)) {
42
26
  if (task.status !== 'archived') {
43
27
  filtered[id] = task
@@ -50,23 +34,7 @@ export async function GET(req: Request) {
50
34
  export async function DELETE(req: Request) {
51
35
  const { searchParams } = new URL(req.url)
52
36
  const filter = searchParams.get('filter') // 'all' | 'schedule' | 'done' | null
53
- const tasks = loadTasks()
54
- let removed = 0
55
-
56
- const shouldRemove = (task: { status: string; sourceType?: string }) =>
57
- filter === 'all' ||
58
- (filter === 'schedule' && task.sourceType === 'schedule') ||
59
- (filter === 'done' && (task.status === 'completed' || task.status === 'failed')) ||
60
- (!filter && task.status === 'archived')
61
-
62
- for (const [id, task] of Object.entries(tasks)) {
63
- if (shouldRemove(task as { status: string; sourceType?: string })) {
64
- deleteTask(id)
65
- removed++
66
- }
67
- }
68
- notify('tasks')
69
- return NextResponse.json({ removed, remaining: Object.keys(tasks).length - removed })
37
+ return NextResponse.json(deleteTasksByFilter(filter))
70
38
  }
71
39
 
72
40
  export async function POST(req: Request) {
@@ -76,136 +44,8 @@ export async function POST(req: Request) {
76
44
  if (!parsed.success) {
77
45
  return NextResponse.json(formatZodError(parsed.error as z.ZodError), { status: 400 })
78
46
  }
79
- const body = { ...raw, ...parsed.data } as Record<string, unknown> & typeof parsed.data
80
- const id = genId()
81
- const now = Date.now()
82
- const tasks = loadTasks()
83
- const settings = loadSettings()
84
- const maxAttempts = Number.isFinite(Number(body.maxAttempts))
85
- ? Math.max(1, Math.min(20, Math.trunc(Number(body.maxAttempts))))
86
- : Math.max(1, Math.min(20, Math.trunc(Number(settings.defaultTaskMaxAttempts ?? 3))))
87
- const retryBackoffSec = Number.isFinite(Number(body.retryBackoffSec))
88
- ? Math.max(1, Math.min(3600, Math.trunc(Number(body.retryBackoffSec))))
89
- : Math.max(1, Math.min(3600, Math.trunc(Number(settings.taskRetryBackoffSec ?? 30))))
90
- // DAG validation: reject if proposed blockedBy would create a cycle
91
- if (Array.isArray(body.blockedBy) && body.blockedBy.length > 0) {
92
- const dagResult = validateDag(tasks, id, body.blockedBy)
93
- if (!dagResult.valid) {
94
- return NextResponse.json(
95
- { error: 'Dependency cycle detected', cycle: dagResult.cycle },
96
- { status: 400 },
97
- )
98
- }
99
- }
100
-
101
- // Resolve @mentions in description to auto-assign agent
102
- const resolvedAgentId = body.description
103
- ? resolveTaskAgentFromDescription(body.description, body.agentId || '', loadAgents())
104
- : (body.agentId || '')
105
-
106
- const prepared = prepareTaskCreation({
107
- id,
108
- input: {
109
- ...body,
110
- agentId: resolvedAgentId,
111
- },
112
- tasks,
113
- now,
114
- settings,
115
- seed: {
116
- projectId: typeof body.projectId === 'string' && body.projectId ? body.projectId : null,
117
- goalContract: body.goalContract || null,
118
- cwd: typeof body.cwd === 'string' ? body.cwd : null,
119
- file: typeof body.file === 'string' ? body.file : null,
120
- sessionId: typeof body.sessionId === 'string' ? body.sessionId : null,
121
- result: typeof body.result === 'string' ? body.result : null,
122
- error: typeof body.error === 'string' ? body.error : null,
123
- outputFiles: Array.isArray(body.outputFiles)
124
- ? body.outputFiles.filter((entry: unknown) => typeof entry === 'string').slice(0, 24)
125
- : [],
126
- artifacts: Array.isArray(body.artifacts)
127
- ? body.artifacts
128
- .filter((artifact: unknown) => artifact && typeof artifact === 'object')
129
- .map((artifact: unknown) => {
130
- const row = artifact as {
131
- url?: unknown
132
- type?: unknown
133
- filename?: unknown
134
- }
135
- const normalizedType = String(row.type || '')
136
- return {
137
- url: String(row.url || ''),
138
- type: ['image', 'video', 'pdf', 'file'].includes(normalizedType)
139
- ? (normalizedType as 'image' | 'video' | 'pdf' | 'file')
140
- : 'file',
141
- filename: String(row.filename || ''),
142
- }
143
- })
144
- .filter((artifact: { url: string; filename: string }) => artifact.url && artifact.filename)
145
- .slice(0, 24)
146
- : [],
147
- archivedAt: null,
148
- attempts: 0,
149
- maxAttempts,
150
- retryBackoffSec,
151
- retryScheduledAt: null,
152
- deadLetteredAt: null,
153
- checkpoint: null,
154
- blockedBy: Array.isArray(body.blockedBy) ? body.blockedBy.filter((s: unknown) => typeof s === 'string') : [],
155
- blocks: Array.isArray(body.blocks) ? body.blocks.filter((s: unknown) => typeof s === 'string') : [],
156
- tags: Array.isArray(body.tags) ? body.tags.filter((s: unknown) => typeof s === 'string') : [],
157
- dueAt: typeof body.dueAt === 'number' ? body.dueAt : null,
158
- customFields: body.customFields && typeof body.customFields === 'object' ? body.customFields : undefined,
159
- priority: body.priority && ['low', 'medium', 'high', 'critical'].includes(body.priority) ? body.priority : undefined,
160
- },
161
- })
162
- if (!prepared.ok) {
163
- return NextResponse.json({ error: prepared.error }, { status: 400 })
164
- }
165
-
166
- if (prepared.duplicate) {
167
- return NextResponse.json({ ...prepared.duplicate, deduplicated: true })
168
- }
169
-
170
- const task = prepared.task
171
- if (task.status === 'completed') {
172
- const agentExtensions = resolvedAgentId ? getEnabledCapabilityIds(loadAgents()[resolvedAgentId]) : []
173
- getExtensionManager().runHook(
174
- 'onTaskComplete',
175
- { taskId: id, result: task.result },
176
- { enabledIds: agentExtensions },
177
- )
178
- }
179
-
180
- // Parent/child hierarchy
181
- const parentTaskId = typeof body.parentTaskId === 'string' && body.parentTaskId.trim() ? body.parentTaskId.trim() : null
182
- if (parentTaskId) {
183
- task.parentTaskId = parentTaskId
184
- const parentTask = tasks[parentTaskId]
185
- if (parentTask) {
186
- const subtaskIds = Array.isArray(parentTask.subtaskIds) ? parentTask.subtaskIds : []
187
- if (!subtaskIds.includes(id)) {
188
- parentTask.subtaskIds = [...subtaskIds, id]
189
- parentTask.updatedAt = now
190
- upsertTask(parentTaskId, parentTask)
191
- }
192
- }
193
- }
194
-
195
- upsertTask(id, task)
196
- const mission = ensureMissionForTask(task, { source: 'manual' })
197
- const finalTask = enrichTaskWithMissionSummary({
198
- ...task,
199
- missionId: mission?.id || task.missionId || null,
200
- })
201
- logActivity({ entityType: 'task', entityId: id, action: 'created', actor: 'user', summary: `Task created: "${task.title}"` })
202
- pushMainLoopEventToMainSessions({
203
- type: 'task_created',
204
- text: `Task created: "${task.title}" (${id}) with status ${task.status}.`,
205
- })
206
- if (task.status === 'queued') {
207
- enqueueTask(id)
208
- }
209
- notify('tasks')
210
- return NextResponse.json(finalTask)
47
+ const result = createTaskFromRoute({ ...raw, ...parsed.data } as Record<string, unknown>)
48
+ return result.ok
49
+ ? NextResponse.json(result.payload)
50
+ : NextResponse.json(result.payload, { status: result.status })
211
51
  }
@@ -2,8 +2,8 @@ import { NextResponse } from 'next/server'
2
2
  import fs from 'fs'
3
3
  import path from 'path'
4
4
  import { genId } from '@/lib/id'
5
- import { UPLOAD_DIR } from '@/lib/server/storage'
6
5
  import { log } from '@/lib/server/logger'
6
+ import { UPLOAD_DIR } from '@/lib/server/upload-path'
7
7
 
8
8
  const TAG = 'api-upload'
9
9
 
@@ -2,8 +2,8 @@ import { NextResponse } from 'next/server'
2
2
  import { notFound } from '@/lib/server/collection-helpers'
3
3
  import fs from 'fs'
4
4
  import path from 'path'
5
- import { UPLOAD_DIR } from '@/lib/server/storage'
6
5
  import { MIME_TYPES } from '@/lib/server/mime'
6
+ import { UPLOAD_DIR } from '@/lib/server/upload-path'
7
7
 
8
8
  export async function GET(_req: Request, { params }: { params: Promise<{ filename: string }> }) {
9
9
  const { filename } = await params
@@ -2,8 +2,8 @@ import { NextResponse } from 'next/server'
2
2
  import fs from 'fs'
3
3
  import path from 'path'
4
4
  import { safeParseBody } from '@/lib/server/safe-parse-body'
5
- import { UPLOAD_DIR } from '@/lib/server/storage'
6
5
  import { getFileCategory } from '@/lib/server/mime'
6
+ import { UPLOAD_DIR } from '@/lib/server/upload-path'
7
7
 
8
8
  interface UploadFile {
9
9
  name: string
@@ -5,8 +5,8 @@ export async function GET(_req: Request, { params }: { params: Promise<{ id: str
5
5
  const { id } = await params
6
6
  const allLogs = loadWebhookLogs()
7
7
  const entries = Object.values(allLogs)
8
- .filter((entry: any) => entry.webhookId === id)
9
- .sort((a: any, b: any) => (b.timestamp || 0) - (a.timestamp || 0))
8
+ .filter((entry) => (entry as Record<string, unknown>).webhookId === id)
9
+ .sort((a, b) => ((b as Record<string, unknown>).timestamp as number || 0) - ((a as Record<string, unknown>).timestamp as number || 0))
10
10
  .slice(0, 100)
11
11
 
12
12
  return NextResponse.json(entries)
@@ -2,6 +2,7 @@ import type { Metadata, Viewport } from "next"
2
2
  import { TooltipProvider } from "@/components/ui/tooltip"
3
3
  import { Toaster } from "@/components/ui/sonner"
4
4
  import { DashboardShell } from "@/components/layout/dashboard-shell"
5
+ import { AppQueryProvider } from "@/components/providers/app-query-provider"
5
6
  import "./globals.css"
6
7
 
7
8
  export const metadata: Metadata = {
@@ -28,12 +29,14 @@ export default function RootLayout({
28
29
  return (
29
30
  <html lang="en" className="dark">
30
31
  <body className="antialiased" cz-shortcut-listen="true">
31
- <TooltipProvider>
32
- <DashboardShell>
33
- {children}
34
- </DashboardShell>
35
- <Toaster />
36
- </TooltipProvider>
32
+ <AppQueryProvider>
33
+ <TooltipProvider>
34
+ <DashboardShell>
35
+ {children}
36
+ </DashboardShell>
37
+ <Toaster />
38
+ </TooltipProvider>
39
+ </AppQueryProvider>
37
40
  </body>
38
41
  </html>
39
42
  )
@@ -2,8 +2,19 @@
2
2
 
3
3
  import { useCallback, useEffect, useMemo, useState } from 'react'
4
4
  import { useRouter, useSearchParams } from 'next/navigation'
5
- import { api } from '@/lib/app/api-client'
6
- import { useWs } from '@/hooks/use-ws'
5
+ import { useAgentsQuery } from '@/features/agents/queries'
6
+ import { useChatroomsQuery } from '@/features/chatrooms/queries'
7
+ import { useMissionsQuery } from '@/features/missions/queries'
8
+ import {
9
+ useCreateProtocolRunMutation,
10
+ useDeleteProtocolTemplateMutation,
11
+ useProtocolRunActionMutation,
12
+ useProtocolRunDetailQuery,
13
+ useProtocolRunsQuery,
14
+ useProtocolTemplatesQuery,
15
+ useUpsertProtocolTemplateMutation,
16
+ } from '@/features/protocols/queries'
17
+ import { useTasksQuery } from '@/features/tasks/queries'
7
18
  import { MainContent } from '@/components/layout/main-content'
8
19
  import { StructuredSessionLauncher } from '@/components/protocols/structured-session-launcher'
9
20
  import { timeAgo } from '@/lib/time-format'
@@ -67,10 +78,6 @@ const DEFAULT_TEMPLATE_DRAFT: TemplateDraft = {
67
78
  entryStepId: 'present',
68
79
  }
69
80
 
70
- async function postRunAction(runId: string, payload: RunActionPayload) {
71
- await api('POST', `/protocols/runs/${runId}/actions`, payload)
72
- }
73
-
74
81
  function toTemplateDraft(template: ProtocolTemplate | null): TemplateDraft {
75
82
  if (!template) return DEFAULT_TEMPLATE_DRAFT
76
83
  return {
@@ -125,16 +132,7 @@ function splitCsv(value: string): string[] {
125
132
  export default function ProtocolsPage() {
126
133
  const router = useRouter()
127
134
  const searchParams = useSearchParams()
128
- const [templates, setTemplates] = useState<ProtocolTemplate[]>([])
129
- const [runs, setRuns] = useState<ProtocolRun[]>([])
130
- const [detail, setDetail] = useState<ProtocolRunDetail | null>(null)
131
135
  const [selectedRunId, setSelectedRunId] = useState<string | null>(null)
132
- const [agents, setAgents] = useState<AgentList>({})
133
- const [chatrooms, setChatrooms] = useState<Record<string, Chatroom>>({})
134
- const [missions, setMissions] = useState<Mission[]>([])
135
- const [tasks, setTasks] = useState<Record<string, BoardTask>>({})
136
- const [loading, setLoading] = useState(true)
137
- const [detailLoading, setDetailLoading] = useState(false)
138
136
  const [actionPending, setActionPending] = useState<string | null>(null)
139
137
  const [templatePending, setTemplatePending] = useState<string | null>(null)
140
138
  const [templateEditorOpen, setTemplateEditorOpen] = useState(false)
@@ -160,66 +158,55 @@ export default function ProtocolsPage() {
160
158
  autoStart: true,
161
159
  })
162
160
  const requestedRunId = searchParams.get('runId')
163
-
164
- const loadRuns = useCallback(async () => {
165
- const [templateList, runList, agentList, roomList, missionList, taskList] = await Promise.all([
166
- api<ProtocolTemplate[]>('GET', '/protocols/templates'),
167
- api<ProtocolRun[]>('GET', '/protocols/runs?limit=120'),
168
- api<AgentList>('GET', '/agents'),
169
- api<Record<string, Chatroom>>('GET', '/chatrooms'),
170
- api<Mission[]>('GET', '/missions?limit=80'),
171
- api<Record<string, BoardTask>>('GET', '/tasks'),
172
- ])
173
- setTemplates(Array.isArray(templateList) ? templateList : [])
174
- const normalizedRuns = Array.isArray(runList) ? runList : []
175
- setRuns(normalizedRuns)
176
- setAgents(agentList || {})
177
- setChatrooms(roomList || {})
178
- setMissions(Array.isArray(missionList) ? missionList : [])
179
- setTasks(taskList || {})
180
- setSelectedRunId((current) => {
181
- if (current && normalizedRuns.some((run) => run.id === current)) return current
182
- return normalizedRuns[0]?.id || null
183
- })
184
- setLoading(false)
185
- }, [])
161
+ const templatesQuery = useProtocolTemplatesQuery()
162
+ const runsQuery = useProtocolRunsQuery({ limit: 120 })
163
+ const agentsQuery = useAgentsQuery()
164
+ const chatroomsQuery = useChatroomsQuery()
165
+ const missionsQuery = useMissionsQuery({ limit: 80 })
166
+ const tasksQuery = useTasksQuery({ includeArchived: true })
167
+ const detailQuery = useProtocolRunDetailQuery(selectedRunId, { enabled: Boolean(selectedRunId) })
168
+ const createRunMutation = useCreateProtocolRunMutation()
169
+ const runActionMutation = useProtocolRunActionMutation()
170
+ const upsertTemplateMutation = useUpsertProtocolTemplateMutation()
171
+ const deleteTemplateMutation = useDeleteProtocolTemplateMutation()
172
+ const templates = templatesQuery.data ?? []
173
+ const runs = runsQuery.data ?? []
174
+ const detail = detailQuery.data ?? null
175
+ const agents = agentsQuery.data ?? {}
176
+ const chatrooms = chatroomsQuery.data ?? {}
177
+ const missions = missionsQuery.data ?? []
178
+ const tasks = tasksQuery.data ?? {}
179
+ const loading = (
180
+ templatesQuery.isLoading
181
+ || runsQuery.isLoading
182
+ || agentsQuery.isLoading
183
+ || chatroomsQuery.isLoading
184
+ || missionsQuery.isLoading
185
+ || tasksQuery.isLoading
186
+ )
187
+ const detailLoading = detailQuery.isLoading || detailQuery.isFetching
188
+ const queryError = [
189
+ templatesQuery.error,
190
+ runsQuery.error,
191
+ agentsQuery.error,
192
+ chatroomsQuery.error,
193
+ missionsQuery.error,
194
+ tasksQuery.error,
195
+ detailQuery.error,
196
+ ].find(Boolean)
197
+ const resolvedError = error || (queryError instanceof Error ? queryError.message : null)
186
198
 
187
199
  // Apply URL-requested run selection separately so WS refreshes don't snap back
188
200
  useEffect(() => {
189
201
  if (requestedRunId) setSelectedRunId(requestedRunId)
190
202
  }, [requestedRunId])
191
203
 
192
- const loadDetail = useCallback(async (runId: string | null) => {
193
- if (!runId) {
194
- setDetail(null)
195
- return
196
- }
197
- setDetailLoading(true)
198
- try {
199
- const value = await api<ProtocolRunDetail>('GET', `/protocols/runs/${runId}`)
200
- setDetail(value)
201
- setError(null)
202
- } catch (err) {
203
- setError(err instanceof Error ? err.message : 'Unable to load structured session.')
204
- setDetail(null)
205
- } finally {
206
- setDetailLoading(false)
207
- }
208
- }, [])
209
-
210
204
  useEffect(() => {
211
- void loadRuns().catch((err) => {
212
- setError(err instanceof Error ? err.message : 'Unable to load structured sessions.')
213
- setLoading(false)
205
+ setSelectedRunId((current) => {
206
+ if (current && runs.some((run) => run.id === current)) return current
207
+ return runs[0]?.id || null
214
208
  })
215
- }, [loadRuns])
216
-
217
- useEffect(() => {
218
- void loadDetail(selectedRunId)
219
- }, [loadDetail, selectedRunId])
220
-
221
- useWs('protocol_runs', loadRuns, 2000)
222
- useWs('protocol_templates', loadRuns, 2000)
209
+ }, [runs])
223
210
 
224
211
  const selectedTemplate = useMemo(() => templates.find((template) => template.id === form.templateId) || null, [form.templateId, templates])
225
212
  const customTemplates = useMemo(() => templates.filter((template) => !template.builtIn), [templates])
@@ -244,7 +231,7 @@ export default function ProtocolsPage() {
244
231
  return
245
232
  }
246
233
  try {
247
- const run = await api<ProtocolRun>('POST', '/protocols/runs', {
234
+ const run = await createRunMutation.mutateAsync({
248
235
  title: form.title.trim(),
249
236
  templateId: form.templateId,
250
237
  participantAgentIds: form.participantAgentIds,
@@ -269,37 +256,34 @@ export default function ProtocolsPage() {
269
256
  decisionMode: '',
270
257
  }))
271
258
  setSelectedRunId(run.id)
272
- await loadRuns()
273
259
  } catch (err) {
274
260
  setError(err instanceof Error ? err.message : 'Unable to create structured session.')
275
261
  }
276
- }, [form, loadRuns])
262
+ }, [createRunMutation, form])
277
263
 
278
264
  const handleAction = useCallback(async (payload: RunActionPayload) => {
279
265
  if (!detail?.run.id) return
280
266
  setActionPending(payload.action)
281
267
  try {
282
- await postRunAction(detail.run.id, payload)
283
- await Promise.all([loadRuns(), loadDetail(detail.run.id)])
268
+ await runActionMutation.mutateAsync({ runId: detail.run.id, payload })
284
269
  if (payload.action === 'inject_context') setContextDraft('')
285
270
  } catch (err) {
286
271
  setError(err instanceof Error ? err.message : 'Unable to update structured session.')
287
272
  } finally {
288
273
  setActionPending(null)
289
274
  }
290
- }, [detail, loadDetail, loadRuns])
275
+ }, [detail, runActionMutation])
291
276
 
292
277
  const handleBranchAction = useCallback(async (runId: string, payload: RunActionPayload) => {
293
278
  setActionPending(`${payload.action}:${runId}`)
294
279
  try {
295
- await postRunAction(runId, payload)
296
- await Promise.all([loadRuns(), detail?.run.id ? loadDetail(detail.run.id) : Promise.resolve()])
280
+ await runActionMutation.mutateAsync({ runId, payload })
297
281
  } catch (err) {
298
282
  setError(err instanceof Error ? err.message : 'Unable to update branch run.')
299
283
  } finally {
300
284
  setActionPending(null)
301
285
  }
302
- }, [detail?.run.id, loadDetail, loadRuns])
286
+ }, [runActionMutation])
303
287
 
304
288
  const openTemplateEditor = useCallback((template: ProtocolTemplate | null = null) => {
305
289
  setEditingTemplateId(template?.builtIn ? null : template?.id || null)
@@ -328,27 +312,27 @@ export default function ProtocolsPage() {
328
312
  return
329
313
  }
330
314
  setTemplatePending(editingTemplateId ? 'save-edit' : 'save-new')
331
- if (editingTemplateId) {
332
- await api('PATCH', `/protocols/templates/${editingTemplateId}`, payload)
333
- } else {
334
- const created = await api<ProtocolTemplate>('POST', '/protocols/templates', payload)
315
+ const created = await upsertTemplateMutation.mutateAsync({
316
+ templateId: editingTemplateId,
317
+ payload,
318
+ })
319
+ if (!editingTemplateId) {
335
320
  setForm((current) => ({ ...current, templateId: created.id }))
336
321
  }
337
322
  setTemplateEditorOpen(false)
338
323
  setEditingTemplateId(null)
339
324
  setTemplateDraft(DEFAULT_TEMPLATE_DRAFT)
340
- await loadRuns()
341
325
  } catch (err) {
342
326
  setError(err instanceof Error ? err.message : 'Unable to save structured-session template.')
343
327
  } finally {
344
328
  setTemplatePending(null)
345
329
  }
346
- }, [editingTemplateId, loadRuns, templateDraft])
330
+ }, [editingTemplateId, templateDraft, upsertTemplateMutation])
347
331
 
348
332
  const handleDeleteTemplate = useCallback(async (templateId: string) => {
349
333
  setTemplatePending(`delete:${templateId}`)
350
334
  try {
351
- await api('DELETE', `/protocols/templates/${templateId}`)
335
+ await deleteTemplateMutation.mutateAsync(templateId)
352
336
  setTemplateEditorOpen(false)
353
337
  setEditingTemplateId(null)
354
338
  setTemplateDraft(DEFAULT_TEMPLATE_DRAFT)
@@ -356,13 +340,12 @@ export default function ProtocolsPage() {
356
340
  ...current,
357
341
  templateId: current.templateId === templateId ? 'facilitated_discussion' : current.templateId,
358
342
  }))
359
- await loadRuns()
360
343
  } catch (err) {
361
344
  setError(err instanceof Error ? err.message : 'Unable to delete structured-session template.')
362
345
  } finally {
363
346
  setTemplatePending(null)
364
347
  }
365
- }, [loadRuns])
348
+ }, [deleteTemplateMutation])
366
349
 
367
350
  const handleSelectRun = useCallback((runId: string) => {
368
351
  setSelectedRunId(runId)
@@ -715,9 +698,9 @@ export default function ProtocolsPage() {
715
698
  )}
716
699
  </div>
717
700
  </div>
718
- {error && (
701
+ {resolvedError && (
719
702
  <div className="mt-4 rounded-[14px] border border-red-500/20 bg-red-500/10 px-4 py-3 text-[13px] text-red-200">
720
- {error}
703
+ {resolvedError}
721
704
  </div>
722
705
  )}
723
706
  </section>
@@ -1272,9 +1255,8 @@ export default function ProtocolsPage() {
1272
1255
  open={launcherOpen}
1273
1256
  onClose={() => setLauncherOpen(false)}
1274
1257
  onCreated={(run) => {
1258
+ setLauncherOpen(false)
1275
1259
  setSelectedRunId(run.id)
1276
- void loadRuns()
1277
- void loadDetail(run.id)
1278
1260
  router.push(`/protocols?runId=${encodeURIComponent(run.id)}`)
1279
1261
  }}
1280
1262
  allowContextSelection