comisai 1.0.11 → 1.0.13

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 (255) hide show
  1. package/node_modules/@comis/agent/dist/model/operation-model-defaults.js +1 -0
  2. package/node_modules/@comis/agent/dist/session/comis-session-manager.js +9 -9
  3. package/node_modules/@comis/agent/package.json +1 -1
  4. package/node_modules/@comis/channels/package.json +1 -1
  5. package/node_modules/@comis/cli/package.json +1 -1
  6. package/node_modules/@comis/core/dist/event-bus/events-infra.d.ts +2 -0
  7. package/node_modules/@comis/core/package.json +1 -1
  8. package/node_modules/@comis/daemon/dist/wiring/setup-channels.js +12 -1
  9. package/node_modules/@comis/daemon/dist/wiring/setup-gateway.js +1 -0
  10. package/node_modules/@comis/daemon/dist/wiring/setup-schedulers.js +1 -0
  11. package/node_modules/@comis/daemon/package.json +1 -1
  12. package/node_modules/@comis/gateway/package.json +1 -1
  13. package/node_modules/@comis/infra/package.json +1 -1
  14. package/node_modules/@comis/memory/package.json +1 -1
  15. package/node_modules/@comis/scheduler/dist/cron/cron-types.d.ts +6 -0
  16. package/node_modules/@comis/scheduler/dist/cron/cron-types.js +2 -0
  17. package/node_modules/@comis/scheduler/package.json +1 -1
  18. package/node_modules/@comis/shared/package.json +1 -1
  19. package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.d.ts +1 -1
  20. package/node_modules/@comis/skills/package.json +1 -1
  21. package/package.json +18 -18
  22. package/node_modules/@comis/core/dist/approval/approval-gate.test.d.ts +0 -1
  23. package/node_modules/@comis/core/dist/approval/approval-gate.test.js +0 -1003
  24. package/node_modules/@comis/core/dist/bootstrap.test.d.ts +0 -1
  25. package/node_modules/@comis/core/dist/bootstrap.test.js +0 -150
  26. package/node_modules/@comis/core/dist/config/backup.test.d.ts +0 -1
  27. package/node_modules/@comis/core/dist/config/backup.test.js +0 -160
  28. package/node_modules/@comis/core/dist/config/env-substitution.test.d.ts +0 -1
  29. package/node_modules/@comis/core/dist/config/env-substitution.test.js +0 -259
  30. package/node_modules/@comis/core/dist/config/field-metadata.test.d.ts +0 -1
  31. package/node_modules/@comis/core/dist/config/field-metadata.test.js +0 -72
  32. package/node_modules/@comis/core/dist/config/git-manager.test.d.ts +0 -1
  33. package/node_modules/@comis/core/dist/config/git-manager.test.js +0 -1042
  34. package/node_modules/@comis/core/dist/config/immutable-keys.test.d.ts +0 -1
  35. package/node_modules/@comis/core/dist/config/immutable-keys.test.js +0 -283
  36. package/node_modules/@comis/core/dist/config/include-resolver.test.d.ts +0 -1
  37. package/node_modules/@comis/core/dist/config/include-resolver.test.js +0 -292
  38. package/node_modules/@comis/core/dist/config/layered.test.d.ts +0 -1
  39. package/node_modules/@comis/core/dist/config/layered.test.js +0 -211
  40. package/node_modules/@comis/core/dist/config/loader.test.d.ts +0 -1
  41. package/node_modules/@comis/core/dist/config/loader.test.js +0 -300
  42. package/node_modules/@comis/core/dist/config/migrate.test.d.ts +0 -1
  43. package/node_modules/@comis/core/dist/config/migrate.test.js +0 -244
  44. package/node_modules/@comis/core/dist/config/partial-validator.test.d.ts +0 -1
  45. package/node_modules/@comis/core/dist/config/partial-validator.test.js +0 -98
  46. package/node_modules/@comis/core/dist/config/schema-agent-model.test.d.ts +0 -1
  47. package/node_modules/@comis/core/dist/config/schema-agent-model.test.js +0 -279
  48. package/node_modules/@comis/core/dist/config/schema-agent-session.test.d.ts +0 -1
  49. package/node_modules/@comis/core/dist/config/schema-agent-session.test.js +0 -242
  50. package/node_modules/@comis/core/dist/config/schema-agent.test.d.ts +0 -1
  51. package/node_modules/@comis/core/dist/config/schema-agent.test.js +0 -645
  52. package/node_modules/@comis/core/dist/config/schema-channel.test.d.ts +0 -1
  53. package/node_modules/@comis/core/dist/config/schema-channel.test.js +0 -341
  54. package/node_modules/@comis/core/dist/config/schema-coalescer.test.d.ts +0 -1
  55. package/node_modules/@comis/core/dist/config/schema-coalescer.test.js +0 -51
  56. package/node_modules/@comis/core/dist/config/schema-context-engine.test.d.ts +0 -1
  57. package/node_modules/@comis/core/dist/config/schema-context-engine.test.js +0 -797
  58. package/node_modules/@comis/core/dist/config/schema-context-guard.test.d.ts +0 -1
  59. package/node_modules/@comis/core/dist/config/schema-context-guard.test.js +0 -111
  60. package/node_modules/@comis/core/dist/config/schema-daemon.test.d.ts +0 -1
  61. package/node_modules/@comis/core/dist/config/schema-daemon.test.js +0 -107
  62. package/node_modules/@comis/core/dist/config/schema-delivery-timing.test.d.ts +0 -1
  63. package/node_modules/@comis/core/dist/config/schema-delivery-timing.test.js +0 -51
  64. package/node_modules/@comis/core/dist/config/schema-documentation.test.d.ts +0 -1
  65. package/node_modules/@comis/core/dist/config/schema-documentation.test.js +0 -63
  66. package/node_modules/@comis/core/dist/config/schema-gateway.test.d.ts +0 -1
  67. package/node_modules/@comis/core/dist/config/schema-gateway.test.js +0 -219
  68. package/node_modules/@comis/core/dist/config/schema-gemini-cache.test.d.ts +0 -1
  69. package/node_modules/@comis/core/dist/config/schema-gemini-cache.test.js +0 -41
  70. package/node_modules/@comis/core/dist/config/schema-integrations.test.d.ts +0 -1
  71. package/node_modules/@comis/core/dist/config/schema-integrations.test.js +0 -92
  72. package/node_modules/@comis/core/dist/config/schema-lifecycle-reactions.test.d.ts +0 -1
  73. package/node_modules/@comis/core/dist/config/schema-lifecycle-reactions.test.js +0 -61
  74. package/node_modules/@comis/core/dist/config/schema-misc.test.d.ts +0 -1
  75. package/node_modules/@comis/core/dist/config/schema-misc.test.js +0 -394
  76. package/node_modules/@comis/core/dist/config/schema-observability.test.d.ts +0 -1
  77. package/node_modules/@comis/core/dist/config/schema-observability.test.js +0 -76
  78. package/node_modules/@comis/core/dist/config/schema-providers.test.d.ts +0 -1
  79. package/node_modules/@comis/core/dist/config/schema-providers.test.js +0 -294
  80. package/node_modules/@comis/core/dist/config/schema-queue.test.d.ts +0 -1
  81. package/node_modules/@comis/core/dist/config/schema-queue.test.js +0 -234
  82. package/node_modules/@comis/core/dist/config/schema-response-prefix.test.d.ts +0 -1
  83. package/node_modules/@comis/core/dist/config/schema-response-prefix.test.js +0 -36
  84. package/node_modules/@comis/core/dist/config/schema-security.test.d.ts +0 -1
  85. package/node_modules/@comis/core/dist/config/schema-security.test.js +0 -54
  86. package/node_modules/@comis/core/dist/config/schema-sender-trust-display.test.d.ts +0 -1
  87. package/node_modules/@comis/core/dist/config/schema-sender-trust-display.test.js +0 -60
  88. package/node_modules/@comis/core/dist/config/schema-serializer.test.d.ts +0 -1
  89. package/node_modules/@comis/core/dist/config/schema-serializer.test.js +0 -73
  90. package/node_modules/@comis/core/dist/config/schema-telegram-file-guard.test.d.ts +0 -1
  91. package/node_modules/@comis/core/dist/config/schema-telegram-file-guard.test.js +0 -39
  92. package/node_modules/@comis/core/dist/context/context.test.d.ts +0 -1
  93. package/node_modules/@comis/core/dist/context/context.test.js +0 -276
  94. package/node_modules/@comis/core/dist/domain/agent-response.test.d.ts +0 -1
  95. package/node_modules/@comis/core/dist/domain/agent-response.test.js +0 -159
  96. package/node_modules/@comis/core/dist/domain/credential-mapping.test.d.ts +0 -1
  97. package/node_modules/@comis/core/dist/domain/credential-mapping.test.js +0 -135
  98. package/node_modules/@comis/core/dist/domain/delivery-origin.test.d.ts +0 -1
  99. package/node_modules/@comis/core/dist/domain/delivery-origin.test.js +0 -68
  100. package/node_modules/@comis/core/dist/domain/execution-graph.test.d.ts +0 -1
  101. package/node_modules/@comis/core/dist/domain/execution-graph.test.js +0 -441
  102. package/node_modules/@comis/core/dist/domain/memory-entry.test.d.ts +0 -1
  103. package/node_modules/@comis/core/dist/domain/memory-entry.test.js +0 -193
  104. package/node_modules/@comis/core/dist/domain/normalized-message.test.d.ts +0 -1
  105. package/node_modules/@comis/core/dist/domain/normalized-message.test.js +0 -255
  106. package/node_modules/@comis/core/dist/domain/session-key.test.d.ts +0 -1
  107. package/node_modules/@comis/core/dist/domain/session-key.test.js +0 -291
  108. package/node_modules/@comis/core/dist/domain/subagent-context-config.test.d.ts +0 -1
  109. package/node_modules/@comis/core/dist/domain/subagent-context-config.test.js +0 -131
  110. package/node_modules/@comis/core/dist/domain/subagent-context-types.test.d.ts +0 -1
  111. package/node_modules/@comis/core/dist/domain/subagent-context-types.test.js +0 -182
  112. package/node_modules/@comis/core/dist/event-bus/bus.test.d.ts +0 -1
  113. package/node_modules/@comis/core/dist/event-bus/bus.test.js +0 -152
  114. package/node_modules/@comis/core/dist/event-bus/events-agent.test.d.ts +0 -1
  115. package/node_modules/@comis/core/dist/event-bus/events-agent.test.js +0 -409
  116. package/node_modules/@comis/core/dist/event-bus/events-channel.test.d.ts +0 -1
  117. package/node_modules/@comis/core/dist/event-bus/events-channel.test.js +0 -240
  118. package/node_modules/@comis/core/dist/event-bus/events-infra.test.d.ts +0 -1
  119. package/node_modules/@comis/core/dist/event-bus/events-infra.test.js +0 -416
  120. package/node_modules/@comis/core/dist/event-bus/events-messaging.test.d.ts +0 -1
  121. package/node_modules/@comis/core/dist/event-bus/events-messaging.test.js +0 -433
  122. package/node_modules/@comis/core/dist/event-bus/events.test.d.ts +0 -1
  123. package/node_modules/@comis/core/dist/event-bus/events.test.js +0 -93
  124. package/node_modules/@comis/core/dist/hooks/hook-runner-global.test.d.ts +0 -1
  125. package/node_modules/@comis/core/dist/hooks/hook-runner-global.test.js +0 -29
  126. package/node_modules/@comis/core/dist/hooks/hook-runner.test.d.ts +0 -1
  127. package/node_modules/@comis/core/dist/hooks/hook-runner.test.js +0 -490
  128. package/node_modules/@comis/core/dist/hooks/hook-strategies.test.d.ts +0 -1
  129. package/node_modules/@comis/core/dist/hooks/hook-strategies.test.js +0 -209
  130. package/node_modules/@comis/core/dist/hooks/integration.test.d.ts +0 -1
  131. package/node_modules/@comis/core/dist/hooks/integration.test.js +0 -235
  132. package/node_modules/@comis/core/dist/hooks/plugin-registry.test.d.ts +0 -1
  133. package/node_modules/@comis/core/dist/hooks/plugin-registry.test.js +0 -470
  134. package/node_modules/@comis/core/dist/load-env.test.d.ts +0 -1
  135. package/node_modules/@comis/core/dist/load-env.test.js +0 -21
  136. package/node_modules/@comis/core/dist/security/action-classifier.test.d.ts +0 -1
  137. package/node_modules/@comis/core/dist/security/action-classifier.test.js +0 -298
  138. package/node_modules/@comis/core/dist/security/audit-aggregator.test.d.ts +0 -1
  139. package/node_modules/@comis/core/dist/security/audit-aggregator.test.js +0 -128
  140. package/node_modules/@comis/core/dist/security/audit.test.d.ts +0 -1
  141. package/node_modules/@comis/core/dist/security/audit.test.js +0 -154
  142. package/node_modules/@comis/core/dist/security/canary-token.test.d.ts +0 -1
  143. package/node_modules/@comis/core/dist/security/canary-token.test.js +0 -45
  144. package/node_modules/@comis/core/dist/security/config-redaction.test.d.ts +0 -1
  145. package/node_modules/@comis/core/dist/security/config-redaction.test.js +0 -107
  146. package/node_modules/@comis/core/dist/security/external-content.test.d.ts +0 -1
  147. package/node_modules/@comis/core/dist/security/external-content.test.js +0 -210
  148. package/node_modules/@comis/core/dist/security/injection-patterns.test.d.ts +0 -1
  149. package/node_modules/@comis/core/dist/security/injection-patterns.test.js +0 -213
  150. package/node_modules/@comis/core/dist/security/injection-rate-limiter.test.d.ts +0 -1
  151. package/node_modules/@comis/core/dist/security/injection-rate-limiter.test.js +0 -194
  152. package/node_modules/@comis/core/dist/security/input-guard.test.d.ts +0 -1
  153. package/node_modules/@comis/core/dist/security/input-guard.test.js +0 -215
  154. package/node_modules/@comis/core/dist/security/input-security-guard.test.d.ts +0 -1
  155. package/node_modules/@comis/core/dist/security/input-security-guard.test.js +0 -215
  156. package/node_modules/@comis/core/dist/security/input-validator.test.d.ts +0 -1
  157. package/node_modules/@comis/core/dist/security/input-validator.test.js +0 -133
  158. package/node_modules/@comis/core/dist/security/log-sanitizer.test.d.ts +0 -1
  159. package/node_modules/@comis/core/dist/security/log-sanitizer.test.js +0 -260
  160. package/node_modules/@comis/core/dist/security/memory-write-validator.test.d.ts +0 -1
  161. package/node_modules/@comis/core/dist/security/memory-write-validator.test.js +0 -62
  162. package/node_modules/@comis/core/dist/security/output-guard.test.d.ts +0 -1
  163. package/node_modules/@comis/core/dist/security/output-guard.test.js +0 -397
  164. package/node_modules/@comis/core/dist/security/safe-path.test.d.ts +0 -1
  165. package/node_modules/@comis/core/dist/security/safe-path.test.js +0 -95
  166. package/node_modules/@comis/core/dist/security/scoped-secret-manager.test.d.ts +0 -1
  167. package/node_modules/@comis/core/dist/security/scoped-secret-manager.test.js +0 -231
  168. package/node_modules/@comis/core/dist/security/secret-access.test.d.ts +0 -1
  169. package/node_modules/@comis/core/dist/security/secret-access.test.js +0 -41
  170. package/node_modules/@comis/core/dist/security/secret-crypto.test.d.ts +0 -1
  171. package/node_modules/@comis/core/dist/security/secret-crypto.test.js +0 -113
  172. package/node_modules/@comis/core/dist/security/secret-manager.test.d.ts +0 -1
  173. package/node_modules/@comis/core/dist/security/secret-manager.test.js +0 -394
  174. package/node_modules/@comis/core/dist/security/secret-ref-resolver.test.d.ts +0 -1
  175. package/node_modules/@comis/core/dist/security/secret-ref-resolver.test.js +0 -268
  176. package/node_modules/@comis/core/dist/security/secrets-audit.test.d.ts +0 -10
  177. package/node_modules/@comis/core/dist/security/secrets-audit.test.js +0 -295
  178. package/node_modules/@comis/core/dist/security/ssrf-guard.test.d.ts +0 -1
  179. package/node_modules/@comis/core/dist/security/ssrf-guard.test.js +0 -127
  180. package/node_modules/@comis/core/dist/security/token-generator.test.d.ts +0 -1
  181. package/node_modules/@comis/core/dist/security/token-generator.test.js +0 -35
  182. package/node_modules/@comis/core/dist/tool-metadata.test.d.ts +0 -1
  183. package/node_modules/@comis/core/dist/tool-metadata.test.js +0 -112
  184. package/node_modules/@comis/memory/dist/compaction.test.d.ts +0 -1
  185. package/node_modules/@comis/memory/dist/compaction.test.js +0 -298
  186. package/node_modules/@comis/memory/dist/context-schema.test.d.ts +0 -1
  187. package/node_modules/@comis/memory/dist/context-schema.test.js +0 -246
  188. package/node_modules/@comis/memory/dist/context-store.test.d.ts +0 -1
  189. package/node_modules/@comis/memory/dist/context-store.test.js +0 -708
  190. package/node_modules/@comis/memory/dist/credential-mapping-schema.test.d.ts +0 -7
  191. package/node_modules/@comis/memory/dist/credential-mapping-schema.test.js +0 -73
  192. package/node_modules/@comis/memory/dist/credential-mapping-store.test.d.ts +0 -8
  193. package/node_modules/@comis/memory/dist/credential-mapping-store.test.js +0 -340
  194. package/node_modules/@comis/memory/dist/delivery-mirror-adapter.test.d.ts +0 -1
  195. package/node_modules/@comis/memory/dist/delivery-mirror-adapter.test.js +0 -165
  196. package/node_modules/@comis/memory/dist/delivery-queue-adapter.test.d.ts +0 -1
  197. package/node_modules/@comis/memory/dist/delivery-queue-adapter.test.js +0 -263
  198. package/node_modules/@comis/memory/dist/embedding-batch-indexer.test.d.ts +0 -1
  199. package/node_modules/@comis/memory/dist/embedding-batch-indexer.test.js +0 -202
  200. package/node_modules/@comis/memory/dist/embedding-cache-lru.test.d.ts +0 -1
  201. package/node_modules/@comis/memory/dist/embedding-cache-lru.test.js +0 -241
  202. package/node_modules/@comis/memory/dist/embedding-cache-sqlite.test.d.ts +0 -8
  203. package/node_modules/@comis/memory/dist/embedding-cache-sqlite.test.js +0 -678
  204. package/node_modules/@comis/memory/dist/embedding-cache.test.d.ts +0 -1
  205. package/node_modules/@comis/memory/dist/embedding-cache.test.js +0 -213
  206. package/node_modules/@comis/memory/dist/embedding-fingerprint.test.d.ts +0 -1
  207. package/node_modules/@comis/memory/dist/embedding-fingerprint.test.js +0 -87
  208. package/node_modules/@comis/memory/dist/embedding-hash.test.d.ts +0 -1
  209. package/node_modules/@comis/memory/dist/embedding-hash.test.js +0 -31
  210. package/node_modules/@comis/memory/dist/embedding-integration.test.d.ts +0 -8
  211. package/node_modules/@comis/memory/dist/embedding-integration.test.js +0 -232
  212. package/node_modules/@comis/memory/dist/embedding-provider-factory.test.d.ts +0 -1
  213. package/node_modules/@comis/memory/dist/embedding-provider-factory.test.js +0 -176
  214. package/node_modules/@comis/memory/dist/embedding-provider-local.test.d.ts +0 -8
  215. package/node_modules/@comis/memory/dist/embedding-provider-local.test.js +0 -76
  216. package/node_modules/@comis/memory/dist/embedding-provider-openai.test.d.ts +0 -8
  217. package/node_modules/@comis/memory/dist/embedding-provider-openai.test.js +0 -100
  218. package/node_modules/@comis/memory/dist/embedding-queue.test.d.ts +0 -1
  219. package/node_modules/@comis/memory/dist/embedding-queue.test.js +0 -236
  220. package/node_modules/@comis/memory/dist/hybrid-search.test.d.ts +0 -1
  221. package/node_modules/@comis/memory/dist/hybrid-search.test.js +0 -476
  222. package/node_modules/@comis/memory/dist/identity-link-store.test.d.ts +0 -1
  223. package/node_modules/@comis/memory/dist/identity-link-store.test.js +0 -88
  224. package/node_modules/@comis/memory/dist/memory-api.test.d.ts +0 -1
  225. package/node_modules/@comis/memory/dist/memory-api.test.js +0 -495
  226. package/node_modules/@comis/memory/dist/memory-concurrency.test.d.ts +0 -20
  227. package/node_modules/@comis/memory/dist/memory-concurrency.test.js +0 -222
  228. package/node_modules/@comis/memory/dist/named-graph-store.test.d.ts +0 -1
  229. package/node_modules/@comis/memory/dist/named-graph-store.test.js +0 -227
  230. package/node_modules/@comis/memory/dist/observability-store.test.d.ts +0 -1
  231. package/node_modules/@comis/memory/dist/observability-store.test.js +0 -704
  232. package/node_modules/@comis/memory/dist/row-mapper.test.d.ts +0 -1
  233. package/node_modules/@comis/memory/dist/row-mapper.test.js +0 -409
  234. package/node_modules/@comis/memory/dist/schema.test.d.ts +0 -1
  235. package/node_modules/@comis/memory/dist/schema.test.js +0 -240
  236. package/node_modules/@comis/memory/dist/secret-store-schema.test.d.ts +0 -7
  237. package/node_modules/@comis/memory/dist/secret-store-schema.test.js +0 -90
  238. package/node_modules/@comis/memory/dist/session-store.test.d.ts +0 -1
  239. package/node_modules/@comis/memory/dist/session-store.test.js +0 -262
  240. package/node_modules/@comis/memory/dist/setup-secrets.test.d.ts +0 -1
  241. package/node_modules/@comis/memory/dist/setup-secrets.test.js +0 -140
  242. package/node_modules/@comis/memory/dist/sqlite-memory-adapter.test.d.ts +0 -1
  243. package/node_modules/@comis/memory/dist/sqlite-memory-adapter.test.js +0 -888
  244. package/node_modules/@comis/memory/dist/sqlite-secret-store.test.d.ts +0 -8
  245. package/node_modules/@comis/memory/dist/sqlite-secret-store.test.js +0 -245
  246. package/node_modules/@comis/shared/dist/abort.test.d.ts +0 -1
  247. package/node_modules/@comis/shared/dist/abort.test.js +0 -71
  248. package/node_modules/@comis/shared/dist/result.test.d.ts +0 -1
  249. package/node_modules/@comis/shared/dist/result.test.js +0 -178
  250. package/node_modules/@comis/shared/dist/suppress-error.test.d.ts +0 -1
  251. package/node_modules/@comis/shared/dist/suppress-error.test.js +0 -50
  252. package/node_modules/@comis/shared/dist/timeout.test.d.ts +0 -1
  253. package/node_modules/@comis/shared/dist/timeout.test.js +0 -75
  254. package/node_modules/@comis/shared/dist/ttl-cache.test.d.ts +0 -1
  255. package/node_modules/@comis/shared/dist/ttl-cache.test.js +0 -81
@@ -1,708 +0,0 @@
1
- import { describe, it, expect, beforeEach } from "vitest";
2
- import Database from "better-sqlite3";
3
- import { createContextStore } from "./context-store.js";
4
- let db;
5
- let store;
6
- let convCounter = 0;
7
- /** Create a fresh store for each test. */
8
- function freshStore() {
9
- const d = new Database(":memory:");
10
- d.pragma("foreign_keys = ON");
11
- const s = createContextStore(d);
12
- return { db: d, store: s };
13
- }
14
- /** Helper: create a test conversation with unique session key. */
15
- function createTestConversation(s) {
16
- convCounter++;
17
- return s.createConversation({
18
- tenantId: "t1",
19
- agentId: "a1",
20
- sessionKey: `sess-${convCounter}`,
21
- });
22
- }
23
- beforeEach(() => {
24
- convCounter = 0;
25
- const fresh = freshStore();
26
- db = fresh.db;
27
- store = fresh.store;
28
- });
29
- // =========================================================================
30
- // Group 1: Conversations
31
- // =========================================================================
32
- describe("conversations", () => {
33
- it("createConversation returns conversation_id with conv_ prefix", () => {
34
- const id = createTestConversation(store);
35
- expect(id).toMatch(/^conv_[0-9a-f]{16}$/);
36
- });
37
- it("getConversation returns inserted row", () => {
38
- const id = store.createConversation({
39
- tenantId: "t1",
40
- agentId: "a1",
41
- sessionKey: "sess-get",
42
- title: "Test Title",
43
- });
44
- const row = store.getConversation(id);
45
- expect(row).toBeDefined();
46
- expect(row.conversation_id).toBe(id);
47
- expect(row.tenant_id).toBe("t1");
48
- expect(row.agent_id).toBe("a1");
49
- expect(row.session_key).toBe("sess-get");
50
- expect(row.title).toBe("Test Title");
51
- expect(row.created_at).toBeTruthy();
52
- expect(row.updated_at).toBeTruthy();
53
- });
54
- it("getConversationBySession finds by tenant+session", () => {
55
- const id = store.createConversation({
56
- tenantId: "t1",
57
- agentId: "a1",
58
- sessionKey: "sess-find",
59
- });
60
- const row = store.getConversationBySession("t1", "sess-find");
61
- expect(row).toBeDefined();
62
- expect(row.conversation_id).toBe(id);
63
- });
64
- it("getConversationBySession returns undefined for missing", () => {
65
- const row = store.getConversationBySession("t1", "nonexistent");
66
- expect(row).toBeUndefined();
67
- });
68
- it("touchConversation updates updated_at", () => {
69
- const id = createTestConversation(store);
70
- const before = store.getConversation(id).updated_at;
71
- // Touch should update the timestamp
72
- store.touchConversation(id);
73
- const after = store.getConversation(id).updated_at;
74
- // At minimum, they should both be valid timestamps
75
- expect(before).toBeTruthy();
76
- expect(after).toBeTruthy();
77
- // SQLite datetime('now') has 1-second resolution, so we just verify
78
- // the field exists and is a valid datetime string
79
- expect(after).toMatch(/^\d{4}-\d{2}-\d{2}/);
80
- });
81
- });
82
- // =========================================================================
83
- // Group 2: Messages
84
- // =========================================================================
85
- describe("messages", () => {
86
- it("insertMessage returns auto-incremented message_id", () => {
87
- const convId = createTestConversation(store);
88
- const id1 = store.insertMessage({
89
- conversationId: convId,
90
- seq: 1,
91
- role: "user",
92
- content: "msg1",
93
- contentHash: "h1",
94
- tokenCount: 5,
95
- });
96
- const id2 = store.insertMessage({
97
- conversationId: convId,
98
- seq: 2,
99
- role: "assistant",
100
- content: "msg2",
101
- contentHash: "h2",
102
- tokenCount: 10,
103
- });
104
- const id3 = store.insertMessage({
105
- conversationId: convId,
106
- seq: 3,
107
- role: "user",
108
- content: "msg3",
109
- contentHash: "h3",
110
- tokenCount: 7,
111
- });
112
- expect(id1).toBe(1);
113
- expect(id2).toBe(2);
114
- expect(id3).toBe(3);
115
- });
116
- it("insertMessage creates FTS entry", () => {
117
- const convId = createTestConversation(store);
118
- const msgId = store.insertMessage({
119
- conversationId: convId,
120
- seq: 1,
121
- role: "user",
122
- content: "hello world test phrase",
123
- contentHash: "hfts",
124
- tokenCount: 4,
125
- });
126
- // Query FTS5 directly
127
- const results = db
128
- .prepare("SELECT rowid, content FROM ctx_messages_fts WHERE ctx_messages_fts MATCH 'hello'")
129
- .all();
130
- expect(results).toHaveLength(1);
131
- expect(results[0].rowid).toBe(msgId);
132
- expect(results[0].content).toBe("hello world test phrase");
133
- });
134
- it("getMessagesByConversation returns messages in seq order", () => {
135
- const convId = createTestConversation(store);
136
- store.insertMessage({ conversationId: convId, seq: 3, role: "user", content: "c", contentHash: "h3", tokenCount: 1 });
137
- store.insertMessage({ conversationId: convId, seq: 1, role: "user", content: "a", contentHash: "h1", tokenCount: 1 });
138
- store.insertMessage({ conversationId: convId, seq: 2, role: "assistant", content: "b", contentHash: "h2", tokenCount: 1 });
139
- const msgs = store.getMessagesByConversation(convId);
140
- expect(msgs).toHaveLength(3);
141
- expect(msgs[0].seq).toBe(1);
142
- expect(msgs[1].seq).toBe(2);
143
- expect(msgs[2].seq).toBe(3);
144
- });
145
- it("getMessagesByConversation with afterSeq filters correctly", () => {
146
- const convId = createTestConversation(store);
147
- for (let i = 1; i <= 5; i++) {
148
- store.insertMessage({ conversationId: convId, seq: i, role: "user", content: `m${i}`, contentHash: `h${i}`, tokenCount: 1 });
149
- }
150
- const msgs = store.getMessagesByConversation(convId, { afterSeq: 3 });
151
- expect(msgs).toHaveLength(2);
152
- expect(msgs[0].seq).toBe(4);
153
- expect(msgs[1].seq).toBe(5);
154
- });
155
- it("getMessagesByConversation with limit caps results", () => {
156
- const convId = createTestConversation(store);
157
- for (let i = 1; i <= 10; i++) {
158
- store.insertMessage({ conversationId: convId, seq: i, role: "user", content: `m${i}`, contentHash: `h${i}`, tokenCount: 1 });
159
- }
160
- const msgs = store.getMessagesByConversation(convId, { limit: 3 });
161
- expect(msgs).toHaveLength(3);
162
- });
163
- it("getMessagesByIds batch fetches correctly", () => {
164
- const convId = createTestConversation(store);
165
- const ids = [];
166
- for (let i = 1; i <= 5; i++) {
167
- ids.push(store.insertMessage({ conversationId: convId, seq: i, role: "user", content: `m${i}`, contentHash: `h${i}`, tokenCount: 1 }));
168
- }
169
- const results = store.getMessagesByIds([ids[0], ids[2], ids[4]]);
170
- expect(results).toHaveLength(3);
171
- expect(results[0].seq).toBe(1);
172
- expect(results[1].seq).toBe(3);
173
- expect(results[2].seq).toBe(5);
174
- });
175
- it("getMessagesByIds returns empty for empty input", () => {
176
- const results = store.getMessagesByIds([]);
177
- expect(results).toEqual([]);
178
- });
179
- it("getMessagesByIds handles large batches", () => {
180
- const convId = createTestConversation(store);
181
- // Insert 550 messages to exercise chunking (>500)
182
- for (let i = 1; i <= 550; i++) {
183
- store.insertMessage({
184
- conversationId: convId,
185
- seq: i,
186
- role: "user",
187
- content: `msg-${i}`,
188
- contentHash: `hash-${i}`,
189
- tokenCount: 1,
190
- });
191
- }
192
- // Fetch all 550 by IDs
193
- const allIds = Array.from({ length: 550 }, (_, i) => i + 1);
194
- const results = store.getMessagesByIds(allIds);
195
- expect(results).toHaveLength(550);
196
- });
197
- it("getMessageByHash finds by content_hash", () => {
198
- const convId = createTestConversation(store);
199
- store.insertMessage({
200
- conversationId: convId,
201
- seq: 1,
202
- role: "user",
203
- content: "unique content",
204
- contentHash: "unique-hash-123",
205
- tokenCount: 2,
206
- });
207
- const row = store.getMessageByHash(convId, "unique-hash-123");
208
- expect(row).toBeDefined();
209
- expect(row.content).toBe("unique content");
210
- });
211
- it("getLastMessageSeq returns max seq", () => {
212
- const convId = createTestConversation(store);
213
- store.insertMessage({ conversationId: convId, seq: 1, role: "user", content: "a", contentHash: "h1", tokenCount: 1 });
214
- store.insertMessage({ conversationId: convId, seq: 2, role: "assistant", content: "b", contentHash: "h2", tokenCount: 1 });
215
- store.insertMessage({ conversationId: convId, seq: 3, role: "user", content: "c", contentHash: "h3", tokenCount: 1 });
216
- expect(store.getLastMessageSeq(convId)).toBe(3);
217
- });
218
- it("getLastMessageSeq returns 0 for empty conversation", () => {
219
- const convId = createTestConversation(store);
220
- expect(store.getLastMessageSeq(convId)).toBe(0);
221
- });
222
- });
223
- // =========================================================================
224
- // Group 3: Message Parts
225
- // =========================================================================
226
- describe("message parts", () => {
227
- it("insertParts and getPartsByMessage round-trips correctly", () => {
228
- const convId = createTestConversation(store);
229
- const msgId = store.insertMessage({
230
- conversationId: convId,
231
- seq: 1,
232
- role: "user",
233
- content: "multipart",
234
- contentHash: "hmp",
235
- tokenCount: 1,
236
- });
237
- store.insertParts(msgId, [
238
- { ordinal: 0, partType: "text", content: "Hello" },
239
- { ordinal: 1, partType: "image", metadata: '{"url":"http://..."}' },
240
- { ordinal: 2, partType: "text", content: "World" },
241
- ]);
242
- const parts = store.getPartsByMessage(msgId);
243
- expect(parts).toHaveLength(3);
244
- expect(parts[0].ordinal).toBe(0);
245
- expect(parts[0].part_type).toBe("text");
246
- expect(parts[0].content).toBe("Hello");
247
- expect(parts[1].ordinal).toBe(1);
248
- expect(parts[1].part_type).toBe("image");
249
- expect(parts[1].metadata).toBe('{"url":"http://..."}');
250
- expect(parts[2].ordinal).toBe(2);
251
- expect(parts[2].content).toBe("World");
252
- });
253
- it("getPartsByMessages batches across multiple messages", () => {
254
- const convId = createTestConversation(store);
255
- const msgIds = [];
256
- for (let i = 1; i <= 3; i++) {
257
- const msgId = store.insertMessage({
258
- conversationId: convId,
259
- seq: i,
260
- role: "user",
261
- content: `msg-${i}`,
262
- contentHash: `h${i}`,
263
- tokenCount: 1,
264
- });
265
- msgIds.push(msgId);
266
- store.insertParts(msgId, [
267
- { ordinal: 0, partType: "text", content: `part-${i}-a` },
268
- { ordinal: 1, partType: "text", content: `part-${i}-b` },
269
- ]);
270
- }
271
- const partsMap = store.getPartsByMessages(msgIds);
272
- expect(partsMap.size).toBe(3);
273
- for (const msgId of msgIds) {
274
- const parts = partsMap.get(msgId);
275
- expect(parts).toHaveLength(2);
276
- }
277
- });
278
- });
279
- // =========================================================================
280
- // Group 4: Summaries
281
- // =========================================================================
282
- describe("summaries", () => {
283
- it("insertSummary stores and FTS-indexes the summary", () => {
284
- const convId = createTestConversation(store);
285
- const sumId = store.insertSummary({
286
- summaryId: "sum-001",
287
- conversationId: convId,
288
- kind: "leaf",
289
- depth: 0,
290
- content: "Important discussion about architecture",
291
- tokenCount: 20,
292
- });
293
- expect(sumId).toBe("sum-001");
294
- // Verify stored
295
- const row = store.getSummary("sum-001");
296
- expect(row).toBeDefined();
297
- expect(row.content).toBe("Important discussion about architecture");
298
- expect(row.kind).toBe("leaf");
299
- expect(row.depth).toBe(0);
300
- // Verify FTS indexed
301
- const fts = db
302
- .prepare("SELECT summary_id, content FROM ctx_summaries_fts WHERE content MATCH 'architecture'")
303
- .all();
304
- expect(fts).toHaveLength(1);
305
- expect(fts[0].summary_id).toBe("sum-001");
306
- });
307
- it("getSummariesByConversation returns all summaries", () => {
308
- const convId = createTestConversation(store);
309
- store.insertSummary({ summaryId: "s1", conversationId: convId, kind: "leaf", depth: 0, content: "sum1", tokenCount: 5 });
310
- store.insertSummary({ summaryId: "s2", conversationId: convId, kind: "leaf", depth: 0, content: "sum2", tokenCount: 5 });
311
- store.insertSummary({ summaryId: "s3", conversationId: convId, kind: "condensed", depth: 1, content: "sum3", tokenCount: 5 });
312
- const sums = store.getSummariesByConversation(convId);
313
- expect(sums).toHaveLength(3);
314
- });
315
- it("getSummariesByConversation filters by depth", () => {
316
- const convId = createTestConversation(store);
317
- store.insertSummary({ summaryId: "sd0a", conversationId: convId, kind: "leaf", depth: 0, content: "d0a", tokenCount: 5 });
318
- store.insertSummary({ summaryId: "sd0b", conversationId: convId, kind: "leaf", depth: 0, content: "d0b", tokenCount: 5 });
319
- store.insertSummary({ summaryId: "sd1a", conversationId: convId, kind: "condensed", depth: 1, content: "d1a", tokenCount: 5 });
320
- const depth0 = store.getSummariesByConversation(convId, { depth: 0 });
321
- expect(depth0).toHaveLength(2);
322
- expect(depth0.every((s) => s.depth === 0)).toBe(true);
323
- });
324
- it("updateSummaryCountsDirty sets dirty flag", () => {
325
- const convId = createTestConversation(store);
326
- store.insertSummary({ summaryId: "sd", conversationId: convId, kind: "leaf", depth: 0, content: "dirty test", tokenCount: 5 });
327
- // Initially counts_dirty = 0
328
- expect(store.getSummary("sd").counts_dirty).toBe(0);
329
- // Set dirty
330
- store.updateSummaryCountsDirty(["sd"], true);
331
- expect(store.getSummary("sd").counts_dirty).toBe(1);
332
- // Clear dirty
333
- store.updateSummaryCountsDirty(["sd"], false);
334
- expect(store.getSummary("sd").counts_dirty).toBe(0);
335
- });
336
- it("deleteSummary removes from table and FTS", () => {
337
- const convId = createTestConversation(store);
338
- store.insertSummary({ summaryId: "sdel", conversationId: convId, kind: "leaf", depth: 0, content: "delete me please", tokenCount: 5 });
339
- // Verify it exists
340
- expect(store.getSummary("sdel")).toBeDefined();
341
- // Delete
342
- store.deleteSummary("sdel");
343
- // Verify gone from table
344
- expect(store.getSummary("sdel")).toBeUndefined();
345
- // Verify gone from FTS
346
- const fts = db
347
- .prepare("SELECT summary_id FROM ctx_summaries_fts WHERE content MATCH 'delete'")
348
- .all();
349
- expect(fts).toHaveLength(0);
350
- });
351
- });
352
- // =========================================================================
353
- // Group 5: Summary Links
354
- // =========================================================================
355
- describe("summary links", () => {
356
- it("linkSummaryMessages and getSourceMessageIds round-trip", () => {
357
- const convId = createTestConversation(store);
358
- const msgIds = [];
359
- for (let i = 1; i <= 3; i++) {
360
- msgIds.push(store.insertMessage({ conversationId: convId, seq: i, role: "user", content: `m${i}`, contentHash: `h${i}`, tokenCount: 1 }));
361
- }
362
- store.insertSummary({ summaryId: "slink", conversationId: convId, kind: "leaf", depth: 0, content: "link test", tokenCount: 5 });
363
- store.linkSummaryMessages("slink", msgIds);
364
- const result = store.getSourceMessageIds("slink");
365
- expect(result).toEqual(msgIds);
366
- });
367
- it("linkSummaryParents and getParentSummaryIds round-trip", () => {
368
- const convId = createTestConversation(store);
369
- store.insertSummary({ summaryId: "parent1", conversationId: convId, kind: "leaf", depth: 0, content: "p1", tokenCount: 5 });
370
- store.insertSummary({ summaryId: "parent2", conversationId: convId, kind: "leaf", depth: 0, content: "p2", tokenCount: 5 });
371
- store.insertSummary({ summaryId: "child", conversationId: convId, kind: "condensed", depth: 1, content: "child", tokenCount: 5 });
372
- store.linkSummaryParents("child", ["parent1", "parent2"]);
373
- const parents = store.getParentSummaryIds("child");
374
- expect(parents).toEqual(["parent1", "parent2"]);
375
- });
376
- it("getChildSummaryIds returns children of a parent", () => {
377
- const convId = createTestConversation(store);
378
- store.insertSummary({ summaryId: "p1", conversationId: convId, kind: "leaf", depth: 0, content: "parent", tokenCount: 5 });
379
- store.insertSummary({ summaryId: "c1", conversationId: convId, kind: "condensed", depth: 1, content: "child", tokenCount: 5 });
380
- store.linkSummaryParents("c1", ["p1"]);
381
- const children = store.getChildSummaryIds("p1");
382
- expect(children).toEqual(["c1"]);
383
- });
384
- });
385
- // =========================================================================
386
- // Group 6: Context Items
387
- // =========================================================================
388
- describe("context items", () => {
389
- it("replaceContextItems sets and getContextItems retrieves", () => {
390
- const convId = createTestConversation(store);
391
- const m1 = store.insertMessage({ conversationId: convId, seq: 1, role: "user", content: "a", contentHash: "h1", tokenCount: 1 });
392
- const m2 = store.insertMessage({ conversationId: convId, seq: 2, role: "assistant", content: "b", contentHash: "h2", tokenCount: 1 });
393
- store.insertSummary({ summaryId: "si", conversationId: convId, kind: "leaf", depth: 0, content: "sum", tokenCount: 5 });
394
- store.replaceContextItems(convId, [
395
- { ordinal: 0, itemType: "message", messageId: m1 },
396
- { ordinal: 1, itemType: "summary", summaryId: "si" },
397
- { ordinal: 2, itemType: "message", messageId: m2 },
398
- ]);
399
- const items = store.getContextItems(convId);
400
- expect(items).toHaveLength(3);
401
- expect(items[0].ordinal).toBe(0);
402
- expect(items[0].item_type).toBe("message");
403
- expect(items[0].message_id).toBe(m1);
404
- expect(items[1].ordinal).toBe(1);
405
- expect(items[1].item_type).toBe("summary");
406
- expect(items[1].summary_id).toBe("si");
407
- expect(items[2].ordinal).toBe(2);
408
- expect(items[2].item_type).toBe("message");
409
- expect(items[2].message_id).toBe(m2);
410
- });
411
- it("replaceContextItems replaces existing items", () => {
412
- const convId = createTestConversation(store);
413
- const m1 = store.insertMessage({ conversationId: convId, seq: 1, role: "user", content: "a", contentHash: "h1", tokenCount: 1 });
414
- const m2 = store.insertMessage({ conversationId: convId, seq: 2, role: "user", content: "b", contentHash: "h2", tokenCount: 1 });
415
- // First set
416
- store.replaceContextItems(convId, [
417
- { ordinal: 0, itemType: "message", messageId: m1 },
418
- ]);
419
- expect(store.getContextItems(convId)).toHaveLength(1);
420
- // Replace with different items
421
- store.replaceContextItems(convId, [
422
- { ordinal: 0, itemType: "message", messageId: m2 },
423
- ]);
424
- const items = store.getContextItems(convId);
425
- expect(items).toHaveLength(1);
426
- expect(items[0].message_id).toBe(m2);
427
- });
428
- });
429
- // =========================================================================
430
- // Group 7: Large Files
431
- // =========================================================================
432
- describe("large files", () => {
433
- it("insertLargeFile and getLargeFile round-trip", () => {
434
- const convId = createTestConversation(store);
435
- const fileId = store.insertLargeFile({
436
- fileId: "file-001",
437
- conversationId: convId,
438
- fileName: "document.pdf",
439
- mimeType: "application/pdf",
440
- byteSize: 12345,
441
- contentHash: "filehash123",
442
- storagePath: "/data/files/file-001.pdf",
443
- explorationSummary: "A PDF document about testing",
444
- });
445
- expect(fileId).toBe("file-001");
446
- const row = store.getLargeFile("file-001");
447
- expect(row).toBeDefined();
448
- expect(row.file_name).toBe("document.pdf");
449
- expect(row.mime_type).toBe("application/pdf");
450
- expect(row.byte_size).toBe(12345);
451
- expect(row.content_hash).toBe("filehash123");
452
- expect(row.storage_path).toBe("/data/files/file-001.pdf");
453
- expect(row.exploration_summary).toBe("A PDF document about testing");
454
- });
455
- it("getLargeFileByHash finds by conversation+hash", () => {
456
- const convId = createTestConversation(store);
457
- store.insertLargeFile({
458
- fileId: "file-hash-test",
459
- conversationId: convId,
460
- contentHash: "unique-file-hash",
461
- storagePath: "/data/files/test.bin",
462
- });
463
- const row = store.getLargeFileByHash(convId, "unique-file-hash");
464
- expect(row).toBeDefined();
465
- expect(row.file_id).toBe("file-hash-test");
466
- });
467
- });
468
- // =========================================================================
469
- // Group 8: Expansion Grants
470
- // =========================================================================
471
- describe("expansion grants", () => {
472
- it("createGrant and getGrant round-trip", () => {
473
- const convId = createTestConversation(store);
474
- const grantId = store.createGrant({
475
- grantId: "grant-001",
476
- issuerSession: "sess-1",
477
- conversationIds: [convId],
478
- summaryIds: ["sum-a", "sum-b"],
479
- maxDepth: 5,
480
- tokenCap: 8000,
481
- expiresAt: "2099-12-31 23:59:59",
482
- });
483
- expect(grantId).toBe("grant-001");
484
- const row = store.getGrant("grant-001");
485
- expect(row).toBeDefined();
486
- expect(row.issuer_session).toBe("sess-1");
487
- expect(JSON.parse(row.conversation_ids)).toEqual([convId]);
488
- expect(JSON.parse(row.summary_ids)).toEqual(["sum-a", "sum-b"]);
489
- expect(row.max_depth).toBe(5);
490
- expect(row.token_cap).toBe(8000);
491
- expect(row.tokens_consumed).toBe(0);
492
- expect(row.revoked).toBe(0);
493
- });
494
- it("getActiveGrants filters by session and non-revoked non-expired", () => {
495
- const convId = createTestConversation(store);
496
- // Active grant
497
- store.createGrant({
498
- grantId: "g-active",
499
- issuerSession: "sess-x",
500
- conversationIds: [convId],
501
- expiresAt: "2099-12-31 23:59:59",
502
- });
503
- // Revoked grant
504
- store.createGrant({
505
- grantId: "g-revoked",
506
- issuerSession: "sess-x",
507
- conversationIds: [convId],
508
- expiresAt: "2099-12-31 23:59:59",
509
- });
510
- store.revokeGrant("g-revoked");
511
- // Expired grant
512
- store.createGrant({
513
- grantId: "g-expired",
514
- issuerSession: "sess-x",
515
- conversationIds: [convId],
516
- expiresAt: "2000-01-01 00:00:00",
517
- });
518
- const active = store.getActiveGrants("sess-x");
519
- expect(active).toHaveLength(1);
520
- expect(active[0].grant_id).toBe("g-active");
521
- });
522
- it("consumeGrantTokens increments tokens_consumed", () => {
523
- const convId = createTestConversation(store);
524
- store.createGrant({
525
- grantId: "g-consume",
526
- issuerSession: "sess-c",
527
- conversationIds: [convId],
528
- expiresAt: "2099-12-31 23:59:59",
529
- });
530
- store.consumeGrantTokens("g-consume", 100);
531
- expect(store.getGrant("g-consume").tokens_consumed).toBe(100);
532
- store.consumeGrantTokens("g-consume", 200);
533
- expect(store.getGrant("g-consume").tokens_consumed).toBe(300);
534
- });
535
- it("revokeGrant sets revoked flag", () => {
536
- const convId = createTestConversation(store);
537
- store.createGrant({
538
- grantId: "g-revoke",
539
- issuerSession: "sess-r",
540
- conversationIds: [convId],
541
- expiresAt: "2099-12-31 23:59:59",
542
- });
543
- store.revokeGrant("g-revoke");
544
- expect(store.getGrant("g-revoke").revoked).toBe(1);
545
- });
546
- it("cleanupExpiredGrants removes expired and revoked", () => {
547
- const convId = createTestConversation(store);
548
- // Expired grant
549
- store.createGrant({
550
- grantId: "g-exp",
551
- issuerSession: "sess-cl",
552
- conversationIds: [convId],
553
- expiresAt: "2000-01-01 00:00:00",
554
- });
555
- // Revoked grant
556
- store.createGrant({
557
- grantId: "g-rev",
558
- issuerSession: "sess-cl",
559
- conversationIds: [convId],
560
- expiresAt: "2099-12-31 23:59:59",
561
- });
562
- store.revokeGrant("g-rev");
563
- // Active grant (should survive)
564
- store.createGrant({
565
- grantId: "g-keep",
566
- issuerSession: "sess-cl",
567
- conversationIds: [convId],
568
- expiresAt: "2099-12-31 23:59:59",
569
- });
570
- const count = store.cleanupExpiredGrants();
571
- expect(count).toBe(2);
572
- expect(store.getGrant("g-exp")).toBeUndefined();
573
- expect(store.getGrant("g-rev")).toBeUndefined();
574
- expect(store.getGrant("g-keep")).toBeDefined();
575
- });
576
- });
577
- // =========================================================================
578
- // Group 9: FTS5 Search
579
- // =========================================================================
580
- describe("FTS5 search", () => {
581
- it("searchMessages in fts mode finds matching messages", () => {
582
- const convId = createTestConversation(store);
583
- store.insertMessage({ conversationId: convId, seq: 1, role: "user", content: "the quick brown fox", contentHash: "h1", tokenCount: 4 });
584
- store.insertMessage({ conversationId: convId, seq: 2, role: "user", content: "lazy dog jumps", contentHash: "h2", tokenCount: 3 });
585
- store.insertMessage({ conversationId: convId, seq: 3, role: "user", content: "brown bear sleeps", contentHash: "h3", tokenCount: 3 });
586
- store.insertMessage({ conversationId: convId, seq: 4, role: "user", content: "hello world", contentHash: "h4", tokenCount: 2 });
587
- store.insertMessage({ conversationId: convId, seq: 5, role: "user", content: "another brown animal", contentHash: "h5", tokenCount: 3 });
588
- const results = store.searchMessages(convId, "brown", { mode: "fts", limit: 10 });
589
- expect(results.length).toBeGreaterThanOrEqual(2);
590
- // All results should contain "brown" content
591
- for (const r of results) {
592
- expect(r.content.toLowerCase()).toContain("brown");
593
- }
594
- });
595
- it("searchMessages in fts mode respects limit", () => {
596
- const convId = createTestConversation(store);
597
- for (let i = 1; i <= 10; i++) {
598
- store.insertMessage({
599
- conversationId: convId,
600
- seq: i,
601
- role: "user",
602
- content: `matching keyword phrase ${i}`,
603
- contentHash: `hk${i}`,
604
- tokenCount: 3,
605
- });
606
- }
607
- const results = store.searchMessages(convId, "keyword", { mode: "fts", limit: 3 });
608
- expect(results).toHaveLength(3);
609
- });
610
- it("searchMessages in regex mode uses LIKE pre-filter", () => {
611
- const convId = createTestConversation(store);
612
- store.insertMessage({ conversationId: convId, seq: 1, role: "user", content: "error code 404 not found", contentHash: "hr1", tokenCount: 5 });
613
- store.insertMessage({ conversationId: convId, seq: 2, role: "user", content: "error code 500 server", contentHash: "hr2", tokenCount: 4 });
614
- store.insertMessage({ conversationId: convId, seq: 3, role: "user", content: "all good here", contentHash: "hr3", tokenCount: 3 });
615
- const results = store.searchMessages(convId, "error.*\\d+", { mode: "regex", limit: 10 });
616
- expect(results).toHaveLength(2);
617
- });
618
- it("searchSummaries in fts mode finds matching summaries", () => {
619
- const convId = createTestConversation(store);
620
- store.insertSummary({ summaryId: "sf1", conversationId: convId, kind: "leaf", depth: 0, content: "discussion about databases", tokenCount: 5 });
621
- store.insertSummary({ summaryId: "sf2", conversationId: convId, kind: "leaf", depth: 0, content: "review of architecture patterns", tokenCount: 5 });
622
- store.insertSummary({ summaryId: "sf3", conversationId: convId, kind: "leaf", depth: 0, content: "database migration plan", tokenCount: 5 });
623
- const results = store.searchSummaries(convId, "database", { mode: "fts", limit: 10 });
624
- // "databases" stems to "databas" via porter, "database" stems to "databas" -- should match
625
- expect(results.length).toBeGreaterThanOrEqual(1);
626
- });
627
- it("searchMessages returns empty for no matches", () => {
628
- const convId = createTestConversation(store);
629
- store.insertMessage({ conversationId: convId, seq: 1, role: "user", content: "hello world", contentHash: "hn", tokenCount: 2 });
630
- const results = store.searchMessages(convId, "xyznonexistent", { mode: "fts", limit: 10 });
631
- expect(results).toEqual([]);
632
- });
633
- });
634
- // =========================================================================
635
- // Group 10: deleteConversation (bulk)
636
- // =========================================================================
637
- describe("deleteConversation", () => {
638
- it("deleteConversation removes all related data", () => {
639
- const convId = createTestConversation(store);
640
- // Insert messages
641
- const m1 = store.insertMessage({ conversationId: convId, seq: 1, role: "user", content: "msg1", contentHash: "h1", tokenCount: 1 });
642
- const m2 = store.insertMessage({ conversationId: convId, seq: 2, role: "assistant", content: "msg2", contentHash: "h2", tokenCount: 1 });
643
- // Insert parts
644
- store.insertParts(m1, [{ ordinal: 0, partType: "text", content: "part1" }]);
645
- // Insert summaries
646
- store.insertSummary({ summaryId: "sdel1", conversationId: convId, kind: "leaf", depth: 0, content: "sum1", tokenCount: 5 });
647
- store.insertSummary({ summaryId: "sdel2", conversationId: convId, kind: "condensed", depth: 1, content: "sum2", tokenCount: 5 });
648
- // Link summary to messages
649
- store.linkSummaryMessages("sdel1", [m1, m2]);
650
- store.linkSummaryParents("sdel2", ["sdel1"]);
651
- // Context items
652
- store.replaceContextItems(convId, [
653
- { ordinal: 0, itemType: "message", messageId: m1 },
654
- { ordinal: 1, itemType: "summary", summaryId: "sdel1" },
655
- ]);
656
- // Large file
657
- store.insertLargeFile({
658
- fileId: "fdel",
659
- conversationId: convId,
660
- storagePath: "/tmp/fdel",
661
- });
662
- // Grant
663
- store.createGrant({
664
- grantId: "gdel",
665
- issuerSession: "sess-del",
666
- conversationIds: [convId],
667
- expiresAt: "2099-12-31 23:59:59",
668
- });
669
- // Delete everything
670
- store.deleteConversation(convId);
671
- // Verify all tables are empty for that conversation
672
- expect(store.getConversation(convId)).toBeUndefined();
673
- const msgs = db.prepare("SELECT COUNT(*) as c FROM ctx_messages WHERE conversation_id = ?").get(convId);
674
- expect(msgs.c).toBe(0);
675
- const parts = db.prepare("SELECT COUNT(*) as c FROM ctx_message_parts").get();
676
- expect(parts.c).toBe(0);
677
- const sums = db.prepare("SELECT COUNT(*) as c FROM ctx_summaries WHERE conversation_id = ?").get(convId);
678
- expect(sums.c).toBe(0);
679
- const sumMsgs = db.prepare("SELECT COUNT(*) as c FROM ctx_summary_messages").get();
680
- expect(sumMsgs.c).toBe(0);
681
- const sumParents = db.prepare("SELECT COUNT(*) as c FROM ctx_summary_parents").get();
682
- expect(sumParents.c).toBe(0);
683
- const items = db.prepare("SELECT COUNT(*) as c FROM ctx_context_items WHERE conversation_id = ?").get(convId);
684
- expect(items.c).toBe(0);
685
- const files = db.prepare("SELECT COUNT(*) as c FROM ctx_large_files WHERE conversation_id = ?").get(convId);
686
- expect(files.c).toBe(0);
687
- });
688
- it("deleteConversation cleans up FTS entries", () => {
689
- const convId = createTestConversation(store);
690
- // Insert messages with FTS
691
- store.insertMessage({ conversationId: convId, seq: 1, role: "user", content: "fts cleanup verification msg", contentHash: "hfts1", tokenCount: 4 });
692
- store.insertMessage({ conversationId: convId, seq: 2, role: "user", content: "another fts cleanup msg", contentHash: "hfts2", tokenCount: 4 });
693
- // Insert summaries with FTS
694
- store.insertSummary({ summaryId: "sfts1", conversationId: convId, kind: "leaf", depth: 0, content: "fts cleanup verification summary", tokenCount: 5 });
695
- // Verify FTS entries exist before delete
696
- const msgFtsBefore = db.prepare("SELECT COUNT(*) as c FROM ctx_messages_fts WHERE ctx_messages_fts MATCH 'cleanup'").get();
697
- expect(msgFtsBefore.c).toBeGreaterThan(0);
698
- const sumFtsBefore = db.prepare("SELECT COUNT(*) as c FROM ctx_summaries_fts WHERE content MATCH 'cleanup'").get();
699
- expect(sumFtsBefore.c).toBeGreaterThan(0);
700
- // Delete conversation
701
- store.deleteConversation(convId);
702
- // Verify FTS entries are cleaned up
703
- const msgFtsAfter = db.prepare("SELECT COUNT(*) as c FROM ctx_messages_fts WHERE ctx_messages_fts MATCH 'cleanup'").get();
704
- expect(msgFtsAfter.c).toBe(0);
705
- const sumFtsAfter = db.prepare("SELECT COUNT(*) as c FROM ctx_summaries_fts WHERE content MATCH 'cleanup'").get();
706
- expect(sumFtsAfter.c).toBe(0);
707
- });
708
- });