@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,111 @@
1
+ import { loadSettings, loadCredentials, decryptKey } from './storage'
2
+
3
+ let localPipeline: any = null
4
+ let localPipelineLoading: Promise<any> | null = null
5
+
6
+ async function getLocalPipeline() {
7
+ if (localPipeline) return localPipeline
8
+ if (localPipelineLoading) return localPipelineLoading
9
+
10
+ localPipelineLoading = (async () => {
11
+ const { pipeline } = await import('@huggingface/transformers')
12
+ localPipeline = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2', {
13
+ dtype: 'fp32',
14
+ })
15
+ return localPipeline
16
+ })()
17
+
18
+ return localPipelineLoading
19
+ }
20
+
21
+ export async function getEmbedding(text: string): Promise<number[] | null> {
22
+ const settings = loadSettings()
23
+ const provider = settings.embeddingProvider
24
+ if (!provider) return null
25
+
26
+ const model = settings.embeddingModel || 'text-embedding-3-small'
27
+
28
+ let apiKey: string | null = null
29
+ if (settings.embeddingCredentialId) {
30
+ const creds = loadCredentials()
31
+ const cred = creds[settings.embeddingCredentialId]
32
+ if (cred?.encryptedKey) {
33
+ try { apiKey = decryptKey(cred.encryptedKey) } catch { /* ignore */ }
34
+ }
35
+ }
36
+
37
+ try {
38
+ if (provider === 'local') {
39
+ return await localEmbed(text)
40
+ } else if (provider === 'openai') {
41
+ return await openaiEmbed(text, model, apiKey)
42
+ } else if (provider === 'ollama') {
43
+ return await ollamaEmbed(text, model, settings.langGraphEndpoint)
44
+ }
45
+ } catch (err: any) {
46
+ console.error(`[embeddings] Error computing embedding:`, err.message)
47
+ }
48
+
49
+ return null
50
+ }
51
+
52
+ async function localEmbed(text: string): Promise<number[]> {
53
+ const pipe = await getLocalPipeline()
54
+ const output = await pipe(text.slice(0, 8000), { pooling: 'mean', normalize: true })
55
+ return Array.from(output.data as Float32Array)
56
+ }
57
+
58
+ async function openaiEmbed(text: string, model: string, apiKey: string | null): Promise<number[]> {
59
+ if (!apiKey) throw new Error('OpenAI API key required for embeddings')
60
+ const res = await fetch('https://api.openai.com/v1/embeddings', {
61
+ method: 'POST',
62
+ headers: {
63
+ 'Content-Type': 'application/json',
64
+ Authorization: `Bearer ${apiKey}`,
65
+ },
66
+ body: JSON.stringify({
67
+ model,
68
+ input: text.slice(0, 8000),
69
+ }),
70
+ })
71
+ if (!res.ok) throw new Error(`OpenAI embeddings API error: ${res.status}`)
72
+ const data = await res.json()
73
+ return data.data[0].embedding
74
+ }
75
+
76
+ async function ollamaEmbed(text: string, model: string, endpoint?: string | null): Promise<number[]> {
77
+ const baseUrl = endpoint || 'http://localhost:11434'
78
+ const res = await fetch(`${baseUrl}/api/embeddings`, {
79
+ method: 'POST',
80
+ headers: { 'Content-Type': 'application/json' },
81
+ body: JSON.stringify({
82
+ model,
83
+ prompt: text.slice(0, 8000),
84
+ }),
85
+ })
86
+ if (!res.ok) throw new Error(`Ollama embeddings API error: ${res.status}`)
87
+ const data = await res.json()
88
+ return data.embedding
89
+ }
90
+
91
+ export function cosineSimilarity(a: number[], b: number[]): number {
92
+ if (a.length !== b.length) return 0
93
+ let dotProduct = 0
94
+ let normA = 0
95
+ let normB = 0
96
+ for (let i = 0; i < a.length; i++) {
97
+ dotProduct += a[i] * b[i]
98
+ normA += a[i] * a[i]
99
+ normB += b[i] * b[i]
100
+ }
101
+ const denom = Math.sqrt(normA) * Math.sqrt(normB)
102
+ return denom === 0 ? 0 : dotProduct / denom
103
+ }
104
+
105
+ export function serializeEmbedding(embedding: number[]): Buffer {
106
+ return Buffer.from(new Float32Array(embedding).buffer)
107
+ }
108
+
109
+ export function deserializeEmbedding(buf: Buffer): number[] {
110
+ return Array.from(new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4))
111
+ }
@@ -0,0 +1,257 @@
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+ import crypto from 'crypto'
4
+ import Database from 'better-sqlite3'
5
+
6
+ // ---------------------------------------------------------------------------
7
+ // Types
8
+ // ---------------------------------------------------------------------------
9
+
10
+ export type LogCategory =
11
+ | 'trigger' // what kicked off the action
12
+ | 'decision' // reasoning / model choice
13
+ | 'tool_call' // tool invocation with input
14
+ | 'tool_result' // tool output
15
+ | 'outbound' // messages sent to users/platforms
16
+ | 'file_op' // file read/write/delete with checksums
17
+ | 'commit' // git commit activity
18
+ | 'error' // errors during execution
19
+
20
+ export interface ExecutionLogEntry {
21
+ id: string
22
+ sessionId: string
23
+ runId: string | null
24
+ agentId: string | null
25
+ category: LogCategory
26
+ summary: string
27
+ detail: Record<string, unknown> | null
28
+ ts: number
29
+ }
30
+
31
+ export interface LogQueryOpts {
32
+ sessionId?: string
33
+ agentId?: string
34
+ runId?: string
35
+ category?: LogCategory
36
+ since?: number
37
+ until?: number
38
+ limit?: number
39
+ offset?: number
40
+ search?: string
41
+ }
42
+
43
+ // ---------------------------------------------------------------------------
44
+ // Database setup
45
+ // ---------------------------------------------------------------------------
46
+
47
+ import { DATA_DIR } from './data-dir'
48
+ if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR, { recursive: true })
49
+
50
+ const DB_PATH = path.join(DATA_DIR, 'logs.db')
51
+
52
+ let _db: Database.Database | null = null
53
+
54
+ function getDb(): Database.Database {
55
+ if (_db) return _db
56
+ _db = new Database(DB_PATH)
57
+ _db.pragma('journal_mode = WAL')
58
+ _db.exec(`
59
+ CREATE TABLE IF NOT EXISTS execution_logs (
60
+ id TEXT PRIMARY KEY,
61
+ session_id TEXT NOT NULL,
62
+ run_id TEXT,
63
+ agent_id TEXT,
64
+ category TEXT NOT NULL,
65
+ summary TEXT NOT NULL,
66
+ detail TEXT,
67
+ ts INTEGER NOT NULL
68
+ )
69
+ `)
70
+ _db.exec(`CREATE INDEX IF NOT EXISTS idx_logs_session ON execution_logs(session_id, ts)`)
71
+ _db.exec(`CREATE INDEX IF NOT EXISTS idx_logs_agent ON execution_logs(agent_id, ts)`)
72
+ _db.exec(`CREATE INDEX IF NOT EXISTS idx_logs_run ON execution_logs(run_id)`)
73
+ _db.exec(`CREATE INDEX IF NOT EXISTS idx_logs_cat ON execution_logs(category)`)
74
+ return _db
75
+ }
76
+
77
+ // ---------------------------------------------------------------------------
78
+ // Write
79
+ // ---------------------------------------------------------------------------
80
+
81
+ const insertStmt = () =>
82
+ getDb().prepare(
83
+ `INSERT INTO execution_logs (id, session_id, run_id, agent_id, category, summary, detail, ts)
84
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
85
+ )
86
+
87
+ export function logExecution(
88
+ sessionId: string,
89
+ category: LogCategory,
90
+ summary: string,
91
+ opts?: {
92
+ runId?: string | null
93
+ agentId?: string | null
94
+ detail?: Record<string, unknown>
95
+ },
96
+ ): string {
97
+ const id = crypto.randomBytes(8).toString('hex')
98
+ const ts = Date.now()
99
+ try {
100
+ insertStmt().run(
101
+ id,
102
+ sessionId,
103
+ opts?.runId ?? null,
104
+ opts?.agentId ?? null,
105
+ category,
106
+ summary,
107
+ opts?.detail ? JSON.stringify(opts.detail) : null,
108
+ ts,
109
+ )
110
+ } catch {
111
+ // Non-critical — never block agent execution for logging failures
112
+ }
113
+ return id
114
+ }
115
+
116
+ // Batch insert for bulk writes (e.g. file ops)
117
+ export function logExecutionBatch(
118
+ entries: Array<{
119
+ sessionId: string
120
+ category: LogCategory
121
+ summary: string
122
+ runId?: string | null
123
+ agentId?: string | null
124
+ detail?: Record<string, unknown>
125
+ }>,
126
+ ): void {
127
+ const db = getDb()
128
+ const stmt = db.prepare(
129
+ `INSERT INTO execution_logs (id, session_id, run_id, agent_id, category, summary, detail, ts)
130
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
131
+ )
132
+ const ts = Date.now()
133
+ const tx = db.transaction(() => {
134
+ for (const e of entries) {
135
+ stmt.run(
136
+ crypto.randomBytes(8).toString('hex'),
137
+ e.sessionId,
138
+ e.runId ?? null,
139
+ e.agentId ?? null,
140
+ e.category,
141
+ e.summary,
142
+ e.detail ? JSON.stringify(e.detail) : null,
143
+ ts,
144
+ )
145
+ }
146
+ })
147
+ try { tx() } catch { /* non-critical */ }
148
+ }
149
+
150
+ // ---------------------------------------------------------------------------
151
+ // Read / Query
152
+ // ---------------------------------------------------------------------------
153
+
154
+ export function queryLogs(opts: LogQueryOpts): ExecutionLogEntry[] {
155
+ const conditions: string[] = []
156
+ const params: unknown[] = []
157
+
158
+ if (opts.sessionId) {
159
+ conditions.push('session_id = ?')
160
+ params.push(opts.sessionId)
161
+ }
162
+ if (opts.agentId) {
163
+ conditions.push('agent_id = ?')
164
+ params.push(opts.agentId)
165
+ }
166
+ if (opts.runId) {
167
+ conditions.push('run_id = ?')
168
+ params.push(opts.runId)
169
+ }
170
+ if (opts.category) {
171
+ conditions.push('category = ?')
172
+ params.push(opts.category)
173
+ }
174
+ if (opts.since) {
175
+ conditions.push('ts >= ?')
176
+ params.push(opts.since)
177
+ }
178
+ if (opts.until) {
179
+ conditions.push('ts <= ?')
180
+ params.push(opts.until)
181
+ }
182
+ if (opts.search) {
183
+ conditions.push('(summary LIKE ? OR detail LIKE ?)')
184
+ const pattern = `%${opts.search}%`
185
+ params.push(pattern, pattern)
186
+ }
187
+
188
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''
189
+ const limit = opts.limit ?? 200
190
+ const offset = opts.offset ?? 0
191
+
192
+ const rows = getDb()
193
+ .prepare(`SELECT * FROM execution_logs ${where} ORDER BY ts DESC LIMIT ? OFFSET ?`)
194
+ .all(...params, limit, offset) as Array<{
195
+ id: string
196
+ session_id: string
197
+ run_id: string | null
198
+ agent_id: string | null
199
+ category: string
200
+ summary: string
201
+ detail: string | null
202
+ ts: number
203
+ }>
204
+
205
+ return rows.map((r) => ({
206
+ id: r.id,
207
+ sessionId: r.session_id,
208
+ runId: r.run_id,
209
+ agentId: r.agent_id,
210
+ category: r.category as LogCategory,
211
+ summary: r.summary,
212
+ detail: r.detail ? JSON.parse(r.detail) : null,
213
+ ts: r.ts,
214
+ }))
215
+ }
216
+
217
+ export function countLogs(opts: Omit<LogQueryOpts, 'limit' | 'offset'>): number {
218
+ const conditions: string[] = []
219
+ const params: unknown[] = []
220
+
221
+ if (opts.sessionId) { conditions.push('session_id = ?'); params.push(opts.sessionId) }
222
+ if (opts.agentId) { conditions.push('agent_id = ?'); params.push(opts.agentId) }
223
+ if (opts.runId) { conditions.push('run_id = ?'); params.push(opts.runId) }
224
+ if (opts.category) { conditions.push('category = ?'); params.push(opts.category) }
225
+ if (opts.since) { conditions.push('ts >= ?'); params.push(opts.since) }
226
+ if (opts.until) { conditions.push('ts <= ?'); params.push(opts.until) }
227
+ if (opts.search) {
228
+ conditions.push('(summary LIKE ? OR detail LIKE ?)')
229
+ const pattern = `%${opts.search}%`
230
+ params.push(pattern, pattern)
231
+ }
232
+
233
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''
234
+ const row = getDb()
235
+ .prepare(`SELECT COUNT(*) as cnt FROM execution_logs ${where}`)
236
+ .get(...params) as { cnt: number }
237
+ return row.cnt
238
+ }
239
+
240
+ // ---------------------------------------------------------------------------
241
+ // Clear
242
+ // ---------------------------------------------------------------------------
243
+
244
+ export function clearLogs(sessionId?: string): number {
245
+ if (sessionId) {
246
+ const result = getDb().prepare('DELETE FROM execution_logs WHERE session_id = ?').run(sessionId)
247
+ return result.changes
248
+ }
249
+ const result = getDb().prepare('DELETE FROM execution_logs').run()
250
+ return result.changes
251
+ }
252
+
253
+ export function clearLogsByAge(maxAgeMs: number): number {
254
+ const cutoff = Date.now() - maxAgeMs
255
+ const result = getDb().prepare('DELETE FROM execution_logs WHERE ts < ?').run(cutoff)
256
+ return result.changes
257
+ }
@@ -0,0 +1,54 @@
1
+ import assert from 'node:assert/strict'
2
+ import { test } from 'node:test'
3
+ import {
4
+ createGatewayRequestFrame,
5
+ parseGatewayFrame,
6
+ serializeGatewayFrame,
7
+ } from './protocol.ts'
8
+
9
+ test('gateway protocol parses request/response/event frames', () => {
10
+ const req = parseGatewayFrame('{"type":"req","id":"1","method":"connect","params":{"foo":"bar"}}')
11
+ assert.deepEqual(req, {
12
+ type: 'req',
13
+ id: '1',
14
+ method: 'connect',
15
+ params: { foo: 'bar' },
16
+ })
17
+
18
+ const res = parseGatewayFrame('{"type":"res","id":"1","ok":true,"payload":{"ok":true}}')
19
+ assert.deepEqual(res, {
20
+ type: 'res',
21
+ id: '1',
22
+ ok: true,
23
+ payload: { ok: true },
24
+ error: null,
25
+ })
26
+
27
+ const event = parseGatewayFrame('{"type":"event","event":"tick","payload":{"n":1}}')
28
+ assert.deepEqual(event, {
29
+ type: 'event',
30
+ event: 'tick',
31
+ payload: { n: 1 },
32
+ })
33
+ })
34
+
35
+ test('gateway protocol rejects malformed frames', () => {
36
+ assert.equal(parseGatewayFrame('not-json'), null)
37
+ assert.equal(parseGatewayFrame('{"type":"event"}'), null)
38
+ assert.equal(parseGatewayFrame('{"type":"req","id":"1"}'), null)
39
+ assert.equal(parseGatewayFrame({ nope: true }), null)
40
+ })
41
+
42
+ test('gateway request helper and serializer produce valid frame', () => {
43
+ const frame = createGatewayRequestFrame('abc', 'chat.send', { message: 'hello' })
44
+ assert.deepEqual(frame, {
45
+ type: 'req',
46
+ id: 'abc',
47
+ method: 'chat.send',
48
+ params: { message: 'hello' },
49
+ })
50
+
51
+ const encoded = serializeGatewayFrame(frame)
52
+ const decoded = parseGatewayFrame(encoded)
53
+ assert.deepEqual(decoded, frame)
54
+ })
@@ -0,0 +1,114 @@
1
+ export interface GatewayRequestFrame {
2
+ type: 'req'
3
+ id: string
4
+ method: string
5
+ params?: Record<string, unknown>
6
+ }
7
+
8
+ export interface GatewayResponseFrame {
9
+ type: 'res'
10
+ id: string
11
+ ok: boolean
12
+ payload?: unknown
13
+ error?: {
14
+ code?: string
15
+ message?: string
16
+ } | null
17
+ }
18
+
19
+ export interface GatewayEventFrame {
20
+ type: 'event'
21
+ event: string
22
+ payload?: unknown
23
+ }
24
+
25
+ export type GatewayFrame = GatewayRequestFrame | GatewayResponseFrame | GatewayEventFrame
26
+
27
+ function toJsonString(raw: unknown): string | null {
28
+ if (typeof raw === 'string') return raw
29
+ if (raw instanceof Uint8Array) return Buffer.from(raw).toString('utf8')
30
+ if (Buffer.isBuffer(raw)) return raw.toString('utf8')
31
+ if (raw && typeof raw === 'object' && 'toString' in raw) {
32
+ try {
33
+ return String((raw as { toString: () => string }).toString())
34
+ } catch {
35
+ return null
36
+ }
37
+ }
38
+ return null
39
+ }
40
+
41
+ function isRecord(value: unknown): value is Record<string, unknown> {
42
+ return !!value && typeof value === 'object' && !Array.isArray(value)
43
+ }
44
+
45
+ function parseFrameObject(value: unknown): GatewayFrame | null {
46
+ if (!isRecord(value)) return null
47
+ const type = typeof value.type === 'string' ? value.type : ''
48
+ if (type === 'req') {
49
+ const id = typeof value.id === 'string' ? value.id : ''
50
+ const method = typeof value.method === 'string' ? value.method : ''
51
+ if (!id || !method) return null
52
+ const params = isRecord(value.params) ? value.params : undefined
53
+ return { type: 'req', id, method, params }
54
+ }
55
+ if (type === 'res') {
56
+ const id = typeof value.id === 'string' ? value.id : ''
57
+ const ok = value.ok === true
58
+ if (!id) return null
59
+ const error = isRecord(value.error)
60
+ ? {
61
+ code: typeof value.error.code === 'string' ? value.error.code : undefined,
62
+ message: typeof value.error.message === 'string' ? value.error.message : undefined,
63
+ }
64
+ : null
65
+ return {
66
+ type: 'res',
67
+ id,
68
+ ok,
69
+ payload: value.payload,
70
+ error,
71
+ }
72
+ }
73
+ if (type === 'event') {
74
+ const event = typeof value.event === 'string' ? value.event : ''
75
+ if (!event) return null
76
+ return {
77
+ type: 'event',
78
+ event,
79
+ payload: value.payload,
80
+ }
81
+ }
82
+ return null
83
+ }
84
+
85
+ export function parseGatewayFrame(raw: unknown): GatewayFrame | null {
86
+ if (isRecord(raw) && typeof raw.type === 'string') {
87
+ return parseFrameObject(raw)
88
+ }
89
+ const text = toJsonString(raw)
90
+ if (!text) return null
91
+ try {
92
+ const parsed = JSON.parse(text)
93
+ return parseFrameObject(parsed)
94
+ } catch {
95
+ return null
96
+ }
97
+ }
98
+
99
+ export function serializeGatewayFrame(frame: GatewayFrame): string {
100
+ return JSON.stringify(frame)
101
+ }
102
+
103
+ export function createGatewayRequestFrame(
104
+ id: string,
105
+ method: string,
106
+ params?: Record<string, unknown>,
107
+ ): GatewayRequestFrame {
108
+ return {
109
+ type: 'req',
110
+ id,
111
+ method,
112
+ params,
113
+ }
114
+ }