cognova 0.2.12 → 0.2.14

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 (398) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_nuxt/-mGU8_Hv.js +1 -0
  3. package/.output/public/_nuxt/0GqCtQ9S.js +1 -0
  4. package/.output/public/_nuxt/{CLfF6dSn.js → 1wCoXZ14.js} +1 -1
  5. package/.output/public/_nuxt/2J1KxdHv.js +1 -0
  6. package/.output/public/_nuxt/6VDUZECX.js +1 -0
  7. package/.output/public/_nuxt/{DEd2xVbS.js → 8WMJQbWG.js} +1 -1
  8. package/.output/public/_nuxt/9Rw88G6h.js +1 -0
  9. package/.output/public/_nuxt/{PP_4ebzl.js → B-yW4SD3.js} +1 -1
  10. package/.output/public/_nuxt/B20jNAhm.js +1 -0
  11. package/.output/public/_nuxt/B3OpTH_e.js +1 -0
  12. package/.output/public/_nuxt/B9Y3_B5G.js +1 -0
  13. package/.output/public/_nuxt/B9_vmatf.js +1 -0
  14. package/.output/public/_nuxt/B9o6sFST.js +1 -0
  15. package/.output/public/_nuxt/{D8M722pn.js → BAY3_Hrq.js} +1 -1
  16. package/.output/public/_nuxt/{0yk-pS3R.js → BD5rPkc2.js} +3 -3
  17. package/.output/public/_nuxt/BGYoW3kO.js +1 -0
  18. package/.output/public/_nuxt/BHUy5EAj.js +1 -0
  19. package/.output/public/_nuxt/BLdMRpJD.js +1 -0
  20. package/.output/public/_nuxt/{2dNDtTiv.js → BOCn_SGf.js} +12 -12
  21. package/.output/public/_nuxt/BQi8LIrn.js +1 -0
  22. package/.output/public/_nuxt/BS1t375R.js +1 -0
  23. package/.output/public/_nuxt/BTGeP1bA.js +1 -0
  24. package/.output/public/_nuxt/{4K03TkGA.js → BVYYP4eQ.js} +5 -5
  25. package/.output/public/_nuxt/BWQ7zE01.js +1 -0
  26. package/.output/public/_nuxt/BWqJInD3.js +1 -0
  27. package/.output/public/_nuxt/BXdQ9ASJ.js +1 -0
  28. package/.output/public/_nuxt/{BHtY0l0l.js → BXxZokD_.js} +2 -2
  29. package/.output/public/_nuxt/BZ7DAtPi.js +1 -0
  30. package/.output/public/_nuxt/BZm9w7en.js +1 -0
  31. package/.output/public/_nuxt/B_hNqC64.js +1 -0
  32. package/.output/public/_nuxt/Be47YE2L.js +1 -0
  33. package/.output/public/_nuxt/BgIS9_ti.js +39 -0
  34. package/.output/public/_nuxt/BgavRUl_.js +1 -0
  35. package/.output/public/_nuxt/Bh0yGTzr.js +1 -0
  36. package/.output/public/_nuxt/BhhMUror.js +1 -0
  37. package/.output/public/_nuxt/Bj5e7QPz.js +1 -0
  38. package/.output/public/_nuxt/{7oCGSglN.js → Bnn59-FS.js} +1 -1
  39. package/.output/public/_nuxt/BoCU3mWu.js +1 -0
  40. package/.output/public/_nuxt/Br6epbzY.js +1 -0
  41. package/.output/public/_nuxt/Br81Sr2R.js +1 -0
  42. package/.output/public/_nuxt/BrkEOLHu.js +9 -0
  43. package/.output/public/_nuxt/BtrNCc2S.js +1 -0
  44. package/.output/public/_nuxt/BxsQlbgl.js +1 -0
  45. package/.output/public/_nuxt/BzrhNhdT.js +1 -0
  46. package/.output/public/_nuxt/{CVqlefTY.js → C-78dDNd.js} +1 -1
  47. package/.output/public/_nuxt/C1oaoi1p.js +1 -0
  48. package/.output/public/_nuxt/C2-_hcwB.js +1 -0
  49. package/.output/public/_nuxt/{rfGRTJJW.js → C2Gh3qqr.js} +1 -1
  50. package/.output/public/_nuxt/C2OHaW6l.js +1 -0
  51. package/.output/public/_nuxt/C3szZmvA.js +1 -0
  52. package/.output/public/_nuxt/C3xzleKP.js +1 -0
  53. package/.output/public/_nuxt/C4sG6rdW.js +1 -0
  54. package/.output/public/_nuxt/C5Cu9Fx6.js +1 -0
  55. package/.output/public/_nuxt/{JX1oqJI9.js → C5gfF7J1.js} +1 -1
  56. package/.output/public/_nuxt/C6Eoiclo.js +1 -0
  57. package/.output/public/_nuxt/C7hFztZL.js +1 -0
  58. package/.output/public/_nuxt/CAVUAxNU.js +2 -0
  59. package/.output/public/_nuxt/{t8aDAkZ5.js → CCeM5lW2.js} +1 -1
  60. package/.output/public/_nuxt/CDcQ31Eh.js +1 -0
  61. package/.output/public/_nuxt/CHbHVtCy.js +1 -0
  62. package/.output/public/_nuxt/CHnVFnBW.js +1 -0
  63. package/.output/public/_nuxt/CLNcmNQS.js +1 -0
  64. package/.output/public/_nuxt/{EgKnQnf-.js → CPwCTHun.js} +1 -1
  65. package/.output/public/_nuxt/{ixlNW2So.js → CS8SdmH2.js} +1 -1
  66. package/.output/public/_nuxt/CTGm_mc8.js +1 -0
  67. package/.output/public/_nuxt/{CPutXj8l.js → CTQFGHjS.js} +1 -1
  68. package/.output/public/_nuxt/{BS2ZNXI1.js → CVVtVzXC.js} +1 -1
  69. package/.output/public/_nuxt/CZNOaqeX.js +1 -0
  70. package/.output/public/_nuxt/C_A_NkeC.js +2 -0
  71. package/.output/public/_nuxt/{gTrVszwd.js → C_doutX-.js} +1 -1
  72. package/.output/public/_nuxt/{CY-QVcA5.js → CbDLhrNA.js} +2 -2
  73. package/.output/public/_nuxt/CePKCqSb.js +1 -0
  74. package/.output/public/_nuxt/Cet4T_yO.js +1 -0
  75. package/.output/public/_nuxt/{fbyIeNkc.js → CjJPkhbc.js} +1 -1
  76. package/.output/public/_nuxt/CkZT4LGq.js +1 -0
  77. package/.output/public/_nuxt/CkrW8sGL.js +1 -0
  78. package/.output/public/_nuxt/Co0UxVsx.js +1 -0
  79. package/.output/public/_nuxt/CqS0zXYQ.js +1 -0
  80. package/.output/public/_nuxt/CskGv_Km.js +1 -0
  81. package/.output/public/_nuxt/{Bk6JUtIo.js → CuvkTsqs.js} +1 -1
  82. package/.output/public/_nuxt/{Db4KMnt8.js → CxMuZEwX.js} +1 -1
  83. package/.output/public/_nuxt/{vIOxcXKR.js → CxSijT71.js} +8 -8
  84. package/.output/public/_nuxt/Cywo6tZI.js +1 -0
  85. package/.output/public/_nuxt/D1reIfpX.js +1 -0
  86. package/.output/public/_nuxt/D1uV2uir.js +1 -0
  87. package/.output/public/_nuxt/{CuxrHsu-.js → D2edadpd.js} +1 -1
  88. package/.output/public/_nuxt/D3cEeQh7.js +1 -0
  89. package/.output/public/_nuxt/D41A2mE2.js +1 -0
  90. package/.output/public/_nuxt/D49P2RhG.js +1 -0
  91. package/.output/public/_nuxt/D867g6Ds.js +1 -0
  92. package/.output/public/_nuxt/DEFWIjWl.js +1 -0
  93. package/.output/public/_nuxt/DFTEBTzZ.js +1 -0
  94. package/.output/public/_nuxt/{DwY7rCd_.js → DHRexj9X.js} +2 -2
  95. package/.output/public/_nuxt/DL4EpgqQ.js +1 -0
  96. package/.output/public/_nuxt/DLHnP6tw.js +1 -0
  97. package/.output/public/_nuxt/DM20WaLq.js +1 -0
  98. package/.output/public/_nuxt/{BYHCP8x7.js → DNAMjHbb.js} +1 -1
  99. package/.output/public/_nuxt/{C6qKcgOY.js → DP8t9VWs.js} +26 -26
  100. package/.output/public/_nuxt/DTR31rjp.js +1 -0
  101. package/.output/public/_nuxt/{CZoEPC_Q.js → DTlSLSo1.js} +3 -3
  102. package/.output/public/_nuxt/{DtjjnHnt.js → DVdnhfXT.js} +1 -1
  103. package/.output/public/_nuxt/DZikIgHK.js +1 -0
  104. package/.output/public/_nuxt/D_YGqvw_.js +1 -0
  105. package/.output/public/_nuxt/D_k1YwXm.js +1 -0
  106. package/.output/public/_nuxt/Daf2MbWG.js +7 -0
  107. package/.output/public/_nuxt/DcJ0GstJ.js +1 -0
  108. package/.output/public/_nuxt/DcYKh6QA.js +1 -0
  109. package/.output/public/_nuxt/DhN3_DYH.js +1 -0
  110. package/.output/public/_nuxt/DhSvBSt3.js +1 -0
  111. package/.output/public/_nuxt/DhlIWT8K.js +1 -0
  112. package/.output/public/_nuxt/DjsqyRqt.js +1 -0
  113. package/.output/public/_nuxt/DpSM97eH.js +1 -0
  114. package/.output/public/_nuxt/DqR9SdeU.js +1 -0
  115. package/.output/public/_nuxt/DqmQtg6Y.js +1 -0
  116. package/.output/public/_nuxt/Dsuag_iW.js +1 -0
  117. package/.output/public/_nuxt/Dtr_0IbE.js +1 -0
  118. package/.output/public/_nuxt/DwrmFEZN.js +1 -0
  119. package/.output/public/_nuxt/DxE8r3r4.js +1 -0
  120. package/.output/public/_nuxt/G_CsVlOR.js +1 -0
  121. package/.output/public/_nuxt/Gyw_2f6x.js +1 -0
  122. package/.output/public/_nuxt/HStC1ZyE.js +1 -0
  123. package/.output/public/_nuxt/Hh9mv0Ek.js +1 -0
  124. package/.output/public/_nuxt/LR0kCjbe.js +1 -0
  125. package/.output/public/_nuxt/MYeLvJGV.js +1 -0
  126. package/.output/public/_nuxt/NKs0EGrh.js +1 -0
  127. package/.output/public/_nuxt/PY5EGujn.js +1 -0
  128. package/.output/public/_nuxt/PamJNol9.js +1 -0
  129. package/.output/public/_nuxt/RlvwEbHv.js +1 -0
  130. package/.output/public/_nuxt/{BKXg-alD.js → TNpdJOlc.js} +1 -1
  131. package/.output/public/_nuxt/TSYrPbLR.js +1 -0
  132. package/.output/public/_nuxt/U-eBrbR4.js +1 -0
  133. package/.output/public/_nuxt/UuqXScSw.js +1 -0
  134. package/.output/public/_nuxt/V04-MrLN.js +1 -0
  135. package/.output/public/_nuxt/X0o5GcBb.js +1 -0
  136. package/.output/public/_nuxt/XQNzmQy9.js +1 -0
  137. package/.output/public/_nuxt/Y59XKprr.js +1 -0
  138. package/.output/public/_nuxt/YZQjZgsH.js +1 -0
  139. package/.output/public/_nuxt/_-TGPiLQ.js +1 -0
  140. package/.output/public/_nuxt/builds/latest.json +1 -1
  141. package/.output/public/_nuxt/builds/meta/d7188f50-b42c-4d2c-afab-066d318f0af8.json +1 -0
  142. package/.output/public/_nuxt/dashboard.ByJQ-Wzh.css +1 -0
  143. package/.output/public/_nuxt/entry.ZJxWCrU6.css +1 -0
  144. package/.output/public/_nuxt/{DhI5cA_n.js → hCeWb_38.js} +1 -1
  145. package/.output/public/_nuxt/{CQqCBrXj.js → hrPTRWjM.js} +1 -1
  146. package/.output/public/_nuxt/jC4pIK1p.js +1 -0
  147. package/.output/public/_nuxt/jTT1R-Vz.js +1 -0
  148. package/.output/public/_nuxt/{BLnYhy_t.js → kkAMoB43.js} +1 -1
  149. package/.output/public/_nuxt/{DzGy77Vr.js → mzM4BGtv.js} +1 -1
  150. package/.output/public/_nuxt/o-s8JBm2.js +1 -0
  151. package/.output/public/_nuxt/pwJTkIQ4.js +1 -0
  152. package/.output/public/_nuxt/{BDyn4ApB.js → rIgg2Arf.js} +3 -3
  153. package/.output/public/_nuxt/subdxgSQ.js +1 -0
  154. package/.output/public/_nuxt/t8B72leH.js +1 -0
  155. package/.output/public/_nuxt/vCFAHwdt.js +1 -0
  156. package/.output/public/_nuxt/z-MP0bB7.js +1 -0
  157. package/.output/server/chunks/build/CodeIcon-CWD5HcV7.mjs +1 -1
  158. package/.output/server/chunks/build/DropdownMenu-BBrV9nXz.mjs +1 -1
  159. package/.output/server/chunks/build/{EditorToolbar-DIfb5arC.mjs → EditorToolbar-B9OwSI5e.mjs} +3 -3
  160. package/.output/server/chunks/build/{EditorToolbar-DIfb5arC.mjs.map → EditorToolbar-B9OwSI5e.mjs.map} +1 -1
  161. package/.output/server/chunks/build/Img-CWLmvN1t.mjs +1 -1
  162. package/.output/server/chunks/build/MDC-Dx0YPDhe.mjs +1 -1
  163. package/.output/server/chunks/build/Select-BB1oLrCD.mjs +1 -1
  164. package/.output/server/chunks/build/SelectMenu-DPssg6zD.mjs +1 -1
  165. package/.output/server/chunks/build/Table-DCwTlhCj.mjs +1 -1
  166. package/.output/server/chunks/build/Tooltip-TRyl6dje.mjs +1 -1
  167. package/.output/server/chunks/build/Tree-DUhXKd8y.mjs +1 -1
  168. package/.output/server/chunks/build/{_id_-DN00UDdO.mjs → _id_-CwBeklIM.mjs} +296 -430
  169. package/.output/server/chunks/build/_id_-CwBeklIM.mjs.map +1 -0
  170. package/.output/server/chunks/build/{_name_-BnS_KEfX.mjs → _name_-D7MtW4Vu.mjs} +2 -2
  171. package/.output/server/chunks/build/{_name_-BnS_KEfX.mjs.map → _name_-D7MtW4Vu.mjs.map} +1 -1
  172. package/.output/server/chunks/build/{_uuid_-DfJaumTE.mjs → _uuid_-lvG92ein.mjs} +4 -4
  173. package/.output/server/chunks/build/{_uuid_-DfJaumTE.mjs.map → _uuid_-lvG92ein.mjs.map} +1 -1
  174. package/.output/server/chunks/build/account-LDZ4TL6x.mjs +513 -0
  175. package/.output/server/chunks/build/account-LDZ4TL6x.mjs.map +1 -0
  176. package/.output/server/chunks/build/app-CGhRZPNT.mjs +252 -0
  177. package/.output/server/chunks/build/app-CGhRZPNT.mjs.map +1 -0
  178. package/.output/server/chunks/build/{chat-CR3JIVEq.mjs → chat-BphuYhvA.mjs} +532 -39
  179. package/.output/server/chunks/build/chat-BphuYhvA.mjs.map +1 -0
  180. package/.output/server/chunks/build/client.precomputed.mjs +1 -1
  181. package/.output/server/chunks/build/cookie-C_iulBi6.mjs +1 -1
  182. package/.output/server/chunks/build/{dashboard-CiVTAZuF.mjs → dashboard-nDujDyOg.mjs} +55 -16
  183. package/.output/server/chunks/build/{dashboard-CiVTAZuF.mjs.map → dashboard-nDujDyOg.mjs.map} +1 -1
  184. package/.output/server/chunks/build/{docs-ChGwOPg5.mjs → docs-Bwucwp0r.mjs} +664 -592
  185. package/.output/server/chunks/build/docs-Bwucwp0r.mjs.map +1 -0
  186. package/.output/server/chunks/build/fetch-BB7Qzkwe.mjs +1 -1
  187. package/.output/server/chunks/build/{hooks-D328DcO6.mjs → hooks-D6PmTth8.mjs} +19 -29
  188. package/.output/server/chunks/build/hooks-D6PmTth8.mjs.map +1 -0
  189. package/.output/server/chunks/build/{index-CAnGTRlu.mjs → index-CJkn2jiM.mjs} +2 -2
  190. package/.output/server/chunks/build/{index-CAnGTRlu.mjs.map → index-CJkn2jiM.mjs.map} +1 -1
  191. package/.output/server/chunks/build/index-CxDxc9fm.mjs +1 -1
  192. package/.output/server/chunks/build/index-Cz9Y6NwN.mjs +723 -0
  193. package/.output/server/chunks/build/index-Cz9Y6NwN.mjs.map +1 -0
  194. package/.output/server/chunks/build/index-hwhuKhuZ.mjs +76 -0
  195. package/.output/server/chunks/build/index-hwhuKhuZ.mjs.map +1 -0
  196. package/.output/server/chunks/build/integrations-D5JWURiJ.mjs +1499 -0
  197. package/.output/server/chunks/build/integrations-D5JWURiJ.mjs.map +1 -0
  198. package/.output/server/chunks/build/{library-Aeg7_Lsb.mjs → library-DxRdgP6X.mjs} +2 -2
  199. package/.output/server/chunks/build/{library-Aeg7_Lsb.mjs.map → library-DxRdgP6X.mjs.map} +1 -1
  200. package/.output/server/chunks/build/{login-DnnElTl2.mjs → login-BmER_VoU.mjs} +2 -2
  201. package/.output/server/chunks/build/{login-DnnElTl2.mjs.map → login-BmER_VoU.mjs.map} +1 -1
  202. package/.output/server/chunks/build/manage-secrets-BWzqc_-f.mjs +669 -0
  203. package/.output/server/chunks/build/manage-secrets-BWzqc_-f.mjs.map +1 -0
  204. package/.output/server/chunks/build/server.mjs +65 -34
  205. package/.output/server/chunks/build/server.mjs.map +1 -1
  206. package/.output/server/chunks/build/settings-neokAVE5.mjs +154 -0
  207. package/.output/server/chunks/build/settings-neokAVE5.mjs.map +1 -0
  208. package/.output/server/chunks/build/styles.mjs +2 -2
  209. package/.output/server/chunks/build/{tasks-DnAFqbtt.mjs → tasks-CnpIQNpS.mjs} +27 -37
  210. package/.output/server/chunks/build/tasks-CnpIQNpS.mjs.map +1 -0
  211. package/.output/server/chunks/build/{useAgents-DHEXiFSc.mjs → useAgents-nmbkB9-_.mjs} +5 -26
  212. package/.output/server/chunks/build/useAgents-nmbkB9-_.mjs.map +1 -0
  213. package/.output/server/chunks/build/{view-n2sYa4Zh.mjs → view-mb0ISqfM.mjs} +3 -3
  214. package/.output/server/chunks/build/{view-n2sYa4Zh.mjs.map → view-mb0ISqfM.mjs.map} +1 -1
  215. package/.output/server/chunks/nitro/nitro.mjs +1021 -867
  216. package/.output/server/chunks/nitro/nitro.mjs.map +1 -1
  217. package/.output/server/chunks/routes/_ws/chat.mjs +39 -5
  218. package/.output/server/chunks/routes/_ws/chat.mjs.map +1 -1
  219. package/.output/server/chunks/routes/api/index.get.mjs +17 -2
  220. package/.output/server/chunks/routes/api/index.get.mjs.map +1 -1
  221. package/.output/server/chunks/routes/api/index.get3.mjs +21 -3
  222. package/.output/server/chunks/routes/api/index.get3.mjs.map +1 -1
  223. package/.output/server/chunks/routes/api/index.get4.mjs +10 -9
  224. package/.output/server/chunks/routes/api/index.get4.mjs.map +1 -1
  225. package/.output/server/chunks/routes/api/index.get9.mjs +10 -8
  226. package/.output/server/chunks/routes/api/index.get9.mjs.map +1 -1
  227. package/.output/server/chunks/routes/api/memory/search.get.mjs +10 -5
  228. package/.output/server/chunks/routes/api/memory/search.get.mjs.map +1 -1
  229. package/.output/server/chunks/routes/renderer.mjs +1 -1
  230. package/.output/server/index.mjs +1 -1
  231. package/.output/server/package.json +1 -1
  232. package/Claude/skills/_lib/api.py +3 -2
  233. package/app/components/AssistantPanel.client.vue +16 -314
  234. package/app/components/agents/AgentCard.vue +111 -0
  235. package/app/components/agents/AgentInfoCard.vue +54 -0
  236. package/app/components/agents/AgentRunHistory.vue +103 -0
  237. package/app/components/agents/AgentRunModal.vue +1 -24
  238. package/app/components/agents/AgentStatsCards.vue +1 -26
  239. package/app/components/assistant/AssistantChat.vue +156 -0
  240. package/app/components/assistant/AssistantTerminal.client.vue +178 -0
  241. package/app/components/chat/ChatInput.vue +133 -5
  242. package/app/components/chat/ConversationList.vue +2 -12
  243. package/app/components/chat/MessageBubble.vue +188 -6
  244. package/app/components/files/FileTree.vue +23 -202
  245. package/app/components/files/FileTreeModals.vue +226 -0
  246. package/app/components/hooks/HookStatsCards.vue +1 -6
  247. package/app/components/hooks/ToolBreakdownTable.vue +1 -6
  248. package/app/components/search/DashboardSearch.vue +38 -1
  249. package/app/components/tasks/TaskCard.vue +2 -7
  250. package/app/components/tasks/TaskDetail.vue +4 -9
  251. package/app/composables/useAttachments.ts +196 -0
  252. package/app/composables/useChat.ts +14 -2
  253. package/app/composables/useSearch.ts +21 -11
  254. package/app/layouts/dashboard.vue +1 -1
  255. package/app/pages/agents/[id].vue +6 -197
  256. package/app/pages/agents/index.vue +9 -142
  257. package/app/pages/chat.vue +4 -2
  258. package/app/pages/settings/account.vue +243 -0
  259. package/app/pages/settings/app.vue +230 -0
  260. package/app/pages/settings/index.vue +12 -0
  261. package/app/pages/settings/integrations.vue +794 -0
  262. package/app/pages/settings/manage-secrets.vue +295 -0
  263. package/app/pages/settings.vue +20 -1581
  264. package/dist/cli/index.js +26 -2
  265. package/package.json +1 -1
  266. package/server/api/agents/index.get.ts +20 -1
  267. package/server/api/conversations/index.get.ts +23 -5
  268. package/server/api/documents/index.get.ts +11 -9
  269. package/server/api/memory/search.get.ts +11 -6
  270. package/server/api/tasks/index.get.ts +11 -9
  271. package/server/bridge/adapters/imessage.ts +9 -2
  272. package/server/drizzle/migrations/0016_full_text_search.sql +127 -0
  273. package/server/drizzle/migrations/meta/_journal.json +7 -0
  274. package/server/routes/_ws/chat.ts +61 -7
  275. package/server/utils/chat-session-manager.ts +20 -2
  276. package/shared/types/index.ts +34 -2
  277. package/shared/utils/formatting.ts +105 -0
  278. package/.output/public/_nuxt/0NJ3PaRM.js +0 -1
  279. package/.output/public/_nuxt/10_wwHSz.js +0 -1
  280. package/.output/public/_nuxt/1zUTf4AP.js +0 -1
  281. package/.output/public/_nuxt/2nOqGUPr.js +0 -1
  282. package/.output/public/_nuxt/3jQMk_5H.js +0 -1
  283. package/.output/public/_nuxt/6boMs_nF.js +0 -1
  284. package/.output/public/_nuxt/8q5NepGW.js +0 -9
  285. package/.output/public/_nuxt/B93pdGAW.js +0 -1
  286. package/.output/public/_nuxt/BA_pRWwX.js +0 -1
  287. package/.output/public/_nuxt/BGgwYWjH.js +0 -1
  288. package/.output/public/_nuxt/BH0QDWEm.js +0 -1
  289. package/.output/public/_nuxt/BIXrSYwR.js +0 -1
  290. package/.output/public/_nuxt/BK8S2ony.js +0 -1
  291. package/.output/public/_nuxt/BR8bKz8h.js +0 -1
  292. package/.output/public/_nuxt/BUghTae1.js +0 -1
  293. package/.output/public/_nuxt/BXuWCWsJ.js +0 -1
  294. package/.output/public/_nuxt/BZrSPX0S.js +0 -1
  295. package/.output/public/_nuxt/B_wilgcd.js +0 -1
  296. package/.output/public/_nuxt/BaZwjF8o.js +0 -1
  297. package/.output/public/_nuxt/Bciqk4dX.js +0 -1
  298. package/.output/public/_nuxt/Beom-INj.js +0 -1
  299. package/.output/public/_nuxt/BfcLZ_fC.js +0 -1
  300. package/.output/public/_nuxt/BkfI94R8.js +0 -1
  301. package/.output/public/_nuxt/BmOtR1wX.js +0 -1
  302. package/.output/public/_nuxt/BmVLxnDU.js +0 -1
  303. package/.output/public/_nuxt/BtHQ1T0f.js +0 -1
  304. package/.output/public/_nuxt/ByYh0uRg.js +0 -1
  305. package/.output/public/_nuxt/C27WoGNZ.js +0 -1
  306. package/.output/public/_nuxt/C2FxZbO_.js +0 -1
  307. package/.output/public/_nuxt/C2vq6Te8.js +0 -1
  308. package/.output/public/_nuxt/C39dQ88F.js +0 -1
  309. package/.output/public/_nuxt/C3NST0BU.js +0 -1
  310. package/.output/public/_nuxt/C4mwL7SE.js +0 -7
  311. package/.output/public/_nuxt/C5R5QaCA.js +0 -1
  312. package/.output/public/_nuxt/C71_a1IX.js +0 -1
  313. package/.output/public/_nuxt/C9WIgRRL.js +0 -1
  314. package/.output/public/_nuxt/CBTkrk2M.js +0 -1
  315. package/.output/public/_nuxt/CEnSeCqn.js +0 -1
  316. package/.output/public/_nuxt/CKxSHyww.js +0 -1
  317. package/.output/public/_nuxt/CSKJ-Ahh.js +0 -1
  318. package/.output/public/_nuxt/CTCcEJU3.js +0 -1
  319. package/.output/public/_nuxt/CVFwOzbl.js +0 -1
  320. package/.output/public/_nuxt/CVdCqaby.js +0 -1
  321. package/.output/public/_nuxt/CWyMCJQH.js +0 -1
  322. package/.output/public/_nuxt/CY-cjAwJ.js +0 -1
  323. package/.output/public/_nuxt/CYPO5o_C.js +0 -1
  324. package/.output/public/_nuxt/CZRnNmU8.js +0 -1
  325. package/.output/public/_nuxt/CZVzFlpI.js +0 -1
  326. package/.output/public/_nuxt/Cc-2ziaZ.js +0 -1
  327. package/.output/public/_nuxt/ChcO9s3o.js +0 -1
  328. package/.output/public/_nuxt/Cl-LOSDV.js +0 -1
  329. package/.output/public/_nuxt/CobqYwkp.js +0 -1
  330. package/.output/public/_nuxt/CqNwHCpo.js +0 -1
  331. package/.output/public/_nuxt/CwPdIZ9J.js +0 -1
  332. package/.output/public/_nuxt/D0ifH682.js +0 -1
  333. package/.output/public/_nuxt/D3AZaldL.js +0 -1
  334. package/.output/public/_nuxt/D4UJwDQJ.js +0 -1
  335. package/.output/public/_nuxt/D5jZq8b3.js +0 -1
  336. package/.output/public/_nuxt/D5q8SnqE.js +0 -1
  337. package/.output/public/_nuxt/D8lwrAYS.js +0 -1
  338. package/.output/public/_nuxt/D9nmzBAw.js +0 -1
  339. package/.output/public/_nuxt/DC4idAGt.js +0 -1
  340. package/.output/public/_nuxt/DH4YkDAi.js +0 -1
  341. package/.output/public/_nuxt/DJTCT0bl.js +0 -1
  342. package/.output/public/_nuxt/DNP5E1bC.js +0 -39
  343. package/.output/public/_nuxt/DO9SFIh1.js +0 -1
  344. package/.output/public/_nuxt/DPklr_tJ.js +0 -2
  345. package/.output/public/_nuxt/DQM4eBdt.js +0 -1
  346. package/.output/public/_nuxt/DQh6I9z9.js +0 -1
  347. package/.output/public/_nuxt/DS2wStH1.js +0 -1
  348. package/.output/public/_nuxt/DTAStixR.js +0 -1
  349. package/.output/public/_nuxt/DTGenhcA.js +0 -1
  350. package/.output/public/_nuxt/DUAAXJ6Q.js +0 -1
  351. package/.output/public/_nuxt/DUVzIl3o.js +0 -1
  352. package/.output/public/_nuxt/DXdwpJ-I.js +0 -16
  353. package/.output/public/_nuxt/DY3uK7wM.js +0 -1
  354. package/.output/public/_nuxt/DYbZBZet.js +0 -1
  355. package/.output/public/_nuxt/D_4LPm1U.js +0 -1
  356. package/.output/public/_nuxt/DcJbYLTp.js +0 -1
  357. package/.output/public/_nuxt/DfF81NlA.js +0 -1
  358. package/.output/public/_nuxt/DhuOKJda.js +0 -1
  359. package/.output/public/_nuxt/Di-Nc75e.js +0 -1
  360. package/.output/public/_nuxt/Djs0Tlpa.js +0 -1
  361. package/.output/public/_nuxt/DmmdPt_5.js +0 -1
  362. package/.output/public/_nuxt/Dn5a-guE.js +0 -1
  363. package/.output/public/_nuxt/DuvzM-P1.js +0 -1
  364. package/.output/public/_nuxt/Dya5oK8u.js +0 -1
  365. package/.output/public/_nuxt/DzA58_Lm.js +0 -1
  366. package/.output/public/_nuxt/E3rXPwU8.js +0 -1
  367. package/.output/public/_nuxt/GtEM7xVU.js +0 -1
  368. package/.output/public/_nuxt/H6JbrRBU.js +0 -1
  369. package/.output/public/_nuxt/ILEvizzp.js +0 -1
  370. package/.output/public/_nuxt/JbHa4oXq.js +0 -1
  371. package/.output/public/_nuxt/Kw0zy3FG.js +0 -1
  372. package/.output/public/_nuxt/N5XtbYVD.js +0 -1
  373. package/.output/public/_nuxt/SrncdpaW.js +0 -1
  374. package/.output/public/_nuxt/U1MWjQMi.js +0 -1
  375. package/.output/public/_nuxt/Um1vPiAz.js +0 -1
  376. package/.output/public/_nuxt/XCjS70z4.js +0 -1
  377. package/.output/public/_nuxt/YX8avsvq.js +0 -2
  378. package/.output/public/_nuxt/_cy8R3nk.js +0 -1
  379. package/.output/public/_nuxt/apYB9dr5.js +0 -1
  380. package/.output/public/_nuxt/builds/meta/a892969f-e07d-4f44-8d7e-57a4b7f33d94.json +0 -1
  381. package/.output/public/_nuxt/dashboard.BxCOkJwt.css +0 -1
  382. package/.output/public/_nuxt/entry.NKPfH2kE.css +0 -1
  383. package/.output/public/_nuxt/g5MjDvm5.js +0 -1
  384. package/.output/public/_nuxt/nnQqD5pb.js +0 -1
  385. package/.output/public/_nuxt/vScW1Zgm.js +0 -1
  386. package/.output/public/_nuxt/wO6z2ugJ.js +0 -1
  387. package/.output/public/_nuxt/x6FRJ5ac.js +0 -1
  388. package/.output/public/_nuxt/zq-a1TeT.js +0 -1
  389. package/.output/server/chunks/build/_id_-DN00UDdO.mjs.map +0 -1
  390. package/.output/server/chunks/build/chat-CR3JIVEq.mjs.map +0 -1
  391. package/.output/server/chunks/build/docs-ChGwOPg5.mjs.map +0 -1
  392. package/.output/server/chunks/build/hooks-D328DcO6.mjs.map +0 -1
  393. package/.output/server/chunks/build/index-C9PuieXh.mjs +0 -820
  394. package/.output/server/chunks/build/index-C9PuieXh.mjs.map +0 -1
  395. package/.output/server/chunks/build/settings-B2KXoGcz.mjs +0 -3232
  396. package/.output/server/chunks/build/settings-B2KXoGcz.mjs.map +0 -1
  397. package/.output/server/chunks/build/tasks-DnAFqbtt.mjs.map +0 -1
  398. package/.output/server/chunks/build/useAgents-DHEXiFSc.mjs.map +0 -1
package/dist/cli/index.js CHANGED
@@ -1250,14 +1250,38 @@ async function checkPrerequisites() {
1250
1250
  } else {
1251
1251
  R2.success(`Claude Code ${claudeOut}`);
1252
1252
  }
1253
+ let hasDocker = !!dockerOut;
1253
1254
  if (dockerOut) {
1254
1255
  R2.success(`Docker available ${import_picocolors3.default.dim("(for local PostgreSQL)")}`);
1255
1256
  } else {
1256
- R2.info(`Docker not found ${import_picocolors3.default.dim("(needed only for local PostgreSQL)")}`);
1257
+ const installDocker = await Re({
1258
+ message: "Docker not found. Install it? (needed only for local PostgreSQL)",
1259
+ initialValue: false
1260
+ });
1261
+ if (!Ct(installDocker) && installDocker) {
1262
+ const s = bt2();
1263
+ s.start("Installing Docker");
1264
+ try {
1265
+ if (process.platform === "darwin") {
1266
+ execSync("brew install --cask docker", { stdio: "inherit" });
1267
+ s.stop("Docker Desktop installed \u2014 open it from Applications to finish setup");
1268
+ } else {
1269
+ const cmd = process.platform === "linux" ? "sudo apt-get update -qq && sudo apt-get install -y -qq docker.io docker-compose-plugin" : "apt-get update -qq && apt-get install -y -qq docker.io docker-compose-plugin";
1270
+ execSync(cmd, { stdio: "inherit" });
1271
+ s.stop("Docker installed");
1272
+ }
1273
+ hasDocker = !!checkCommand("docker --version");
1274
+ } catch {
1275
+ s.stop("Docker installation failed");
1276
+ R2.warn("Install Docker manually: https://docs.docker.com/get-docker/");
1277
+ }
1278
+ } else {
1279
+ R2.info(`Skipped \u2014 you can use a remote PostgreSQL instead`);
1280
+ }
1257
1281
  }
1258
1282
  return {
1259
1283
  ok: true,
1260
- hasDocker: !!dockerOut,
1284
+ hasDocker,
1261
1285
  nodeVersion: nodeOut || "",
1262
1286
  pythonVersion: pythonOut || "",
1263
1287
  pnpmVersion: pnpmOut || "",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cognova",
3
3
  "type": "module",
4
- "version": "0.2.12",
4
+ "version": "0.2.14",
5
5
  "description": "Personal knowledge management system with Claude Code integration",
6
6
  "repository": {
7
7
  "type": "git",
@@ -1,12 +1,31 @@
1
- import { getDb } from '~~/server/db'
1
+ import { inArray, sql } from 'drizzle-orm'
2
+ import { getDb, schema } from '~~/server/db'
2
3
  import { requireDb } from '~~/server/utils/db-guard'
3
4
 
4
5
  export default defineEventHandler(async (event) => {
5
6
  requireDb(event)
6
7
 
8
+ const query = getQuery(event)
9
+ const search = query.search as string | undefined
10
+
7
11
  const db = getDb()
8
12
 
13
+ // Full-text search via tsvector + GIN index
14
+ let where
15
+ if (search) {
16
+ const ftsResult = await db.execute<{ id: string }>(sql`
17
+ SELECT id FROM cron_agents
18
+ WHERE search_vector @@ plainto_tsquery('english', ${search})
19
+ ORDER BY ts_rank(search_vector, plainto_tsquery('english', ${search})) DESC
20
+ LIMIT 50
21
+ `)
22
+ const ids = ftsResult.map(r => r.id)
23
+ if (ids.length === 0) return { data: [] }
24
+ where = inArray(schema.cronAgents.id, ids)
25
+ }
26
+
9
27
  const agents = await db.query.cronAgents.findMany({
28
+ where,
10
29
  with: { creator: true },
11
30
  orderBy: (agents, { asc }) => [asc(agents.name)]
12
31
  })
@@ -1,4 +1,4 @@
1
- import { desc } from 'drizzle-orm'
1
+ import { desc, inArray, sql } from 'drizzle-orm'
2
2
  import { getDb } from '~~/server/db'
3
3
  import * as schema from '~~/server/db/schema'
4
4
  import { requireDb } from '~~/server/utils/db-guard'
@@ -6,13 +6,31 @@ import { requireDb } from '~~/server/utils/db-guard'
6
6
  export default defineEventHandler(async (event) => {
7
7
  requireDb(event)
8
8
 
9
+ const query = getQuery(event)
10
+ const search = query.search as string | undefined
11
+
9
12
  const db = getDb()
10
13
 
14
+ // Full-text search via tsvector + GIN index
15
+ let where
16
+ if (search) {
17
+ const ftsResult = await db.execute<{ id: string }>(sql`
18
+ SELECT id FROM conversations
19
+ WHERE search_vector @@ plainto_tsquery('english', ${search})
20
+ ORDER BY ts_rank(search_vector, plainto_tsquery('english', ${search})) DESC
21
+ LIMIT 50
22
+ `)
23
+ const ids = ftsResult.map(r => r.id)
24
+ if (ids.length === 0) return { data: [] }
25
+ where = inArray(schema.conversations.id, ids)
26
+ }
27
+
11
28
  // Main Chat always first, then by most recent
12
- const conversations = await db.select()
13
- .from(schema.conversations)
14
- .orderBy(desc(schema.conversations.isMain), desc(schema.conversations.startedAt))
15
- .limit(50)
29
+ const conversations = await db.query.conversations.findMany({
30
+ where,
31
+ orderBy: [desc(schema.conversations.isMain), desc(schema.conversations.startedAt)],
32
+ limit: 50
33
+ })
16
34
 
17
35
  return { data: conversations }
18
36
  })
@@ -1,4 +1,4 @@
1
- import { eq, isNull, ilike, and, or } from 'drizzle-orm'
1
+ import { eq, isNull, inArray, and, sql } from 'drizzle-orm'
2
2
  import { getDb } from '~~/server/db'
3
3
  import * as schema from '~~/server/db/schema'
4
4
  import { requireDb } from '~~/server/utils/db-guard'
@@ -24,15 +24,17 @@ export default defineEventHandler(async (event) => {
24
24
  if (fileType)
25
25
  conditions.push(eq(schema.documents.fileType, fileType))
26
26
 
27
+ // Full-text search via tsvector + GIN index
27
28
  if (search) {
28
- const pattern = `%${search}%`
29
- conditions.push(
30
- or(
31
- ilike(schema.documents.title, pattern),
32
- ilike(schema.documents.content, pattern),
33
- ilike(schema.documents.path, pattern)
34
- )
35
- )
29
+ const ftsResult = await db.execute<{ id: string }>(sql`
30
+ SELECT id FROM documents
31
+ WHERE search_vector @@ plainto_tsquery('english', ${search})
32
+ ORDER BY ts_rank(search_vector, plainto_tsquery('english', ${search})) DESC
33
+ LIMIT 50
34
+ `)
35
+ const ids = ftsResult.map(r => r.id)
36
+ if (ids.length === 0) return { data: [] }
37
+ conditions.push(inArray(schema.documents.id, ids))
36
38
  }
37
39
 
38
40
  const documents = await db.query.documents.findMany({
@@ -1,4 +1,4 @@
1
- import { sql, desc, eq, and, gte, ilike, or } from 'drizzle-orm'
1
+ import { sql, desc, eq, and, gte, inArray } from 'drizzle-orm'
2
2
  import { getDb, schema } from '~~/server/db'
3
3
  import { requireDb } from '~~/server/utils/db-guard'
4
4
  import type { MemoryChunk, MemoryChunkType } from '~~/shared/types'
@@ -29,12 +29,17 @@ export default defineEventHandler(async (event) => {
29
29
  if (minRelevance !== undefined)
30
30
  conditions.push(gte(schema.memoryChunks.relevanceScore, minRelevance))
31
31
 
32
- // Text search (simple ILIKE for now, will upgrade to tsvector)
32
+ // Full-text search via tsvector + GIN index
33
33
  if (searchQuery) {
34
- conditions.push(or(
35
- ilike(schema.memoryChunks.content, `%${searchQuery}%`),
36
- ilike(schema.memoryChunks.sourceExcerpt, `%${searchQuery}%`)
37
- ))
34
+ const ftsResult = await db.execute<{ id: string }>(sql`
35
+ SELECT id FROM memory_chunks
36
+ WHERE search_vector @@ plainto_tsquery('english', ${searchQuery})
37
+ ORDER BY ts_rank(search_vector, plainto_tsquery('english', ${searchQuery})) DESC
38
+ LIMIT ${limit}
39
+ `)
40
+ const ids = ftsResult.map(r => r.id)
41
+ if (ids.length === 0) return { data: [] as MemoryChunk[] }
42
+ conditions.push(inArray(schema.memoryChunks.id, ids))
38
43
  }
39
44
 
40
45
  let dbQuery = db.select()
@@ -1,4 +1,4 @@
1
- import { eq, isNull, ilike, inArray, and, or } from 'drizzle-orm'
1
+ import { eq, isNull, inArray, and, sql } from 'drizzle-orm'
2
2
  import { getDb, schema } from '~~/server/db'
3
3
  import { requireDb } from '~~/server/utils/db-guard'
4
4
 
@@ -29,15 +29,17 @@ export default defineEventHandler(async (event) => {
29
29
  if (projectId)
30
30
  conditions.push(eq(schema.tasks.projectId, projectId))
31
31
 
32
- // Search filter (title and description)
32
+ // Full-text search via tsvector + GIN index
33
33
  if (search) {
34
- const searchPattern = `%${search}%`
35
- conditions.push(
36
- or(
37
- ilike(schema.tasks.title, searchPattern),
38
- ilike(schema.tasks.description, searchPattern)
39
- )
40
- )
34
+ const ftsResult = await db.execute<{ id: string }>(sql`
35
+ SELECT id FROM tasks
36
+ WHERE search_vector @@ plainto_tsquery('english', ${search})
37
+ ORDER BY ts_rank(search_vector, plainto_tsquery('english', ${search})) DESC
38
+ LIMIT 50
39
+ `)
40
+ const ids = ftsResult.map(r => r.id)
41
+ if (ids.length === 0) return { data: [] }
42
+ conditions.push(inArray(schema.tasks.id, ids))
41
43
  }
42
44
 
43
45
  const tasks = await db.query.tasks.findMany({
@@ -263,12 +263,19 @@ export class IMessageAdapter implements BridgeAdapter {
263
263
  }
264
264
 
265
265
  try {
266
+ // Format chatGuid if recipient is a bare phone number or email
267
+ const chatGuid = msg.recipient.includes(';')
268
+ ? msg.recipient
269
+ : `iMessage;-;${msg.recipient.startsWith('+') ? msg.recipient : `+${msg.recipient}`}`
270
+
266
271
  const resp = await fetch(`${this.bbUrl}/api/v1/message/text?password=${encodeURIComponent(this.bbPassword)}`, {
267
272
  method: 'POST',
268
273
  headers: { 'Content-Type': 'application/json' },
269
274
  body: JSON.stringify({
270
- chatGuid: msg.recipient,
271
- message: msg.text
275
+ chatGuid,
276
+ tempGuid: `temp-${randomBytes(16).toString('hex')}`,
277
+ message: msg.text,
278
+ method: 'private-api'
272
279
  })
273
280
  })
274
281
 
@@ -0,0 +1,127 @@
1
+ -- Full-Text Search: Add tsvector columns, GIN indexes, and auto-update triggers
2
+ -- This migration enables PostgreSQL full-text search across 5 tables.
3
+
4
+ -- =============================================================================
5
+ -- TASKS
6
+ -- =============================================================================
7
+
8
+ ALTER TABLE tasks ADD COLUMN search_vector tsvector;
9
+
10
+ CREATE INDEX idx_tasks_search ON tasks USING GIN (search_vector);
11
+
12
+ CREATE OR REPLACE FUNCTION tasks_search_vector_update() RETURNS trigger AS $$
13
+ BEGIN
14
+ NEW.search_vector := to_tsvector('english',
15
+ coalesce(NEW.title, '') || ' ' || coalesce(NEW.description, '')
16
+ );
17
+ RETURN NEW;
18
+ END;
19
+ $$ LANGUAGE plpgsql;
20
+
21
+ CREATE TRIGGER tasks_search_vector_trigger
22
+ BEFORE INSERT OR UPDATE OF title, description ON tasks
23
+ FOR EACH ROW EXECUTE FUNCTION tasks_search_vector_update();
24
+
25
+ UPDATE tasks SET search_vector = to_tsvector('english',
26
+ coalesce(title, '') || ' ' || coalesce(description, '')
27
+ );
28
+
29
+ -- =============================================================================
30
+ -- DOCUMENTS
31
+ -- =============================================================================
32
+
33
+ ALTER TABLE documents ADD COLUMN search_vector tsvector;
34
+
35
+ CREATE INDEX idx_documents_search ON documents USING GIN (search_vector);
36
+
37
+ CREATE OR REPLACE FUNCTION documents_search_vector_update() RETURNS trigger AS $$
38
+ BEGIN
39
+ NEW.search_vector := to_tsvector('english',
40
+ coalesce(NEW.title, '') || ' ' || coalesce(NEW.content, '') || ' ' || coalesce(NEW.path, '')
41
+ );
42
+ RETURN NEW;
43
+ END;
44
+ $$ LANGUAGE plpgsql;
45
+
46
+ CREATE TRIGGER documents_search_vector_trigger
47
+ BEFORE INSERT OR UPDATE OF title, content, path ON documents
48
+ FOR EACH ROW EXECUTE FUNCTION documents_search_vector_update();
49
+
50
+ UPDATE documents SET search_vector = to_tsvector('english',
51
+ coalesce(title, '') || ' ' || coalesce(content, '') || ' ' || coalesce(path, '')
52
+ );
53
+
54
+ -- =============================================================================
55
+ -- MEMORY CHUNKS
56
+ -- =============================================================================
57
+
58
+ ALTER TABLE memory_chunks ADD COLUMN search_vector tsvector;
59
+
60
+ CREATE INDEX idx_memory_chunks_search ON memory_chunks USING GIN (search_vector);
61
+
62
+ CREATE OR REPLACE FUNCTION memory_chunks_search_vector_update() RETURNS trigger AS $$
63
+ BEGIN
64
+ NEW.search_vector := to_tsvector('english',
65
+ coalesce(NEW.content, '') || ' ' || coalesce(NEW.source_excerpt, '')
66
+ );
67
+ RETURN NEW;
68
+ END;
69
+ $$ LANGUAGE plpgsql;
70
+
71
+ CREATE TRIGGER memory_chunks_search_vector_trigger
72
+ BEFORE INSERT OR UPDATE OF content, source_excerpt ON memory_chunks
73
+ FOR EACH ROW EXECUTE FUNCTION memory_chunks_search_vector_update();
74
+
75
+ UPDATE memory_chunks SET search_vector = to_tsvector('english',
76
+ coalesce(content, '') || ' ' || coalesce(source_excerpt, '')
77
+ );
78
+
79
+ -- =============================================================================
80
+ -- CRON AGENTS
81
+ -- =============================================================================
82
+
83
+ ALTER TABLE cron_agents ADD COLUMN search_vector tsvector;
84
+
85
+ CREATE INDEX idx_cron_agents_search ON cron_agents USING GIN (search_vector);
86
+
87
+ CREATE OR REPLACE FUNCTION cron_agents_search_vector_update() RETURNS trigger AS $$
88
+ BEGIN
89
+ NEW.search_vector := to_tsvector('english',
90
+ coalesce(NEW.name, '') || ' ' || coalesce(NEW.description, '') || ' ' || coalesce(NEW.prompt, '')
91
+ );
92
+ RETURN NEW;
93
+ END;
94
+ $$ LANGUAGE plpgsql;
95
+
96
+ CREATE TRIGGER cron_agents_search_vector_trigger
97
+ BEFORE INSERT OR UPDATE OF name, description, prompt ON cron_agents
98
+ FOR EACH ROW EXECUTE FUNCTION cron_agents_search_vector_update();
99
+
100
+ UPDATE cron_agents SET search_vector = to_tsvector('english',
101
+ coalesce(name, '') || ' ' || coalesce(description, '') || ' ' || coalesce(prompt, '')
102
+ );
103
+
104
+ -- =============================================================================
105
+ -- CONVERSATIONS
106
+ -- =============================================================================
107
+
108
+ ALTER TABLE conversations ADD COLUMN search_vector tsvector;
109
+
110
+ CREATE INDEX idx_conversations_search ON conversations USING GIN (search_vector);
111
+
112
+ CREATE OR REPLACE FUNCTION conversations_search_vector_update() RETURNS trigger AS $$
113
+ BEGIN
114
+ NEW.search_vector := to_tsvector('english',
115
+ coalesce(NEW.title, '') || ' ' || coalesce(NEW.summary, '')
116
+ );
117
+ RETURN NEW;
118
+ END;
119
+ $$ LANGUAGE plpgsql;
120
+
121
+ CREATE TRIGGER conversations_search_vector_trigger
122
+ BEFORE INSERT OR UPDATE OF title, summary ON conversations
123
+ FOR EACH ROW EXECUTE FUNCTION conversations_search_vector_update();
124
+
125
+ UPDATE conversations SET search_vector = to_tsvector('english',
126
+ coalesce(title, '') || ' ' || coalesce(summary, '')
127
+ );
@@ -113,6 +113,13 @@
113
113
  "when": 1771736174854,
114
114
  "tag": "0015_great_mephistopheles",
115
115
  "breakpoints": true
116
+ },
117
+ {
118
+ "idx": 16,
119
+ "version": "7",
120
+ "when": 1772006400000,
121
+ "tag": "0016_full_text_search",
122
+ "breakpoints": true
116
123
  }
117
124
  ]
118
125
  }
@@ -4,7 +4,8 @@ import { chatSessionManager } from '~~/server/utils/chat-session-manager'
4
4
  import { getDb } from '~~/server/db'
5
5
  import * as schema from '~~/server/db/schema'
6
6
  import { logTokenUsage } from '~~/server/utils/log-token-usage'
7
- import type { ChatClientMessage, ChatContentBlock } from '~~/shared/types'
7
+ import type { ChatClientMessage, ChatContentBlock, ChatImageBlock, ChatDocumentBlock } from '~~/shared/types'
8
+ import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages/messages'
8
9
 
9
10
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
11
  function send(peer: any, data: object) {
@@ -16,6 +17,42 @@ function send(peer: any, data: object) {
16
17
  }
17
18
  }
18
19
 
20
+ function buildSdkContent(
21
+ message: string,
22
+ attachments?: ChatImageBlock[],
23
+ documents?: ChatDocumentBlock[]
24
+ ): string | ContentBlockParam[] {
25
+ if (!attachments?.length && !documents?.length) return message
26
+
27
+ const blocks: ContentBlockParam[] = []
28
+
29
+ if (attachments?.length) {
30
+ for (const img of attachments) {
31
+ blocks.push({
32
+ type: 'image',
33
+ source: {
34
+ type: 'base64',
35
+ media_type: img.source.media_type,
36
+ data: img.source.data
37
+ }
38
+ })
39
+ }
40
+ }
41
+
42
+ if (documents?.length) {
43
+ for (const doc of documents) {
44
+ blocks.push({
45
+ type: 'document',
46
+ source: doc.source,
47
+ title: doc.title
48
+ })
49
+ }
50
+ }
51
+
52
+ if (message) blocks.push({ type: 'text', text: message })
53
+ return blocks
54
+ }
55
+
19
56
  export default defineWebSocketHandler({
20
57
  open(peer) {
21
58
  console.log(`[chat] WebSocket opened: ${peer.id}`)
@@ -28,7 +65,7 @@ export default defineWebSocketHandler({
28
65
 
29
66
  switch (msg.type) {
30
67
  case 'chat:send':
31
- handleSend(peer, msg.message, msg.conversationId)
68
+ handleSend(peer, msg.message, msg.conversationId, msg.attachments, msg.documents)
32
69
  break
33
70
  case 'chat:interrupt':
34
71
  handleInterrupt(peer, msg.conversationId)
@@ -49,8 +86,14 @@ export default defineWebSocketHandler({
49
86
  }
50
87
  })
51
88
 
52
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
- async function handleSend(peer: any, message: string, conversationId?: string) {
89
+ async function handleSend(
90
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
91
+ peer: any,
92
+ message: string,
93
+ conversationId?: string,
94
+ attachments?: ChatImageBlock[],
95
+ documents?: ChatDocumentBlock[]
96
+ ) {
54
97
  const db = getDb()
55
98
 
56
99
  let convId = conversationId
@@ -61,7 +104,7 @@ async function handleSend(peer: any, message: string, conversationId?: string) {
61
104
  const [conv] = await db.insert(schema.conversations)
62
105
  .values({
63
106
  sessionId: randomUUID(),
64
- title: message.slice(0, 100),
107
+ title: message.slice(0, 100) || 'File attachment',
65
108
  status: 'streaming',
66
109
  messageCount: 0,
67
110
  totalCostUsd: 0
@@ -93,16 +136,27 @@ async function handleSend(peer: any, message: string, conversationId?: string) {
93
136
  .where(eq(schema.conversations.id, convId))
94
137
  }
95
138
 
139
+ // Build content blocks for persistence
140
+ const userContent: ChatContentBlock[] = []
141
+ if (attachments?.length)
142
+ for (const img of attachments) userContent.push(img)
143
+ if (documents?.length)
144
+ for (const doc of documents) userContent.push(doc)
145
+ if (message) userContent.push({ type: 'text', text: message })
146
+
96
147
  // Persist user message
97
148
  await db.insert(schema.conversationMessages).values({
98
149
  conversationId: convId,
99
150
  role: 'user',
100
- content: JSON.stringify([{ type: 'text', text: message }]),
151
+ content: JSON.stringify(userContent),
101
152
  source: 'web'
102
153
  })
103
154
 
155
+ // Build prompt for SDK (string for text-only, ContentBlockParam[] for multimodal)
156
+ const prompt = buildSdkContent(message, attachments, documents)
157
+
104
158
  // Start SDK streaming (fire-and-forget so WS stays responsive for interrupts)
105
- const session = chatSessionManager.startSession(convId, message, resumeSessionId)
159
+ const session = chatSessionManager.startSession(convId, prompt, resumeSessionId)
106
160
  send(peer, { type: 'chat:stream_start', conversationId: convId })
107
161
 
108
162
  streamSDKResponse(peer, session, convId).catch((err) => {
@@ -1,4 +1,8 @@
1
1
  import { query } from '@anthropic-ai/claude-agent-sdk'
2
+ import type { SDKUserMessage } from '@anthropic-ai/claude-agent-sdk'
3
+ import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages/messages'
4
+
5
+ export type PromptInput = string | ContentBlockParam[]
2
6
 
3
7
  interface ActiveSession {
4
8
  conversationId: string
@@ -8,15 +12,29 @@ interface ActiveSession {
8
12
  startedAt: Date
9
13
  }
10
14
 
15
+ async function* createMultimodalPrompt(content: ContentBlockParam[]): AsyncGenerator<SDKUserMessage> {
16
+ yield {
17
+ type: 'user',
18
+ message: { role: 'user', content },
19
+ parent_tool_use_id: null,
20
+ session_id: ''
21
+ } as SDKUserMessage
22
+ }
23
+
11
24
  class ChatSessionManager {
12
25
  private sessions = new Map<string, ActiveSession>()
13
26
 
14
- startSession(conversationId: string, prompt: string, resumeSessionId?: string): ActiveSession {
27
+ startSession(conversationId: string, prompt: PromptInput, resumeSessionId?: string): ActiveSession {
15
28
  // Use the project directory as CWD so the SDK picks up .claude/ (skills, rules, CLAUDE.md)
16
29
  // The vault is accessible via VAULT_PATH env var in tools
17
30
  const projectDir = process.env.COGNOVA_PROJECT_DIR || process.cwd()
31
+
32
+ const promptArg = typeof prompt === 'string'
33
+ ? prompt
34
+ : createMultimodalPrompt(prompt)
35
+
18
36
  const conversation = query({
19
- prompt,
37
+ prompt: promptArg,
20
38
  options: {
21
39
  cwd: projectDir,
22
40
  settingSources: ['user', 'project'],
@@ -207,10 +207,16 @@ export interface PublicDocumentResponse {
207
207
 
208
208
  export interface Conversation {
209
209
  id: string
210
+ sessionId: string
211
+ sdkSessionId?: string
212
+ title?: string
213
+ summary?: string
214
+ status: 'idle' | 'streaming' | 'interrupted' | 'error'
215
+ totalCostUsd: number
216
+ isMain: boolean
210
217
  startedAt: Date
211
218
  endedAt?: Date
212
219
  messageCount: number
213
- summary?: string
214
220
  }
215
221
 
216
222
  export interface ConversationDetail extends Conversation {
@@ -689,7 +695,31 @@ export interface ChatToolResultBlock {
689
695
  is_error?: boolean
690
696
  }
691
697
 
692
- export type ChatContentBlock = ChatTextBlock | ChatToolUseBlock | ChatToolResultBlock
698
+ export type ChatImageMediaType = 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp'
699
+
700
+ export interface ChatImageBlock {
701
+ type: 'image'
702
+ source: {
703
+ type: 'base64'
704
+ media_type: ChatImageMediaType
705
+ data: string
706
+ }
707
+ }
708
+
709
+ export interface ChatDocumentBlock {
710
+ type: 'document'
711
+ source:
712
+ | { type: 'base64', media_type: 'application/pdf', data: string }
713
+ | { type: 'text', media_type: 'text/plain', data: string }
714
+ title?: string
715
+ }
716
+
717
+ export type ChatContentBlock
718
+ = ChatTextBlock
719
+ | ChatImageBlock
720
+ | ChatDocumentBlock
721
+ | ChatToolUseBlock
722
+ | ChatToolResultBlock
693
723
 
694
724
  export type MessageSource = 'web' | 'telegram' | 'discord' | 'imessage' | 'email' | 'google'
695
725
 
@@ -724,6 +754,8 @@ export interface ChatConversation {
724
754
  export interface ChatSendMessage {
725
755
  type: 'chat:send'
726
756
  message: string
757
+ attachments?: ChatImageBlock[]
758
+ documents?: ChatDocumentBlock[]
727
759
  conversationId?: string
728
760
  }
729
761