@swarmclawai/swarmclaw 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. package/README.md +577 -0
  2. package/bin/server-cmd.js +359 -0
  3. package/bin/swarmclaw.js +29 -0
  4. package/bin/swarmclaw.mjs +1504 -0
  5. package/next.config.ts +33 -0
  6. package/package.json +112 -0
  7. package/postcss.config.mjs +7 -0
  8. package/public/branding/swarmclaw-org-avatar.png +0 -0
  9. package/public/branding/swarmclaw-org-avatar.svg +58 -0
  10. package/public/file.svg +1 -0
  11. package/public/globe.svg +1 -0
  12. package/public/next.svg +1 -0
  13. package/public/screenshots/agents.png +0 -0
  14. package/public/screenshots/connectors.png +0 -0
  15. package/public/screenshots/dashboard.png +0 -0
  16. package/public/screenshots/new-session-openclaw.png +0 -0
  17. package/public/screenshots/providers.png +0 -0
  18. package/public/screenshots/schedules.png +0 -0
  19. package/public/screenshots/tasks.png +0 -0
  20. package/public/vercel.svg +1 -0
  21. package/public/window.svg +1 -0
  22. package/src/app/api/agents/[id]/route.ts +30 -0
  23. package/src/app/api/agents/[id]/thread/route.ts +66 -0
  24. package/src/app/api/agents/generate/route.ts +42 -0
  25. package/src/app/api/agents/route.ts +33 -0
  26. package/src/app/api/auth/route.ts +25 -0
  27. package/src/app/api/claude-skills/route.ts +42 -0
  28. package/src/app/api/clawhub/install/route.ts +39 -0
  29. package/src/app/api/clawhub/search/route.ts +11 -0
  30. package/src/app/api/connectors/[id]/route.ts +79 -0
  31. package/src/app/api/connectors/route.ts +60 -0
  32. package/src/app/api/credentials/[id]/route.ts +14 -0
  33. package/src/app/api/credentials/route.ts +31 -0
  34. package/src/app/api/daemon/health-check/route.ts +11 -0
  35. package/src/app/api/daemon/route.ts +22 -0
  36. package/src/app/api/dirs/pick/route.ts +60 -0
  37. package/src/app/api/dirs/route.ts +29 -0
  38. package/src/app/api/documents/[id]/route.ts +47 -0
  39. package/src/app/api/documents/route.ts +93 -0
  40. package/src/app/api/files/serve/route.ts +69 -0
  41. package/src/app/api/generate/info/route.ts +12 -0
  42. package/src/app/api/generate/route.ts +106 -0
  43. package/src/app/api/ip/route.ts +6 -0
  44. package/src/app/api/knowledge/[id]/route.ts +61 -0
  45. package/src/app/api/knowledge/route.ts +48 -0
  46. package/src/app/api/knowledge/upload/route.ts +86 -0
  47. package/src/app/api/logs/route.ts +65 -0
  48. package/src/app/api/mcp-servers/[id]/route.ts +32 -0
  49. package/src/app/api/mcp-servers/[id]/test/route.ts +23 -0
  50. package/src/app/api/mcp-servers/[id]/tools/route.ts +32 -0
  51. package/src/app/api/mcp-servers/route.ts +27 -0
  52. package/src/app/api/memory/[id]/route.ts +126 -0
  53. package/src/app/api/memory/maintenance/route.ts +63 -0
  54. package/src/app/api/memory/route.ts +111 -0
  55. package/src/app/api/memory-images/[filename]/route.ts +36 -0
  56. package/src/app/api/orchestrator/run/route.ts +43 -0
  57. package/src/app/api/plugins/install/route.ts +58 -0
  58. package/src/app/api/plugins/marketplace/route.ts +33 -0
  59. package/src/app/api/plugins/route.ts +21 -0
  60. package/src/app/api/preview-server/route.ts +339 -0
  61. package/src/app/api/providers/[id]/models/route.ts +29 -0
  62. package/src/app/api/providers/[id]/route.ts +34 -0
  63. package/src/app/api/providers/configs/route.ts +7 -0
  64. package/src/app/api/providers/ollama/route.ts +30 -0
  65. package/src/app/api/providers/openclaw/health/route.ts +23 -0
  66. package/src/app/api/providers/route.ts +28 -0
  67. package/src/app/api/runs/[id]/route.ts +9 -0
  68. package/src/app/api/runs/route.ts +13 -0
  69. package/src/app/api/schedules/[id]/route.ts +28 -0
  70. package/src/app/api/schedules/[id]/run/route.ts +104 -0
  71. package/src/app/api/schedules/route.ts +78 -0
  72. package/src/app/api/secrets/[id]/route.ts +29 -0
  73. package/src/app/api/secrets/route.ts +42 -0
  74. package/src/app/api/sessions/[id]/browser/route.ts +13 -0
  75. package/src/app/api/sessions/[id]/chat/route.ts +96 -0
  76. package/src/app/api/sessions/[id]/clear/route.ts +19 -0
  77. package/src/app/api/sessions/[id]/deploy/route.ts +34 -0
  78. package/src/app/api/sessions/[id]/devserver/route.ts +69 -0
  79. package/src/app/api/sessions/[id]/mailbox/route.ts +70 -0
  80. package/src/app/api/sessions/[id]/main-loop/route.ts +94 -0
  81. package/src/app/api/sessions/[id]/messages/route.ts +9 -0
  82. package/src/app/api/sessions/[id]/retry/route.ts +28 -0
  83. package/src/app/api/sessions/[id]/route.ts +103 -0
  84. package/src/app/api/sessions/[id]/stop/route.ts +13 -0
  85. package/src/app/api/sessions/heartbeat/route.ts +26 -0
  86. package/src/app/api/sessions/route.ts +85 -0
  87. package/src/app/api/settings/route.ts +58 -0
  88. package/src/app/api/setup/check-provider/route.ts +326 -0
  89. package/src/app/api/setup/doctor/route.ts +250 -0
  90. package/src/app/api/skills/[id]/route.ts +40 -0
  91. package/src/app/api/skills/import/route.ts +69 -0
  92. package/src/app/api/skills/route.ts +28 -0
  93. package/src/app/api/tasks/[id]/route.ts +102 -0
  94. package/src/app/api/tasks/route.ts +115 -0
  95. package/src/app/api/tts/route.ts +40 -0
  96. package/src/app/api/upload/route.ts +18 -0
  97. package/src/app/api/uploads/[filename]/route.ts +59 -0
  98. package/src/app/api/usage/route.ts +35 -0
  99. package/src/app/api/version/route.ts +81 -0
  100. package/src/app/api/version/update/route.ts +95 -0
  101. package/src/app/api/webhooks/[id]/history/route.ts +13 -0
  102. package/src/app/api/webhooks/[id]/route.ts +204 -0
  103. package/src/app/api/webhooks/route.ts +37 -0
  104. package/src/app/favicon.ico +0 -0
  105. package/src/app/globals.css +370 -0
  106. package/src/app/layout.tsx +52 -0
  107. package/src/app/page.tsx +172 -0
  108. package/src/cli/index.js +1232 -0
  109. package/src/cli/index.test.js +281 -0
  110. package/src/cli/index.ts +1158 -0
  111. package/src/cli/spec.js +284 -0
  112. package/src/components/agents/agent-card.tsx +219 -0
  113. package/src/components/agents/agent-chat-list.tsx +165 -0
  114. package/src/components/agents/agent-list.tsx +110 -0
  115. package/src/components/agents/agent-sheet.tsx +1220 -0
  116. package/src/components/auth/access-key-gate.tsx +248 -0
  117. package/src/components/auth/setup-wizard.tsx +940 -0
  118. package/src/components/auth/user-picker.tsx +88 -0
  119. package/src/components/chat/chat-area.tsx +406 -0
  120. package/src/components/chat/chat-header.tsx +491 -0
  121. package/src/components/chat/chat-tool-toggles.tsx +161 -0
  122. package/src/components/chat/code-block.tsx +146 -0
  123. package/src/components/chat/dev-server-bar.tsx +39 -0
  124. package/src/components/chat/message-bubble.tsx +486 -0
  125. package/src/components/chat/message-list.tsx +299 -0
  126. package/src/components/chat/session-debug-panel.tsx +196 -0
  127. package/src/components/chat/streaming-bubble.tsx +85 -0
  128. package/src/components/chat/thinking-indicator.tsx +26 -0
  129. package/src/components/chat/tool-call-bubble.tsx +438 -0
  130. package/src/components/chat/tool-request-banner.tsx +103 -0
  131. package/src/components/connectors/connector-list.tsx +196 -0
  132. package/src/components/connectors/connector-sheet.tsx +804 -0
  133. package/src/components/input/chat-input.tsx +235 -0
  134. package/src/components/knowledge/knowledge-list.tsx +206 -0
  135. package/src/components/knowledge/knowledge-sheet.tsx +316 -0
  136. package/src/components/layout/app-layout.tsx +1016 -0
  137. package/src/components/layout/daemon-indicator.tsx +56 -0
  138. package/src/components/layout/mobile-header.tsx +31 -0
  139. package/src/components/layout/network-banner.tsx +17 -0
  140. package/src/components/layout/update-banner.tsx +130 -0
  141. package/src/components/logs/log-list.tsx +358 -0
  142. package/src/components/mcp-servers/mcp-server-list.tsx +122 -0
  143. package/src/components/mcp-servers/mcp-server-sheet.tsx +243 -0
  144. package/src/components/memory/memory-card.tsx +63 -0
  145. package/src/components/memory/memory-detail.tsx +339 -0
  146. package/src/components/memory/memory-list.tsx +198 -0
  147. package/src/components/memory/memory-sheet.tsx +70 -0
  148. package/src/components/plugins/plugin-list.tsx +60 -0
  149. package/src/components/plugins/plugin-sheet.tsx +311 -0
  150. package/src/components/providers/provider-list.tsx +96 -0
  151. package/src/components/providers/provider-sheet.tsx +542 -0
  152. package/src/components/runs/run-list.tsx +231 -0
  153. package/src/components/schedules/schedule-card.tsx +63 -0
  154. package/src/components/schedules/schedule-list.tsx +76 -0
  155. package/src/components/schedules/schedule-sheet.tsx +336 -0
  156. package/src/components/secrets/secret-sheet.tsx +180 -0
  157. package/src/components/secrets/secrets-list.tsx +91 -0
  158. package/src/components/sessions/new-session-sheet.tsx +478 -0
  159. package/src/components/sessions/session-card.tsx +144 -0
  160. package/src/components/sessions/session-list.tsx +202 -0
  161. package/src/components/shared/ai-gen-block.tsx +77 -0
  162. package/src/components/shared/avatar.tsx +48 -0
  163. package/src/components/shared/bottom-sheet.tsx +30 -0
  164. package/src/components/shared/confirm-dialog.tsx +47 -0
  165. package/src/components/shared/connector-platform-icon.tsx +113 -0
  166. package/src/components/shared/dir-browser.tsx +285 -0
  167. package/src/components/shared/dropdown.tsx +55 -0
  168. package/src/components/shared/icon-button.tsx +25 -0
  169. package/src/components/shared/settings/plugin-manager.tsx +207 -0
  170. package/src/components/shared/settings/section-capability-policy.tsx +93 -0
  171. package/src/components/shared/settings/section-embedding.tsx +99 -0
  172. package/src/components/shared/settings/section-heartbeat.tsx +168 -0
  173. package/src/components/shared/settings/section-memory.tsx +77 -0
  174. package/src/components/shared/settings/section-orchestrator.tsx +108 -0
  175. package/src/components/shared/settings/section-providers.tsx +181 -0
  176. package/src/components/shared/settings/section-runtime-loop.tsx +183 -0
  177. package/src/components/shared/settings/section-secrets.tsx +132 -0
  178. package/src/components/shared/settings/section-user-preferences.tsx +24 -0
  179. package/src/components/shared/settings/section-voice.tsx +53 -0
  180. package/src/components/shared/settings/settings-sheet.tsx +88 -0
  181. package/src/components/shared/settings/types.ts +7 -0
  182. package/src/components/shared/settings/utils.ts +13 -0
  183. package/src/components/shared/settings-sheet.tsx +1 -0
  184. package/src/components/shared/skeleton.tsx +19 -0
  185. package/src/components/shared/usage-badge.tsx +28 -0
  186. package/src/components/skills/clawhub-browser.tsx +225 -0
  187. package/src/components/skills/skill-list.tsx +70 -0
  188. package/src/components/skills/skill-sheet.tsx +254 -0
  189. package/src/components/tasks/task-board.tsx +96 -0
  190. package/src/components/tasks/task-card.tsx +179 -0
  191. package/src/components/tasks/task-column.tsx +73 -0
  192. package/src/components/tasks/task-list.tsx +118 -0
  193. package/src/components/tasks/task-sheet.tsx +415 -0
  194. package/src/components/ui/avatar.tsx +109 -0
  195. package/src/components/ui/badge.tsx +48 -0
  196. package/src/components/ui/button.tsx +64 -0
  197. package/src/components/ui/card.tsx +92 -0
  198. package/src/components/ui/dialog.tsx +158 -0
  199. package/src/components/ui/dropdown-menu.tsx +257 -0
  200. package/src/components/ui/input.tsx +21 -0
  201. package/src/components/ui/scroll-area.tsx +58 -0
  202. package/src/components/ui/select.tsx +190 -0
  203. package/src/components/ui/separator.tsx +28 -0
  204. package/src/components/ui/sheet.tsx +143 -0
  205. package/src/components/ui/sonner.tsx +22 -0
  206. package/src/components/ui/textarea.tsx +18 -0
  207. package/src/components/ui/tooltip.tsx +56 -0
  208. package/src/components/usage/usage-list.tsx +105 -0
  209. package/src/components/webhooks/webhook-list.tsx +166 -0
  210. package/src/components/webhooks/webhook-sheet.tsx +402 -0
  211. package/src/hooks/use-auto-resize.ts +20 -0
  212. package/src/hooks/use-media-query.ts +21 -0
  213. package/src/hooks/use-speech-recognition.ts +83 -0
  214. package/src/instrumentation.ts +8 -0
  215. package/src/lib/agents.ts +13 -0
  216. package/src/lib/api-client.ts +100 -0
  217. package/src/lib/chat.ts +60 -0
  218. package/src/lib/memory.ts +42 -0
  219. package/src/lib/openclaw-endpoint.test.ts +48 -0
  220. package/src/lib/openclaw-endpoint.ts +67 -0
  221. package/src/lib/provider-config.ts +13 -0
  222. package/src/lib/providers/anthropic.ts +135 -0
  223. package/src/lib/providers/claude-cli.ts +202 -0
  224. package/src/lib/providers/codex-cli.ts +260 -0
  225. package/src/lib/providers/index.ts +351 -0
  226. package/src/lib/providers/ollama.ts +131 -0
  227. package/src/lib/providers/openai.ts +164 -0
  228. package/src/lib/providers/openclaw.ts +330 -0
  229. package/src/lib/providers/opencode-cli.ts +164 -0
  230. package/src/lib/runtime-loop.ts +15 -0
  231. package/src/lib/schedule-dedupe.test.ts +84 -0
  232. package/src/lib/schedule-dedupe.ts +174 -0
  233. package/src/lib/schedule-name.ts +62 -0
  234. package/src/lib/schedules.ts +16 -0
  235. package/src/lib/server/agent-registry.ts +70 -0
  236. package/src/lib/server/api-routes.test.ts +362 -0
  237. package/src/lib/server/autonomy-contract.ts +200 -0
  238. package/src/lib/server/build-llm.ts +155 -0
  239. package/src/lib/server/capability-router.test.ts +21 -0
  240. package/src/lib/server/capability-router.ts +172 -0
  241. package/src/lib/server/chat-execution.ts +894 -0
  242. package/src/lib/server/clawhub-client.test.ts +161 -0
  243. package/src/lib/server/clawhub-client.ts +26 -0
  244. package/src/lib/server/connectors/connector-routing.test.ts +243 -0
  245. package/src/lib/server/connectors/discord.ts +116 -0
  246. package/src/lib/server/connectors/googlechat.ts +66 -0
  247. package/src/lib/server/connectors/manager.ts +559 -0
  248. package/src/lib/server/connectors/matrix.ts +78 -0
  249. package/src/lib/server/connectors/media.ts +149 -0
  250. package/src/lib/server/connectors/openclaw.test.ts +375 -0
  251. package/src/lib/server/connectors/openclaw.ts +1132 -0
  252. package/src/lib/server/connectors/signal.ts +183 -0
  253. package/src/lib/server/connectors/slack.ts +258 -0
  254. package/src/lib/server/connectors/teams.ts +94 -0
  255. package/src/lib/server/connectors/telegram.ts +221 -0
  256. package/src/lib/server/connectors/types.ts +62 -0
  257. package/src/lib/server/connectors/whatsapp.ts +349 -0
  258. package/src/lib/server/context-manager.ts +232 -0
  259. package/src/lib/server/cost.ts +31 -0
  260. package/src/lib/server/daemon-state.ts +354 -0
  261. package/src/lib/server/data-dir.ts +3 -0
  262. package/src/lib/server/embeddings.ts +111 -0
  263. package/src/lib/server/execution-log.ts +257 -0
  264. package/src/lib/server/gateway/protocol.test.ts +54 -0
  265. package/src/lib/server/gateway/protocol.ts +114 -0
  266. package/src/lib/server/heartbeat-service.ts +366 -0
  267. package/src/lib/server/knowledge-db.test.ts +441 -0
  268. package/src/lib/server/logger.ts +47 -0
  269. package/src/lib/server/main-agent-loop.ts +1017 -0
  270. package/src/lib/server/mcp-client.test.ts +342 -0
  271. package/src/lib/server/mcp-client.ts +130 -0
  272. package/src/lib/server/memory-db.ts +1078 -0
  273. package/src/lib/server/memory-graph.test.ts +153 -0
  274. package/src/lib/server/memory-graph.ts +138 -0
  275. package/src/lib/server/openclaw-health.ts +245 -0
  276. package/src/lib/server/orchestrator-lg.ts +431 -0
  277. package/src/lib/server/orchestrator.ts +364 -0
  278. package/src/lib/server/playwright-proxy.mjs +70 -0
  279. package/src/lib/server/plugins.ts +229 -0
  280. package/src/lib/server/process-manager.ts +327 -0
  281. package/src/lib/server/provider-health.ts +113 -0
  282. package/src/lib/server/queue.ts +859 -0
  283. package/src/lib/server/runtime-settings.ts +119 -0
  284. package/src/lib/server/scheduler.ts +196 -0
  285. package/src/lib/server/session-mailbox.ts +129 -0
  286. package/src/lib/server/session-run-manager.ts +512 -0
  287. package/src/lib/server/session-tools/connector.ts +124 -0
  288. package/src/lib/server/session-tools/context-mgmt.ts +103 -0
  289. package/src/lib/server/session-tools/context.ts +114 -0
  290. package/src/lib/server/session-tools/crud.ts +673 -0
  291. package/src/lib/server/session-tools/delegate.ts +708 -0
  292. package/src/lib/server/session-tools/file.ts +264 -0
  293. package/src/lib/server/session-tools/index.ts +164 -0
  294. package/src/lib/server/session-tools/memory.ts +230 -0
  295. package/src/lib/server/session-tools/session-info.ts +422 -0
  296. package/src/lib/server/session-tools/session-tools-wiring.test.ts +166 -0
  297. package/src/lib/server/session-tools/shell.ts +171 -0
  298. package/src/lib/server/session-tools/web.ts +408 -0
  299. package/src/lib/server/session-tools.ts +9 -0
  300. package/src/lib/server/skills-normalize.ts +130 -0
  301. package/src/lib/server/storage-mcp.test.ts +161 -0
  302. package/src/lib/server/storage.ts +670 -0
  303. package/src/lib/server/stream-agent-chat.ts +571 -0
  304. package/src/lib/server/task-reports.ts +122 -0
  305. package/src/lib/server/task-result.ts +161 -0
  306. package/src/lib/server/task-validation.test.ts +27 -0
  307. package/src/lib/server/task-validation.ts +90 -0
  308. package/src/lib/server/tool-capability-policy.test.ts +58 -0
  309. package/src/lib/server/tool-capability-policy.ts +262 -0
  310. package/src/lib/sessions.ts +68 -0
  311. package/src/lib/tasks.ts +20 -0
  312. package/src/lib/tts.ts +42 -0
  313. package/src/lib/upload.ts +10 -0
  314. package/src/lib/utils.ts +6 -0
  315. package/src/proxy.ts +43 -0
  316. package/src/stores/use-app-store.ts +468 -0
  317. package/src/stores/use-chat-store.ts +323 -0
  318. package/src/types/index.ts +621 -0
  319. package/tsconfig.json +34 -0
@@ -0,0 +1,262 @@
1
+ import type { AppSettings } from '@/types'
2
+
3
+ export type CapabilityPolicyMode = 'permissive' | 'balanced' | 'strict'
4
+
5
+ export interface CapabilityPolicyBlock {
6
+ tool: string
7
+ reason: string
8
+ source: 'safety' | 'policy'
9
+ }
10
+
11
+ export interface SessionToolPolicyDecision {
12
+ mode: CapabilityPolicyMode
13
+ requestedTools: string[]
14
+ enabledTools: string[]
15
+ blockedTools: CapabilityPolicyBlock[]
16
+ }
17
+
18
+ type CapabilityCategory =
19
+ | 'filesystem'
20
+ | 'execution'
21
+ | 'network'
22
+ | 'browser'
23
+ | 'memory'
24
+ | 'delegation'
25
+ | 'platform'
26
+ | 'outbound'
27
+
28
+ interface ToolDescriptor {
29
+ categories: CapabilityCategory[]
30
+ concreteTools: string[]
31
+ destructive?: boolean
32
+ }
33
+
34
+ const TOOL_DESCRIPTORS: Record<string, ToolDescriptor> = {
35
+ shell: { categories: ['execution'], concreteTools: ['execute_command'] },
36
+ process: { categories: ['execution'], concreteTools: ['process_tool'] },
37
+ files: { categories: ['filesystem'], concreteTools: ['read_file', 'write_file', 'list_files', 'send_file'] },
38
+ read_file: { categories: ['filesystem'], concreteTools: ['read_file'] },
39
+ write_file: { categories: ['filesystem'], concreteTools: ['write_file'] },
40
+ list_files: { categories: ['filesystem'], concreteTools: ['list_files'] },
41
+ send_file: { categories: ['filesystem'], concreteTools: ['send_file'] },
42
+ copy_file: { categories: ['filesystem'], concreteTools: ['copy_file'] },
43
+ move_file: { categories: ['filesystem'], concreteTools: ['move_file'] },
44
+ edit_file: { categories: ['filesystem'], concreteTools: ['edit_file'] },
45
+ delete_file: { categories: ['filesystem'], concreteTools: ['delete_file'], destructive: true },
46
+ web_search: { categories: ['network'], concreteTools: ['web_search'] },
47
+ web_fetch: { categories: ['network'], concreteTools: ['web_fetch'] },
48
+ browser: { categories: ['browser', 'network'], concreteTools: ['browser'] },
49
+ claude_code: { categories: ['delegation', 'execution'], concreteTools: ['delegate_to_claude_code'] },
50
+ codex_cli: { categories: ['delegation', 'execution'], concreteTools: ['delegate_to_codex_cli'] },
51
+ opencode_cli: { categories: ['delegation', 'execution'], concreteTools: ['delegate_to_opencode_cli'] },
52
+ memory: { categories: ['memory'], concreteTools: ['memory_tool', 'context_status', 'context_summarize'] },
53
+ manage_agents: { categories: ['platform'], concreteTools: ['manage_agents'] },
54
+ manage_tasks: { categories: ['platform'], concreteTools: ['manage_tasks'] },
55
+ manage_schedules: { categories: ['platform'], concreteTools: ['manage_schedules'] },
56
+ manage_skills: { categories: ['platform'], concreteTools: ['manage_skills'] },
57
+ manage_documents: { categories: ['platform'], concreteTools: ['manage_documents'] },
58
+ manage_webhooks: { categories: ['platform', 'network'], concreteTools: ['manage_webhooks'] },
59
+ manage_connectors: { categories: ['platform', 'outbound'], concreteTools: ['manage_connectors', 'connector_message_tool'] },
60
+ manage_sessions: { categories: ['platform'], concreteTools: ['manage_sessions', 'sessions_tool', 'search_history_tool', 'whoami_tool'] },
61
+ manage_secrets: { categories: ['platform'], concreteTools: ['manage_secrets'] },
62
+ }
63
+
64
+ const CONCRETE_TOOL_TO_SESSION_TOOL = new Map<string, string>()
65
+ for (const [sessionTool, descriptor] of Object.entries(TOOL_DESCRIPTORS)) {
66
+ for (const concreteName of descriptor.concreteTools) {
67
+ CONCRETE_TOOL_TO_SESSION_TOOL.set(concreteName, sessionTool)
68
+ }
69
+ }
70
+
71
+ function normalizeName(value: unknown): string {
72
+ return typeof value === 'string' ? value.trim().toLowerCase() : ''
73
+ }
74
+
75
+ function normalizeMode(value: unknown): CapabilityPolicyMode {
76
+ const mode = normalizeName(value)
77
+ if (mode === 'strict') return 'strict'
78
+ if (mode === 'balanced') return 'balanced'
79
+ return 'permissive'
80
+ }
81
+
82
+ function normalizeList(value: unknown): string[] {
83
+ if (!Array.isArray(value)) return []
84
+ const names: string[] = []
85
+ for (const entry of value) {
86
+ const normalized = normalizeName(entry)
87
+ if (!normalized) continue
88
+ names.push(normalized)
89
+ }
90
+ return Array.from(new Set(names))
91
+ }
92
+
93
+ function getSettingsList(settings: Record<string, unknown>, key: string): string[] {
94
+ return normalizeList(settings[key])
95
+ }
96
+
97
+ function modeBlocksTool(mode: CapabilityPolicyMode, toolName: string, descriptor?: ToolDescriptor): string | null {
98
+ if (!descriptor) return null
99
+ if (mode === 'permissive') return null
100
+ if (mode === 'balanced' && descriptor.destructive) {
101
+ return 'blocked by balanced policy (destructive tool)'
102
+ }
103
+ if (mode !== 'strict') return null
104
+ if (descriptor.destructive) return 'blocked by strict policy (destructive tool)'
105
+ if (descriptor.categories.some((c) => ['execution', 'delegation', 'platform', 'outbound', 'filesystem'].includes(c))) {
106
+ return 'blocked by strict policy'
107
+ }
108
+ if (toolName === 'manage_connectors') return 'blocked by strict policy'
109
+ return null
110
+ }
111
+
112
+ function safetyMatchesTool(safetyBlocked: Set<string>, toolName: string, descriptor?: ToolDescriptor): boolean {
113
+ if (safetyBlocked.has(toolName)) return true
114
+ if (!descriptor) return false
115
+ for (const concreteName of descriptor.concreteTools) {
116
+ if (safetyBlocked.has(concreteName)) return true
117
+ }
118
+ if (toolName === 'memory' && safetyBlocked.has('memory_tool')) return true
119
+ if (toolName === 'manage_connectors' && safetyBlocked.has('connector_message_tool')) return true
120
+ if (toolName === 'manage_sessions' && (
121
+ safetyBlocked.has('sessions_tool')
122
+ || safetyBlocked.has('search_history_tool')
123
+ || safetyBlocked.has('whoami_tool')
124
+ )) return true
125
+ if (toolName === 'claude_code' && safetyBlocked.has('delegate_to_claude_code')) return true
126
+ if (toolName === 'codex_cli' && safetyBlocked.has('delegate_to_codex_cli')) return true
127
+ if (toolName === 'opencode_cli' && safetyBlocked.has('delegate_to_opencode_cli')) return true
128
+ return false
129
+ }
130
+
131
+ function policyMatchesTool(blockedNames: Set<string>, toolName: string, descriptor?: ToolDescriptor): boolean {
132
+ if (blockedNames.has(toolName)) return true
133
+ if (!descriptor) return false
134
+ return descriptor.concreteTools.some((concreteName) => blockedNames.has(concreteName))
135
+ }
136
+
137
+ function categoryBlockReason(blockedCategories: Set<string>, descriptor?: ToolDescriptor): string | null {
138
+ if (!descriptor || !blockedCategories.size) return null
139
+ for (const category of descriptor.categories) {
140
+ if (blockedCategories.has(category)) {
141
+ return `blocked by policy category "${category}"`
142
+ }
143
+ }
144
+ return null
145
+ }
146
+
147
+ function ensureSettings(settings?: AppSettings | Record<string, unknown> | null): Record<string, unknown> {
148
+ if (!settings || typeof settings !== 'object') return {}
149
+ return settings as Record<string, unknown>
150
+ }
151
+
152
+ function parsePolicyConfig(settings: Record<string, unknown>) {
153
+ const mode = normalizeMode(settings.capabilityPolicyMode)
154
+ const safetyBlocked = new Set(getSettingsList(settings, 'safetyBlockedTools'))
155
+ const policyBlockedNames = new Set(getSettingsList(settings, 'capabilityBlockedTools'))
156
+ const policyAllowedNames = new Set(getSettingsList(settings, 'capabilityAllowedTools'))
157
+ const blockedCategories = new Set(getSettingsList(settings, 'capabilityBlockedCategories'))
158
+ return {
159
+ mode,
160
+ safetyBlocked,
161
+ policyBlockedNames,
162
+ policyAllowedNames,
163
+ blockedCategories,
164
+ }
165
+ }
166
+
167
+ export function resolveSessionToolPolicy(
168
+ sessionTools: string[] | undefined,
169
+ settings?: AppSettings | Record<string, unknown> | null,
170
+ ): SessionToolPolicyDecision {
171
+ const normalizedSettings = ensureSettings(settings)
172
+ const {
173
+ mode,
174
+ safetyBlocked,
175
+ policyBlockedNames,
176
+ policyAllowedNames,
177
+ blockedCategories,
178
+ } = parsePolicyConfig(normalizedSettings)
179
+
180
+ const requestedTools = Array.isArray(sessionTools)
181
+ ? Array.from(new Set(sessionTools.map((tool) => normalizeName(tool)).filter(Boolean)))
182
+ : []
183
+
184
+ const enabledTools: string[] = []
185
+ const blockedTools: CapabilityPolicyBlock[] = []
186
+
187
+ for (const toolName of requestedTools) {
188
+ const descriptor = TOOL_DESCRIPTORS[toolName]
189
+
190
+ if (safetyMatchesTool(safetyBlocked, toolName, descriptor)) {
191
+ blockedTools.push({ tool: toolName, reason: 'blocked by safety policy', source: 'safety' })
192
+ continue
193
+ }
194
+
195
+ if (policyAllowedNames.has(toolName)) {
196
+ enabledTools.push(toolName)
197
+ continue
198
+ }
199
+
200
+ if (policyMatchesTool(policyBlockedNames, toolName, descriptor)) {
201
+ blockedTools.push({ tool: toolName, reason: 'blocked by explicit policy rule', source: 'policy' })
202
+ continue
203
+ }
204
+
205
+ const categoryReason = categoryBlockReason(blockedCategories, descriptor)
206
+ if (categoryReason) {
207
+ blockedTools.push({ tool: toolName, reason: categoryReason, source: 'policy' })
208
+ continue
209
+ }
210
+
211
+ const modeReason = modeBlocksTool(mode, toolName, descriptor)
212
+ if (modeReason) {
213
+ blockedTools.push({ tool: toolName, reason: modeReason, source: 'policy' })
214
+ continue
215
+ }
216
+
217
+ enabledTools.push(toolName)
218
+ }
219
+
220
+ return {
221
+ mode,
222
+ requestedTools,
223
+ enabledTools,
224
+ blockedTools,
225
+ }
226
+ }
227
+
228
+ export function resolveConcreteToolPolicyBlock(
229
+ concreteToolName: string,
230
+ decision: SessionToolPolicyDecision,
231
+ settings?: AppSettings | Record<string, unknown> | null,
232
+ ): string | null {
233
+ const name = normalizeName(concreteToolName)
234
+ if (!name) return 'invalid tool name'
235
+
236
+ const normalizedSettings = ensureSettings(settings)
237
+ const {
238
+ safetyBlocked,
239
+ policyBlockedNames,
240
+ policyAllowedNames,
241
+ } = parsePolicyConfig(normalizedSettings)
242
+
243
+ if (safetyBlocked.has(name)) return 'blocked by safety policy'
244
+
245
+ const mappedTool = CONCRETE_TOOL_TO_SESSION_TOOL.get(name)
246
+ if (mappedTool && safetyBlocked.has(mappedTool)) return `blocked because "${mappedTool}" is safety-blocked`
247
+
248
+ if (policyBlockedNames.has(name)) return 'blocked by explicit policy rule'
249
+ if (mappedTool && policyBlockedNames.has(mappedTool) && !policyAllowedNames.has(mappedTool)) {
250
+ return `blocked because "${mappedTool}" is policy-blocked`
251
+ }
252
+
253
+ if (mappedTool) {
254
+ const blockedRoot = decision.blockedTools.find((entry) => entry.tool === mappedTool)
255
+ if (blockedRoot) return blockedRoot.reason
256
+
257
+ const enabledRoot = decision.enabledTools.includes(mappedTool)
258
+ if (!enabledRoot) return `tool family "${mappedTool}" is not enabled for this session`
259
+ }
260
+
261
+ return null
262
+ }
@@ -0,0 +1,68 @@
1
+ import { api } from './api-client'
2
+ import type {
3
+ Sessions, Session, Message, Directory, DevServerStatus, DeployResult,
4
+ ProviderInfo, Credential, Credentials, ProviderType, SessionType,
5
+ } from '../types'
6
+
7
+ export const fetchSessions = () => api<Sessions>('GET', '/sessions')
8
+
9
+ export const createSession = (
10
+ name: string,
11
+ cwd: string,
12
+ user: string,
13
+ provider?: ProviderType,
14
+ model?: string,
15
+ credentialId?: string | null,
16
+ apiEndpoint?: string | null,
17
+ sessionType?: SessionType,
18
+ agentId?: string | null,
19
+ tools?: string[],
20
+ file?: string | null,
21
+ ) =>
22
+ api<Session>('POST', '/sessions', {
23
+ name, cwd: cwd || undefined, user,
24
+ provider, model, credentialId, apiEndpoint,
25
+ sessionType, agentId, tools, file: file || undefined,
26
+ })
27
+
28
+ export const updateSession = (id: string, updates: Partial<Pick<Session, 'name' | 'cwd'>>) =>
29
+ api<Session>('PUT', `/sessions/${id}`, updates)
30
+
31
+ export const deleteSession = (id: string) =>
32
+ api<string>('DELETE', `/sessions/${id}`)
33
+
34
+ export const fetchMessages = (id: string) =>
35
+ api<Message[]>('GET', `/sessions/${id}/messages`)
36
+
37
+ export const clearMessages = (id: string) =>
38
+ api<string>('POST', `/sessions/${id}/clear`)
39
+
40
+ export const stopSession = (id: string) =>
41
+ api<string>('POST', `/sessions/${id}/stop`)
42
+
43
+ export const fetchDirs = async () => {
44
+ const data = await api<{ dirs: Directory[] }>('GET', '/dirs')
45
+ return data.dirs
46
+ }
47
+
48
+ export const devServer = (id: string, action: 'start' | 'stop' | 'status') =>
49
+ api<DevServerStatus>('POST', `/sessions/${id}/devserver`, { action })
50
+
51
+ export const checkBrowser = (id: string) =>
52
+ api<{ active: boolean }>('GET', `/sessions/${id}/browser`)
53
+
54
+ export const stopBrowser = (id: string) =>
55
+ api<string>('DELETE', `/sessions/${id}/browser`)
56
+
57
+ export const deploy = (id: string, message: string) =>
58
+ api<DeployResult>('POST', `/sessions/${id}/deploy`, { message })
59
+
60
+ export const fetchProviders = () => api<ProviderInfo[]>('GET', '/providers')
61
+
62
+ export const fetchCredentials = () => api<Credentials>('GET', '/credentials')
63
+
64
+ export const createCredential = (provider: string, name: string, apiKey: string) =>
65
+ api<Credential>('POST', '/credentials', { provider, name, apiKey })
66
+
67
+ export const deleteCredential = (id: string) =>
68
+ api<string>('DELETE', `/credentials/${id}`)
@@ -0,0 +1,20 @@
1
+ import { api } from './api-client'
2
+ import type { BoardTask } from '../types'
3
+
4
+ export const fetchTasks = (includeArchived = false) =>
5
+ api<Record<string, BoardTask>>('GET', `/tasks${includeArchived ? '?includeArchived=true' : ''}`)
6
+
7
+ export const createTask = (data: { title: string; description: string; agentId: string }) =>
8
+ api<BoardTask>('POST', '/tasks', data)
9
+
10
+ export const updateTask = (id: string, data: Partial<BoardTask>) =>
11
+ api<BoardTask>('PUT', `/tasks/${id}`, data)
12
+
13
+ export const deleteTask = (id: string) =>
14
+ api<BoardTask>('DELETE', `/tasks/${id}`)
15
+
16
+ export const archiveTask = (id: string) =>
17
+ api<BoardTask>('PUT', `/tasks/${id}`, { status: 'archived' })
18
+
19
+ export const unarchiveTask = (id: string) =>
20
+ api<BoardTask>('PUT', `/tasks/${id}`, { status: 'backlog' })
package/src/lib/tts.ts ADDED
@@ -0,0 +1,42 @@
1
+ let audioCtx: AudioContext | null = null
2
+ let currentSource: AudioBufferSourceNode | null = null
3
+
4
+ function ensureContext() {
5
+ if (!audioCtx) audioCtx = new AudioContext()
6
+ if (audioCtx.state === 'suspended') audioCtx.resume()
7
+ }
8
+
9
+ export function initAudioContext() {
10
+ ensureContext()
11
+ }
12
+
13
+ export async function speak(text: string) {
14
+ if (currentSource) {
15
+ try { currentSource.stop() } catch { /* noop */ }
16
+ currentSource = null
17
+ }
18
+ ensureContext()
19
+ if (!audioCtx) return
20
+
21
+ const res = await fetch('/api/tts', {
22
+ method: 'POST',
23
+ headers: { 'Content-Type': 'application/json' },
24
+ body: JSON.stringify({ text: text.slice(0, 2000) }),
25
+ })
26
+ if (!res.ok) return
27
+
28
+ const buf = await res.arrayBuffer()
29
+ const audio = await audioCtx.decodeAudioData(buf)
30
+ currentSource = audioCtx.createBufferSource()
31
+ currentSource.buffer = audio
32
+ currentSource.connect(audioCtx.destination)
33
+ currentSource.onended = () => { currentSource = null }
34
+ currentSource.start()
35
+ }
36
+
37
+ export function stopSpeaking() {
38
+ if (currentSource) {
39
+ try { currentSource.stop() } catch { /* noop */ }
40
+ currentSource = null
41
+ }
42
+ }
@@ -0,0 +1,10 @@
1
+ import type { UploadResult } from '../types'
2
+
3
+ export async function uploadImage(file: File): Promise<UploadResult> {
4
+ const res = await fetch('/api/upload', {
5
+ method: 'POST',
6
+ headers: { 'X-Filename': file.name },
7
+ body: file,
8
+ })
9
+ return res.json()
10
+ }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
package/src/proxy.ts ADDED
@@ -0,0 +1,43 @@
1
+ import { NextResponse } from 'next/server'
2
+ import type { NextRequest } from 'next/server'
3
+
4
+ /** Simple access key auth proxy.
5
+ * Checks X-Access-Key header or ?key= param on all /api/ routes except /api/auth.
6
+ * The key is validated against the ACCESS_KEY env var.
7
+ */
8
+ export function proxy(request: NextRequest) {
9
+ const { pathname } = request.nextUrl
10
+ const isWebhookTrigger = request.method === 'POST'
11
+ && /^\/api\/webhooks\/[^/]+\/?$/.test(pathname)
12
+
13
+ // Only protect API routes (not auth, uploads served as static assets, or inbound webhooks)
14
+ if (
15
+ !pathname.startsWith('/api/')
16
+ || pathname === '/api/auth'
17
+ || pathname.startsWith('/api/uploads/')
18
+ || isWebhookTrigger
19
+ ) {
20
+ return NextResponse.next()
21
+ }
22
+
23
+ const accessKey = process.env.ACCESS_KEY
24
+ if (!accessKey) {
25
+ // No key configured — allow all (dev mode)
26
+ return NextResponse.next()
27
+ }
28
+
29
+ const providedKey =
30
+ request.headers.get('x-access-key')
31
+ || request.nextUrl.searchParams.get('key')
32
+ || ''
33
+
34
+ if (providedKey !== accessKey) {
35
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
36
+ }
37
+
38
+ return NextResponse.next()
39
+ }
40
+
41
+ export const config = {
42
+ matcher: '/api/:path*',
43
+ }