cowork-os 0.3.21

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 (526) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1638 -0
  3. package/bin/cowork.js +42 -0
  4. package/build/entitlements.mac.plist +16 -0
  5. package/build/icon.icns +0 -0
  6. package/build/icon.png +0 -0
  7. package/dist/electron/electron/activity/ActivityRepository.js +190 -0
  8. package/dist/electron/electron/agent/browser/browser-service.js +639 -0
  9. package/dist/electron/electron/agent/context-manager.js +225 -0
  10. package/dist/electron/electron/agent/custom-skill-loader.js +566 -0
  11. package/dist/electron/electron/agent/daemon.js +975 -0
  12. package/dist/electron/electron/agent/executor.js +3561 -0
  13. package/dist/electron/electron/agent/llm/anthropic-provider.js +155 -0
  14. package/dist/electron/electron/agent/llm/bedrock-provider.js +202 -0
  15. package/dist/electron/electron/agent/llm/gemini-provider.js +375 -0
  16. package/dist/electron/electron/agent/llm/index.js +34 -0
  17. package/dist/electron/electron/agent/llm/ollama-provider.js +263 -0
  18. package/dist/electron/electron/agent/llm/openai-oauth.js +101 -0
  19. package/dist/electron/electron/agent/llm/openai-provider.js +657 -0
  20. package/dist/electron/electron/agent/llm/openrouter-provider.js +232 -0
  21. package/dist/electron/electron/agent/llm/pricing.js +160 -0
  22. package/dist/electron/electron/agent/llm/provider-factory.js +880 -0
  23. package/dist/electron/electron/agent/llm/types.js +178 -0
  24. package/dist/electron/electron/agent/queue-manager.js +378 -0
  25. package/dist/electron/electron/agent/sandbox/docker-sandbox.js +402 -0
  26. package/dist/electron/electron/agent/sandbox/macos-sandbox.js +407 -0
  27. package/dist/electron/electron/agent/sandbox/runner.js +410 -0
  28. package/dist/electron/electron/agent/sandbox/sandbox-factory.js +228 -0
  29. package/dist/electron/electron/agent/sandbox/security-utils.js +258 -0
  30. package/dist/electron/electron/agent/search/brave-provider.js +119 -0
  31. package/dist/electron/electron/agent/search/google-provider.js +100 -0
  32. package/dist/electron/electron/agent/search/index.js +28 -0
  33. package/dist/electron/electron/agent/search/provider-factory.js +395 -0
  34. package/dist/electron/electron/agent/search/serpapi-provider.js +112 -0
  35. package/dist/electron/electron/agent/search/tavily-provider.js +90 -0
  36. package/dist/electron/electron/agent/search/types.js +40 -0
  37. package/dist/electron/electron/agent/security/index.js +12 -0
  38. package/dist/electron/electron/agent/security/input-sanitizer.js +303 -0
  39. package/dist/electron/electron/agent/security/output-filter.js +217 -0
  40. package/dist/electron/electron/agent/skill-eligibility.js +281 -0
  41. package/dist/electron/electron/agent/skill-registry.js +396 -0
  42. package/dist/electron/electron/agent/skills/document.js +878 -0
  43. package/dist/electron/electron/agent/skills/image-generator.js +225 -0
  44. package/dist/electron/electron/agent/skills/organizer.js +141 -0
  45. package/dist/electron/electron/agent/skills/presentation.js +367 -0
  46. package/dist/electron/electron/agent/skills/spreadsheet.js +165 -0
  47. package/dist/electron/electron/agent/tools/browser-tools.js +523 -0
  48. package/dist/electron/electron/agent/tools/builtin-settings.js +384 -0
  49. package/dist/electron/electron/agent/tools/canvas-tools.js +530 -0
  50. package/dist/electron/electron/agent/tools/cron-tools.js +577 -0
  51. package/dist/electron/electron/agent/tools/edit-tools.js +194 -0
  52. package/dist/electron/electron/agent/tools/file-tools.js +719 -0
  53. package/dist/electron/electron/agent/tools/glob-tools.js +283 -0
  54. package/dist/electron/electron/agent/tools/grep-tools.js +387 -0
  55. package/dist/electron/electron/agent/tools/image-tools.js +111 -0
  56. package/dist/electron/electron/agent/tools/mention-tools.js +282 -0
  57. package/dist/electron/electron/agent/tools/node-tools.js +476 -0
  58. package/dist/electron/electron/agent/tools/registry.js +2719 -0
  59. package/dist/electron/electron/agent/tools/search-tools.js +91 -0
  60. package/dist/electron/electron/agent/tools/shell-tools.js +574 -0
  61. package/dist/electron/electron/agent/tools/skill-tools.js +274 -0
  62. package/dist/electron/electron/agent/tools/system-tools.js +578 -0
  63. package/dist/electron/electron/agent/tools/web-fetch-tools.js +444 -0
  64. package/dist/electron/electron/agent/tools/x-tools.js +264 -0
  65. package/dist/electron/electron/agents/AgentRoleRepository.js +420 -0
  66. package/dist/electron/electron/agents/HeartbeatService.js +356 -0
  67. package/dist/electron/electron/agents/MentionRepository.js +197 -0
  68. package/dist/electron/electron/agents/TaskSubscriptionRepository.js +168 -0
  69. package/dist/electron/electron/agents/WorkingStateRepository.js +229 -0
  70. package/dist/electron/electron/canvas/canvas-manager.js +714 -0
  71. package/dist/electron/electron/canvas/canvas-preload.js +53 -0
  72. package/dist/electron/electron/canvas/canvas-protocol.js +195 -0
  73. package/dist/electron/electron/canvas/canvas-store.js +174 -0
  74. package/dist/electron/electron/canvas/index.js +13 -0
  75. package/dist/electron/electron/control-plane/client.js +364 -0
  76. package/dist/electron/electron/control-plane/handlers.js +572 -0
  77. package/dist/electron/electron/control-plane/index.js +41 -0
  78. package/dist/electron/electron/control-plane/node-manager.js +264 -0
  79. package/dist/electron/electron/control-plane/protocol.js +194 -0
  80. package/dist/electron/electron/control-plane/remote-client.js +437 -0
  81. package/dist/electron/electron/control-plane/server.js +640 -0
  82. package/dist/electron/electron/control-plane/settings.js +369 -0
  83. package/dist/electron/electron/control-plane/ssh-tunnel.js +549 -0
  84. package/dist/electron/electron/cron/index.js +30 -0
  85. package/dist/electron/electron/cron/schedule.js +190 -0
  86. package/dist/electron/electron/cron/service.js +614 -0
  87. package/dist/electron/electron/cron/store.js +155 -0
  88. package/dist/electron/electron/cron/types.js +82 -0
  89. package/dist/electron/electron/cron/webhook.js +258 -0
  90. package/dist/electron/electron/database/SecureSettingsRepository.js +444 -0
  91. package/dist/electron/electron/database/TaskLabelRepository.js +120 -0
  92. package/dist/electron/electron/database/repositories.js +1781 -0
  93. package/dist/electron/electron/database/schema.js +978 -0
  94. package/dist/electron/electron/extensions/index.js +33 -0
  95. package/dist/electron/electron/extensions/loader.js +313 -0
  96. package/dist/electron/electron/extensions/registry.js +485 -0
  97. package/dist/electron/electron/extensions/types.js +11 -0
  98. package/dist/electron/electron/gateway/channel-registry.js +1102 -0
  99. package/dist/electron/electron/gateway/channels/bluebubbles-client.js +479 -0
  100. package/dist/electron/electron/gateway/channels/bluebubbles.js +432 -0
  101. package/dist/electron/electron/gateway/channels/discord.js +975 -0
  102. package/dist/electron/electron/gateway/channels/email-client.js +593 -0
  103. package/dist/electron/electron/gateway/channels/email.js +443 -0
  104. package/dist/electron/electron/gateway/channels/google-chat.js +631 -0
  105. package/dist/electron/electron/gateway/channels/imessage-client.js +363 -0
  106. package/dist/electron/electron/gateway/channels/imessage.js +465 -0
  107. package/dist/electron/electron/gateway/channels/index.js +36 -0
  108. package/dist/electron/electron/gateway/channels/line-client.js +470 -0
  109. package/dist/electron/electron/gateway/channels/line.js +479 -0
  110. package/dist/electron/electron/gateway/channels/matrix-client.js +432 -0
  111. package/dist/electron/electron/gateway/channels/matrix.js +592 -0
  112. package/dist/electron/electron/gateway/channels/mattermost-client.js +394 -0
  113. package/dist/electron/electron/gateway/channels/mattermost.js +496 -0
  114. package/dist/electron/electron/gateway/channels/signal-client.js +500 -0
  115. package/dist/electron/electron/gateway/channels/signal.js +582 -0
  116. package/dist/electron/electron/gateway/channels/slack.js +415 -0
  117. package/dist/electron/electron/gateway/channels/teams.js +596 -0
  118. package/dist/electron/electron/gateway/channels/telegram.js +1390 -0
  119. package/dist/electron/electron/gateway/channels/twitch-client.js +502 -0
  120. package/dist/electron/electron/gateway/channels/twitch.js +396 -0
  121. package/dist/electron/electron/gateway/channels/types.js +8 -0
  122. package/dist/electron/electron/gateway/channels/whatsapp.js +953 -0
  123. package/dist/electron/electron/gateway/context-policy.js +268 -0
  124. package/dist/electron/electron/gateway/index.js +1063 -0
  125. package/dist/electron/electron/gateway/infrastructure.js +496 -0
  126. package/dist/electron/electron/gateway/router.js +2700 -0
  127. package/dist/electron/electron/gateway/security.js +375 -0
  128. package/dist/electron/electron/gateway/session.js +115 -0
  129. package/dist/electron/electron/gateway/tunnel.js +503 -0
  130. package/dist/electron/electron/guardrails/guardrail-manager.js +348 -0
  131. package/dist/electron/electron/hooks/gmail-watcher.js +300 -0
  132. package/dist/electron/electron/hooks/index.js +46 -0
  133. package/dist/electron/electron/hooks/mappings.js +381 -0
  134. package/dist/electron/electron/hooks/server.js +480 -0
  135. package/dist/electron/electron/hooks/settings.js +447 -0
  136. package/dist/electron/electron/hooks/types.js +41 -0
  137. package/dist/electron/electron/ipc/canvas-handlers.js +158 -0
  138. package/dist/electron/electron/ipc/handlers.js +3138 -0
  139. package/dist/electron/electron/ipc/mission-control-handlers.js +141 -0
  140. package/dist/electron/electron/main.js +448 -0
  141. package/dist/electron/electron/mcp/client/MCPClientManager.js +330 -0
  142. package/dist/electron/electron/mcp/client/MCPServerConnection.js +437 -0
  143. package/dist/electron/electron/mcp/client/transports/SSETransport.js +304 -0
  144. package/dist/electron/electron/mcp/client/transports/StdioTransport.js +307 -0
  145. package/dist/electron/electron/mcp/client/transports/WebSocketTransport.js +329 -0
  146. package/dist/electron/electron/mcp/host/MCPHostServer.js +354 -0
  147. package/dist/electron/electron/mcp/host/ToolAdapter.js +100 -0
  148. package/dist/electron/electron/mcp/registry/MCPRegistryManager.js +497 -0
  149. package/dist/electron/electron/mcp/settings.js +446 -0
  150. package/dist/electron/electron/mcp/types.js +59 -0
  151. package/dist/electron/electron/memory/MemoryService.js +435 -0
  152. package/dist/electron/electron/notifications/index.js +17 -0
  153. package/dist/electron/electron/notifications/service.js +118 -0
  154. package/dist/electron/electron/notifications/store.js +144 -0
  155. package/dist/electron/electron/preload.js +842 -0
  156. package/dist/electron/electron/reports/StandupReportService.js +272 -0
  157. package/dist/electron/electron/security/concurrency.js +293 -0
  158. package/dist/electron/electron/security/index.js +15 -0
  159. package/dist/electron/electron/security/policy-manager.js +435 -0
  160. package/dist/electron/electron/settings/appearance-manager.js +193 -0
  161. package/dist/electron/electron/settings/personality-manager.js +724 -0
  162. package/dist/electron/electron/settings/x-manager.js +58 -0
  163. package/dist/electron/electron/tailscale/exposure.js +188 -0
  164. package/dist/electron/electron/tailscale/index.js +28 -0
  165. package/dist/electron/electron/tailscale/settings.js +205 -0
  166. package/dist/electron/electron/tailscale/tailscale.js +355 -0
  167. package/dist/electron/electron/tray/QuickInputWindow.js +568 -0
  168. package/dist/electron/electron/tray/TrayManager.js +895 -0
  169. package/dist/electron/electron/tray/index.js +9 -0
  170. package/dist/electron/electron/updater/index.js +6 -0
  171. package/dist/electron/electron/updater/update-manager.js +418 -0
  172. package/dist/electron/electron/utils/env-migration.js +209 -0
  173. package/dist/electron/electron/utils/process.js +102 -0
  174. package/dist/electron/electron/utils/rate-limiter.js +104 -0
  175. package/dist/electron/electron/utils/validation.js +419 -0
  176. package/dist/electron/electron/utils/x-cli.js +177 -0
  177. package/dist/electron/electron/voice/VoiceService.js +507 -0
  178. package/dist/electron/electron/voice/index.js +14 -0
  179. package/dist/electron/electron/voice/voice-settings-manager.js +359 -0
  180. package/dist/electron/shared/channelMessages.js +170 -0
  181. package/dist/electron/shared/types.js +1185 -0
  182. package/package.json +159 -0
  183. package/resources/skills/1password.json +10 -0
  184. package/resources/skills/add-documentation.json +31 -0
  185. package/resources/skills/analyze-csv.json +17 -0
  186. package/resources/skills/apple-notes.json +10 -0
  187. package/resources/skills/apple-reminders.json +10 -0
  188. package/resources/skills/auto-commenter.json +10 -0
  189. package/resources/skills/bear-notes.json +10 -0
  190. package/resources/skills/bird.json +35 -0
  191. package/resources/skills/blogwatcher.json +10 -0
  192. package/resources/skills/blucli.json +10 -0
  193. package/resources/skills/bluebubbles.json +10 -0
  194. package/resources/skills/camsnap.json +10 -0
  195. package/resources/skills/clean-imports.json +18 -0
  196. package/resources/skills/code-review.json +18 -0
  197. package/resources/skills/coding-agent.json +10 -0
  198. package/resources/skills/compare-files.json +23 -0
  199. package/resources/skills/convert-code.json +34 -0
  200. package/resources/skills/create-changelog.json +24 -0
  201. package/resources/skills/debug-error.json +17 -0
  202. package/resources/skills/dependency-check.json +10 -0
  203. package/resources/skills/discord.json +10 -0
  204. package/resources/skills/eightctl.json +10 -0
  205. package/resources/skills/explain-code.json +29 -0
  206. package/resources/skills/extract-todos.json +18 -0
  207. package/resources/skills/food-order.json +10 -0
  208. package/resources/skills/gemini.json +10 -0
  209. package/resources/skills/generate-readme.json +10 -0
  210. package/resources/skills/gifgrep.json +10 -0
  211. package/resources/skills/git-commit.json +10 -0
  212. package/resources/skills/github.json +10 -0
  213. package/resources/skills/gog.json +10 -0
  214. package/resources/skills/goplaces.json +10 -0
  215. package/resources/skills/himalaya.json +10 -0
  216. package/resources/skills/imsg.json +10 -0
  217. package/resources/skills/karpathy-guidelines.json +12 -0
  218. package/resources/skills/last30days.json +26 -0
  219. package/resources/skills/local-places.json +10 -0
  220. package/resources/skills/mcporter.json +10 -0
  221. package/resources/skills/model-usage.json +10 -0
  222. package/resources/skills/nano-banana-pro.json +10 -0
  223. package/resources/skills/nano-pdf.json +10 -0
  224. package/resources/skills/notion.json +10 -0
  225. package/resources/skills/obsidian.json +10 -0
  226. package/resources/skills/openai-image-gen.json +10 -0
  227. package/resources/skills/openai-whisper-api.json +10 -0
  228. package/resources/skills/openai-whisper.json +10 -0
  229. package/resources/skills/openhue.json +10 -0
  230. package/resources/skills/oracle.json +10 -0
  231. package/resources/skills/ordercli.json +10 -0
  232. package/resources/skills/peekaboo.json +10 -0
  233. package/resources/skills/project-structure.json +10 -0
  234. package/resources/skills/proofread.json +17 -0
  235. package/resources/skills/refactor-code.json +31 -0
  236. package/resources/skills/rename-symbol.json +23 -0
  237. package/resources/skills/sag.json +10 -0
  238. package/resources/skills/security-audit.json +18 -0
  239. package/resources/skills/session-logs.json +10 -0
  240. package/resources/skills/sherpa-onnx-tts.json +10 -0
  241. package/resources/skills/skill-creator.json +15 -0
  242. package/resources/skills/skill-hub.json +29 -0
  243. package/resources/skills/slack.json +10 -0
  244. package/resources/skills/songsee.json +10 -0
  245. package/resources/skills/sonoscli.json +10 -0
  246. package/resources/skills/spotify-player.json +10 -0
  247. package/resources/skills/startup-cfo.json +55 -0
  248. package/resources/skills/summarize-folder.json +18 -0
  249. package/resources/skills/summarize.json +10 -0
  250. package/resources/skills/things-mac.json +10 -0
  251. package/resources/skills/tmux.json +10 -0
  252. package/resources/skills/translate.json +36 -0
  253. package/resources/skills/trello.json +10 -0
  254. package/resources/skills/video-frames.json +10 -0
  255. package/resources/skills/voice-call.json +10 -0
  256. package/resources/skills/wacli.json +10 -0
  257. package/resources/skills/weather.json +10 -0
  258. package/resources/skills/write-tests.json +31 -0
  259. package/src/electron/activity/ActivityRepository.ts +238 -0
  260. package/src/electron/agent/browser/browser-service.ts +721 -0
  261. package/src/electron/agent/context-manager.ts +257 -0
  262. package/src/electron/agent/custom-skill-loader.ts +634 -0
  263. package/src/electron/agent/daemon.ts +1097 -0
  264. package/src/electron/agent/executor.ts +4017 -0
  265. package/src/electron/agent/llm/anthropic-provider.ts +175 -0
  266. package/src/electron/agent/llm/bedrock-provider.ts +236 -0
  267. package/src/electron/agent/llm/gemini-provider.ts +422 -0
  268. package/src/electron/agent/llm/index.ts +9 -0
  269. package/src/electron/agent/llm/ollama-provider.ts +347 -0
  270. package/src/electron/agent/llm/openai-oauth.ts +127 -0
  271. package/src/electron/agent/llm/openai-provider.ts +686 -0
  272. package/src/electron/agent/llm/openrouter-provider.ts +273 -0
  273. package/src/electron/agent/llm/pricing.ts +180 -0
  274. package/src/electron/agent/llm/provider-factory.ts +971 -0
  275. package/src/electron/agent/llm/types.ts +291 -0
  276. package/src/electron/agent/queue-manager.ts +408 -0
  277. package/src/electron/agent/sandbox/docker-sandbox.ts +453 -0
  278. package/src/electron/agent/sandbox/macos-sandbox.ts +426 -0
  279. package/src/electron/agent/sandbox/runner.ts +453 -0
  280. package/src/electron/agent/sandbox/sandbox-factory.ts +337 -0
  281. package/src/electron/agent/sandbox/security-utils.ts +251 -0
  282. package/src/electron/agent/search/brave-provider.ts +141 -0
  283. package/src/electron/agent/search/google-provider.ts +131 -0
  284. package/src/electron/agent/search/index.ts +6 -0
  285. package/src/electron/agent/search/provider-factory.ts +450 -0
  286. package/src/electron/agent/search/serpapi-provider.ts +138 -0
  287. package/src/electron/agent/search/tavily-provider.ts +108 -0
  288. package/src/electron/agent/search/types.ts +118 -0
  289. package/src/electron/agent/security/index.ts +20 -0
  290. package/src/electron/agent/security/input-sanitizer.ts +380 -0
  291. package/src/electron/agent/security/output-filter.ts +259 -0
  292. package/src/electron/agent/skill-eligibility.ts +334 -0
  293. package/src/electron/agent/skill-registry.ts +457 -0
  294. package/src/electron/agent/skills/document.ts +1070 -0
  295. package/src/electron/agent/skills/image-generator.ts +272 -0
  296. package/src/electron/agent/skills/organizer.ts +131 -0
  297. package/src/electron/agent/skills/presentation.ts +418 -0
  298. package/src/electron/agent/skills/spreadsheet.ts +166 -0
  299. package/src/electron/agent/tools/browser-tools.ts +546 -0
  300. package/src/electron/agent/tools/builtin-settings.ts +422 -0
  301. package/src/electron/agent/tools/canvas-tools.ts +572 -0
  302. package/src/electron/agent/tools/cron-tools.ts +723 -0
  303. package/src/electron/agent/tools/edit-tools.ts +196 -0
  304. package/src/electron/agent/tools/file-tools.ts +811 -0
  305. package/src/electron/agent/tools/glob-tools.ts +303 -0
  306. package/src/electron/agent/tools/grep-tools.ts +432 -0
  307. package/src/electron/agent/tools/image-tools.ts +126 -0
  308. package/src/electron/agent/tools/mention-tools.ts +371 -0
  309. package/src/electron/agent/tools/node-tools.ts +550 -0
  310. package/src/electron/agent/tools/registry.ts +3052 -0
  311. package/src/electron/agent/tools/search-tools.ts +111 -0
  312. package/src/electron/agent/tools/shell-tools.ts +651 -0
  313. package/src/electron/agent/tools/skill-tools.ts +340 -0
  314. package/src/electron/agent/tools/system-tools.ts +665 -0
  315. package/src/electron/agent/tools/web-fetch-tools.ts +528 -0
  316. package/src/electron/agent/tools/x-tools.ts +267 -0
  317. package/src/electron/agents/AgentRoleRepository.ts +557 -0
  318. package/src/electron/agents/HeartbeatService.ts +469 -0
  319. package/src/electron/agents/MentionRepository.ts +242 -0
  320. package/src/electron/agents/TaskSubscriptionRepository.ts +231 -0
  321. package/src/electron/agents/WorkingStateRepository.ts +278 -0
  322. package/src/electron/canvas/canvas-manager.ts +818 -0
  323. package/src/electron/canvas/canvas-preload.ts +102 -0
  324. package/src/electron/canvas/canvas-protocol.ts +174 -0
  325. package/src/electron/canvas/canvas-store.ts +200 -0
  326. package/src/electron/canvas/index.ts +8 -0
  327. package/src/electron/control-plane/client.ts +527 -0
  328. package/src/electron/control-plane/handlers.ts +723 -0
  329. package/src/electron/control-plane/index.ts +51 -0
  330. package/src/electron/control-plane/node-manager.ts +322 -0
  331. package/src/electron/control-plane/protocol.ts +269 -0
  332. package/src/electron/control-plane/remote-client.ts +517 -0
  333. package/src/electron/control-plane/server.ts +853 -0
  334. package/src/electron/control-plane/settings.ts +401 -0
  335. package/src/electron/control-plane/ssh-tunnel.ts +624 -0
  336. package/src/electron/cron/index.ts +9 -0
  337. package/src/electron/cron/schedule.ts +217 -0
  338. package/src/electron/cron/service.ts +743 -0
  339. package/src/electron/cron/store.ts +165 -0
  340. package/src/electron/cron/types.ts +291 -0
  341. package/src/electron/cron/webhook.ts +303 -0
  342. package/src/electron/database/SecureSettingsRepository.ts +514 -0
  343. package/src/electron/database/TaskLabelRepository.ts +148 -0
  344. package/src/electron/database/repositories.ts +2397 -0
  345. package/src/electron/database/schema.ts +1017 -0
  346. package/src/electron/extensions/index.ts +18 -0
  347. package/src/electron/extensions/loader.ts +336 -0
  348. package/src/electron/extensions/registry.ts +546 -0
  349. package/src/electron/extensions/types.ts +372 -0
  350. package/src/electron/gateway/channel-registry.ts +1267 -0
  351. package/src/electron/gateway/channels/bluebubbles-client.ts +641 -0
  352. package/src/electron/gateway/channels/bluebubbles.ts +509 -0
  353. package/src/electron/gateway/channels/discord.ts +1150 -0
  354. package/src/electron/gateway/channels/email-client.ts +708 -0
  355. package/src/electron/gateway/channels/email.ts +516 -0
  356. package/src/electron/gateway/channels/google-chat.ts +760 -0
  357. package/src/electron/gateway/channels/imessage-client.ts +473 -0
  358. package/src/electron/gateway/channels/imessage.ts +520 -0
  359. package/src/electron/gateway/channels/index.ts +21 -0
  360. package/src/electron/gateway/channels/line-client.ts +598 -0
  361. package/src/electron/gateway/channels/line.ts +559 -0
  362. package/src/electron/gateway/channels/matrix-client.ts +632 -0
  363. package/src/electron/gateway/channels/matrix.ts +655 -0
  364. package/src/electron/gateway/channels/mattermost-client.ts +526 -0
  365. package/src/electron/gateway/channels/mattermost.ts +550 -0
  366. package/src/electron/gateway/channels/signal-client.ts +722 -0
  367. package/src/electron/gateway/channels/signal.ts +666 -0
  368. package/src/electron/gateway/channels/slack.ts +458 -0
  369. package/src/electron/gateway/channels/teams.ts +681 -0
  370. package/src/electron/gateway/channels/telegram.ts +1727 -0
  371. package/src/electron/gateway/channels/twitch-client.ts +665 -0
  372. package/src/electron/gateway/channels/twitch.ts +468 -0
  373. package/src/electron/gateway/channels/types.ts +1002 -0
  374. package/src/electron/gateway/channels/whatsapp.ts +1101 -0
  375. package/src/electron/gateway/context-policy.ts +382 -0
  376. package/src/electron/gateway/index.ts +1274 -0
  377. package/src/electron/gateway/infrastructure.ts +645 -0
  378. package/src/electron/gateway/router.ts +3206 -0
  379. package/src/electron/gateway/security.ts +422 -0
  380. package/src/electron/gateway/session.ts +144 -0
  381. package/src/electron/gateway/tunnel.ts +626 -0
  382. package/src/electron/guardrails/guardrail-manager.ts +380 -0
  383. package/src/electron/hooks/gmail-watcher.ts +355 -0
  384. package/src/electron/hooks/index.ts +30 -0
  385. package/src/electron/hooks/mappings.ts +404 -0
  386. package/src/electron/hooks/server.ts +574 -0
  387. package/src/electron/hooks/settings.ts +466 -0
  388. package/src/electron/hooks/types.ts +245 -0
  389. package/src/electron/ipc/canvas-handlers.ts +223 -0
  390. package/src/electron/ipc/handlers.ts +3661 -0
  391. package/src/electron/ipc/mission-control-handlers.ts +182 -0
  392. package/src/electron/main.ts +496 -0
  393. package/src/electron/mcp/client/MCPClientManager.ts +406 -0
  394. package/src/electron/mcp/client/MCPServerConnection.ts +514 -0
  395. package/src/electron/mcp/client/transports/SSETransport.ts +360 -0
  396. package/src/electron/mcp/client/transports/StdioTransport.ts +355 -0
  397. package/src/electron/mcp/client/transports/WebSocketTransport.ts +384 -0
  398. package/src/electron/mcp/host/MCPHostServer.ts +388 -0
  399. package/src/electron/mcp/host/ToolAdapter.ts +140 -0
  400. package/src/electron/mcp/registry/MCPRegistryManager.ts +565 -0
  401. package/src/electron/mcp/settings.ts +468 -0
  402. package/src/electron/mcp/types.ts +371 -0
  403. package/src/electron/memory/MemoryService.ts +523 -0
  404. package/src/electron/notifications/index.ts +16 -0
  405. package/src/electron/notifications/service.ts +161 -0
  406. package/src/electron/notifications/store.ts +163 -0
  407. package/src/electron/preload.ts +2845 -0
  408. package/src/electron/reports/StandupReportService.ts +356 -0
  409. package/src/electron/security/concurrency.ts +333 -0
  410. package/src/electron/security/index.ts +17 -0
  411. package/src/electron/security/policy-manager.ts +539 -0
  412. package/src/electron/settings/appearance-manager.ts +182 -0
  413. package/src/electron/settings/personality-manager.ts +800 -0
  414. package/src/electron/settings/x-manager.ts +62 -0
  415. package/src/electron/tailscale/exposure.ts +262 -0
  416. package/src/electron/tailscale/index.ts +34 -0
  417. package/src/electron/tailscale/settings.ts +218 -0
  418. package/src/electron/tailscale/tailscale.ts +379 -0
  419. package/src/electron/tray/QuickInputWindow.ts +609 -0
  420. package/src/electron/tray/TrayManager.ts +1005 -0
  421. package/src/electron/tray/index.ts +6 -0
  422. package/src/electron/updater/index.ts +1 -0
  423. package/src/electron/updater/update-manager.ts +447 -0
  424. package/src/electron/utils/env-migration.ts +203 -0
  425. package/src/electron/utils/process.ts +124 -0
  426. package/src/electron/utils/rate-limiter.ts +130 -0
  427. package/src/electron/utils/validation.ts +493 -0
  428. package/src/electron/utils/x-cli.ts +198 -0
  429. package/src/electron/voice/VoiceService.ts +583 -0
  430. package/src/electron/voice/index.ts +9 -0
  431. package/src/electron/voice/voice-settings-manager.ts +403 -0
  432. package/src/renderer/App.tsx +775 -0
  433. package/src/renderer/components/ActivityFeed.tsx +407 -0
  434. package/src/renderer/components/ActivityFeedItem.tsx +285 -0
  435. package/src/renderer/components/AgentRoleCard.tsx +343 -0
  436. package/src/renderer/components/AgentRoleEditor.tsx +805 -0
  437. package/src/renderer/components/AgentSquadSettings.tsx +295 -0
  438. package/src/renderer/components/AgentWorkingStatePanel.tsx +411 -0
  439. package/src/renderer/components/AppearanceSettings.tsx +122 -0
  440. package/src/renderer/components/ApprovalDialog.tsx +100 -0
  441. package/src/renderer/components/BlueBubblesSettings.tsx +505 -0
  442. package/src/renderer/components/BuiltinToolsSettings.tsx +307 -0
  443. package/src/renderer/components/CanvasPreview.tsx +1189 -0
  444. package/src/renderer/components/CommandOutput.tsx +202 -0
  445. package/src/renderer/components/ContextPolicySettings.tsx +523 -0
  446. package/src/renderer/components/ControlPlaneSettings.tsx +1134 -0
  447. package/src/renderer/components/DisclaimerModal.tsx +124 -0
  448. package/src/renderer/components/DiscordSettings.tsx +436 -0
  449. package/src/renderer/components/EmailSettings.tsx +606 -0
  450. package/src/renderer/components/ExtensionsSettings.tsx +542 -0
  451. package/src/renderer/components/FileViewer.tsx +224 -0
  452. package/src/renderer/components/GoogleChatSettings.tsx +535 -0
  453. package/src/renderer/components/GuardrailSettings.tsx +487 -0
  454. package/src/renderer/components/HooksSettings.tsx +581 -0
  455. package/src/renderer/components/ImessageSettings.tsx +484 -0
  456. package/src/renderer/components/LineSettings.tsx +483 -0
  457. package/src/renderer/components/MCPRegistryBrowser.tsx +386 -0
  458. package/src/renderer/components/MCPSettings.tsx +943 -0
  459. package/src/renderer/components/MainContent.tsx +2433 -0
  460. package/src/renderer/components/MatrixSettings.tsx +510 -0
  461. package/src/renderer/components/MattermostSettings.tsx +473 -0
  462. package/src/renderer/components/MemorySettings.tsx +247 -0
  463. package/src/renderer/components/MentionBadge.tsx +87 -0
  464. package/src/renderer/components/MentionInput.tsx +409 -0
  465. package/src/renderer/components/MentionList.tsx +476 -0
  466. package/src/renderer/components/MissionControlPanel.tsx +1995 -0
  467. package/src/renderer/components/NodesSettings.tsx +316 -0
  468. package/src/renderer/components/NotificationPanel.tsx +481 -0
  469. package/src/renderer/components/Onboarding/AwakeningOrb.tsx +44 -0
  470. package/src/renderer/components/Onboarding/Onboarding.tsx +443 -0
  471. package/src/renderer/components/Onboarding/TypewriterText.tsx +102 -0
  472. package/src/renderer/components/Onboarding/index.ts +3 -0
  473. package/src/renderer/components/OnboardingModal.tsx +698 -0
  474. package/src/renderer/components/PairingCodeDisplay.tsx +324 -0
  475. package/src/renderer/components/PersonalitySettings.tsx +597 -0
  476. package/src/renderer/components/QueueSettings.tsx +119 -0
  477. package/src/renderer/components/QuickTaskFAB.tsx +71 -0
  478. package/src/renderer/components/RightPanel.tsx +413 -0
  479. package/src/renderer/components/ScheduledTasksSettings.tsx +1328 -0
  480. package/src/renderer/components/SearchSettings.tsx +328 -0
  481. package/src/renderer/components/Settings.tsx +1504 -0
  482. package/src/renderer/components/Sidebar.tsx +344 -0
  483. package/src/renderer/components/SignalSettings.tsx +673 -0
  484. package/src/renderer/components/SkillHubBrowser.tsx +458 -0
  485. package/src/renderer/components/SkillParameterModal.tsx +185 -0
  486. package/src/renderer/components/SkillsSettings.tsx +451 -0
  487. package/src/renderer/components/SlackSettings.tsx +442 -0
  488. package/src/renderer/components/StandupReportViewer.tsx +614 -0
  489. package/src/renderer/components/TaskBoard.tsx +498 -0
  490. package/src/renderer/components/TaskBoardCard.tsx +357 -0
  491. package/src/renderer/components/TaskBoardColumn.tsx +211 -0
  492. package/src/renderer/components/TaskLabelManager.tsx +472 -0
  493. package/src/renderer/components/TaskQueuePanel.tsx +144 -0
  494. package/src/renderer/components/TaskQuickActions.tsx +492 -0
  495. package/src/renderer/components/TaskTimeline.tsx +216 -0
  496. package/src/renderer/components/TaskView.tsx +162 -0
  497. package/src/renderer/components/TeamsSettings.tsx +518 -0
  498. package/src/renderer/components/TelegramSettings.tsx +421 -0
  499. package/src/renderer/components/Toast.tsx +76 -0
  500. package/src/renderer/components/TraySettings.tsx +189 -0
  501. package/src/renderer/components/TwitchSettings.tsx +511 -0
  502. package/src/renderer/components/UpdateSettings.tsx +295 -0
  503. package/src/renderer/components/VoiceIndicator.tsx +270 -0
  504. package/src/renderer/components/VoiceSettings.tsx +867 -0
  505. package/src/renderer/components/WhatsAppSettings.tsx +721 -0
  506. package/src/renderer/components/WorkingStateEditor.tsx +309 -0
  507. package/src/renderer/components/WorkingStateHistory.tsx +481 -0
  508. package/src/renderer/components/WorkspaceSelector.tsx +150 -0
  509. package/src/renderer/components/XSettings.tsx +311 -0
  510. package/src/renderer/global.d.ts +9 -0
  511. package/src/renderer/hooks/useAgentContext.ts +153 -0
  512. package/src/renderer/hooks/useOnboardingFlow.ts +548 -0
  513. package/src/renderer/hooks/useVoiceInput.ts +268 -0
  514. package/src/renderer/index.html +12 -0
  515. package/src/renderer/main.tsx +10 -0
  516. package/src/renderer/public/cowork-os-logo.png +0 -0
  517. package/src/renderer/quick-input.html +164 -0
  518. package/src/renderer/styles/index.css +14504 -0
  519. package/src/renderer/utils/agentMessages.ts +749 -0
  520. package/src/renderer/utils/voice-directives.ts +169 -0
  521. package/src/shared/channelMessages.ts +213 -0
  522. package/src/shared/types.ts +3608 -0
  523. package/tsconfig.electron.json +26 -0
  524. package/tsconfig.json +26 -0
  525. package/tsconfig.node.json +10 -0
  526. package/vite.config.ts +23 -0
@@ -0,0 +1,1781 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemorySettingsRepository = exports.MemorySummaryRepository = exports.MemoryRepository = exports.AuditLogRepository = exports.RateLimitRepository = exports.DeliveryTrackingRepository = exports.ScheduledMessageRepository = exports.MessageQueueRepository = exports.ChannelMessageRepository = exports.ChannelSessionRepository = exports.ChannelUserRepository = exports.ChannelRepository = exports.LLMModelRepository = exports.SkillRepository = exports.ApprovalRepository = exports.ArtifactRepository = exports.TaskEventRepository = exports.TaskRepository = exports.WorkspaceRepository = void 0;
4
+ const uuid_1 = require("uuid");
5
+ /**
6
+ * Safely parse JSON with error handling
7
+ * Returns defaultValue if parsing fails
8
+ */
9
+ function safeJsonParse(jsonString, defaultValue, context) {
10
+ try {
11
+ return JSON.parse(jsonString);
12
+ }
13
+ catch (error) {
14
+ console.error(`Failed to parse JSON${context ? ` in ${context}` : ''}:`, error, 'Input:', jsonString?.slice(0, 100));
15
+ return defaultValue;
16
+ }
17
+ }
18
+ class WorkspaceRepository {
19
+ constructor(db) {
20
+ this.db = db;
21
+ }
22
+ create(name, path, permissions) {
23
+ const workspace = {
24
+ id: (0, uuid_1.v4)(),
25
+ name,
26
+ path,
27
+ createdAt: Date.now(),
28
+ permissions,
29
+ };
30
+ const stmt = this.db.prepare(`
31
+ INSERT INTO workspaces (id, name, path, created_at, permissions)
32
+ VALUES (?, ?, ?, ?, ?)
33
+ `);
34
+ stmt.run(workspace.id, workspace.name, workspace.path, workspace.createdAt, JSON.stringify(workspace.permissions));
35
+ return workspace;
36
+ }
37
+ findById(id) {
38
+ const stmt = this.db.prepare('SELECT * FROM workspaces WHERE id = ?');
39
+ const row = stmt.get(id);
40
+ return row ? this.mapRowToWorkspace(row) : undefined;
41
+ }
42
+ findAll() {
43
+ const stmt = this.db.prepare('SELECT * FROM workspaces ORDER BY created_at DESC');
44
+ const rows = stmt.all();
45
+ return rows.map(row => this.mapRowToWorkspace(row));
46
+ }
47
+ /**
48
+ * Check if a workspace with the given path already exists
49
+ */
50
+ existsByPath(path) {
51
+ const stmt = this.db.prepare('SELECT 1 FROM workspaces WHERE path = ?');
52
+ const row = stmt.get(path);
53
+ return !!row;
54
+ }
55
+ /**
56
+ * Find a workspace by its path
57
+ */
58
+ findByPath(path) {
59
+ const stmt = this.db.prepare('SELECT * FROM workspaces WHERE path = ?');
60
+ const row = stmt.get(path);
61
+ return row ? this.mapRowToWorkspace(row) : undefined;
62
+ }
63
+ /**
64
+ * Update workspace permissions
65
+ */
66
+ updatePermissions(id, permissions) {
67
+ const stmt = this.db.prepare('UPDATE workspaces SET permissions = ? WHERE id = ?');
68
+ stmt.run(JSON.stringify(permissions), id);
69
+ }
70
+ /**
71
+ * Delete a workspace by ID
72
+ */
73
+ delete(id) {
74
+ const stmt = this.db.prepare('DELETE FROM workspaces WHERE id = ?');
75
+ stmt.run(id);
76
+ }
77
+ mapRowToWorkspace(row) {
78
+ // Note: network is true by default for browser tools (web access)
79
+ const defaultPermissions = { read: true, write: true, delete: false, network: true, shell: false };
80
+ const storedPermissions = safeJsonParse(row.permissions, defaultPermissions, 'workspace.permissions');
81
+ // Merge with defaults to ensure new fields (like network) get proper defaults
82
+ // for workspaces created before those fields existed
83
+ const mergedPermissions = {
84
+ ...defaultPermissions,
85
+ ...storedPermissions,
86
+ };
87
+ // Migration: if network was explicitly false (old default), upgrade it to true
88
+ // This ensures existing workspaces get browser tool access
89
+ if (storedPermissions.network === false) {
90
+ mergedPermissions.network = true;
91
+ }
92
+ return {
93
+ id: row.id,
94
+ name: row.name,
95
+ path: row.path,
96
+ createdAt: row.created_at,
97
+ permissions: mergedPermissions,
98
+ };
99
+ }
100
+ }
101
+ exports.WorkspaceRepository = WorkspaceRepository;
102
+ class TaskRepository {
103
+ constructor(db) {
104
+ this.db = db;
105
+ }
106
+ create(task) {
107
+ const newTask = {
108
+ ...task,
109
+ id: (0, uuid_1.v4)(),
110
+ createdAt: Date.now(),
111
+ updatedAt: Date.now(),
112
+ };
113
+ const stmt = this.db.prepare(`
114
+ INSERT INTO tasks (id, title, prompt, status, workspace_id, created_at, updated_at, budget_tokens, budget_cost, success_criteria, max_attempts, current_attempt, parent_task_id, agent_type, agent_config, depth, result_summary)
115
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
116
+ `);
117
+ stmt.run(newTask.id, newTask.title, newTask.prompt, newTask.status, newTask.workspaceId, newTask.createdAt, newTask.updatedAt, newTask.budgetTokens || null, newTask.budgetCost || null, newTask.successCriteria ? JSON.stringify(newTask.successCriteria) : null, newTask.maxAttempts || null, newTask.currentAttempt || 1, newTask.parentTaskId || null, newTask.agentType || 'main', newTask.agentConfig ? JSON.stringify(newTask.agentConfig) : null, newTask.depth ?? 0, newTask.resultSummary || null);
118
+ return newTask;
119
+ }
120
+ update(id, updates) {
121
+ const fields = [];
122
+ const values = [];
123
+ Object.entries(updates).forEach(([key, value]) => {
124
+ // Validate field name against whitelist
125
+ if (!TaskRepository.ALLOWED_UPDATE_FIELDS.has(key)) {
126
+ console.warn(`Ignoring unknown field in task update: ${key}`);
127
+ return;
128
+ }
129
+ const snakeKey = key.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
130
+ fields.push(`${snakeKey} = ?`);
131
+ // JSON serialize object/array fields
132
+ if ((key === 'successCriteria' || key === 'agentConfig' || key === 'labels' || key === 'mentionedAgentRoleIds') && value != null) {
133
+ values.push(JSON.stringify(value));
134
+ }
135
+ else {
136
+ values.push(value);
137
+ }
138
+ });
139
+ if (fields.length === 0) {
140
+ return; // No valid fields to update
141
+ }
142
+ fields.push('updated_at = ?');
143
+ values.push(Date.now());
144
+ values.push(id);
145
+ const stmt = this.db.prepare(`UPDATE tasks SET ${fields.join(', ')} WHERE id = ?`);
146
+ stmt.run(...values);
147
+ }
148
+ findById(id) {
149
+ const stmt = this.db.prepare('SELECT * FROM tasks WHERE id = ?');
150
+ const row = stmt.get(id);
151
+ return row ? this.mapRowToTask(row) : undefined;
152
+ }
153
+ findAll(limit = 100, offset = 0) {
154
+ const stmt = this.db.prepare(`
155
+ SELECT * FROM tasks
156
+ ORDER BY created_at DESC
157
+ LIMIT ? OFFSET ?
158
+ `);
159
+ const rows = stmt.all(limit, offset);
160
+ return rows.map(row => this.mapRowToTask(row));
161
+ }
162
+ /**
163
+ * Find tasks by status (single status or array of statuses)
164
+ */
165
+ findByStatus(status) {
166
+ const statuses = Array.isArray(status) ? status : [status];
167
+ const placeholders = statuses.map(() => '?').join(', ');
168
+ const stmt = this.db.prepare(`
169
+ SELECT * FROM tasks
170
+ WHERE status IN (${placeholders})
171
+ ORDER BY created_at ASC
172
+ `);
173
+ const rows = stmt.all(...statuses);
174
+ return rows.map(row => this.mapRowToTask(row));
175
+ }
176
+ /**
177
+ * Find tasks by workspace ID
178
+ */
179
+ findByWorkspace(workspaceId) {
180
+ const stmt = this.db.prepare(`
181
+ SELECT * FROM tasks
182
+ WHERE workspace_id = ?
183
+ ORDER BY created_at DESC
184
+ `);
185
+ const rows = stmt.all(workspaceId);
186
+ return rows.map(row => this.mapRowToTask(row));
187
+ }
188
+ delete(id) {
189
+ // Use transaction to ensure atomic deletion
190
+ const deleteTransaction = this.db.transaction((taskId) => {
191
+ // Delete related records from all tables with foreign keys to tasks
192
+ const deleteEvents = this.db.prepare('DELETE FROM task_events WHERE task_id = ?');
193
+ deleteEvents.run(taskId);
194
+ const deleteArtifacts = this.db.prepare('DELETE FROM artifacts WHERE task_id = ?');
195
+ deleteArtifacts.run(taskId);
196
+ const deleteApprovals = this.db.prepare('DELETE FROM approvals WHERE task_id = ?');
197
+ deleteApprovals.run(taskId);
198
+ // Delete activity feed entries for this task
199
+ const deleteActivities = this.db.prepare('DELETE FROM activity_feed WHERE task_id = ?');
200
+ deleteActivities.run(taskId);
201
+ // Delete agent mentions for this task
202
+ const deleteMentions = this.db.prepare('DELETE FROM agent_mentions WHERE task_id = ?');
203
+ deleteMentions.run(taskId);
204
+ // Delete working state entries for this task
205
+ const deleteWorkingState = this.db.prepare('DELETE FROM agent_working_state WHERE task_id = ?');
206
+ deleteWorkingState.run(taskId);
207
+ // Nullify task_id in memories rather than deleting them
208
+ const clearMemoryTaskId = this.db.prepare('UPDATE memories SET task_id = NULL WHERE task_id = ?');
209
+ clearMemoryTaskId.run(taskId);
210
+ // Nullify task_id in channel_sessions rather than deleting the session
211
+ const clearSessionTaskId = this.db.prepare('UPDATE channel_sessions SET task_id = NULL WHERE task_id = ?');
212
+ clearSessionTaskId.run(taskId);
213
+ // Finally delete the task
214
+ const deleteTask = this.db.prepare('DELETE FROM tasks WHERE id = ?');
215
+ deleteTask.run(taskId);
216
+ });
217
+ deleteTransaction(id);
218
+ }
219
+ mapRowToTask(row) {
220
+ return {
221
+ id: row.id,
222
+ title: row.title,
223
+ prompt: row.prompt,
224
+ status: row.status,
225
+ workspaceId: row.workspace_id,
226
+ createdAt: row.created_at,
227
+ updatedAt: row.updated_at,
228
+ completedAt: row.completed_at || undefined,
229
+ budgetTokens: row.budget_tokens || undefined,
230
+ budgetCost: row.budget_cost || undefined,
231
+ error: row.error || undefined,
232
+ // Goal Mode fields
233
+ successCriteria: row.success_criteria ? safeJsonParse(row.success_criteria, undefined, 'task.successCriteria') : undefined,
234
+ maxAttempts: row.max_attempts || undefined,
235
+ currentAttempt: row.current_attempt || undefined,
236
+ // Sub-Agent / Parallel Agent fields
237
+ parentTaskId: row.parent_task_id || undefined,
238
+ agentType: row.agent_type || undefined,
239
+ agentConfig: row.agent_config ? safeJsonParse(row.agent_config, undefined, 'task.agentConfig') : undefined,
240
+ depth: row.depth ?? undefined,
241
+ resultSummary: row.result_summary || undefined,
242
+ // Agent Squad fields
243
+ assignedAgentRoleId: row.assigned_agent_role_id || undefined,
244
+ boardColumn: row.board_column || undefined,
245
+ priority: row.priority ?? undefined,
246
+ // Task Board fields
247
+ labels: row.labels ? safeJsonParse(row.labels, [], 'task.labels') : undefined,
248
+ dueDate: row.due_date || undefined,
249
+ estimatedMinutes: row.estimated_minutes || undefined,
250
+ actualMinutes: row.actual_minutes || undefined,
251
+ mentionedAgentRoleIds: row.mentioned_agent_role_ids ? safeJsonParse(row.mentioned_agent_role_ids, [], 'task.mentionedAgentRoleIds') : undefined,
252
+ };
253
+ }
254
+ /**
255
+ * Find tasks by parent task ID
256
+ */
257
+ findByParent(parentTaskId) {
258
+ const stmt = this.db.prepare(`
259
+ SELECT * FROM tasks
260
+ WHERE parent_task_id = ?
261
+ ORDER BY created_at ASC
262
+ `);
263
+ const rows = stmt.all(parentTaskId);
264
+ return rows.map(row => this.mapRowToTask(row));
265
+ }
266
+ // ============ Task Board Methods ============
267
+ /**
268
+ * Find tasks by workspace and board column
269
+ */
270
+ findByBoardColumn(workspaceId, boardColumn) {
271
+ const stmt = this.db.prepare(`
272
+ SELECT * FROM tasks
273
+ WHERE workspace_id = ? AND board_column = ?
274
+ ORDER BY priority DESC, created_at ASC
275
+ `);
276
+ const rows = stmt.all(workspaceId, boardColumn);
277
+ return rows.map(row => this.mapRowToTask(row));
278
+ }
279
+ /**
280
+ * Get tasks grouped by board column for a workspace
281
+ */
282
+ getTaskBoard(workspaceId) {
283
+ const stmt = this.db.prepare(`
284
+ SELECT * FROM tasks
285
+ WHERE workspace_id = ? AND parent_task_id IS NULL
286
+ ORDER BY board_column, priority DESC, created_at ASC
287
+ `);
288
+ const rows = stmt.all(workspaceId);
289
+ const tasks = rows.map(row => this.mapRowToTask(row));
290
+ // Group tasks by board column
291
+ const board = {
292
+ backlog: [],
293
+ todo: [],
294
+ in_progress: [],
295
+ review: [],
296
+ done: [],
297
+ };
298
+ for (const task of tasks) {
299
+ const column = task.boardColumn || 'backlog';
300
+ if (board[column]) {
301
+ board[column].push(task);
302
+ }
303
+ else {
304
+ board.backlog.push(task);
305
+ }
306
+ }
307
+ return board;
308
+ }
309
+ /**
310
+ * Move a task to a different board column
311
+ */
312
+ moveToColumn(id, boardColumn) {
313
+ this.update(id, { boardColumn: boardColumn });
314
+ return this.findById(id);
315
+ }
316
+ /**
317
+ * Set task priority
318
+ */
319
+ setPriority(id, priority) {
320
+ this.update(id, { priority });
321
+ return this.findById(id);
322
+ }
323
+ /**
324
+ * Set task due date
325
+ */
326
+ setDueDate(id, dueDate) {
327
+ this.update(id, { dueDate: dueDate || undefined });
328
+ return this.findById(id);
329
+ }
330
+ /**
331
+ * Set task time estimate
332
+ */
333
+ setEstimate(id, estimatedMinutes) {
334
+ this.update(id, { estimatedMinutes: estimatedMinutes || undefined });
335
+ return this.findById(id);
336
+ }
337
+ /**
338
+ * Add a label to a task
339
+ */
340
+ addLabel(id, labelId) {
341
+ const task = this.findById(id);
342
+ if (!task)
343
+ return undefined;
344
+ const labels = task.labels || [];
345
+ if (!labels.includes(labelId)) {
346
+ labels.push(labelId);
347
+ this.update(id, { labels });
348
+ }
349
+ return this.findById(id);
350
+ }
351
+ /**
352
+ * Remove a label from a task
353
+ */
354
+ removeLabel(id, labelId) {
355
+ const task = this.findById(id);
356
+ if (!task)
357
+ return undefined;
358
+ const labels = task.labels || [];
359
+ const newLabels = labels.filter(l => l !== labelId);
360
+ this.update(id, { labels: newLabels });
361
+ return this.findById(id);
362
+ }
363
+ /**
364
+ * Assign an agent role to a task
365
+ */
366
+ assignAgentRole(id, agentRoleId) {
367
+ this.update(id, { assignedAgentRoleId: agentRoleId || undefined });
368
+ return this.findById(id);
369
+ }
370
+ }
371
+ exports.TaskRepository = TaskRepository;
372
+ // Whitelist of allowed update fields to prevent SQL injection
373
+ TaskRepository.ALLOWED_UPDATE_FIELDS = new Set([
374
+ 'title', 'status', 'error', 'result', 'budgetTokens', 'budgetCost',
375
+ 'successCriteria', 'maxAttempts', 'currentAttempt', 'completedAt',
376
+ 'parentTaskId', 'agentType', 'agentConfig', 'depth', 'resultSummary',
377
+ // Agent Squad fields
378
+ 'assignedAgentRoleId', 'boardColumn', 'priority',
379
+ // Task Board fields
380
+ 'labels', 'dueDate', 'estimatedMinutes', 'actualMinutes', 'mentionedAgentRoleIds'
381
+ ]);
382
+ class TaskEventRepository {
383
+ constructor(db) {
384
+ this.db = db;
385
+ }
386
+ create(event) {
387
+ const newEvent = {
388
+ ...event,
389
+ id: (0, uuid_1.v4)(),
390
+ };
391
+ const stmt = this.db.prepare(`
392
+ INSERT INTO task_events (id, task_id, timestamp, type, payload)
393
+ VALUES (?, ?, ?, ?, ?)
394
+ `);
395
+ stmt.run(newEvent.id, newEvent.taskId, newEvent.timestamp, newEvent.type, JSON.stringify(newEvent.payload));
396
+ return newEvent;
397
+ }
398
+ findByTaskId(taskId) {
399
+ const stmt = this.db.prepare(`
400
+ SELECT * FROM task_events
401
+ WHERE task_id = ?
402
+ ORDER BY timestamp ASC
403
+ `);
404
+ const rows = stmt.all(taskId);
405
+ return rows.map(row => this.mapRowToEvent(row));
406
+ }
407
+ mapRowToEvent(row) {
408
+ return {
409
+ id: row.id,
410
+ taskId: row.task_id,
411
+ timestamp: row.timestamp,
412
+ type: row.type,
413
+ payload: safeJsonParse(row.payload, {}, 'taskEvent.payload'),
414
+ };
415
+ }
416
+ /**
417
+ * Prune old conversation snapshots for a task, keeping only the most recent one.
418
+ * This prevents database bloat from accumulating snapshots over time.
419
+ */
420
+ pruneOldSnapshots(taskId) {
421
+ // Find all conversation_snapshot events for this task, ordered by timestamp descending
422
+ const findStmt = this.db.prepare(`
423
+ SELECT id, timestamp FROM task_events
424
+ WHERE task_id = ? AND type = 'conversation_snapshot'
425
+ ORDER BY timestamp DESC
426
+ `);
427
+ const snapshots = findStmt.all(taskId);
428
+ // Keep only the most recent one, delete the rest
429
+ if (snapshots.length > 1) {
430
+ const idsToDelete = snapshots.slice(1).map(s => s.id);
431
+ const deleteStmt = this.db.prepare(`
432
+ DELETE FROM task_events WHERE id = ?
433
+ `);
434
+ for (const id of idsToDelete) {
435
+ deleteStmt.run(id);
436
+ }
437
+ console.log(`[TaskEventRepository] Pruned ${idsToDelete.length} old snapshot(s) for task ${taskId}`);
438
+ }
439
+ }
440
+ }
441
+ exports.TaskEventRepository = TaskEventRepository;
442
+ class ArtifactRepository {
443
+ constructor(db) {
444
+ this.db = db;
445
+ }
446
+ create(artifact) {
447
+ const newArtifact = {
448
+ ...artifact,
449
+ id: (0, uuid_1.v4)(),
450
+ };
451
+ const stmt = this.db.prepare(`
452
+ INSERT INTO artifacts (id, task_id, path, mime_type, sha256, size, created_at)
453
+ VALUES (?, ?, ?, ?, ?, ?, ?)
454
+ `);
455
+ stmt.run(newArtifact.id, newArtifact.taskId, newArtifact.path, newArtifact.mimeType, newArtifact.sha256, newArtifact.size, newArtifact.createdAt);
456
+ return newArtifact;
457
+ }
458
+ findByTaskId(taskId) {
459
+ const stmt = this.db.prepare('SELECT * FROM artifacts WHERE task_id = ? ORDER BY created_at DESC');
460
+ const rows = stmt.all(taskId);
461
+ return rows.map(row => this.mapRowToArtifact(row));
462
+ }
463
+ mapRowToArtifact(row) {
464
+ return {
465
+ id: row.id,
466
+ taskId: row.task_id,
467
+ path: row.path,
468
+ mimeType: row.mime_type,
469
+ sha256: row.sha256,
470
+ size: row.size,
471
+ createdAt: row.created_at,
472
+ };
473
+ }
474
+ }
475
+ exports.ArtifactRepository = ArtifactRepository;
476
+ class ApprovalRepository {
477
+ constructor(db) {
478
+ this.db = db;
479
+ }
480
+ create(approval) {
481
+ const newApproval = {
482
+ ...approval,
483
+ id: (0, uuid_1.v4)(),
484
+ };
485
+ const stmt = this.db.prepare(`
486
+ INSERT INTO approvals (id, task_id, type, description, details, status, requested_at)
487
+ VALUES (?, ?, ?, ?, ?, ?, ?)
488
+ `);
489
+ stmt.run(newApproval.id, newApproval.taskId, newApproval.type, newApproval.description, JSON.stringify(newApproval.details), newApproval.status, newApproval.requestedAt);
490
+ return newApproval;
491
+ }
492
+ update(id, status) {
493
+ const stmt = this.db.prepare(`
494
+ UPDATE approvals
495
+ SET status = ?, resolved_at = ?
496
+ WHERE id = ?
497
+ `);
498
+ stmt.run(status, Date.now(), id);
499
+ }
500
+ findPendingByTaskId(taskId) {
501
+ const stmt = this.db.prepare(`
502
+ SELECT * FROM approvals
503
+ WHERE task_id = ? AND status = 'pending'
504
+ ORDER BY requested_at ASC
505
+ `);
506
+ const rows = stmt.all(taskId);
507
+ return rows.map(row => this.mapRowToApproval(row));
508
+ }
509
+ mapRowToApproval(row) {
510
+ return {
511
+ id: row.id,
512
+ taskId: row.task_id,
513
+ type: row.type,
514
+ description: row.description,
515
+ details: safeJsonParse(row.details, {}, 'approval.details'),
516
+ status: row.status,
517
+ requestedAt: row.requested_at,
518
+ resolvedAt: row.resolved_at || undefined,
519
+ };
520
+ }
521
+ }
522
+ exports.ApprovalRepository = ApprovalRepository;
523
+ class SkillRepository {
524
+ constructor(db) {
525
+ this.db = db;
526
+ }
527
+ create(skill) {
528
+ const newSkill = {
529
+ ...skill,
530
+ id: (0, uuid_1.v4)(),
531
+ };
532
+ const stmt = this.db.prepare(`
533
+ INSERT INTO skills (id, name, description, category, prompt, script_path, parameters)
534
+ VALUES (?, ?, ?, ?, ?, ?, ?)
535
+ `);
536
+ stmt.run(newSkill.id, newSkill.name, newSkill.description, newSkill.category, newSkill.prompt, newSkill.scriptPath || null, newSkill.parameters ? JSON.stringify(newSkill.parameters) : null);
537
+ return newSkill;
538
+ }
539
+ findAll() {
540
+ const stmt = this.db.prepare('SELECT * FROM skills ORDER BY name ASC');
541
+ const rows = stmt.all();
542
+ return rows.map(row => this.mapRowToSkill(row));
543
+ }
544
+ findById(id) {
545
+ const stmt = this.db.prepare('SELECT * FROM skills WHERE id = ?');
546
+ const row = stmt.get(id);
547
+ return row ? this.mapRowToSkill(row) : undefined;
548
+ }
549
+ mapRowToSkill(row) {
550
+ return {
551
+ id: row.id,
552
+ name: row.name,
553
+ description: row.description,
554
+ category: row.category,
555
+ prompt: row.prompt,
556
+ scriptPath: row.script_path || undefined,
557
+ parameters: row.parameters ? safeJsonParse(row.parameters, undefined, 'skill.parameters') : undefined,
558
+ };
559
+ }
560
+ }
561
+ exports.SkillRepository = SkillRepository;
562
+ class LLMModelRepository {
563
+ constructor(db) {
564
+ this.db = db;
565
+ }
566
+ findAll() {
567
+ const stmt = this.db.prepare(`
568
+ SELECT * FROM llm_models
569
+ WHERE is_active = 1
570
+ ORDER BY sort_order ASC
571
+ `);
572
+ const rows = stmt.all();
573
+ return rows.map(row => this.mapRowToModel(row));
574
+ }
575
+ findByKey(key) {
576
+ const stmt = this.db.prepare('SELECT * FROM llm_models WHERE key = ?');
577
+ const row = stmt.get(key);
578
+ return row ? this.mapRowToModel(row) : undefined;
579
+ }
580
+ findById(id) {
581
+ const stmt = this.db.prepare('SELECT * FROM llm_models WHERE id = ?');
582
+ const row = stmt.get(id);
583
+ return row ? this.mapRowToModel(row) : undefined;
584
+ }
585
+ mapRowToModel(row) {
586
+ return {
587
+ id: row.id,
588
+ key: row.key,
589
+ displayName: row.display_name,
590
+ description: row.description,
591
+ anthropicModelId: row.anthropic_model_id,
592
+ bedrockModelId: row.bedrock_model_id,
593
+ sortOrder: row.sort_order,
594
+ isActive: row.is_active === 1,
595
+ createdAt: row.created_at,
596
+ updatedAt: row.updated_at,
597
+ };
598
+ }
599
+ }
600
+ exports.LLMModelRepository = LLMModelRepository;
601
+ class ChannelRepository {
602
+ constructor(db) {
603
+ this.db = db;
604
+ }
605
+ create(channel) {
606
+ const now = Date.now();
607
+ const newChannel = {
608
+ ...channel,
609
+ id: (0, uuid_1.v4)(),
610
+ createdAt: now,
611
+ updatedAt: now,
612
+ };
613
+ const stmt = this.db.prepare(`
614
+ INSERT INTO channels (id, type, name, enabled, config, security_config, status, bot_username, created_at, updated_at)
615
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
616
+ `);
617
+ stmt.run(newChannel.id, newChannel.type, newChannel.name, newChannel.enabled ? 1 : 0, JSON.stringify(newChannel.config), JSON.stringify(newChannel.securityConfig), newChannel.status, newChannel.botUsername || null, newChannel.createdAt, newChannel.updatedAt);
618
+ return newChannel;
619
+ }
620
+ update(id, updates) {
621
+ const fields = [];
622
+ const values = [];
623
+ if (updates.name !== undefined) {
624
+ fields.push('name = ?');
625
+ values.push(updates.name);
626
+ }
627
+ if (updates.enabled !== undefined) {
628
+ fields.push('enabled = ?');
629
+ values.push(updates.enabled ? 1 : 0);
630
+ }
631
+ if (updates.config !== undefined) {
632
+ fields.push('config = ?');
633
+ values.push(JSON.stringify(updates.config));
634
+ }
635
+ if (updates.securityConfig !== undefined) {
636
+ fields.push('security_config = ?');
637
+ values.push(JSON.stringify(updates.securityConfig));
638
+ }
639
+ if (updates.status !== undefined) {
640
+ fields.push('status = ?');
641
+ values.push(updates.status);
642
+ }
643
+ if (updates.botUsername !== undefined) {
644
+ fields.push('bot_username = ?');
645
+ values.push(updates.botUsername);
646
+ }
647
+ if (fields.length === 0)
648
+ return;
649
+ fields.push('updated_at = ?');
650
+ values.push(Date.now());
651
+ values.push(id);
652
+ const stmt = this.db.prepare(`UPDATE channels SET ${fields.join(', ')} WHERE id = ?`);
653
+ stmt.run(...values);
654
+ }
655
+ findById(id) {
656
+ const stmt = this.db.prepare('SELECT * FROM channels WHERE id = ?');
657
+ const row = stmt.get(id);
658
+ return row ? this.mapRowToChannel(row) : undefined;
659
+ }
660
+ findByType(type) {
661
+ const stmt = this.db.prepare('SELECT * FROM channels WHERE type = ?');
662
+ const row = stmt.get(type);
663
+ return row ? this.mapRowToChannel(row) : undefined;
664
+ }
665
+ findAll() {
666
+ const stmt = this.db.prepare('SELECT * FROM channels ORDER BY created_at ASC');
667
+ const rows = stmt.all();
668
+ return rows.map(row => this.mapRowToChannel(row));
669
+ }
670
+ findEnabled() {
671
+ const stmt = this.db.prepare('SELECT * FROM channels WHERE enabled = 1 ORDER BY created_at ASC');
672
+ const rows = stmt.all();
673
+ return rows.map(row => this.mapRowToChannel(row));
674
+ }
675
+ delete(id) {
676
+ const stmt = this.db.prepare('DELETE FROM channels WHERE id = ?');
677
+ stmt.run(id);
678
+ }
679
+ mapRowToChannel(row) {
680
+ const defaultSecurityConfig = { mode: 'pairing' };
681
+ return {
682
+ id: row.id,
683
+ type: row.type,
684
+ name: row.name,
685
+ enabled: row.enabled === 1,
686
+ config: safeJsonParse(row.config, {}, 'channel.config'),
687
+ securityConfig: safeJsonParse(row.security_config, defaultSecurityConfig, 'channel.securityConfig'),
688
+ status: row.status,
689
+ botUsername: row.bot_username || undefined,
690
+ createdAt: row.created_at,
691
+ updatedAt: row.updated_at,
692
+ };
693
+ }
694
+ }
695
+ exports.ChannelRepository = ChannelRepository;
696
+ class ChannelUserRepository {
697
+ constructor(db) {
698
+ this.db = db;
699
+ }
700
+ create(user) {
701
+ const now = Date.now();
702
+ const newUser = {
703
+ ...user,
704
+ id: (0, uuid_1.v4)(),
705
+ pairingAttempts: 0,
706
+ createdAt: now,
707
+ lastSeenAt: now,
708
+ };
709
+ const stmt = this.db.prepare(`
710
+ INSERT INTO channel_users (id, channel_id, channel_user_id, display_name, username, allowed, pairing_code, pairing_attempts, pairing_expires_at, created_at, last_seen_at)
711
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
712
+ `);
713
+ stmt.run(newUser.id, newUser.channelId, newUser.channelUserId, newUser.displayName, newUser.username || null, newUser.allowed ? 1 : 0, newUser.pairingCode || null, newUser.pairingAttempts, newUser.pairingExpiresAt || null, newUser.createdAt, newUser.lastSeenAt);
714
+ return newUser;
715
+ }
716
+ update(id, updates) {
717
+ const fields = [];
718
+ const values = [];
719
+ if (updates.displayName !== undefined) {
720
+ fields.push('display_name = ?');
721
+ values.push(updates.displayName);
722
+ }
723
+ if (updates.username !== undefined) {
724
+ fields.push('username = ?');
725
+ values.push(updates.username);
726
+ }
727
+ if (updates.allowed !== undefined) {
728
+ fields.push('allowed = ?');
729
+ values.push(updates.allowed ? 1 : 0);
730
+ }
731
+ if (updates.pairingCode !== undefined) {
732
+ fields.push('pairing_code = ?');
733
+ values.push(updates.pairingCode);
734
+ }
735
+ if (updates.pairingAttempts !== undefined) {
736
+ fields.push('pairing_attempts = ?');
737
+ values.push(updates.pairingAttempts);
738
+ }
739
+ if (updates.pairingExpiresAt !== undefined) {
740
+ fields.push('pairing_expires_at = ?');
741
+ values.push(updates.pairingExpiresAt);
742
+ }
743
+ if (updates.lockoutUntil !== undefined) {
744
+ fields.push('lockout_until = ?');
745
+ values.push(updates.lockoutUntil);
746
+ }
747
+ if (updates.lastSeenAt !== undefined) {
748
+ fields.push('last_seen_at = ?');
749
+ values.push(updates.lastSeenAt);
750
+ }
751
+ if (fields.length === 0)
752
+ return;
753
+ values.push(id);
754
+ const stmt = this.db.prepare(`UPDATE channel_users SET ${fields.join(', ')} WHERE id = ?`);
755
+ stmt.run(...values);
756
+ }
757
+ findById(id) {
758
+ const stmt = this.db.prepare('SELECT * FROM channel_users WHERE id = ?');
759
+ const row = stmt.get(id);
760
+ return row ? this.mapRowToUser(row) : undefined;
761
+ }
762
+ findByChannelUserId(channelId, channelUserId) {
763
+ const stmt = this.db.prepare('SELECT * FROM channel_users WHERE channel_id = ? AND channel_user_id = ?');
764
+ const row = stmt.get(channelId, channelUserId);
765
+ return row ? this.mapRowToUser(row) : undefined;
766
+ }
767
+ findByChannelId(channelId) {
768
+ const stmt = this.db.prepare('SELECT * FROM channel_users WHERE channel_id = ? ORDER BY last_seen_at DESC');
769
+ const rows = stmt.all(channelId);
770
+ return rows.map(row => this.mapRowToUser(row));
771
+ }
772
+ findAllowedByChannelId(channelId) {
773
+ const stmt = this.db.prepare('SELECT * FROM channel_users WHERE channel_id = ? AND allowed = 1 ORDER BY last_seen_at DESC');
774
+ const rows = stmt.all(channelId);
775
+ return rows.map(row => this.mapRowToUser(row));
776
+ }
777
+ deleteByChannelId(channelId) {
778
+ const stmt = this.db.prepare('DELETE FROM channel_users WHERE channel_id = ?');
779
+ stmt.run(channelId);
780
+ }
781
+ delete(id) {
782
+ const stmt = this.db.prepare('DELETE FROM channel_users WHERE id = ?');
783
+ stmt.run(id);
784
+ }
785
+ /**
786
+ * Delete expired pending pairing entries
787
+ * These are placeholder entries created when generating pairing codes that have expired
788
+ * Returns the number of deleted entries
789
+ */
790
+ deleteExpiredPending(channelId) {
791
+ const now = Date.now();
792
+ const stmt = this.db.prepare(`
793
+ DELETE FROM channel_users
794
+ WHERE channel_id = ?
795
+ AND allowed = 0
796
+ AND channel_user_id LIKE 'pending_%'
797
+ AND pairing_expires_at IS NOT NULL
798
+ AND pairing_expires_at < ?
799
+ `);
800
+ const result = stmt.run(channelId, now);
801
+ return result.changes;
802
+ }
803
+ findByPairingCode(channelId, pairingCode) {
804
+ const stmt = this.db.prepare('SELECT * FROM channel_users WHERE channel_id = ? AND UPPER(pairing_code) = UPPER(?)');
805
+ const row = stmt.get(channelId, pairingCode);
806
+ return row ? this.mapRowToUser(row) : undefined;
807
+ }
808
+ mapRowToUser(row) {
809
+ return {
810
+ id: row.id,
811
+ channelId: row.channel_id,
812
+ channelUserId: row.channel_user_id,
813
+ displayName: row.display_name,
814
+ username: row.username || undefined,
815
+ allowed: row.allowed === 1,
816
+ pairingCode: row.pairing_code || undefined,
817
+ pairingAttempts: row.pairing_attempts,
818
+ pairingExpiresAt: row.pairing_expires_at || undefined,
819
+ lockoutUntil: row.lockout_until || undefined,
820
+ createdAt: row.created_at,
821
+ lastSeenAt: row.last_seen_at,
822
+ };
823
+ }
824
+ }
825
+ exports.ChannelUserRepository = ChannelUserRepository;
826
+ class ChannelSessionRepository {
827
+ constructor(db) {
828
+ this.db = db;
829
+ }
830
+ create(session) {
831
+ const now = Date.now();
832
+ const newSession = {
833
+ ...session,
834
+ id: (0, uuid_1.v4)(),
835
+ createdAt: now,
836
+ lastActivityAt: now,
837
+ };
838
+ const stmt = this.db.prepare(`
839
+ INSERT INTO channel_sessions (id, channel_id, chat_id, user_id, task_id, workspace_id, state, context, created_at, last_activity_at)
840
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
841
+ `);
842
+ stmt.run(newSession.id, newSession.channelId, newSession.chatId, newSession.userId || null, newSession.taskId || null, newSession.workspaceId || null, newSession.state, newSession.context ? JSON.stringify(newSession.context) : null, newSession.createdAt, newSession.lastActivityAt);
843
+ return newSession;
844
+ }
845
+ update(id, updates) {
846
+ const fields = [];
847
+ const values = [];
848
+ // Use 'in' check to allow setting fields to null/undefined (clearing them)
849
+ if ('taskId' in updates) {
850
+ fields.push('task_id = ?');
851
+ values.push(updates.taskId ?? null); // Convert undefined to null for SQLite
852
+ }
853
+ if ('workspaceId' in updates) {
854
+ fields.push('workspace_id = ?');
855
+ values.push(updates.workspaceId ?? null);
856
+ }
857
+ if ('state' in updates) {
858
+ fields.push('state = ?');
859
+ values.push(updates.state);
860
+ }
861
+ if ('lastActivityAt' in updates) {
862
+ fields.push('last_activity_at = ?');
863
+ values.push(updates.lastActivityAt);
864
+ }
865
+ // Handle shellEnabled and debugMode by merging into context
866
+ const hasContextUpdate = 'context' in updates || 'shellEnabled' in updates || 'debugMode' in updates;
867
+ if (hasContextUpdate) {
868
+ // Load existing session to merge context
869
+ const existing = this.findById(id);
870
+ const existingContext = existing?.context || {};
871
+ const newContext = {
872
+ ...existingContext,
873
+ ...('context' in updates ? updates.context : {}),
874
+ ...('shellEnabled' in updates ? { shellEnabled: updates.shellEnabled } : {}),
875
+ ...('debugMode' in updates ? { debugMode: updates.debugMode } : {}),
876
+ };
877
+ fields.push('context = ?');
878
+ values.push(JSON.stringify(newContext));
879
+ }
880
+ if (fields.length === 0)
881
+ return;
882
+ values.push(id);
883
+ const stmt = this.db.prepare(`UPDATE channel_sessions SET ${fields.join(', ')} WHERE id = ?`);
884
+ stmt.run(...values);
885
+ }
886
+ findById(id) {
887
+ const stmt = this.db.prepare('SELECT * FROM channel_sessions WHERE id = ?');
888
+ const row = stmt.get(id);
889
+ return row ? this.mapRowToSession(row) : undefined;
890
+ }
891
+ findByChatId(channelId, chatId) {
892
+ const stmt = this.db.prepare('SELECT * FROM channel_sessions WHERE channel_id = ? AND chat_id = ? ORDER BY last_activity_at DESC LIMIT 1');
893
+ const row = stmt.get(channelId, chatId);
894
+ return row ? this.mapRowToSession(row) : undefined;
895
+ }
896
+ findByTaskId(taskId) {
897
+ const stmt = this.db.prepare('SELECT * FROM channel_sessions WHERE task_id = ?');
898
+ const row = stmt.get(taskId);
899
+ return row ? this.mapRowToSession(row) : undefined;
900
+ }
901
+ findActiveByChannelId(channelId) {
902
+ const stmt = this.db.prepare("SELECT * FROM channel_sessions WHERE channel_id = ? AND state != 'idle' ORDER BY last_activity_at DESC");
903
+ const rows = stmt.all(channelId);
904
+ return rows.map(row => this.mapRowToSession(row));
905
+ }
906
+ deleteByChannelId(channelId) {
907
+ const stmt = this.db.prepare('DELETE FROM channel_sessions WHERE channel_id = ?');
908
+ stmt.run(channelId);
909
+ }
910
+ mapRowToSession(row) {
911
+ const context = row.context ? safeJsonParse(row.context, {}, 'session.context') : undefined;
912
+ // Extract shellEnabled and debugMode from context
913
+ const shellEnabled = context?.shellEnabled;
914
+ const debugMode = context?.debugMode;
915
+ return {
916
+ id: row.id,
917
+ channelId: row.channel_id,
918
+ chatId: row.chat_id,
919
+ userId: row.user_id || undefined,
920
+ taskId: row.task_id || undefined,
921
+ workspaceId: row.workspace_id || undefined,
922
+ state: row.state,
923
+ context,
924
+ shellEnabled,
925
+ debugMode,
926
+ createdAt: row.created_at,
927
+ lastActivityAt: row.last_activity_at,
928
+ };
929
+ }
930
+ }
931
+ exports.ChannelSessionRepository = ChannelSessionRepository;
932
+ class ChannelMessageRepository {
933
+ constructor(db) {
934
+ this.db = db;
935
+ }
936
+ create(message) {
937
+ const newMessage = {
938
+ ...message,
939
+ id: (0, uuid_1.v4)(),
940
+ };
941
+ const stmt = this.db.prepare(`
942
+ INSERT INTO channel_messages (id, channel_id, session_id, channel_message_id, chat_id, user_id, direction, content, attachments, timestamp)
943
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
944
+ `);
945
+ stmt.run(newMessage.id, newMessage.channelId, newMessage.sessionId || null, newMessage.channelMessageId, newMessage.chatId, newMessage.userId || null, newMessage.direction, newMessage.content, newMessage.attachments ? JSON.stringify(newMessage.attachments) : null, newMessage.timestamp);
946
+ return newMessage;
947
+ }
948
+ findBySessionId(sessionId, limit = 50) {
949
+ const stmt = this.db.prepare('SELECT * FROM channel_messages WHERE session_id = ? ORDER BY timestamp DESC LIMIT ?');
950
+ const rows = stmt.all(sessionId, limit);
951
+ return rows.map(row => this.mapRowToMessage(row)).reverse();
952
+ }
953
+ findByChatId(channelId, chatId, limit = 50) {
954
+ const stmt = this.db.prepare('SELECT * FROM channel_messages WHERE channel_id = ? AND chat_id = ? ORDER BY timestamp DESC LIMIT ?');
955
+ const rows = stmt.all(channelId, chatId, limit);
956
+ return rows.map(row => this.mapRowToMessage(row)).reverse();
957
+ }
958
+ deleteByChannelId(channelId) {
959
+ const stmt = this.db.prepare('DELETE FROM channel_messages WHERE channel_id = ?');
960
+ stmt.run(channelId);
961
+ }
962
+ mapRowToMessage(row) {
963
+ return {
964
+ id: row.id,
965
+ channelId: row.channel_id,
966
+ sessionId: row.session_id || undefined,
967
+ channelMessageId: row.channel_message_id,
968
+ chatId: row.chat_id,
969
+ userId: row.user_id || undefined,
970
+ direction: row.direction,
971
+ content: row.content,
972
+ attachments: row.attachments ? safeJsonParse(row.attachments, undefined, 'message.attachments') : undefined,
973
+ timestamp: row.timestamp,
974
+ };
975
+ }
976
+ }
977
+ exports.ChannelMessageRepository = ChannelMessageRepository;
978
+ class MessageQueueRepository {
979
+ constructor(db) {
980
+ this.db = db;
981
+ }
982
+ enqueue(item) {
983
+ const newItem = {
984
+ ...item,
985
+ id: (0, uuid_1.v4)(),
986
+ status: 'pending',
987
+ attempts: 0,
988
+ createdAt: Date.now(),
989
+ };
990
+ const stmt = this.db.prepare(`
991
+ INSERT INTO message_queue (id, channel_type, chat_id, message, priority, status, attempts, max_attempts, last_attempt_at, error, created_at, scheduled_at)
992
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
993
+ `);
994
+ stmt.run(newItem.id, newItem.channelType, newItem.chatId, JSON.stringify(newItem.message), newItem.priority, newItem.status, newItem.attempts, newItem.maxAttempts, newItem.lastAttemptAt || null, newItem.error || null, newItem.createdAt, newItem.scheduledAt || null);
995
+ return newItem;
996
+ }
997
+ update(id, updates) {
998
+ const fields = [];
999
+ const values = [];
1000
+ if (updates.status !== undefined) {
1001
+ fields.push('status = ?');
1002
+ values.push(updates.status);
1003
+ }
1004
+ if (updates.attempts !== undefined) {
1005
+ fields.push('attempts = ?');
1006
+ values.push(updates.attempts);
1007
+ }
1008
+ if (updates.lastAttemptAt !== undefined) {
1009
+ fields.push('last_attempt_at = ?');
1010
+ values.push(updates.lastAttemptAt);
1011
+ }
1012
+ if (updates.error !== undefined) {
1013
+ fields.push('error = ?');
1014
+ values.push(updates.error);
1015
+ }
1016
+ if (fields.length === 0)
1017
+ return;
1018
+ values.push(id);
1019
+ const stmt = this.db.prepare(`UPDATE message_queue SET ${fields.join(', ')} WHERE id = ?`);
1020
+ stmt.run(...values);
1021
+ }
1022
+ findPending(limit = 50) {
1023
+ const now = Date.now();
1024
+ const stmt = this.db.prepare(`
1025
+ SELECT * FROM message_queue
1026
+ WHERE status = 'pending' AND (scheduled_at IS NULL OR scheduled_at <= ?)
1027
+ ORDER BY priority DESC, created_at ASC
1028
+ LIMIT ?
1029
+ `);
1030
+ const rows = stmt.all(now, limit);
1031
+ return rows.map(row => this.mapRowToItem(row));
1032
+ }
1033
+ findById(id) {
1034
+ const stmt = this.db.prepare('SELECT * FROM message_queue WHERE id = ?');
1035
+ const row = stmt.get(id);
1036
+ return row ? this.mapRowToItem(row) : undefined;
1037
+ }
1038
+ delete(id) {
1039
+ const stmt = this.db.prepare('DELETE FROM message_queue WHERE id = ?');
1040
+ stmt.run(id);
1041
+ }
1042
+ deleteOld(olderThanMs) {
1043
+ const cutoff = Date.now() - olderThanMs;
1044
+ const stmt = this.db.prepare("DELETE FROM message_queue WHERE status IN ('sent', 'failed') AND created_at < ?");
1045
+ const result = stmt.run(cutoff);
1046
+ return result.changes;
1047
+ }
1048
+ mapRowToItem(row) {
1049
+ return {
1050
+ id: row.id,
1051
+ channelType: row.channel_type,
1052
+ chatId: row.chat_id,
1053
+ message: safeJsonParse(row.message, {}, 'queue.message'),
1054
+ priority: row.priority,
1055
+ status: row.status,
1056
+ attempts: row.attempts,
1057
+ maxAttempts: row.max_attempts,
1058
+ lastAttemptAt: row.last_attempt_at || undefined,
1059
+ error: row.error || undefined,
1060
+ createdAt: row.created_at,
1061
+ scheduledAt: row.scheduled_at || undefined,
1062
+ };
1063
+ }
1064
+ }
1065
+ exports.MessageQueueRepository = MessageQueueRepository;
1066
+ class ScheduledMessageRepository {
1067
+ constructor(db) {
1068
+ this.db = db;
1069
+ }
1070
+ create(item) {
1071
+ const newItem = {
1072
+ ...item,
1073
+ id: (0, uuid_1.v4)(),
1074
+ status: 'pending',
1075
+ createdAt: Date.now(),
1076
+ };
1077
+ const stmt = this.db.prepare(`
1078
+ INSERT INTO scheduled_messages (id, channel_type, chat_id, message, scheduled_at, status, sent_message_id, error, created_at)
1079
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
1080
+ `);
1081
+ stmt.run(newItem.id, newItem.channelType, newItem.chatId, JSON.stringify(newItem.message), newItem.scheduledAt, newItem.status, newItem.sentMessageId || null, newItem.error || null, newItem.createdAt);
1082
+ return newItem;
1083
+ }
1084
+ update(id, updates) {
1085
+ const fields = [];
1086
+ const values = [];
1087
+ if (updates.status !== undefined) {
1088
+ fields.push('status = ?');
1089
+ values.push(updates.status);
1090
+ }
1091
+ if (updates.sentMessageId !== undefined) {
1092
+ fields.push('sent_message_id = ?');
1093
+ values.push(updates.sentMessageId);
1094
+ }
1095
+ if (updates.error !== undefined) {
1096
+ fields.push('error = ?');
1097
+ values.push(updates.error);
1098
+ }
1099
+ if (updates.scheduledAt !== undefined) {
1100
+ fields.push('scheduled_at = ?');
1101
+ values.push(updates.scheduledAt);
1102
+ }
1103
+ if (fields.length === 0)
1104
+ return;
1105
+ values.push(id);
1106
+ const stmt = this.db.prepare(`UPDATE scheduled_messages SET ${fields.join(', ')} WHERE id = ?`);
1107
+ stmt.run(...values);
1108
+ }
1109
+ findDue(limit = 50) {
1110
+ const now = Date.now();
1111
+ const stmt = this.db.prepare(`
1112
+ SELECT * FROM scheduled_messages
1113
+ WHERE status = 'pending' AND scheduled_at <= ?
1114
+ ORDER BY scheduled_at ASC
1115
+ LIMIT ?
1116
+ `);
1117
+ const rows = stmt.all(now, limit);
1118
+ return rows.map(row => this.mapRowToItem(row));
1119
+ }
1120
+ findById(id) {
1121
+ const stmt = this.db.prepare('SELECT * FROM scheduled_messages WHERE id = ?');
1122
+ const row = stmt.get(id);
1123
+ return row ? this.mapRowToItem(row) : undefined;
1124
+ }
1125
+ findByChatId(channelType, chatId) {
1126
+ const stmt = this.db.prepare(`
1127
+ SELECT * FROM scheduled_messages
1128
+ WHERE channel_type = ? AND chat_id = ? AND status = 'pending'
1129
+ ORDER BY scheduled_at ASC
1130
+ `);
1131
+ const rows = stmt.all(channelType, chatId);
1132
+ return rows.map(row => this.mapRowToItem(row));
1133
+ }
1134
+ cancel(id) {
1135
+ const stmt = this.db.prepare("UPDATE scheduled_messages SET status = 'cancelled' WHERE id = ? AND status = 'pending'");
1136
+ stmt.run(id);
1137
+ }
1138
+ delete(id) {
1139
+ const stmt = this.db.prepare('DELETE FROM scheduled_messages WHERE id = ?');
1140
+ stmt.run(id);
1141
+ }
1142
+ mapRowToItem(row) {
1143
+ return {
1144
+ id: row.id,
1145
+ channelType: row.channel_type,
1146
+ chatId: row.chat_id,
1147
+ message: safeJsonParse(row.message, {}, 'scheduled.message'),
1148
+ scheduledAt: row.scheduled_at,
1149
+ status: row.status,
1150
+ sentMessageId: row.sent_message_id || undefined,
1151
+ error: row.error || undefined,
1152
+ createdAt: row.created_at,
1153
+ };
1154
+ }
1155
+ }
1156
+ exports.ScheduledMessageRepository = ScheduledMessageRepository;
1157
+ class DeliveryTrackingRepository {
1158
+ constructor(db) {
1159
+ this.db = db;
1160
+ }
1161
+ create(item) {
1162
+ const newItem = {
1163
+ ...item,
1164
+ id: (0, uuid_1.v4)(),
1165
+ createdAt: Date.now(),
1166
+ };
1167
+ const stmt = this.db.prepare(`
1168
+ INSERT INTO delivery_tracking (id, channel_type, chat_id, message_id, status, sent_at, delivered_at, read_at, error, created_at)
1169
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1170
+ `);
1171
+ stmt.run(newItem.id, newItem.channelType, newItem.chatId, newItem.messageId, newItem.status, newItem.sentAt || null, newItem.deliveredAt || null, newItem.readAt || null, newItem.error || null, newItem.createdAt);
1172
+ return newItem;
1173
+ }
1174
+ update(id, updates) {
1175
+ const fields = [];
1176
+ const values = [];
1177
+ if (updates.status !== undefined) {
1178
+ fields.push('status = ?');
1179
+ values.push(updates.status);
1180
+ }
1181
+ if (updates.sentAt !== undefined) {
1182
+ fields.push('sent_at = ?');
1183
+ values.push(updates.sentAt);
1184
+ }
1185
+ if (updates.deliveredAt !== undefined) {
1186
+ fields.push('delivered_at = ?');
1187
+ values.push(updates.deliveredAt);
1188
+ }
1189
+ if (updates.readAt !== undefined) {
1190
+ fields.push('read_at = ?');
1191
+ values.push(updates.readAt);
1192
+ }
1193
+ if (updates.error !== undefined) {
1194
+ fields.push('error = ?');
1195
+ values.push(updates.error);
1196
+ }
1197
+ if (fields.length === 0)
1198
+ return;
1199
+ values.push(id);
1200
+ const stmt = this.db.prepare(`UPDATE delivery_tracking SET ${fields.join(', ')} WHERE id = ?`);
1201
+ stmt.run(...values);
1202
+ }
1203
+ findByMessageId(messageId) {
1204
+ const stmt = this.db.prepare('SELECT * FROM delivery_tracking WHERE message_id = ?');
1205
+ const row = stmt.get(messageId);
1206
+ return row ? this.mapRowToItem(row) : undefined;
1207
+ }
1208
+ findByChatId(channelType, chatId, limit = 50) {
1209
+ const stmt = this.db.prepare(`
1210
+ SELECT * FROM delivery_tracking
1211
+ WHERE channel_type = ? AND chat_id = ?
1212
+ ORDER BY created_at DESC
1213
+ LIMIT ?
1214
+ `);
1215
+ const rows = stmt.all(channelType, chatId, limit);
1216
+ return rows.map(row => this.mapRowToItem(row));
1217
+ }
1218
+ deleteOld(olderThanMs) {
1219
+ const cutoff = Date.now() - olderThanMs;
1220
+ const stmt = this.db.prepare('DELETE FROM delivery_tracking WHERE created_at < ?');
1221
+ const result = stmt.run(cutoff);
1222
+ return result.changes;
1223
+ }
1224
+ mapRowToItem(row) {
1225
+ return {
1226
+ id: row.id,
1227
+ channelType: row.channel_type,
1228
+ chatId: row.chat_id,
1229
+ messageId: row.message_id,
1230
+ status: row.status,
1231
+ sentAt: row.sent_at || undefined,
1232
+ deliveredAt: row.delivered_at || undefined,
1233
+ readAt: row.read_at || undefined,
1234
+ error: row.error || undefined,
1235
+ createdAt: row.created_at,
1236
+ };
1237
+ }
1238
+ }
1239
+ exports.DeliveryTrackingRepository = DeliveryTrackingRepository;
1240
+ class RateLimitRepository {
1241
+ constructor(db) {
1242
+ this.db = db;
1243
+ }
1244
+ getOrCreate(channelType, userId) {
1245
+ const stmt = this.db.prepare('SELECT * FROM rate_limits WHERE channel_type = ? AND user_id = ?');
1246
+ const row = stmt.get(channelType, userId);
1247
+ if (row) {
1248
+ return this.mapRowToItem(row);
1249
+ }
1250
+ // Create new record
1251
+ const newItem = {
1252
+ id: (0, uuid_1.v4)(),
1253
+ channelType,
1254
+ userId,
1255
+ messageCount: 0,
1256
+ windowStart: Date.now(),
1257
+ isLimited: false,
1258
+ };
1259
+ const insertStmt = this.db.prepare(`
1260
+ INSERT INTO rate_limits (id, channel_type, user_id, message_count, window_start, is_limited, limit_expires_at)
1261
+ VALUES (?, ?, ?, ?, ?, ?, ?)
1262
+ `);
1263
+ insertStmt.run(newItem.id, newItem.channelType, newItem.userId, newItem.messageCount, newItem.windowStart, newItem.isLimited ? 1 : 0, newItem.limitExpiresAt || null);
1264
+ return newItem;
1265
+ }
1266
+ update(channelType, userId, updates) {
1267
+ const fields = [];
1268
+ const values = [];
1269
+ if (updates.messageCount !== undefined) {
1270
+ fields.push('message_count = ?');
1271
+ values.push(updates.messageCount);
1272
+ }
1273
+ if (updates.windowStart !== undefined) {
1274
+ fields.push('window_start = ?');
1275
+ values.push(updates.windowStart);
1276
+ }
1277
+ if (updates.isLimited !== undefined) {
1278
+ fields.push('is_limited = ?');
1279
+ values.push(updates.isLimited ? 1 : 0);
1280
+ }
1281
+ if (updates.limitExpiresAt !== undefined) {
1282
+ fields.push('limit_expires_at = ?');
1283
+ values.push(updates.limitExpiresAt);
1284
+ }
1285
+ if (fields.length === 0)
1286
+ return;
1287
+ values.push(channelType, userId);
1288
+ const stmt = this.db.prepare(`UPDATE rate_limits SET ${fields.join(', ')} WHERE channel_type = ? AND user_id = ?`);
1289
+ stmt.run(...values);
1290
+ }
1291
+ resetWindow(channelType, userId) {
1292
+ const stmt = this.db.prepare(`
1293
+ UPDATE rate_limits
1294
+ SET message_count = 0, window_start = ?, is_limited = 0, limit_expires_at = NULL
1295
+ WHERE channel_type = ? AND user_id = ?
1296
+ `);
1297
+ stmt.run(Date.now(), channelType, userId);
1298
+ }
1299
+ mapRowToItem(row) {
1300
+ return {
1301
+ id: row.id,
1302
+ channelType: row.channel_type,
1303
+ userId: row.user_id,
1304
+ messageCount: row.message_count,
1305
+ windowStart: row.window_start,
1306
+ isLimited: row.is_limited === 1,
1307
+ limitExpiresAt: row.limit_expires_at || undefined,
1308
+ };
1309
+ }
1310
+ }
1311
+ exports.RateLimitRepository = RateLimitRepository;
1312
+ class AuditLogRepository {
1313
+ constructor(db) {
1314
+ this.db = db;
1315
+ }
1316
+ log(entry) {
1317
+ const newEntry = {
1318
+ ...entry,
1319
+ id: (0, uuid_1.v4)(),
1320
+ timestamp: Date.now(),
1321
+ };
1322
+ const stmt = this.db.prepare(`
1323
+ INSERT INTO audit_log (id, timestamp, action, channel_type, user_id, chat_id, details, severity)
1324
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
1325
+ `);
1326
+ stmt.run(newEntry.id, newEntry.timestamp, newEntry.action, newEntry.channelType || null, newEntry.userId || null, newEntry.chatId || null, newEntry.details ? JSON.stringify(newEntry.details) : null, newEntry.severity);
1327
+ return newEntry;
1328
+ }
1329
+ find(options) {
1330
+ const conditions = [];
1331
+ const values = [];
1332
+ if (options.action) {
1333
+ conditions.push('action = ?');
1334
+ values.push(options.action);
1335
+ }
1336
+ if (options.channelType) {
1337
+ conditions.push('channel_type = ?');
1338
+ values.push(options.channelType);
1339
+ }
1340
+ if (options.userId) {
1341
+ conditions.push('user_id = ?');
1342
+ values.push(options.userId);
1343
+ }
1344
+ if (options.chatId) {
1345
+ conditions.push('chat_id = ?');
1346
+ values.push(options.chatId);
1347
+ }
1348
+ if (options.fromTimestamp) {
1349
+ conditions.push('timestamp >= ?');
1350
+ values.push(options.fromTimestamp);
1351
+ }
1352
+ if (options.toTimestamp) {
1353
+ conditions.push('timestamp <= ?');
1354
+ values.push(options.toTimestamp);
1355
+ }
1356
+ if (options.severity) {
1357
+ conditions.push('severity = ?');
1358
+ values.push(options.severity);
1359
+ }
1360
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
1361
+ const limit = options.limit || 100;
1362
+ const offset = options.offset || 0;
1363
+ const stmt = this.db.prepare(`
1364
+ SELECT * FROM audit_log
1365
+ ${whereClause}
1366
+ ORDER BY timestamp DESC
1367
+ LIMIT ? OFFSET ?
1368
+ `);
1369
+ values.push(limit, offset);
1370
+ const rows = stmt.all(...values);
1371
+ return rows.map(row => this.mapRowToEntry(row));
1372
+ }
1373
+ deleteOld(olderThanMs) {
1374
+ const cutoff = Date.now() - olderThanMs;
1375
+ const stmt = this.db.prepare('DELETE FROM audit_log WHERE timestamp < ?');
1376
+ const result = stmt.run(cutoff);
1377
+ return result.changes;
1378
+ }
1379
+ mapRowToEntry(row) {
1380
+ return {
1381
+ id: row.id,
1382
+ timestamp: row.timestamp,
1383
+ action: row.action,
1384
+ channelType: row.channel_type || undefined,
1385
+ userId: row.user_id || undefined,
1386
+ chatId: row.chat_id || undefined,
1387
+ details: row.details ? safeJsonParse(row.details, undefined, 'audit.details') : undefined,
1388
+ severity: row.severity,
1389
+ };
1390
+ }
1391
+ }
1392
+ exports.AuditLogRepository = AuditLogRepository;
1393
+ class MemoryRepository {
1394
+ constructor(db) {
1395
+ this.db = db;
1396
+ }
1397
+ create(memory) {
1398
+ const now = Date.now();
1399
+ const newMemory = {
1400
+ ...memory,
1401
+ id: (0, uuid_1.v4)(),
1402
+ createdAt: now,
1403
+ updatedAt: now,
1404
+ };
1405
+ const stmt = this.db.prepare(`
1406
+ INSERT INTO memories (id, workspace_id, task_id, type, content, summary, tokens, is_compressed, is_private, created_at, updated_at)
1407
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1408
+ `);
1409
+ stmt.run(newMemory.id, newMemory.workspaceId, newMemory.taskId || null, newMemory.type, newMemory.content, newMemory.summary || null, newMemory.tokens, newMemory.isCompressed ? 1 : 0, newMemory.isPrivate ? 1 : 0, newMemory.createdAt, newMemory.updatedAt);
1410
+ return newMemory;
1411
+ }
1412
+ update(id, updates) {
1413
+ const fields = [];
1414
+ const values = [];
1415
+ if (updates.summary !== undefined) {
1416
+ fields.push('summary = ?');
1417
+ values.push(updates.summary);
1418
+ }
1419
+ if (updates.tokens !== undefined) {
1420
+ fields.push('tokens = ?');
1421
+ values.push(updates.tokens);
1422
+ }
1423
+ if (updates.isCompressed !== undefined) {
1424
+ fields.push('is_compressed = ?');
1425
+ values.push(updates.isCompressed ? 1 : 0);
1426
+ }
1427
+ if (fields.length === 0)
1428
+ return;
1429
+ fields.push('updated_at = ?');
1430
+ values.push(Date.now());
1431
+ values.push(id);
1432
+ const stmt = this.db.prepare(`UPDATE memories SET ${fields.join(', ')} WHERE id = ?`);
1433
+ stmt.run(...values);
1434
+ }
1435
+ findById(id) {
1436
+ const stmt = this.db.prepare('SELECT * FROM memories WHERE id = ?');
1437
+ const row = stmt.get(id);
1438
+ return row ? this.mapRowToMemory(row) : undefined;
1439
+ }
1440
+ findByIds(ids) {
1441
+ if (ids.length === 0)
1442
+ return [];
1443
+ const placeholders = ids.map(() => '?').join(', ');
1444
+ const stmt = this.db.prepare(`SELECT * FROM memories WHERE id IN (${placeholders})`);
1445
+ const rows = stmt.all(...ids);
1446
+ return rows.map(row => this.mapRowToMemory(row));
1447
+ }
1448
+ /**
1449
+ * Layer 1: Search returns IDs + brief snippets (~50 tokens each)
1450
+ * Uses FTS5 for full-text search with relevance ranking
1451
+ */
1452
+ search(workspaceId, query, limit = 20) {
1453
+ try {
1454
+ // Try FTS5 search first
1455
+ const stmt = this.db.prepare(`
1456
+ SELECT m.id, m.summary, m.content, m.type, m.created_at, m.task_id,
1457
+ bm25(memories_fts) as score
1458
+ FROM memories_fts f
1459
+ JOIN memories m ON f.rowid = m.rowid
1460
+ WHERE memories_fts MATCH ? AND m.workspace_id = ? AND m.is_private = 0
1461
+ ORDER BY score
1462
+ LIMIT ?
1463
+ `);
1464
+ const rows = stmt.all(query, workspaceId, limit);
1465
+ return rows.map(row => ({
1466
+ id: row.id,
1467
+ snippet: row.summary || this.truncateToSnippet(row.content, 200),
1468
+ type: row.type,
1469
+ relevanceScore: Math.abs(row.score),
1470
+ createdAt: row.created_at,
1471
+ taskId: row.task_id || undefined,
1472
+ }));
1473
+ }
1474
+ catch {
1475
+ // Fall back to LIKE search if FTS5 is not available
1476
+ const stmt = this.db.prepare(`
1477
+ SELECT id, summary, content, type, created_at, task_id
1478
+ FROM memories
1479
+ WHERE workspace_id = ? AND is_private = 0
1480
+ AND (content LIKE ? OR summary LIKE ?)
1481
+ ORDER BY created_at DESC
1482
+ LIMIT ?
1483
+ `);
1484
+ const likeQuery = `%${query}%`;
1485
+ const rows = stmt.all(workspaceId, likeQuery, likeQuery, limit);
1486
+ return rows.map(row => ({
1487
+ id: row.id,
1488
+ snippet: row.summary || this.truncateToSnippet(row.content, 200),
1489
+ type: row.type,
1490
+ relevanceScore: 1,
1491
+ createdAt: row.created_at,
1492
+ taskId: row.task_id || undefined,
1493
+ }));
1494
+ }
1495
+ }
1496
+ /**
1497
+ * Layer 2: Get timeline context around a specific memory
1498
+ * Returns surrounding memories within a time window
1499
+ */
1500
+ getTimelineContext(memoryId, windowSize = 5) {
1501
+ const memory = this.findById(memoryId);
1502
+ if (!memory)
1503
+ return [];
1504
+ const stmt = this.db.prepare(`
1505
+ SELECT id, content, type, created_at, task_id
1506
+ FROM memories
1507
+ WHERE workspace_id = ? AND is_private = 0
1508
+ AND created_at BETWEEN ? AND ?
1509
+ ORDER BY created_at ASC
1510
+ LIMIT ?
1511
+ `);
1512
+ const timeWindow = 30 * 60 * 1000; // 30 minutes
1513
+ const rows = stmt.all(memory.workspaceId, memory.createdAt - timeWindow, memory.createdAt + timeWindow, windowSize * 2 + 1);
1514
+ return rows.map(row => ({
1515
+ id: row.id,
1516
+ content: row.content,
1517
+ type: row.type,
1518
+ createdAt: row.created_at,
1519
+ taskId: row.task_id || undefined,
1520
+ }));
1521
+ }
1522
+ /**
1523
+ * Layer 3: Get full details for selected IDs
1524
+ * Only called for specific memories when full content is needed
1525
+ */
1526
+ getFullDetails(ids) {
1527
+ return this.findByIds(ids);
1528
+ }
1529
+ /**
1530
+ * Get recent memories for context injection
1531
+ */
1532
+ getRecentForWorkspace(workspaceId, limit = 10) {
1533
+ const stmt = this.db.prepare(`
1534
+ SELECT * FROM memories
1535
+ WHERE workspace_id = ? AND is_private = 0
1536
+ ORDER BY created_at DESC
1537
+ LIMIT ?
1538
+ `);
1539
+ const rows = stmt.all(workspaceId, limit);
1540
+ return rows.map(row => this.mapRowToMemory(row));
1541
+ }
1542
+ /**
1543
+ * Get uncompressed memories for batch compression
1544
+ */
1545
+ getUncompressed(limit = 50) {
1546
+ const stmt = this.db.prepare(`
1547
+ SELECT * FROM memories
1548
+ WHERE is_compressed = 0 AND summary IS NULL
1549
+ ORDER BY created_at ASC
1550
+ LIMIT ?
1551
+ `);
1552
+ const rows = stmt.all(limit);
1553
+ return rows.map(row => this.mapRowToMemory(row));
1554
+ }
1555
+ /**
1556
+ * Find memories by workspace
1557
+ */
1558
+ findByWorkspace(workspaceId, limit = 100, offset = 0) {
1559
+ const stmt = this.db.prepare(`
1560
+ SELECT * FROM memories
1561
+ WHERE workspace_id = ?
1562
+ ORDER BY created_at DESC
1563
+ LIMIT ? OFFSET ?
1564
+ `);
1565
+ const rows = stmt.all(workspaceId, limit, offset);
1566
+ return rows.map(row => this.mapRowToMemory(row));
1567
+ }
1568
+ /**
1569
+ * Find memories by task
1570
+ */
1571
+ findByTask(taskId) {
1572
+ const stmt = this.db.prepare(`
1573
+ SELECT * FROM memories
1574
+ WHERE task_id = ?
1575
+ ORDER BY created_at ASC
1576
+ `);
1577
+ const rows = stmt.all(taskId);
1578
+ return rows.map(row => this.mapRowToMemory(row));
1579
+ }
1580
+ /**
1581
+ * Cleanup old memories based on retention policy
1582
+ */
1583
+ deleteOlderThan(workspaceId, cutoffTimestamp) {
1584
+ const stmt = this.db.prepare(`
1585
+ DELETE FROM memories
1586
+ WHERE workspace_id = ? AND created_at < ?
1587
+ `);
1588
+ const result = stmt.run(workspaceId, cutoffTimestamp);
1589
+ return result.changes;
1590
+ }
1591
+ /**
1592
+ * Delete all memories for a workspace
1593
+ */
1594
+ deleteByWorkspace(workspaceId) {
1595
+ const stmt = this.db.prepare('DELETE FROM memories WHERE workspace_id = ?');
1596
+ const result = stmt.run(workspaceId);
1597
+ return result.changes;
1598
+ }
1599
+ /**
1600
+ * Get storage statistics for a workspace
1601
+ */
1602
+ getStats(workspaceId) {
1603
+ const stmt = this.db.prepare(`
1604
+ SELECT COUNT(*) as count,
1605
+ COALESCE(SUM(tokens), 0) as total_tokens,
1606
+ SUM(CASE WHEN is_compressed = 1 THEN 1 ELSE 0 END) as compressed_count
1607
+ FROM memories
1608
+ WHERE workspace_id = ?
1609
+ `);
1610
+ const row = stmt.get(workspaceId);
1611
+ const count = row.count;
1612
+ const compressedCount = row.compressed_count;
1613
+ return {
1614
+ count,
1615
+ totalTokens: row.total_tokens,
1616
+ compressedCount,
1617
+ compressionRatio: count > 0 ? compressedCount / count : 0,
1618
+ };
1619
+ }
1620
+ truncateToSnippet(content, maxChars) {
1621
+ if (content.length <= maxChars)
1622
+ return content;
1623
+ return content.slice(0, maxChars - 3) + '...';
1624
+ }
1625
+ mapRowToMemory(row) {
1626
+ return {
1627
+ id: row.id,
1628
+ workspaceId: row.workspace_id,
1629
+ taskId: row.task_id || undefined,
1630
+ type: row.type,
1631
+ content: row.content,
1632
+ summary: row.summary || undefined,
1633
+ tokens: row.tokens,
1634
+ isCompressed: row.is_compressed === 1,
1635
+ isPrivate: row.is_private === 1,
1636
+ createdAt: row.created_at,
1637
+ updatedAt: row.updated_at,
1638
+ };
1639
+ }
1640
+ }
1641
+ exports.MemoryRepository = MemoryRepository;
1642
+ class MemorySummaryRepository {
1643
+ constructor(db) {
1644
+ this.db = db;
1645
+ }
1646
+ create(summary) {
1647
+ const newSummary = {
1648
+ ...summary,
1649
+ id: (0, uuid_1.v4)(),
1650
+ createdAt: Date.now(),
1651
+ };
1652
+ const stmt = this.db.prepare(`
1653
+ INSERT INTO memory_summaries (id, workspace_id, time_period, period_start, period_end, summary, memory_ids, tokens, created_at)
1654
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
1655
+ `);
1656
+ stmt.run(newSummary.id, newSummary.workspaceId, newSummary.timePeriod, newSummary.periodStart, newSummary.periodEnd, newSummary.summary, JSON.stringify(newSummary.memoryIds), newSummary.tokens, newSummary.createdAt);
1657
+ return newSummary;
1658
+ }
1659
+ findByWorkspaceAndPeriod(workspaceId, timePeriod, limit = 10) {
1660
+ const stmt = this.db.prepare(`
1661
+ SELECT * FROM memory_summaries
1662
+ WHERE workspace_id = ? AND time_period = ?
1663
+ ORDER BY period_start DESC
1664
+ LIMIT ?
1665
+ `);
1666
+ const rows = stmt.all(workspaceId, timePeriod, limit);
1667
+ return rows.map(row => this.mapRowToSummary(row));
1668
+ }
1669
+ findByWorkspace(workspaceId, limit = 50) {
1670
+ const stmt = this.db.prepare(`
1671
+ SELECT * FROM memory_summaries
1672
+ WHERE workspace_id = ?
1673
+ ORDER BY period_start DESC
1674
+ LIMIT ?
1675
+ `);
1676
+ const rows = stmt.all(workspaceId, limit);
1677
+ return rows.map(row => this.mapRowToSummary(row));
1678
+ }
1679
+ deleteByWorkspace(workspaceId) {
1680
+ const stmt = this.db.prepare('DELETE FROM memory_summaries WHERE workspace_id = ?');
1681
+ const result = stmt.run(workspaceId);
1682
+ return result.changes;
1683
+ }
1684
+ mapRowToSummary(row) {
1685
+ return {
1686
+ id: row.id,
1687
+ workspaceId: row.workspace_id,
1688
+ timePeriod: row.time_period,
1689
+ periodStart: row.period_start,
1690
+ periodEnd: row.period_end,
1691
+ summary: row.summary,
1692
+ memoryIds: safeJsonParse(row.memory_ids, [], 'memorySummary.memoryIds'),
1693
+ tokens: row.tokens,
1694
+ createdAt: row.created_at,
1695
+ };
1696
+ }
1697
+ }
1698
+ exports.MemorySummaryRepository = MemorySummaryRepository;
1699
+ class MemorySettingsRepository {
1700
+ constructor(db) {
1701
+ this.db = db;
1702
+ }
1703
+ getOrCreate(workspaceId) {
1704
+ const stmt = this.db.prepare('SELECT * FROM memory_settings WHERE workspace_id = ?');
1705
+ const row = stmt.get(workspaceId);
1706
+ if (row) {
1707
+ return this.mapRowToSettings(row);
1708
+ }
1709
+ // Create default settings
1710
+ const defaults = {
1711
+ workspaceId,
1712
+ enabled: true,
1713
+ autoCapture: true,
1714
+ compressionEnabled: true,
1715
+ retentionDays: 90,
1716
+ maxStorageMb: 100,
1717
+ privacyMode: 'normal',
1718
+ excludedPatterns: [],
1719
+ };
1720
+ const insertStmt = this.db.prepare(`
1721
+ INSERT INTO memory_settings (workspace_id, enabled, auto_capture, compression_enabled, retention_days, max_storage_mb, privacy_mode, excluded_patterns)
1722
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
1723
+ `);
1724
+ insertStmt.run(defaults.workspaceId, defaults.enabled ? 1 : 0, defaults.autoCapture ? 1 : 0, defaults.compressionEnabled ? 1 : 0, defaults.retentionDays, defaults.maxStorageMb, defaults.privacyMode, JSON.stringify(defaults.excludedPatterns));
1725
+ return defaults;
1726
+ }
1727
+ update(workspaceId, updates) {
1728
+ const fields = [];
1729
+ const values = [];
1730
+ if (updates.enabled !== undefined) {
1731
+ fields.push('enabled = ?');
1732
+ values.push(updates.enabled ? 1 : 0);
1733
+ }
1734
+ if (updates.autoCapture !== undefined) {
1735
+ fields.push('auto_capture = ?');
1736
+ values.push(updates.autoCapture ? 1 : 0);
1737
+ }
1738
+ if (updates.compressionEnabled !== undefined) {
1739
+ fields.push('compression_enabled = ?');
1740
+ values.push(updates.compressionEnabled ? 1 : 0);
1741
+ }
1742
+ if (updates.retentionDays !== undefined) {
1743
+ fields.push('retention_days = ?');
1744
+ values.push(updates.retentionDays);
1745
+ }
1746
+ if (updates.maxStorageMb !== undefined) {
1747
+ fields.push('max_storage_mb = ?');
1748
+ values.push(updates.maxStorageMb);
1749
+ }
1750
+ if (updates.privacyMode !== undefined) {
1751
+ fields.push('privacy_mode = ?');
1752
+ values.push(updates.privacyMode);
1753
+ }
1754
+ if (updates.excludedPatterns !== undefined) {
1755
+ fields.push('excluded_patterns = ?');
1756
+ values.push(JSON.stringify(updates.excludedPatterns));
1757
+ }
1758
+ if (fields.length === 0)
1759
+ return;
1760
+ values.push(workspaceId);
1761
+ const stmt = this.db.prepare(`UPDATE memory_settings SET ${fields.join(', ')} WHERE workspace_id = ?`);
1762
+ stmt.run(...values);
1763
+ }
1764
+ delete(workspaceId) {
1765
+ const stmt = this.db.prepare('DELETE FROM memory_settings WHERE workspace_id = ?');
1766
+ stmt.run(workspaceId);
1767
+ }
1768
+ mapRowToSettings(row) {
1769
+ return {
1770
+ workspaceId: row.workspace_id,
1771
+ enabled: row.enabled === 1,
1772
+ autoCapture: row.auto_capture === 1,
1773
+ compressionEnabled: row.compression_enabled === 1,
1774
+ retentionDays: row.retention_days,
1775
+ maxStorageMb: row.max_storage_mb,
1776
+ privacyMode: row.privacy_mode,
1777
+ excludedPatterns: safeJsonParse(row.excluded_patterns, [], 'memorySettings.excludedPatterns'),
1778
+ };
1779
+ }
1780
+ }
1781
+ exports.MemorySettingsRepository = MemorySettingsRepository;