@swarmclawai/swarmclaw 1.2.3 → 1.2.5

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 (273) hide show
  1. package/README.md +20 -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]/models/route.test.ts +60 -0
  49. package/src/app/api/providers/[id]/models/route.ts +33 -1
  50. package/src/app/api/providers/[id]/route.test.ts +49 -0
  51. package/src/app/api/providers/[id]/route.ts +30 -1
  52. package/src/app/api/providers/ollama/route.ts +6 -5
  53. package/src/app/api/schedules/[id]/route.ts +14 -108
  54. package/src/app/api/schedules/[id]/run/route.ts +6 -67
  55. package/src/app/api/schedules/route.ts +9 -51
  56. package/src/app/api/settings/route.ts +4 -3
  57. package/src/app/api/setup/check-provider/route.ts +15 -1
  58. package/src/app/api/setup/openclaw-device/route.ts +2 -2
  59. package/src/app/api/system/status/route.ts +2 -2
  60. package/src/app/api/tasks/[id]/route.ts +16 -202
  61. package/src/app/api/tasks/bulk/route.ts +5 -86
  62. package/src/app/api/tasks/metrics/route.ts +2 -1
  63. package/src/app/api/tasks/route.ts +11 -171
  64. package/src/app/api/upload/route.ts +1 -1
  65. package/src/app/api/uploads/[filename]/route.ts +1 -1
  66. package/src/app/api/uploads/route.ts +1 -1
  67. package/src/app/api/webhooks/[id]/history/route.ts +2 -2
  68. package/src/app/layout.tsx +9 -6
  69. package/src/app/protocols/page.tsx +71 -89
  70. package/src/app/tasks/page.tsx +32 -32
  71. package/src/cli/index.js +1 -0
  72. package/src/cli/spec.js +1 -0
  73. package/src/components/agents/agent-sheet.tsx +51 -25
  74. package/src/components/agents/inspector-panel.tsx +15 -4
  75. package/src/components/auth/setup-wizard/index.tsx +27 -18
  76. package/src/components/auth/setup-wizard/shared.tsx +2 -2
  77. package/src/components/auth/setup-wizard/step-agents.tsx +51 -38
  78. package/src/components/auth/setup-wizard/step-connect.tsx +48 -17
  79. package/src/components/auth/setup-wizard/types.ts +6 -4
  80. package/src/components/auth/setup-wizard/utils.test.ts +38 -8
  81. package/src/components/auth/setup-wizard/utils.ts +14 -8
  82. package/src/components/chatrooms/chatroom-sheet.tsx +16 -276
  83. package/src/components/connectors/connector-list.tsx +26 -40
  84. package/src/components/connectors/connector-sheet.tsx +95 -149
  85. package/src/components/gateways/gateway-sheet.tsx +61 -110
  86. package/src/components/layout/live-query-sync.tsx +121 -0
  87. package/src/components/protocols/structured-session-launcher.tsx +24 -45
  88. package/src/components/providers/app-query-provider.tsx +17 -0
  89. package/src/components/providers/provider-list.tsx +150 -77
  90. package/src/components/providers/provider-sheet.tsx +102 -77
  91. package/src/components/shared/model-combobox.tsx +5 -4
  92. package/src/components/skills/skill-list.tsx +5 -18
  93. package/src/components/skills/skill-sheet.tsx +21 -20
  94. package/src/components/skills/skills-workspace.tsx +48 -87
  95. package/src/components/tasks/task-card.tsx +20 -13
  96. package/src/components/tasks/task-column.tsx +22 -7
  97. package/src/components/tasks/task-list.tsx +8 -11
  98. package/src/components/tasks/task-sheet.tsx +111 -103
  99. package/src/features/agents/queries.ts +20 -0
  100. package/src/features/chatrooms/queries.ts +20 -0
  101. package/src/features/chats/queries.ts +27 -0
  102. package/src/features/connectors/queries.ts +145 -0
  103. package/src/features/credentials/queries.ts +37 -0
  104. package/src/features/extensions/queries.ts +26 -0
  105. package/src/features/external-agents/queries.ts +36 -0
  106. package/src/features/gateways/queries.ts +274 -0
  107. package/src/features/missions/queries.ts +23 -0
  108. package/src/features/projects/queries.ts +20 -0
  109. package/src/features/protocols/queries.ts +149 -0
  110. package/src/features/providers/queries.ts +142 -0
  111. package/src/features/settings/queries.ts +20 -0
  112. package/src/features/skills/queries.ts +182 -0
  113. package/src/features/tasks/queries.ts +189 -0
  114. package/src/hooks/use-ws.ts +3 -2
  115. package/src/lib/agent-provider-options.test.ts +152 -0
  116. package/src/lib/agent-provider-options.ts +84 -0
  117. package/src/lib/app/api-client.ts +2 -2
  118. package/src/lib/providers/index.test.ts +78 -0
  119. package/src/lib/providers/index.ts +13 -10
  120. package/src/lib/query/client.ts +17 -0
  121. package/src/lib/server/agents/agent-runtime-config.ts +6 -6
  122. package/src/lib/server/agents/agent-service.ts +429 -0
  123. package/src/lib/server/agents/agent-thread-session.ts +6 -5
  124. package/src/lib/server/agents/autonomy-contract.ts +1 -4
  125. package/src/lib/server/agents/delegation-advisory.test.ts +206 -0
  126. package/src/lib/server/agents/delegation-advisory.ts +251 -0
  127. package/src/lib/server/agents/main-agent-loop.ts +98 -40
  128. package/src/lib/server/agents/subagent-runtime.ts +12 -0
  129. package/src/lib/server/autonomy/supervisor-reflection.test.ts +20 -1
  130. package/src/lib/server/autonomy/supervisor-reflection.ts +39 -19
  131. package/src/lib/server/build-llm.ts +7 -15
  132. package/src/lib/server/capability-router.test.ts +70 -1
  133. package/src/lib/server/capability-router.ts +24 -99
  134. package/src/lib/server/chat-execution/chat-execution-utils.ts +0 -15
  135. package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -4
  136. package/src/lib/server/chat-execution/chat-turn-finalization.ts +77 -12
  137. package/src/lib/server/chat-execution/chat-turn-partial-persistence.ts +4 -4
  138. package/src/lib/server/chat-execution/chat-turn-preflight.ts +2 -2
  139. package/src/lib/server/chat-execution/chat-turn-preparation.ts +41 -17
  140. package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -2
  141. package/src/lib/server/chat-execution/chat-turn-tool-routing.test.ts +45 -0
  142. package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +48 -17
  143. package/src/lib/server/chat-execution/continuation-evaluator.ts +4 -1
  144. package/src/lib/server/chat-execution/direct-memory-intent.test.ts +9 -0
  145. package/src/lib/server/chat-execution/direct-memory-intent.ts +12 -2
  146. package/src/lib/server/chat-execution/message-classifier.test.ts +35 -23
  147. package/src/lib/server/chat-execution/message-classifier.ts +74 -32
  148. package/src/lib/server/chat-execution/prompt-builder.test.ts +29 -0
  149. package/src/lib/server/chat-execution/prompt-builder.ts +37 -2
  150. package/src/lib/server/chat-execution/prompt-sections.test.ts +56 -0
  151. package/src/lib/server/chat-execution/prompt-sections.ts +193 -0
  152. package/src/lib/server/chat-execution/stream-agent-chat.ts +63 -7
  153. package/src/lib/server/chat-execution/stream-continuation.test.ts +36 -0
  154. package/src/lib/server/chat-execution/stream-continuation.ts +28 -13
  155. package/src/lib/server/chatrooms/chatroom-agent-signals.ts +26 -18
  156. package/src/lib/server/chatrooms/chatroom-helpers.ts +19 -18
  157. package/src/lib/server/chatrooms/chatroom-repository.ts +16 -0
  158. package/src/lib/server/chatrooms/chatroom-routing.test.ts +96 -0
  159. package/src/lib/server/chatrooms/chatroom-routing.ts +207 -53
  160. package/src/lib/server/chatrooms/mailbox-utils.ts +4 -2
  161. package/src/lib/server/chatrooms/session-mailbox.ts +50 -40
  162. package/src/lib/server/chats/chat-session-service.ts +410 -0
  163. package/src/lib/server/connectors/access.ts +1 -1
  164. package/src/lib/server/connectors/commands.ts +7 -6
  165. package/src/lib/server/connectors/connector-inbound.ts +14 -7
  166. package/src/lib/server/connectors/connector-outbound.ts +16 -11
  167. package/src/lib/server/connectors/connector-service.ts +453 -0
  168. package/src/lib/server/connectors/delivery.ts +17 -12
  169. package/src/lib/server/connectors/inbound-audio-transcription.ts +5 -14
  170. package/src/lib/server/connectors/media.ts +1 -1
  171. package/src/lib/server/connectors/response-media.ts +1 -1
  172. package/src/lib/server/connectors/session-consolidation.ts +11 -7
  173. package/src/lib/server/connectors/session.ts +9 -7
  174. package/src/lib/server/connectors/voice-note.ts +2 -1
  175. package/src/lib/server/context-manager.ts +20 -1
  176. package/src/lib/server/cost.ts +2 -3
  177. package/src/lib/server/credentials/credential-repository.ts +43 -4
  178. package/src/lib/server/credentials/credential-service.ts +112 -0
  179. package/src/lib/server/daemon/admin-metadata.ts +64 -0
  180. package/src/lib/server/daemon/controller.ts +577 -0
  181. package/src/lib/server/daemon/daemon-runtime.ts +352 -0
  182. package/src/lib/server/daemon/daemon-status-repository.ts +63 -0
  183. package/src/lib/server/daemon/types.ts +101 -0
  184. package/src/lib/server/embeddings.ts +3 -9
  185. package/src/lib/server/eval/agent-regression.ts +3 -2
  186. package/src/lib/server/eval/runner.ts +2 -2
  187. package/src/lib/server/execution-brief.test.ts +167 -0
  188. package/src/lib/server/execution-brief.ts +295 -0
  189. package/src/lib/server/execution-engine/chat-turn.ts +9 -0
  190. package/src/lib/server/execution-engine/import-boundary.test.ts +44 -0
  191. package/src/lib/server/execution-engine/index.ts +35 -0
  192. package/src/lib/server/execution-engine/task-attempt.ts +303 -0
  193. package/src/lib/server/execution-engine/types.ts +33 -0
  194. package/src/lib/server/gateways/gateway-profile-repository.ts +47 -3
  195. package/src/lib/server/gateways/gateway-profile-service.ts +200 -0
  196. package/src/lib/server/memory/session-archive-memory.ts +12 -10
  197. package/src/lib/server/messages/message-repository.ts +330 -0
  198. package/src/lib/server/missions/mission-service/core.ts +8 -6
  199. package/src/lib/server/openclaw/agent-resolver.ts +2 -3
  200. package/src/lib/server/openclaw/doctor.ts +1 -1
  201. package/src/lib/server/openclaw/gateway.test.ts +10 -1
  202. package/src/lib/server/openclaw/gateway.ts +5 -14
  203. package/src/lib/server/openclaw/health.ts +3 -11
  204. package/src/lib/server/openclaw/sync.ts +8 -6
  205. package/src/lib/server/persistence/storage-context.ts +3 -0
  206. package/src/lib/server/protocols/protocol-agent-turn.ts +25 -17
  207. package/src/lib/server/protocols/protocol-normalization.ts +1 -1
  208. package/src/lib/server/protocols/protocol-queries.ts +13 -7
  209. package/src/lib/server/protocols/protocol-run-lifecycle.ts +16 -20
  210. package/src/lib/server/protocols/protocol-run-repository.ts +81 -0
  211. package/src/lib/server/protocols/protocol-step-processors.ts +23 -31
  212. package/src/lib/server/protocols/protocol-swarm.ts +8 -8
  213. package/src/lib/server/protocols/protocol-template-repository.ts +42 -0
  214. package/src/lib/server/protocols/protocol-templates.ts +4 -2
  215. package/src/lib/server/protocols/protocol-types.ts +10 -7
  216. package/src/lib/server/provider-endpoint.ts +7 -12
  217. package/src/lib/server/provider-model-discovery.ts +2 -11
  218. package/src/lib/server/query-expansion.ts +5 -6
  219. package/src/lib/server/run-context.test.ts +365 -0
  220. package/src/lib/server/run-context.ts +367 -0
  221. package/src/lib/server/runtime/heartbeat-service.ts +7 -5
  222. package/src/lib/server/runtime/queue/core.ts +61 -190
  223. package/src/lib/server/runtime/run-ledger.ts +8 -0
  224. package/src/lib/server/runtime/session-run-manager/drain.ts +2 -2
  225. package/src/lib/server/runtime/session-run-manager/enqueue.ts +6 -0
  226. package/src/lib/server/runtime/session-run-manager/state.ts +4 -0
  227. package/src/lib/server/schedules/schedule-route-service.ts +230 -0
  228. package/src/lib/server/service-result.ts +16 -0
  229. package/src/lib/server/session-note.ts +2 -3
  230. package/src/lib/server/session-reset-policy.ts +4 -3
  231. package/src/lib/server/session-tools/connector.ts +9 -6
  232. package/src/lib/server/session-tools/context-mgmt.ts +58 -9
  233. package/src/lib/server/session-tools/crud.ts +162 -10
  234. package/src/lib/server/session-tools/delegate.ts +1 -1
  235. package/src/lib/server/session-tools/manage-tasks.test.ts +152 -0
  236. package/src/lib/server/session-tools/memory.ts +6 -4
  237. package/src/lib/server/session-tools/session-info.test.ts +56 -0
  238. package/src/lib/server/session-tools/session-info.ts +119 -12
  239. package/src/lib/server/session-tools/skill-runtime.ts +3 -1
  240. package/src/lib/server/session-tools/skills.ts +15 -15
  241. package/src/lib/server/session-tools/subagent.test.ts +115 -1
  242. package/src/lib/server/session-tools/subagent.ts +125 -7
  243. package/src/lib/server/session-tools/team-context.ts +4 -3
  244. package/src/lib/server/session-tools/wallet.ts +0 -58
  245. package/src/lib/server/sessions/session-lineage.ts +55 -0
  246. package/src/lib/server/sessions/session-repository.ts +2 -2
  247. package/src/lib/server/skills/learned-skills.ts +24 -23
  248. package/src/lib/server/skills/runtime-skill-resolver.ts +2 -1
  249. package/src/lib/server/skills/skill-repository.ts +136 -13
  250. package/src/lib/server/skills/skill-suggestions.ts +25 -28
  251. package/src/lib/server/storage-normalization.test.ts +42 -215
  252. package/src/lib/server/storage-normalization.ts +98 -0
  253. package/src/lib/server/storage.ts +19 -0
  254. package/src/lib/server/structured-extract.ts +3 -14
  255. package/src/lib/server/tasks/task-followups.ts +16 -11
  256. package/src/lib/server/tasks/task-result.test.ts +25 -29
  257. package/src/lib/server/tasks/task-result.ts +5 -9
  258. package/src/lib/server/tasks/task-route-service.ts +449 -0
  259. package/src/lib/server/text-normalization.ts +41 -0
  260. package/src/lib/server/tool-planning.ts +6 -42
  261. package/src/lib/server/upload-path.ts +5 -0
  262. package/src/lib/server/working-state/extraction.ts +614 -0
  263. package/src/lib/server/working-state/normalization.ts +866 -0
  264. package/src/lib/server/working-state/prompt.ts +60 -0
  265. package/src/lib/server/working-state/repository.ts +38 -0
  266. package/src/lib/server/working-state/service.test.ts +253 -0
  267. package/src/lib/server/working-state/service.ts +293 -0
  268. package/src/lib/validation/schemas.ts +1 -0
  269. package/src/lib/ws-client.ts +3 -3
  270. package/src/stores/slices/task-slice.ts +1 -4
  271. package/src/stores/use-chatroom-store.ts +2 -2
  272. package/src/types/index.ts +288 -22
  273. package/src/views/settings/section-providers.tsx +2 -2
@@ -7,146 +7,10 @@ import { BottomSheet } from '@/components/shared/bottom-sheet'
7
7
  import { ConfirmDialog } from '@/components/shared/confirm-dialog'
8
8
  import { toast } from 'sonner'
9
9
  import { AgentAvatar } from '@/components/agents/agent-avatar'
10
- import type { Agent, ChatroomRoutingRule } from '@/types'
10
+ import type { Agent } from '@/types'
11
11
  import { CheckIcon } from '@/components/shared/check-icon'
12
12
  import { WORKER_ONLY_PROVIDER_IDS } from '@/lib/provider-sets'
13
13
 
14
- function genRuleId(): string {
15
- return `rule-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
16
- }
17
-
18
- interface RuleFormState {
19
- type: 'keyword' | 'capability'
20
- pattern: string
21
- keywords: string
22
- agentId: string
23
- priority: number
24
- }
25
-
26
- const emptyRuleForm: RuleFormState = {
27
- type: 'keyword',
28
- pattern: '',
29
- keywords: '',
30
- agentId: '',
31
- priority: 10,
32
- }
33
-
34
- function RoutingRuleForm({
35
- rule,
36
- memberAgents,
37
- onSave,
38
- onCancel,
39
- }: {
40
- rule: RuleFormState
41
- memberAgents: Agent[]
42
- onSave: (form: RuleFormState) => void
43
- onCancel: () => void
44
- }) {
45
- const [form, setForm] = useState<RuleFormState>(rule)
46
-
47
- return (
48
- <div className="p-3 rounded-[8px] bg-white/[0.04] border border-white/[0.08] space-y-3">
49
- <div className="flex gap-2">
50
- {(['keyword', 'capability'] as const).map((t) => (
51
- <button
52
- key={t}
53
- type="button"
54
- onClick={() => setForm((f) => ({ ...f, type: t }))}
55
- className={`flex-1 py-1.5 text-[11px] font-600 capitalize rounded-[6px] cursor-pointer transition-all ${
56
- form.type === t
57
- ? 'bg-accent-soft text-accent-bright'
58
- : 'bg-white/[0.04] text-text-3 hover:text-text-2'
59
- }`}
60
- >
61
- {t}
62
- </button>
63
- ))}
64
- </div>
65
-
66
- {form.type === 'keyword' && (
67
- <>
68
- <div>
69
- <label className="block text-[11px] font-600 text-text-3 mb-1">Keywords (comma-separated)</label>
70
- <input
71
- type="text"
72
- value={form.keywords}
73
- onChange={(e) => setForm((f) => ({ ...f, keywords: e.target.value }))}
74
- placeholder="e.g. deploy, devops, infrastructure"
75
- className="w-full px-2.5 py-1.5 rounded-[6px] bg-white/[0.06] border border-white/[0.08] text-[12px] text-text placeholder:text-text-3 focus:outline-none focus:border-accent-bright/40"
76
- />
77
- </div>
78
- <div>
79
- <label className="block text-[11px] font-600 text-text-3 mb-1">Regex pattern (optional)</label>
80
- <input
81
- type="text"
82
- value={form.pattern}
83
- onChange={(e) => setForm((f) => ({ ...f, pattern: e.target.value }))}
84
- placeholder="e.g. deploy|release|ship"
85
- className="w-full px-2.5 py-1.5 rounded-[6px] bg-white/[0.06] border border-white/[0.08] text-[12px] text-text placeholder:text-text-3 focus:outline-none focus:border-accent-bright/40"
86
- />
87
- </div>
88
- </>
89
- )}
90
-
91
- {form.type === 'capability' && (
92
- <div>
93
- <label className="block text-[11px] font-600 text-text-3 mb-1">Capability pattern</label>
94
- <input
95
- type="text"
96
- value={form.pattern}
97
- onChange={(e) => setForm((f) => ({ ...f, pattern: e.target.value }))}
98
- placeholder="e.g. frontend, research, devops"
99
- className="w-full px-2.5 py-1.5 rounded-[6px] bg-white/[0.06] border border-white/[0.08] text-[12px] text-text placeholder:text-text-3 focus:outline-none focus:border-accent-bright/40"
100
- />
101
- </div>
102
- )}
103
-
104
- <div className="flex gap-2">
105
- <div className="flex-1">
106
- <label className="block text-[11px] font-600 text-text-3 mb-1">Route to agent</label>
107
- <select
108
- value={form.agentId}
109
- onChange={(e) => setForm((f) => ({ ...f, agentId: e.target.value }))}
110
- className="w-full px-2.5 py-1.5 rounded-[6px] bg-white/[0.06] border border-white/[0.08] text-[12px] text-text focus:outline-none focus:border-accent-bright/40"
111
- >
112
- <option value="">Select agent...</option>
113
- {memberAgents.map((a) => (
114
- <option key={a.id} value={a.id}>{a.name}</option>
115
- ))}
116
- </select>
117
- </div>
118
- <div className="w-20">
119
- <label className="block text-[11px] font-600 text-text-3 mb-1">Priority</label>
120
- <input
121
- type="number"
122
- value={form.priority}
123
- onChange={(e) => setForm((f) => ({ ...f, priority: parseInt(e.target.value, 10) || 0 }))}
124
- className="w-full px-2.5 py-1.5 rounded-[6px] bg-white/[0.06] border border-white/[0.08] text-[12px] text-text focus:outline-none focus:border-accent-bright/40"
125
- />
126
- </div>
127
- </div>
128
-
129
- <div className="flex gap-2 justify-end">
130
- <button
131
- type="button"
132
- onClick={onCancel}
133
- className="px-3 py-1.5 text-[11px] font-600 text-text-3 hover:text-text-2 cursor-pointer"
134
- >
135
- Cancel
136
- </button>
137
- <button
138
- type="button"
139
- onClick={() => onSave(form)}
140
- disabled={!form.agentId || (form.type === 'keyword' && !form.keywords.trim() && !form.pattern.trim()) || (form.type === 'capability' && !form.pattern.trim())}
141
- className="px-3 py-1.5 text-[11px] font-600 bg-accent-bright text-white rounded-[6px] hover:bg-accent-bright/90 disabled:opacity-50 cursor-pointer"
142
- >
143
- Save Rule
144
- </button>
145
- </div>
146
- </div>
147
- )
148
- }
149
-
150
14
  export function ChatroomSheet() {
151
15
  const open = useChatroomStore((s) => s.chatroomSheetOpen)
152
16
  const editingId = useChatroomStore((s) => s.editingChatroomId)
@@ -163,10 +27,8 @@ export function ChatroomSheet() {
163
27
  const [selectedAgentIds, setSelectedAgentIds] = useState<string[]>([])
164
28
  const [chatMode, setChatMode] = useState<'sequential' | 'parallel'>('sequential')
165
29
  const [autoAddress, setAutoAddress] = useState(false)
166
- const [routingRules, setRoutingRules] = useState<ChatroomRoutingRule[]>([])
30
+ const [routingGuidance, setRoutingGuidance] = useState('')
167
31
  const [saving, setSaving] = useState(false)
168
- const [addingRule, setAddingRule] = useState(false)
169
- const [editingRuleId, setEditingRuleId] = useState<string | null>(null)
170
32
  const [confirmDelete, setConfirmDelete] = useState(false)
171
33
 
172
34
  const editing = editingId ? chatrooms[editingId] : null
@@ -178,17 +40,15 @@ export function ChatroomSheet() {
178
40
  setSelectedAgentIds([...editing.agentIds])
179
41
  setChatMode(editing.chatMode || 'sequential')
180
42
  setAutoAddress(editing.autoAddress || false)
181
- setRoutingRules([...(editing.routingRules || [])])
43
+ setRoutingGuidance(editing.routingGuidance || '')
182
44
  } else {
183
45
  setName('')
184
46
  setDescription('')
185
47
  setSelectedAgentIds([])
186
48
  setChatMode('sequential')
187
49
  setAutoAddress(false)
188
- setRoutingRules([])
50
+ setRoutingGuidance('')
189
51
  }
190
- setAddingRule(false)
191
- setEditingRuleId(null)
192
52
  setConfirmDelete(false)
193
53
  }, [editing, open])
194
54
 
@@ -206,7 +66,7 @@ export function ChatroomSheet() {
206
66
  agentIds: selectedAgentIds,
207
67
  chatMode,
208
68
  autoAddress,
209
- routingRules: routingRules.length > 0 ? routingRules : undefined,
69
+ routingGuidance: routingGuidance.trim() || null,
210
70
  }
211
71
  if (editing) {
212
72
  await updateChatroom(editing.id, payload)
@@ -245,53 +105,10 @@ export function ChatroomSheet() {
245
105
  )
246
106
  }
247
107
 
248
- const handleAddRule = (form: RuleFormState) => {
249
- const rule: ChatroomRoutingRule = {
250
- id: genRuleId(),
251
- type: form.type,
252
- agentId: form.agentId,
253
- priority: form.priority,
254
- ...(form.pattern.trim() ? { pattern: form.pattern.trim() } : {}),
255
- ...(form.type === 'keyword' && form.keywords.trim()
256
- ? { keywords: form.keywords.split(',').map((k) => k.trim()).filter(Boolean) }
257
- : {}),
258
- }
259
- setRoutingRules((prev) => [...prev, rule].sort((a, b) => a.priority - b.priority))
260
- setAddingRule(false)
261
- }
262
-
263
- const handleEditRule = (form: RuleFormState) => {
264
- setRoutingRules((prev) =>
265
- prev.map((r) =>
266
- r.id === editingRuleId
267
- ? {
268
- ...r,
269
- type: form.type,
270
- agentId: form.agentId,
271
- priority: form.priority,
272
- pattern: form.pattern.trim() || undefined,
273
- keywords:
274
- form.type === 'keyword' && form.keywords.trim()
275
- ? form.keywords.split(',').map((k) => k.trim()).filter(Boolean)
276
- : undefined,
277
- }
278
- : r,
279
- ).sort((a, b) => a.priority - b.priority),
280
- )
281
- setEditingRuleId(null)
282
- }
283
-
284
- const removeRule = (ruleId: string) => {
285
- setRoutingRules((prev) => prev.filter((r) => r.id !== ruleId))
286
- }
287
-
288
108
  const agentList = Object.values(agents).filter(
289
109
  (a: Agent) => !a.trashedAt && !WORKER_ONLY_PROVIDER_IDS.has(a.provider)
290
110
  ) as Agent[]
291
111
 
292
- const memberAgents = agentList.filter((a) => selectedAgentIds.includes(a.id))
293
- const sortedRules = [...routingRules].sort((a, b) => a.priority - b.priority)
294
-
295
112
  return (
296
113
  <BottomSheet open={open} onClose={() => setChatroomSheetOpen(false)}>
297
114
  <div className="p-6 max-w-[560px] mx-auto">
@@ -350,7 +167,7 @@ export function ChatroomSheet() {
350
167
  <div>
351
168
  <button
352
169
  type="button"
353
- onClick={() => setAutoAddress((v) => !v)}
170
+ onClick={() => setAutoAddress((value) => !value)}
354
171
  className="w-full flex items-center gap-2.5 px-3 py-2.5 rounded-[8px] border border-white/[0.08] bg-white/[0.03] cursor-pointer transition-all hover:bg-white/[0.05]"
355
172
  >
356
173
  <div className={`w-8 h-[18px] rounded-full transition-all relative ${autoAddress ? 'bg-accent-bright' : 'bg-white/[0.12]'}`}>
@@ -361,7 +178,7 @@ export function ChatroomSheet() {
361
178
  <p className="text-[11px] text-text-3 mt-0.5">
362
179
  {autoAddress
363
180
  ? 'Every message is sent to all agents, no @mention needed'
364
- : 'Only agents you @mention will respond'}
181
+ : 'Only agents you @mention respond unless routing guidance selects someone'}
365
182
  </p>
366
183
  </div>
367
184
  </button>
@@ -405,95 +222,18 @@ export function ChatroomSheet() {
405
222
  )}
406
223
  </div>
407
224
 
408
- {/* Routing Rules */}
409
225
  <div>
410
- <label className="block text-[12px] font-600 text-text-2 mb-1.5">
411
- Routing Rules ({sortedRules.length})
412
- </label>
226
+ <label className="block text-[12px] font-600 text-text-2 mb-1.5">Routing Guidance</label>
413
227
  <p className="text-[11px] text-text-3 mb-2">
414
- Route messages to specific agents based on keywords or capabilities. Evaluated before auto-address.
228
+ Optional. Used only when there is no explicit @mention and auto-address is off. Describe which kinds of messages should go to which members.
415
229
  </p>
416
-
417
- {sortedRules.length > 0 && (
418
- <div className="space-y-2 mb-2">
419
- {sortedRules.map((rule) => {
420
- const agent = agents[rule.agentId]
421
- if (editingRuleId === rule.id) {
422
- return (
423
- <RoutingRuleForm
424
- key={rule.id}
425
- rule={{
426
- type: rule.type,
427
- pattern: rule.pattern || '',
428
- keywords: rule.keywords?.join(', ') || '',
429
- agentId: rule.agentId,
430
- priority: rule.priority,
431
- }}
432
- memberAgents={memberAgents}
433
- onSave={handleEditRule}
434
- onCancel={() => setEditingRuleId(null)}
435
- />
436
- )
437
- }
438
- return (
439
- <div
440
- key={rule.id}
441
- className="flex items-center gap-2 px-3 py-2 rounded-[8px] bg-white/[0.04] border border-white/[0.08]"
442
- >
443
- <span className="text-[10px] font-700 text-text-3 bg-white/[0.06] px-1.5 py-0.5 rounded">
444
- P{rule.priority}
445
- </span>
446
- <span className="text-[10px] font-600 text-accent-bright/70 uppercase">
447
- {rule.type}
448
- </span>
449
- <span className="text-[12px] text-text-2 flex-1 truncate">
450
- {rule.type === 'keyword'
451
- ? (rule.keywords?.join(', ') || rule.pattern || '(no match)')
452
- : (rule.pattern || '(no pattern)')}
453
- </span>
454
- <span className="text-[11px] text-text-3 truncate max-w-[100px]">
455
- {agent?.name || 'Unknown'}
456
- </span>
457
- <button
458
- type="button"
459
- onClick={() => setEditingRuleId(rule.id)}
460
- className="text-[11px] text-text-3 hover:text-text-2 cursor-pointer px-1"
461
- >
462
- Edit
463
- </button>
464
- <button
465
- type="button"
466
- onClick={() => removeRule(rule.id)}
467
- className="text-[11px] text-red-400 hover:text-red-300 cursor-pointer px-1"
468
- >
469
- Remove
470
- </button>
471
- </div>
472
- )
473
- })}
474
- </div>
475
- )}
476
-
477
- {addingRule ? (
478
- <RoutingRuleForm
479
- rule={emptyRuleForm}
480
- memberAgents={memberAgents}
481
- onSave={handleAddRule}
482
- onCancel={() => setAddingRule(false)}
483
- />
484
- ) : (
485
- <button
486
- type="button"
487
- onClick={() => setAddingRule(true)}
488
- disabled={memberAgents.length === 0}
489
- className="w-full py-2 rounded-[8px] border border-dashed border-white/[0.12] text-[12px] font-600 text-text-3 hover:text-text-2 hover:border-white/[0.2] cursor-pointer transition-all disabled:opacity-40 disabled:cursor-not-allowed"
490
- >
491
- + Add Rule
492
- </button>
493
- )}
494
- {memberAgents.length === 0 && (
495
- <p className="text-[11px] text-text-3 mt-1">Add members first to create routing rules.</p>
496
- )}
230
+ <textarea
231
+ value={routingGuidance}
232
+ onChange={(e) => setRoutingGuidance(e.target.value)}
233
+ placeholder={'Examples:\nRoute deployment issues to Ops.\nPrefer Maya for design reviews and UI polish.\nSend pricing or market-analysis requests to Research.'}
234
+ rows={6}
235
+ className="w-full px-3 py-2 rounded-[8px] bg-white/[0.06] border border-white/[0.08] text-[13px] text-text placeholder:text-text-3 focus:outline-none focus:border-accent-bright/40 resize-y min-h-[132px]"
236
+ />
497
237
  </div>
498
238
  </div>
499
239
 
@@ -2,10 +2,6 @@
2
2
 
3
3
  import { useCallback, useEffect, useMemo, useState } from 'react'
4
4
  import { useAppStore } from '@/stores/use-app-store'
5
- import { useChatroomStore } from '@/stores/use-chatroom-store'
6
- import { useWs } from '@/hooks/use-ws'
7
- import { useMountedRef } from '@/hooks/use-mounted-ref'
8
- import { api } from '@/lib/app/api-client'
9
5
  import type { Connector } from '@/types'
10
6
  import {
11
7
  ConnectorPlatformIcon,
@@ -16,6 +12,9 @@ import {
16
12
  import { AgentAvatar } from '@/components/agents/agent-avatar'
17
13
  import { PageLoader } from '@/components/ui/page-loader'
18
14
  import { StatusDot } from '@/components/ui/status-dot'
15
+ import { useConnectorsQuery, useConnectorActionMutation } from '@/features/connectors/queries'
16
+ import { useAgentsQuery } from '@/features/agents/queries'
17
+ import { useChatroomsQuery } from '@/features/chatrooms/queries'
19
18
 
20
19
  function relativeTime(ts: number): string {
21
20
  const diff = Date.now() - ts
@@ -45,33 +44,24 @@ function getConnectorGroup(connector: Connector): ConnectorGroup {
45
44
  }
46
45
 
47
46
  export function ConnectorList({ inSidebar }: { inSidebar?: boolean }) {
48
- const connectors = useAppStore((s) => s.connectors)
49
- const loadConnectors = useAppStore((s) => s.loadConnectors)
50
47
  const setConnectorSheetOpen = useAppStore((s) => s.setConnectorSheetOpen)
51
48
  const setEditingConnectorId = useAppStore((s) => s.setEditingConnectorId)
52
- const agents = useAppStore((s) => s.agents)
53
- const loadAgents = useAppStore((s) => s.loadAgents)
54
- const chatrooms = useChatroomStore((s) => s.chatrooms)
55
- const loadChatrooms = useChatroomStore((s) => s.loadChatrooms)
49
+ const connectorsQuery = useConnectorsQuery()
50
+ const agentsQuery = useAgentsQuery()
51
+ const chatroomsQuery = useChatroomsQuery()
52
+ const connectorActionMutation = useConnectorActionMutation()
53
+ const connectors = useMemo(() => connectorsQuery.data ?? {}, [connectorsQuery.data])
54
+ const agents = agentsQuery.data ?? {}
55
+ const chatrooms = chatroomsQuery.data ?? {}
56
56
  const [toggling, setToggling] = useState<string | null>(null)
57
57
  const [reconnecting, setReconnecting] = useState<string | null>(null)
58
- const [loaded, setLoaded] = useState(false)
59
58
  const [error, setError] = useState<string | null>(null)
60
59
  const [groupFilter, setGroupFilter] = useState<'all' | ConnectorGroup>('all')
61
- const mountedRef = useMountedRef()
62
60
  const openConnector = useCallback((id: string | null) => {
63
61
  setEditingConnectorId(id)
64
62
  setConnectorSheetOpen(true)
65
63
  }, [setEditingConnectorId, setConnectorSheetOpen])
66
64
 
67
- const refresh = useCallback(async () => {
68
- await Promise.all([loadConnectors(), loadAgents(), loadChatrooms()])
69
- if (mountedRef.current) setLoaded(true)
70
- }, [loadConnectors, loadAgents, loadChatrooms, mountedRef])
71
-
72
- useEffect(() => { void refresh() }, [refresh])
73
- useWs('connectors', loadConnectors, 15_000)
74
-
75
65
  // Auto-clear error after 5s
76
66
  useEffect(() => {
77
67
  if (error) { const t = setTimeout(() => setError(null), 5000); return () => clearTimeout(t) }
@@ -80,38 +70,34 @@ export function ConnectorList({ inSidebar }: { inSidebar?: boolean }) {
80
70
  const handleToggle = async (e: React.MouseEvent, c: Connector) => {
81
71
  e.stopPropagation()
82
72
  const action = c.status === 'running' ? 'stop' : 'start'
83
- if (mountedRef.current) {
84
- setToggling(c.id)
85
- setError(null)
86
- }
73
+ setToggling(c.id)
74
+ setError(null)
87
75
  try {
88
- await api('PUT', `/connectors/${c.id}`, { action })
89
- await refresh()
76
+ await connectorActionMutation.mutateAsync({ id: c.id, action })
90
77
  } catch (err: unknown) {
91
78
  const msg = err instanceof Error && err.message ? err.message : `Failed to ${action}`
92
- if (mountedRef.current) setError(msg)
93
- await refresh()
79
+ setError(msg)
94
80
  } finally {
95
- if (mountedRef.current) setToggling(null)
81
+ setToggling(null)
96
82
  }
97
83
  }
98
84
 
99
85
  const handleReconnect = async (e: React.MouseEvent, c: Connector) => {
100
86
  e.stopPropagation()
101
- if (mountedRef.current) {
102
- setReconnecting(c.id)
103
- setError(null)
104
- }
87
+ setReconnecting(c.id)
88
+ setError(null)
105
89
  try {
106
- try { await api('PUT', `/connectors/${c.id}`, { action: 'stop' }) } catch { /* may already be stopped */ }
107
- await api('PUT', `/connectors/${c.id}`, { action: 'start' })
108
- await refresh()
90
+ try {
91
+ await connectorActionMutation.mutateAsync({ id: c.id, action: 'stop' })
92
+ } catch {
93
+ // Connector may already be stopped.
94
+ }
95
+ await connectorActionMutation.mutateAsync({ id: c.id, action: 'start' })
109
96
  } catch (err: unknown) {
110
97
  const msg = err instanceof Error && err.message ? err.message : 'Failed to reconnect'
111
- if (mountedRef.current) setError(msg)
112
- await refresh()
98
+ setError(msg)
113
99
  } finally {
114
- if (mountedRef.current) setReconnecting(null)
100
+ setReconnecting(null)
115
101
  }
116
102
  }
117
103
 
@@ -158,7 +144,7 @@ export function ConnectorList({ inSidebar }: { inSidebar?: boolean }) {
158
144
  },
159
145
  }
160
146
 
161
- if (!loaded) {
147
+ if (connectorsQuery.isPending || agentsQuery.isPending || chatroomsQuery.isPending) {
162
148
  return <PageLoader label="Loading connectors..." />
163
149
  }
164
150