comisai 1.0.11 → 1.0.12

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 (247) hide show
  1. package/node_modules/@comis/agent/dist/executor/pi-executor.d.ts +1 -4
  2. package/node_modules/@comis/agent/package.json +1 -1
  3. package/node_modules/@comis/channels/package.json +1 -1
  4. package/node_modules/@comis/cli/package.json +1 -1
  5. package/node_modules/@comis/core/package.json +1 -1
  6. package/node_modules/@comis/daemon/package.json +1 -1
  7. package/node_modules/@comis/gateway/package.json +1 -1
  8. package/node_modules/@comis/infra/package.json +1 -1
  9. package/node_modules/@comis/memory/package.json +1 -1
  10. package/node_modules/@comis/scheduler/package.json +1 -1
  11. package/node_modules/@comis/shared/package.json +1 -1
  12. package/node_modules/@comis/skills/package.json +1 -1
  13. package/package.json +18 -18
  14. package/node_modules/@comis/core/dist/approval/approval-gate.test.d.ts +0 -1
  15. package/node_modules/@comis/core/dist/approval/approval-gate.test.js +0 -1003
  16. package/node_modules/@comis/core/dist/bootstrap.test.d.ts +0 -1
  17. package/node_modules/@comis/core/dist/bootstrap.test.js +0 -150
  18. package/node_modules/@comis/core/dist/config/backup.test.d.ts +0 -1
  19. package/node_modules/@comis/core/dist/config/backup.test.js +0 -160
  20. package/node_modules/@comis/core/dist/config/env-substitution.test.d.ts +0 -1
  21. package/node_modules/@comis/core/dist/config/env-substitution.test.js +0 -259
  22. package/node_modules/@comis/core/dist/config/field-metadata.test.d.ts +0 -1
  23. package/node_modules/@comis/core/dist/config/field-metadata.test.js +0 -72
  24. package/node_modules/@comis/core/dist/config/git-manager.test.d.ts +0 -1
  25. package/node_modules/@comis/core/dist/config/git-manager.test.js +0 -1042
  26. package/node_modules/@comis/core/dist/config/immutable-keys.test.d.ts +0 -1
  27. package/node_modules/@comis/core/dist/config/immutable-keys.test.js +0 -283
  28. package/node_modules/@comis/core/dist/config/include-resolver.test.d.ts +0 -1
  29. package/node_modules/@comis/core/dist/config/include-resolver.test.js +0 -292
  30. package/node_modules/@comis/core/dist/config/layered.test.d.ts +0 -1
  31. package/node_modules/@comis/core/dist/config/layered.test.js +0 -211
  32. package/node_modules/@comis/core/dist/config/loader.test.d.ts +0 -1
  33. package/node_modules/@comis/core/dist/config/loader.test.js +0 -300
  34. package/node_modules/@comis/core/dist/config/migrate.test.d.ts +0 -1
  35. package/node_modules/@comis/core/dist/config/migrate.test.js +0 -244
  36. package/node_modules/@comis/core/dist/config/partial-validator.test.d.ts +0 -1
  37. package/node_modules/@comis/core/dist/config/partial-validator.test.js +0 -98
  38. package/node_modules/@comis/core/dist/config/schema-agent-model.test.d.ts +0 -1
  39. package/node_modules/@comis/core/dist/config/schema-agent-model.test.js +0 -279
  40. package/node_modules/@comis/core/dist/config/schema-agent-session.test.d.ts +0 -1
  41. package/node_modules/@comis/core/dist/config/schema-agent-session.test.js +0 -242
  42. package/node_modules/@comis/core/dist/config/schema-agent.test.d.ts +0 -1
  43. package/node_modules/@comis/core/dist/config/schema-agent.test.js +0 -645
  44. package/node_modules/@comis/core/dist/config/schema-channel.test.d.ts +0 -1
  45. package/node_modules/@comis/core/dist/config/schema-channel.test.js +0 -341
  46. package/node_modules/@comis/core/dist/config/schema-coalescer.test.d.ts +0 -1
  47. package/node_modules/@comis/core/dist/config/schema-coalescer.test.js +0 -51
  48. package/node_modules/@comis/core/dist/config/schema-context-engine.test.d.ts +0 -1
  49. package/node_modules/@comis/core/dist/config/schema-context-engine.test.js +0 -797
  50. package/node_modules/@comis/core/dist/config/schema-context-guard.test.d.ts +0 -1
  51. package/node_modules/@comis/core/dist/config/schema-context-guard.test.js +0 -111
  52. package/node_modules/@comis/core/dist/config/schema-daemon.test.d.ts +0 -1
  53. package/node_modules/@comis/core/dist/config/schema-daemon.test.js +0 -107
  54. package/node_modules/@comis/core/dist/config/schema-delivery-timing.test.d.ts +0 -1
  55. package/node_modules/@comis/core/dist/config/schema-delivery-timing.test.js +0 -51
  56. package/node_modules/@comis/core/dist/config/schema-documentation.test.d.ts +0 -1
  57. package/node_modules/@comis/core/dist/config/schema-documentation.test.js +0 -63
  58. package/node_modules/@comis/core/dist/config/schema-gateway.test.d.ts +0 -1
  59. package/node_modules/@comis/core/dist/config/schema-gateway.test.js +0 -219
  60. package/node_modules/@comis/core/dist/config/schema-gemini-cache.test.d.ts +0 -1
  61. package/node_modules/@comis/core/dist/config/schema-gemini-cache.test.js +0 -41
  62. package/node_modules/@comis/core/dist/config/schema-integrations.test.d.ts +0 -1
  63. package/node_modules/@comis/core/dist/config/schema-integrations.test.js +0 -92
  64. package/node_modules/@comis/core/dist/config/schema-lifecycle-reactions.test.d.ts +0 -1
  65. package/node_modules/@comis/core/dist/config/schema-lifecycle-reactions.test.js +0 -61
  66. package/node_modules/@comis/core/dist/config/schema-misc.test.d.ts +0 -1
  67. package/node_modules/@comis/core/dist/config/schema-misc.test.js +0 -394
  68. package/node_modules/@comis/core/dist/config/schema-observability.test.d.ts +0 -1
  69. package/node_modules/@comis/core/dist/config/schema-observability.test.js +0 -76
  70. package/node_modules/@comis/core/dist/config/schema-providers.test.d.ts +0 -1
  71. package/node_modules/@comis/core/dist/config/schema-providers.test.js +0 -294
  72. package/node_modules/@comis/core/dist/config/schema-queue.test.d.ts +0 -1
  73. package/node_modules/@comis/core/dist/config/schema-queue.test.js +0 -234
  74. package/node_modules/@comis/core/dist/config/schema-response-prefix.test.d.ts +0 -1
  75. package/node_modules/@comis/core/dist/config/schema-response-prefix.test.js +0 -36
  76. package/node_modules/@comis/core/dist/config/schema-security.test.d.ts +0 -1
  77. package/node_modules/@comis/core/dist/config/schema-security.test.js +0 -54
  78. package/node_modules/@comis/core/dist/config/schema-sender-trust-display.test.d.ts +0 -1
  79. package/node_modules/@comis/core/dist/config/schema-sender-trust-display.test.js +0 -60
  80. package/node_modules/@comis/core/dist/config/schema-serializer.test.d.ts +0 -1
  81. package/node_modules/@comis/core/dist/config/schema-serializer.test.js +0 -73
  82. package/node_modules/@comis/core/dist/config/schema-telegram-file-guard.test.d.ts +0 -1
  83. package/node_modules/@comis/core/dist/config/schema-telegram-file-guard.test.js +0 -39
  84. package/node_modules/@comis/core/dist/context/context.test.d.ts +0 -1
  85. package/node_modules/@comis/core/dist/context/context.test.js +0 -276
  86. package/node_modules/@comis/core/dist/domain/agent-response.test.d.ts +0 -1
  87. package/node_modules/@comis/core/dist/domain/agent-response.test.js +0 -159
  88. package/node_modules/@comis/core/dist/domain/credential-mapping.test.d.ts +0 -1
  89. package/node_modules/@comis/core/dist/domain/credential-mapping.test.js +0 -135
  90. package/node_modules/@comis/core/dist/domain/delivery-origin.test.d.ts +0 -1
  91. package/node_modules/@comis/core/dist/domain/delivery-origin.test.js +0 -68
  92. package/node_modules/@comis/core/dist/domain/execution-graph.test.d.ts +0 -1
  93. package/node_modules/@comis/core/dist/domain/execution-graph.test.js +0 -441
  94. package/node_modules/@comis/core/dist/domain/memory-entry.test.d.ts +0 -1
  95. package/node_modules/@comis/core/dist/domain/memory-entry.test.js +0 -193
  96. package/node_modules/@comis/core/dist/domain/normalized-message.test.d.ts +0 -1
  97. package/node_modules/@comis/core/dist/domain/normalized-message.test.js +0 -255
  98. package/node_modules/@comis/core/dist/domain/session-key.test.d.ts +0 -1
  99. package/node_modules/@comis/core/dist/domain/session-key.test.js +0 -291
  100. package/node_modules/@comis/core/dist/domain/subagent-context-config.test.d.ts +0 -1
  101. package/node_modules/@comis/core/dist/domain/subagent-context-config.test.js +0 -131
  102. package/node_modules/@comis/core/dist/domain/subagent-context-types.test.d.ts +0 -1
  103. package/node_modules/@comis/core/dist/domain/subagent-context-types.test.js +0 -182
  104. package/node_modules/@comis/core/dist/event-bus/bus.test.d.ts +0 -1
  105. package/node_modules/@comis/core/dist/event-bus/bus.test.js +0 -152
  106. package/node_modules/@comis/core/dist/event-bus/events-agent.test.d.ts +0 -1
  107. package/node_modules/@comis/core/dist/event-bus/events-agent.test.js +0 -409
  108. package/node_modules/@comis/core/dist/event-bus/events-channel.test.d.ts +0 -1
  109. package/node_modules/@comis/core/dist/event-bus/events-channel.test.js +0 -240
  110. package/node_modules/@comis/core/dist/event-bus/events-infra.test.d.ts +0 -1
  111. package/node_modules/@comis/core/dist/event-bus/events-infra.test.js +0 -416
  112. package/node_modules/@comis/core/dist/event-bus/events-messaging.test.d.ts +0 -1
  113. package/node_modules/@comis/core/dist/event-bus/events-messaging.test.js +0 -433
  114. package/node_modules/@comis/core/dist/event-bus/events.test.d.ts +0 -1
  115. package/node_modules/@comis/core/dist/event-bus/events.test.js +0 -93
  116. package/node_modules/@comis/core/dist/hooks/hook-runner-global.test.d.ts +0 -1
  117. package/node_modules/@comis/core/dist/hooks/hook-runner-global.test.js +0 -29
  118. package/node_modules/@comis/core/dist/hooks/hook-runner.test.d.ts +0 -1
  119. package/node_modules/@comis/core/dist/hooks/hook-runner.test.js +0 -490
  120. package/node_modules/@comis/core/dist/hooks/hook-strategies.test.d.ts +0 -1
  121. package/node_modules/@comis/core/dist/hooks/hook-strategies.test.js +0 -209
  122. package/node_modules/@comis/core/dist/hooks/integration.test.d.ts +0 -1
  123. package/node_modules/@comis/core/dist/hooks/integration.test.js +0 -235
  124. package/node_modules/@comis/core/dist/hooks/plugin-registry.test.d.ts +0 -1
  125. package/node_modules/@comis/core/dist/hooks/plugin-registry.test.js +0 -470
  126. package/node_modules/@comis/core/dist/load-env.test.d.ts +0 -1
  127. package/node_modules/@comis/core/dist/load-env.test.js +0 -21
  128. package/node_modules/@comis/core/dist/security/action-classifier.test.d.ts +0 -1
  129. package/node_modules/@comis/core/dist/security/action-classifier.test.js +0 -298
  130. package/node_modules/@comis/core/dist/security/audit-aggregator.test.d.ts +0 -1
  131. package/node_modules/@comis/core/dist/security/audit-aggregator.test.js +0 -128
  132. package/node_modules/@comis/core/dist/security/audit.test.d.ts +0 -1
  133. package/node_modules/@comis/core/dist/security/audit.test.js +0 -154
  134. package/node_modules/@comis/core/dist/security/canary-token.test.d.ts +0 -1
  135. package/node_modules/@comis/core/dist/security/canary-token.test.js +0 -45
  136. package/node_modules/@comis/core/dist/security/config-redaction.test.d.ts +0 -1
  137. package/node_modules/@comis/core/dist/security/config-redaction.test.js +0 -107
  138. package/node_modules/@comis/core/dist/security/external-content.test.d.ts +0 -1
  139. package/node_modules/@comis/core/dist/security/external-content.test.js +0 -210
  140. package/node_modules/@comis/core/dist/security/injection-patterns.test.d.ts +0 -1
  141. package/node_modules/@comis/core/dist/security/injection-patterns.test.js +0 -213
  142. package/node_modules/@comis/core/dist/security/injection-rate-limiter.test.d.ts +0 -1
  143. package/node_modules/@comis/core/dist/security/injection-rate-limiter.test.js +0 -194
  144. package/node_modules/@comis/core/dist/security/input-guard.test.d.ts +0 -1
  145. package/node_modules/@comis/core/dist/security/input-guard.test.js +0 -215
  146. package/node_modules/@comis/core/dist/security/input-security-guard.test.d.ts +0 -1
  147. package/node_modules/@comis/core/dist/security/input-security-guard.test.js +0 -215
  148. package/node_modules/@comis/core/dist/security/input-validator.test.d.ts +0 -1
  149. package/node_modules/@comis/core/dist/security/input-validator.test.js +0 -133
  150. package/node_modules/@comis/core/dist/security/log-sanitizer.test.d.ts +0 -1
  151. package/node_modules/@comis/core/dist/security/log-sanitizer.test.js +0 -260
  152. package/node_modules/@comis/core/dist/security/memory-write-validator.test.d.ts +0 -1
  153. package/node_modules/@comis/core/dist/security/memory-write-validator.test.js +0 -62
  154. package/node_modules/@comis/core/dist/security/output-guard.test.d.ts +0 -1
  155. package/node_modules/@comis/core/dist/security/output-guard.test.js +0 -397
  156. package/node_modules/@comis/core/dist/security/safe-path.test.d.ts +0 -1
  157. package/node_modules/@comis/core/dist/security/safe-path.test.js +0 -95
  158. package/node_modules/@comis/core/dist/security/scoped-secret-manager.test.d.ts +0 -1
  159. package/node_modules/@comis/core/dist/security/scoped-secret-manager.test.js +0 -231
  160. package/node_modules/@comis/core/dist/security/secret-access.test.d.ts +0 -1
  161. package/node_modules/@comis/core/dist/security/secret-access.test.js +0 -41
  162. package/node_modules/@comis/core/dist/security/secret-crypto.test.d.ts +0 -1
  163. package/node_modules/@comis/core/dist/security/secret-crypto.test.js +0 -113
  164. package/node_modules/@comis/core/dist/security/secret-manager.test.d.ts +0 -1
  165. package/node_modules/@comis/core/dist/security/secret-manager.test.js +0 -394
  166. package/node_modules/@comis/core/dist/security/secret-ref-resolver.test.d.ts +0 -1
  167. package/node_modules/@comis/core/dist/security/secret-ref-resolver.test.js +0 -268
  168. package/node_modules/@comis/core/dist/security/secrets-audit.test.d.ts +0 -10
  169. package/node_modules/@comis/core/dist/security/secrets-audit.test.js +0 -295
  170. package/node_modules/@comis/core/dist/security/ssrf-guard.test.d.ts +0 -1
  171. package/node_modules/@comis/core/dist/security/ssrf-guard.test.js +0 -127
  172. package/node_modules/@comis/core/dist/security/token-generator.test.d.ts +0 -1
  173. package/node_modules/@comis/core/dist/security/token-generator.test.js +0 -35
  174. package/node_modules/@comis/core/dist/tool-metadata.test.d.ts +0 -1
  175. package/node_modules/@comis/core/dist/tool-metadata.test.js +0 -112
  176. package/node_modules/@comis/memory/dist/compaction.test.d.ts +0 -1
  177. package/node_modules/@comis/memory/dist/compaction.test.js +0 -298
  178. package/node_modules/@comis/memory/dist/context-schema.test.d.ts +0 -1
  179. package/node_modules/@comis/memory/dist/context-schema.test.js +0 -246
  180. package/node_modules/@comis/memory/dist/context-store.test.d.ts +0 -1
  181. package/node_modules/@comis/memory/dist/context-store.test.js +0 -708
  182. package/node_modules/@comis/memory/dist/credential-mapping-schema.test.d.ts +0 -7
  183. package/node_modules/@comis/memory/dist/credential-mapping-schema.test.js +0 -73
  184. package/node_modules/@comis/memory/dist/credential-mapping-store.test.d.ts +0 -8
  185. package/node_modules/@comis/memory/dist/credential-mapping-store.test.js +0 -340
  186. package/node_modules/@comis/memory/dist/delivery-mirror-adapter.test.d.ts +0 -1
  187. package/node_modules/@comis/memory/dist/delivery-mirror-adapter.test.js +0 -165
  188. package/node_modules/@comis/memory/dist/delivery-queue-adapter.test.d.ts +0 -1
  189. package/node_modules/@comis/memory/dist/delivery-queue-adapter.test.js +0 -263
  190. package/node_modules/@comis/memory/dist/embedding-batch-indexer.test.d.ts +0 -1
  191. package/node_modules/@comis/memory/dist/embedding-batch-indexer.test.js +0 -202
  192. package/node_modules/@comis/memory/dist/embedding-cache-lru.test.d.ts +0 -1
  193. package/node_modules/@comis/memory/dist/embedding-cache-lru.test.js +0 -241
  194. package/node_modules/@comis/memory/dist/embedding-cache-sqlite.test.d.ts +0 -8
  195. package/node_modules/@comis/memory/dist/embedding-cache-sqlite.test.js +0 -678
  196. package/node_modules/@comis/memory/dist/embedding-cache.test.d.ts +0 -1
  197. package/node_modules/@comis/memory/dist/embedding-cache.test.js +0 -213
  198. package/node_modules/@comis/memory/dist/embedding-fingerprint.test.d.ts +0 -1
  199. package/node_modules/@comis/memory/dist/embedding-fingerprint.test.js +0 -87
  200. package/node_modules/@comis/memory/dist/embedding-hash.test.d.ts +0 -1
  201. package/node_modules/@comis/memory/dist/embedding-hash.test.js +0 -31
  202. package/node_modules/@comis/memory/dist/embedding-integration.test.d.ts +0 -8
  203. package/node_modules/@comis/memory/dist/embedding-integration.test.js +0 -232
  204. package/node_modules/@comis/memory/dist/embedding-provider-factory.test.d.ts +0 -1
  205. package/node_modules/@comis/memory/dist/embedding-provider-factory.test.js +0 -176
  206. package/node_modules/@comis/memory/dist/embedding-provider-local.test.d.ts +0 -8
  207. package/node_modules/@comis/memory/dist/embedding-provider-local.test.js +0 -76
  208. package/node_modules/@comis/memory/dist/embedding-provider-openai.test.d.ts +0 -8
  209. package/node_modules/@comis/memory/dist/embedding-provider-openai.test.js +0 -100
  210. package/node_modules/@comis/memory/dist/embedding-queue.test.d.ts +0 -1
  211. package/node_modules/@comis/memory/dist/embedding-queue.test.js +0 -236
  212. package/node_modules/@comis/memory/dist/hybrid-search.test.d.ts +0 -1
  213. package/node_modules/@comis/memory/dist/hybrid-search.test.js +0 -476
  214. package/node_modules/@comis/memory/dist/identity-link-store.test.d.ts +0 -1
  215. package/node_modules/@comis/memory/dist/identity-link-store.test.js +0 -88
  216. package/node_modules/@comis/memory/dist/memory-api.test.d.ts +0 -1
  217. package/node_modules/@comis/memory/dist/memory-api.test.js +0 -495
  218. package/node_modules/@comis/memory/dist/memory-concurrency.test.d.ts +0 -20
  219. package/node_modules/@comis/memory/dist/memory-concurrency.test.js +0 -222
  220. package/node_modules/@comis/memory/dist/named-graph-store.test.d.ts +0 -1
  221. package/node_modules/@comis/memory/dist/named-graph-store.test.js +0 -227
  222. package/node_modules/@comis/memory/dist/observability-store.test.d.ts +0 -1
  223. package/node_modules/@comis/memory/dist/observability-store.test.js +0 -704
  224. package/node_modules/@comis/memory/dist/row-mapper.test.d.ts +0 -1
  225. package/node_modules/@comis/memory/dist/row-mapper.test.js +0 -409
  226. package/node_modules/@comis/memory/dist/schema.test.d.ts +0 -1
  227. package/node_modules/@comis/memory/dist/schema.test.js +0 -240
  228. package/node_modules/@comis/memory/dist/secret-store-schema.test.d.ts +0 -7
  229. package/node_modules/@comis/memory/dist/secret-store-schema.test.js +0 -90
  230. package/node_modules/@comis/memory/dist/session-store.test.d.ts +0 -1
  231. package/node_modules/@comis/memory/dist/session-store.test.js +0 -262
  232. package/node_modules/@comis/memory/dist/setup-secrets.test.d.ts +0 -1
  233. package/node_modules/@comis/memory/dist/setup-secrets.test.js +0 -140
  234. package/node_modules/@comis/memory/dist/sqlite-memory-adapter.test.d.ts +0 -1
  235. package/node_modules/@comis/memory/dist/sqlite-memory-adapter.test.js +0 -888
  236. package/node_modules/@comis/memory/dist/sqlite-secret-store.test.d.ts +0 -8
  237. package/node_modules/@comis/memory/dist/sqlite-secret-store.test.js +0 -245
  238. package/node_modules/@comis/shared/dist/abort.test.d.ts +0 -1
  239. package/node_modules/@comis/shared/dist/abort.test.js +0 -71
  240. package/node_modules/@comis/shared/dist/result.test.d.ts +0 -1
  241. package/node_modules/@comis/shared/dist/result.test.js +0 -178
  242. package/node_modules/@comis/shared/dist/suppress-error.test.d.ts +0 -1
  243. package/node_modules/@comis/shared/dist/suppress-error.test.js +0 -50
  244. package/node_modules/@comis/shared/dist/timeout.test.d.ts +0 -1
  245. package/node_modules/@comis/shared/dist/timeout.test.js +0 -75
  246. package/node_modules/@comis/shared/dist/ttl-cache.test.d.ts +0 -1
  247. 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
- });