comisai 1.0.10 → 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 (251) hide show
  1. package/README.md +3 -3
  2. package/node_modules/@comis/agent/dist/executor/pi-executor.d.ts +1 -4
  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/dist/wizard/steps/06-channels.js +88 -11
  6. package/node_modules/@comis/cli/dist/wizard/steps/10-write-config.js +4 -0
  7. package/node_modules/@comis/cli/dist/wizard/types.d.ts +1 -0
  8. package/node_modules/@comis/cli/package.json +1 -1
  9. package/node_modules/@comis/core/package.json +1 -1
  10. package/node_modules/@comis/daemon/package.json +1 -1
  11. package/node_modules/@comis/gateway/package.json +1 -1
  12. package/node_modules/@comis/infra/package.json +1 -1
  13. package/node_modules/@comis/memory/package.json +1 -1
  14. package/node_modules/@comis/scheduler/package.json +1 -1
  15. package/node_modules/@comis/shared/package.json +1 -1
  16. package/node_modules/@comis/skills/package.json +1 -1
  17. package/package.json +18 -18
  18. package/node_modules/@comis/core/dist/approval/approval-gate.test.d.ts +0 -1
  19. package/node_modules/@comis/core/dist/approval/approval-gate.test.js +0 -1003
  20. package/node_modules/@comis/core/dist/bootstrap.test.d.ts +0 -1
  21. package/node_modules/@comis/core/dist/bootstrap.test.js +0 -150
  22. package/node_modules/@comis/core/dist/config/backup.test.d.ts +0 -1
  23. package/node_modules/@comis/core/dist/config/backup.test.js +0 -160
  24. package/node_modules/@comis/core/dist/config/env-substitution.test.d.ts +0 -1
  25. package/node_modules/@comis/core/dist/config/env-substitution.test.js +0 -259
  26. package/node_modules/@comis/core/dist/config/field-metadata.test.d.ts +0 -1
  27. package/node_modules/@comis/core/dist/config/field-metadata.test.js +0 -72
  28. package/node_modules/@comis/core/dist/config/git-manager.test.d.ts +0 -1
  29. package/node_modules/@comis/core/dist/config/git-manager.test.js +0 -1042
  30. package/node_modules/@comis/core/dist/config/immutable-keys.test.d.ts +0 -1
  31. package/node_modules/@comis/core/dist/config/immutable-keys.test.js +0 -283
  32. package/node_modules/@comis/core/dist/config/include-resolver.test.d.ts +0 -1
  33. package/node_modules/@comis/core/dist/config/include-resolver.test.js +0 -292
  34. package/node_modules/@comis/core/dist/config/layered.test.d.ts +0 -1
  35. package/node_modules/@comis/core/dist/config/layered.test.js +0 -211
  36. package/node_modules/@comis/core/dist/config/loader.test.d.ts +0 -1
  37. package/node_modules/@comis/core/dist/config/loader.test.js +0 -300
  38. package/node_modules/@comis/core/dist/config/migrate.test.d.ts +0 -1
  39. package/node_modules/@comis/core/dist/config/migrate.test.js +0 -244
  40. package/node_modules/@comis/core/dist/config/partial-validator.test.d.ts +0 -1
  41. package/node_modules/@comis/core/dist/config/partial-validator.test.js +0 -98
  42. package/node_modules/@comis/core/dist/config/schema-agent-model.test.d.ts +0 -1
  43. package/node_modules/@comis/core/dist/config/schema-agent-model.test.js +0 -279
  44. package/node_modules/@comis/core/dist/config/schema-agent-session.test.d.ts +0 -1
  45. package/node_modules/@comis/core/dist/config/schema-agent-session.test.js +0 -242
  46. package/node_modules/@comis/core/dist/config/schema-agent.test.d.ts +0 -1
  47. package/node_modules/@comis/core/dist/config/schema-agent.test.js +0 -645
  48. package/node_modules/@comis/core/dist/config/schema-channel.test.d.ts +0 -1
  49. package/node_modules/@comis/core/dist/config/schema-channel.test.js +0 -341
  50. package/node_modules/@comis/core/dist/config/schema-coalescer.test.d.ts +0 -1
  51. package/node_modules/@comis/core/dist/config/schema-coalescer.test.js +0 -51
  52. package/node_modules/@comis/core/dist/config/schema-context-engine.test.d.ts +0 -1
  53. package/node_modules/@comis/core/dist/config/schema-context-engine.test.js +0 -797
  54. package/node_modules/@comis/core/dist/config/schema-context-guard.test.d.ts +0 -1
  55. package/node_modules/@comis/core/dist/config/schema-context-guard.test.js +0 -111
  56. package/node_modules/@comis/core/dist/config/schema-daemon.test.d.ts +0 -1
  57. package/node_modules/@comis/core/dist/config/schema-daemon.test.js +0 -107
  58. package/node_modules/@comis/core/dist/config/schema-delivery-timing.test.d.ts +0 -1
  59. package/node_modules/@comis/core/dist/config/schema-delivery-timing.test.js +0 -51
  60. package/node_modules/@comis/core/dist/config/schema-documentation.test.d.ts +0 -1
  61. package/node_modules/@comis/core/dist/config/schema-documentation.test.js +0 -63
  62. package/node_modules/@comis/core/dist/config/schema-gateway.test.d.ts +0 -1
  63. package/node_modules/@comis/core/dist/config/schema-gateway.test.js +0 -219
  64. package/node_modules/@comis/core/dist/config/schema-gemini-cache.test.d.ts +0 -1
  65. package/node_modules/@comis/core/dist/config/schema-gemini-cache.test.js +0 -41
  66. package/node_modules/@comis/core/dist/config/schema-integrations.test.d.ts +0 -1
  67. package/node_modules/@comis/core/dist/config/schema-integrations.test.js +0 -92
  68. package/node_modules/@comis/core/dist/config/schema-lifecycle-reactions.test.d.ts +0 -1
  69. package/node_modules/@comis/core/dist/config/schema-lifecycle-reactions.test.js +0 -61
  70. package/node_modules/@comis/core/dist/config/schema-misc.test.d.ts +0 -1
  71. package/node_modules/@comis/core/dist/config/schema-misc.test.js +0 -394
  72. package/node_modules/@comis/core/dist/config/schema-observability.test.d.ts +0 -1
  73. package/node_modules/@comis/core/dist/config/schema-observability.test.js +0 -76
  74. package/node_modules/@comis/core/dist/config/schema-providers.test.d.ts +0 -1
  75. package/node_modules/@comis/core/dist/config/schema-providers.test.js +0 -294
  76. package/node_modules/@comis/core/dist/config/schema-queue.test.d.ts +0 -1
  77. package/node_modules/@comis/core/dist/config/schema-queue.test.js +0 -234
  78. package/node_modules/@comis/core/dist/config/schema-response-prefix.test.d.ts +0 -1
  79. package/node_modules/@comis/core/dist/config/schema-response-prefix.test.js +0 -36
  80. package/node_modules/@comis/core/dist/config/schema-security.test.d.ts +0 -1
  81. package/node_modules/@comis/core/dist/config/schema-security.test.js +0 -54
  82. package/node_modules/@comis/core/dist/config/schema-sender-trust-display.test.d.ts +0 -1
  83. package/node_modules/@comis/core/dist/config/schema-sender-trust-display.test.js +0 -60
  84. package/node_modules/@comis/core/dist/config/schema-serializer.test.d.ts +0 -1
  85. package/node_modules/@comis/core/dist/config/schema-serializer.test.js +0 -73
  86. package/node_modules/@comis/core/dist/config/schema-telegram-file-guard.test.d.ts +0 -1
  87. package/node_modules/@comis/core/dist/config/schema-telegram-file-guard.test.js +0 -39
  88. package/node_modules/@comis/core/dist/context/context.test.d.ts +0 -1
  89. package/node_modules/@comis/core/dist/context/context.test.js +0 -276
  90. package/node_modules/@comis/core/dist/domain/agent-response.test.d.ts +0 -1
  91. package/node_modules/@comis/core/dist/domain/agent-response.test.js +0 -159
  92. package/node_modules/@comis/core/dist/domain/credential-mapping.test.d.ts +0 -1
  93. package/node_modules/@comis/core/dist/domain/credential-mapping.test.js +0 -135
  94. package/node_modules/@comis/core/dist/domain/delivery-origin.test.d.ts +0 -1
  95. package/node_modules/@comis/core/dist/domain/delivery-origin.test.js +0 -68
  96. package/node_modules/@comis/core/dist/domain/execution-graph.test.d.ts +0 -1
  97. package/node_modules/@comis/core/dist/domain/execution-graph.test.js +0 -441
  98. package/node_modules/@comis/core/dist/domain/memory-entry.test.d.ts +0 -1
  99. package/node_modules/@comis/core/dist/domain/memory-entry.test.js +0 -193
  100. package/node_modules/@comis/core/dist/domain/normalized-message.test.d.ts +0 -1
  101. package/node_modules/@comis/core/dist/domain/normalized-message.test.js +0 -255
  102. package/node_modules/@comis/core/dist/domain/session-key.test.d.ts +0 -1
  103. package/node_modules/@comis/core/dist/domain/session-key.test.js +0 -291
  104. package/node_modules/@comis/core/dist/domain/subagent-context-config.test.d.ts +0 -1
  105. package/node_modules/@comis/core/dist/domain/subagent-context-config.test.js +0 -131
  106. package/node_modules/@comis/core/dist/domain/subagent-context-types.test.d.ts +0 -1
  107. package/node_modules/@comis/core/dist/domain/subagent-context-types.test.js +0 -182
  108. package/node_modules/@comis/core/dist/event-bus/bus.test.d.ts +0 -1
  109. package/node_modules/@comis/core/dist/event-bus/bus.test.js +0 -152
  110. package/node_modules/@comis/core/dist/event-bus/events-agent.test.d.ts +0 -1
  111. package/node_modules/@comis/core/dist/event-bus/events-agent.test.js +0 -409
  112. package/node_modules/@comis/core/dist/event-bus/events-channel.test.d.ts +0 -1
  113. package/node_modules/@comis/core/dist/event-bus/events-channel.test.js +0 -240
  114. package/node_modules/@comis/core/dist/event-bus/events-infra.test.d.ts +0 -1
  115. package/node_modules/@comis/core/dist/event-bus/events-infra.test.js +0 -416
  116. package/node_modules/@comis/core/dist/event-bus/events-messaging.test.d.ts +0 -1
  117. package/node_modules/@comis/core/dist/event-bus/events-messaging.test.js +0 -433
  118. package/node_modules/@comis/core/dist/event-bus/events.test.d.ts +0 -1
  119. package/node_modules/@comis/core/dist/event-bus/events.test.js +0 -93
  120. package/node_modules/@comis/core/dist/hooks/hook-runner-global.test.d.ts +0 -1
  121. package/node_modules/@comis/core/dist/hooks/hook-runner-global.test.js +0 -29
  122. package/node_modules/@comis/core/dist/hooks/hook-runner.test.d.ts +0 -1
  123. package/node_modules/@comis/core/dist/hooks/hook-runner.test.js +0 -490
  124. package/node_modules/@comis/core/dist/hooks/hook-strategies.test.d.ts +0 -1
  125. package/node_modules/@comis/core/dist/hooks/hook-strategies.test.js +0 -209
  126. package/node_modules/@comis/core/dist/hooks/integration.test.d.ts +0 -1
  127. package/node_modules/@comis/core/dist/hooks/integration.test.js +0 -235
  128. package/node_modules/@comis/core/dist/hooks/plugin-registry.test.d.ts +0 -1
  129. package/node_modules/@comis/core/dist/hooks/plugin-registry.test.js +0 -470
  130. package/node_modules/@comis/core/dist/load-env.test.d.ts +0 -1
  131. package/node_modules/@comis/core/dist/load-env.test.js +0 -21
  132. package/node_modules/@comis/core/dist/security/action-classifier.test.d.ts +0 -1
  133. package/node_modules/@comis/core/dist/security/action-classifier.test.js +0 -298
  134. package/node_modules/@comis/core/dist/security/audit-aggregator.test.d.ts +0 -1
  135. package/node_modules/@comis/core/dist/security/audit-aggregator.test.js +0 -128
  136. package/node_modules/@comis/core/dist/security/audit.test.d.ts +0 -1
  137. package/node_modules/@comis/core/dist/security/audit.test.js +0 -154
  138. package/node_modules/@comis/core/dist/security/canary-token.test.d.ts +0 -1
  139. package/node_modules/@comis/core/dist/security/canary-token.test.js +0 -45
  140. package/node_modules/@comis/core/dist/security/config-redaction.test.d.ts +0 -1
  141. package/node_modules/@comis/core/dist/security/config-redaction.test.js +0 -107
  142. package/node_modules/@comis/core/dist/security/external-content.test.d.ts +0 -1
  143. package/node_modules/@comis/core/dist/security/external-content.test.js +0 -210
  144. package/node_modules/@comis/core/dist/security/injection-patterns.test.d.ts +0 -1
  145. package/node_modules/@comis/core/dist/security/injection-patterns.test.js +0 -213
  146. package/node_modules/@comis/core/dist/security/injection-rate-limiter.test.d.ts +0 -1
  147. package/node_modules/@comis/core/dist/security/injection-rate-limiter.test.js +0 -194
  148. package/node_modules/@comis/core/dist/security/input-guard.test.d.ts +0 -1
  149. package/node_modules/@comis/core/dist/security/input-guard.test.js +0 -215
  150. package/node_modules/@comis/core/dist/security/input-security-guard.test.d.ts +0 -1
  151. package/node_modules/@comis/core/dist/security/input-security-guard.test.js +0 -215
  152. package/node_modules/@comis/core/dist/security/input-validator.test.d.ts +0 -1
  153. package/node_modules/@comis/core/dist/security/input-validator.test.js +0 -133
  154. package/node_modules/@comis/core/dist/security/log-sanitizer.test.d.ts +0 -1
  155. package/node_modules/@comis/core/dist/security/log-sanitizer.test.js +0 -260
  156. package/node_modules/@comis/core/dist/security/memory-write-validator.test.d.ts +0 -1
  157. package/node_modules/@comis/core/dist/security/memory-write-validator.test.js +0 -62
  158. package/node_modules/@comis/core/dist/security/output-guard.test.d.ts +0 -1
  159. package/node_modules/@comis/core/dist/security/output-guard.test.js +0 -397
  160. package/node_modules/@comis/core/dist/security/safe-path.test.d.ts +0 -1
  161. package/node_modules/@comis/core/dist/security/safe-path.test.js +0 -95
  162. package/node_modules/@comis/core/dist/security/scoped-secret-manager.test.d.ts +0 -1
  163. package/node_modules/@comis/core/dist/security/scoped-secret-manager.test.js +0 -231
  164. package/node_modules/@comis/core/dist/security/secret-access.test.d.ts +0 -1
  165. package/node_modules/@comis/core/dist/security/secret-access.test.js +0 -41
  166. package/node_modules/@comis/core/dist/security/secret-crypto.test.d.ts +0 -1
  167. package/node_modules/@comis/core/dist/security/secret-crypto.test.js +0 -113
  168. package/node_modules/@comis/core/dist/security/secret-manager.test.d.ts +0 -1
  169. package/node_modules/@comis/core/dist/security/secret-manager.test.js +0 -394
  170. package/node_modules/@comis/core/dist/security/secret-ref-resolver.test.d.ts +0 -1
  171. package/node_modules/@comis/core/dist/security/secret-ref-resolver.test.js +0 -268
  172. package/node_modules/@comis/core/dist/security/secrets-audit.test.d.ts +0 -10
  173. package/node_modules/@comis/core/dist/security/secrets-audit.test.js +0 -295
  174. package/node_modules/@comis/core/dist/security/ssrf-guard.test.d.ts +0 -1
  175. package/node_modules/@comis/core/dist/security/ssrf-guard.test.js +0 -127
  176. package/node_modules/@comis/core/dist/security/token-generator.test.d.ts +0 -1
  177. package/node_modules/@comis/core/dist/security/token-generator.test.js +0 -35
  178. package/node_modules/@comis/core/dist/tool-metadata.test.d.ts +0 -1
  179. package/node_modules/@comis/core/dist/tool-metadata.test.js +0 -112
  180. package/node_modules/@comis/memory/dist/compaction.test.d.ts +0 -1
  181. package/node_modules/@comis/memory/dist/compaction.test.js +0 -298
  182. package/node_modules/@comis/memory/dist/context-schema.test.d.ts +0 -1
  183. package/node_modules/@comis/memory/dist/context-schema.test.js +0 -246
  184. package/node_modules/@comis/memory/dist/context-store.test.d.ts +0 -1
  185. package/node_modules/@comis/memory/dist/context-store.test.js +0 -708
  186. package/node_modules/@comis/memory/dist/credential-mapping-schema.test.d.ts +0 -7
  187. package/node_modules/@comis/memory/dist/credential-mapping-schema.test.js +0 -73
  188. package/node_modules/@comis/memory/dist/credential-mapping-store.test.d.ts +0 -8
  189. package/node_modules/@comis/memory/dist/credential-mapping-store.test.js +0 -340
  190. package/node_modules/@comis/memory/dist/delivery-mirror-adapter.test.d.ts +0 -1
  191. package/node_modules/@comis/memory/dist/delivery-mirror-adapter.test.js +0 -165
  192. package/node_modules/@comis/memory/dist/delivery-queue-adapter.test.d.ts +0 -1
  193. package/node_modules/@comis/memory/dist/delivery-queue-adapter.test.js +0 -263
  194. package/node_modules/@comis/memory/dist/embedding-batch-indexer.test.d.ts +0 -1
  195. package/node_modules/@comis/memory/dist/embedding-batch-indexer.test.js +0 -202
  196. package/node_modules/@comis/memory/dist/embedding-cache-lru.test.d.ts +0 -1
  197. package/node_modules/@comis/memory/dist/embedding-cache-lru.test.js +0 -241
  198. package/node_modules/@comis/memory/dist/embedding-cache-sqlite.test.d.ts +0 -8
  199. package/node_modules/@comis/memory/dist/embedding-cache-sqlite.test.js +0 -678
  200. package/node_modules/@comis/memory/dist/embedding-cache.test.d.ts +0 -1
  201. package/node_modules/@comis/memory/dist/embedding-cache.test.js +0 -213
  202. package/node_modules/@comis/memory/dist/embedding-fingerprint.test.d.ts +0 -1
  203. package/node_modules/@comis/memory/dist/embedding-fingerprint.test.js +0 -87
  204. package/node_modules/@comis/memory/dist/embedding-hash.test.d.ts +0 -1
  205. package/node_modules/@comis/memory/dist/embedding-hash.test.js +0 -31
  206. package/node_modules/@comis/memory/dist/embedding-integration.test.d.ts +0 -8
  207. package/node_modules/@comis/memory/dist/embedding-integration.test.js +0 -232
  208. package/node_modules/@comis/memory/dist/embedding-provider-factory.test.d.ts +0 -1
  209. package/node_modules/@comis/memory/dist/embedding-provider-factory.test.js +0 -176
  210. package/node_modules/@comis/memory/dist/embedding-provider-local.test.d.ts +0 -8
  211. package/node_modules/@comis/memory/dist/embedding-provider-local.test.js +0 -76
  212. package/node_modules/@comis/memory/dist/embedding-provider-openai.test.d.ts +0 -8
  213. package/node_modules/@comis/memory/dist/embedding-provider-openai.test.js +0 -100
  214. package/node_modules/@comis/memory/dist/embedding-queue.test.d.ts +0 -1
  215. package/node_modules/@comis/memory/dist/embedding-queue.test.js +0 -236
  216. package/node_modules/@comis/memory/dist/hybrid-search.test.d.ts +0 -1
  217. package/node_modules/@comis/memory/dist/hybrid-search.test.js +0 -476
  218. package/node_modules/@comis/memory/dist/identity-link-store.test.d.ts +0 -1
  219. package/node_modules/@comis/memory/dist/identity-link-store.test.js +0 -88
  220. package/node_modules/@comis/memory/dist/memory-api.test.d.ts +0 -1
  221. package/node_modules/@comis/memory/dist/memory-api.test.js +0 -495
  222. package/node_modules/@comis/memory/dist/memory-concurrency.test.d.ts +0 -20
  223. package/node_modules/@comis/memory/dist/memory-concurrency.test.js +0 -222
  224. package/node_modules/@comis/memory/dist/named-graph-store.test.d.ts +0 -1
  225. package/node_modules/@comis/memory/dist/named-graph-store.test.js +0 -227
  226. package/node_modules/@comis/memory/dist/observability-store.test.d.ts +0 -1
  227. package/node_modules/@comis/memory/dist/observability-store.test.js +0 -704
  228. package/node_modules/@comis/memory/dist/row-mapper.test.d.ts +0 -1
  229. package/node_modules/@comis/memory/dist/row-mapper.test.js +0 -409
  230. package/node_modules/@comis/memory/dist/schema.test.d.ts +0 -1
  231. package/node_modules/@comis/memory/dist/schema.test.js +0 -240
  232. package/node_modules/@comis/memory/dist/secret-store-schema.test.d.ts +0 -7
  233. package/node_modules/@comis/memory/dist/secret-store-schema.test.js +0 -90
  234. package/node_modules/@comis/memory/dist/session-store.test.d.ts +0 -1
  235. package/node_modules/@comis/memory/dist/session-store.test.js +0 -262
  236. package/node_modules/@comis/memory/dist/setup-secrets.test.d.ts +0 -1
  237. package/node_modules/@comis/memory/dist/setup-secrets.test.js +0 -140
  238. package/node_modules/@comis/memory/dist/sqlite-memory-adapter.test.d.ts +0 -1
  239. package/node_modules/@comis/memory/dist/sqlite-memory-adapter.test.js +0 -888
  240. package/node_modules/@comis/memory/dist/sqlite-secret-store.test.d.ts +0 -8
  241. package/node_modules/@comis/memory/dist/sqlite-secret-store.test.js +0 -245
  242. package/node_modules/@comis/shared/dist/abort.test.d.ts +0 -1
  243. package/node_modules/@comis/shared/dist/abort.test.js +0 -71
  244. package/node_modules/@comis/shared/dist/result.test.d.ts +0 -1
  245. package/node_modules/@comis/shared/dist/result.test.js +0 -178
  246. package/node_modules/@comis/shared/dist/suppress-error.test.d.ts +0 -1
  247. package/node_modules/@comis/shared/dist/suppress-error.test.js +0 -50
  248. package/node_modules/@comis/shared/dist/timeout.test.d.ts +0 -1
  249. package/node_modules/@comis/shared/dist/timeout.test.js +0 -75
  250. package/node_modules/@comis/shared/dist/ttl-cache.test.d.ts +0 -1
  251. package/node_modules/@comis/shared/dist/ttl-cache.test.js +0 -81
@@ -1,298 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
- import { createCompactionService } from "./compaction.js";
3
- import { createSessionStore } from "./session-store.js";
4
- import { SqliteMemoryAdapter } from "./sqlite-memory-adapter.js";
5
- // ── Helpers ────────────────────────────────────────────────────────────
6
- const testConfig = {
7
- dbPath: ":memory:",
8
- walMode: false,
9
- embeddingModel: "test-model",
10
- embeddingDimensions: 4,
11
- compaction: { enabled: false, threshold: 1000, targetSize: 500 },
12
- retention: { maxAgeDays: 0, maxEntries: 0 },
13
- };
14
- const testKey = {
15
- tenantId: "default",
16
- userId: "user-1",
17
- channelId: "telegram",
18
- };
19
- const testKey2 = {
20
- tenantId: "default",
21
- userId: "user-2",
22
- channelId: "discord",
23
- };
24
- /** Simple summarizer that just concatenates messages and returns fixed facts. */
25
- const mockSummarizer = async (messages) => ({
26
- summary: `Summary of ${messages.length} messages`,
27
- facts: ["fact-1", "fact-2"],
28
- });
29
- /** Summarizer that returns no facts. */
30
- const noFactsSummarizer = async (messages) => ({
31
- summary: `Summary of ${messages.length} messages`,
32
- facts: [],
33
- });
34
- /** Summarizer that tracks how many times it was called. */
35
- function trackingSummarizer() {
36
- const fn = async (messages) => {
37
- fn.callCount++;
38
- return {
39
- summary: `Summary of ${messages.length} messages`,
40
- facts: ["tracked-fact"],
41
- };
42
- };
43
- fn.callCount = 0;
44
- return fn;
45
- }
46
- // ── Test Suite ─────────────────────────────────────────────────────────
47
- describe("createCompactionService", () => {
48
- let db;
49
- let adapter;
50
- let sessionStore;
51
- let compactionService;
52
- beforeEach(() => {
53
- // Use the adapter's internal DB for everything (single :memory: connection)
54
- adapter = new SqliteMemoryAdapter(testConfig);
55
- db = adapter.getDb();
56
- sessionStore = createSessionStore(db);
57
- compactionService = createCompactionService(db, sessionStore, adapter, mockSummarizer);
58
- });
59
- afterEach(() => {
60
- adapter.close();
61
- });
62
- // ── Stale session detection ──────────────────────────────────────
63
- it("compacts sessions idle for longer than minIdleMs", async () => {
64
- // Save a session and manually set its updatedAt to the past
65
- sessionStore.save(testKey, [{ role: "user", content: "hello" }]);
66
- // Set updatedAt to 5 hours ago
67
- const fiveHoursAgo = Date.now() - 5 * 60 * 60 * 1000;
68
- db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(fiveHoursAgo, "default:user-1:telegram");
69
- const result = await compactionService.compact({ minIdleMs: 4 * 60 * 60 * 1000 });
70
- expect(result.sessionsCompacted).toBe(1);
71
- expect(result.compactedKeys).toContain("default:user-1:telegram");
72
- });
73
- it("does NOT compact active sessions (within idle threshold)", async () => {
74
- // Save a session that was recently updated (within minIdleMs)
75
- sessionStore.save(testKey, [{ role: "user", content: "hello" }]);
76
- const result = await compactionService.compact({ minIdleMs: 4 * 60 * 60 * 1000 });
77
- expect(result.sessionsCompacted).toBe(0);
78
- expect(result.compactedKeys).toHaveLength(0);
79
- });
80
- it("skips sessions with no messages", async () => {
81
- sessionStore.save(testKey, []);
82
- const longAgo = Date.now() - 10 * 60 * 60 * 1000;
83
- db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
84
- const result = await compactionService.compact({ minIdleMs: 1000 });
85
- expect(result.sessionsCompacted).toBe(0);
86
- });
87
- // ── Episodic memory creation ─────────────────────────────────────
88
- it("creates episodic memory from stale session summary", async () => {
89
- sessionStore.save(testKey, [
90
- { role: "user", content: "hello" },
91
- { role: "assistant", content: "hi" },
92
- ]);
93
- const longAgo = Date.now() - 10 * 60 * 60 * 1000;
94
- db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
95
- const result = await compactionService.compact({ minIdleMs: 1000 });
96
- expect(result.episodicCreated).toBe(1);
97
- // Verify the episodic memory is in the database
98
- const episodic = db
99
- .prepare("SELECT * FROM memories WHERE memory_type = 'episodic'")
100
- .all();
101
- expect(episodic).toHaveLength(1);
102
- expect(episodic[0].content).toBe("Summary of 2 messages");
103
- expect(episodic[0].source_who).toBe("compaction");
104
- });
105
- // ── Semantic fact extraction ─────────────────────────────────────
106
- it("creates semantic memories from extracted facts", async () => {
107
- sessionStore.save(testKey, [{ role: "user", content: "hello" }]);
108
- const longAgo = Date.now() - 10 * 60 * 60 * 1000;
109
- db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
110
- const result = await compactionService.compact({ minIdleMs: 1000 });
111
- expect(result.factsExtracted).toBe(2);
112
- const facts = db
113
- .prepare("SELECT content FROM memories WHERE memory_type = 'semantic' AND tags LIKE '%fact%'")
114
- .all();
115
- expect(facts).toHaveLength(2);
116
- expect(facts.map((f) => f.content)).toContain("fact-1");
117
- expect(facts.map((f) => f.content)).toContain("fact-2");
118
- });
119
- it("handles summarizer returning no facts", async () => {
120
- const service = createCompactionService(db, sessionStore, adapter, noFactsSummarizer);
121
- sessionStore.save(testKey, [{ role: "user", content: "hello" }]);
122
- const longAgo = Date.now() - 10 * 60 * 60 * 1000;
123
- db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
124
- const result = await service.compact({ minIdleMs: 1000 });
125
- expect(result.episodicCreated).toBe(1);
126
- expect(result.factsExtracted).toBe(0);
127
- });
128
- // ── Archive creation ─────────────────────────────────────────────
129
- it("archives original messages before deletion", async () => {
130
- const messages = [
131
- { role: "user", content: "hello" },
132
- { role: "assistant", content: "hi there" },
133
- ];
134
- sessionStore.save(testKey, messages);
135
- const longAgo = Date.now() - 10 * 60 * 60 * 1000;
136
- db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
137
- await compactionService.compact({ minIdleMs: 1000 });
138
- const archives = db.prepare("SELECT * FROM archives").all();
139
- expect(archives).toHaveLength(1);
140
- expect(archives[0].session_key).toBe("default:user-1:telegram");
141
- expect(JSON.parse(archives[0].messages)).toEqual(messages);
142
- expect(archives[0].expires_at).toBeGreaterThan(archives[0].archived_at);
143
- });
144
- it("archives respect configurable retention period", async () => {
145
- sessionStore.save(testKey, [{ role: "user", content: "hello" }]);
146
- const longAgo = Date.now() - 10 * 60 * 60 * 1000;
147
- db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
148
- const customRetention = 3 * 24 * 60 * 60 * 1000; // 3 days
149
- await compactionService.compact({
150
- minIdleMs: 1000,
151
- archiveRetentionMs: customRetention,
152
- });
153
- const archive = db.prepare("SELECT * FROM archives").get();
154
- const actualRetention = archive.expires_at - archive.archived_at;
155
- expect(actualRetention).toBe(customRetention);
156
- });
157
- // ── Session deletion ─────────────────────────────────────────────
158
- it("deletes the session after compaction", async () => {
159
- sessionStore.save(testKey, [{ role: "user", content: "hello" }]);
160
- const longAgo = Date.now() - 10 * 60 * 60 * 1000;
161
- db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
162
- await compactionService.compact({ minIdleMs: 1000 });
163
- const session = sessionStore.load(testKey);
164
- expect(session).toBeUndefined();
165
- });
166
- // ── Multiple sessions ────────────────────────────────────────────
167
- it("compacts multiple stale sessions in one run", async () => {
168
- sessionStore.save(testKey, [{ role: "user", content: "hello from user 1" }]);
169
- sessionStore.save(testKey2, [{ role: "user", content: "hello from user 2" }]);
170
- const longAgo = Date.now() - 10 * 60 * 60 * 1000;
171
- db.prepare("UPDATE sessions SET updated_at = ?").run(longAgo);
172
- const result = await compactionService.compact({ minIdleMs: 1000 });
173
- expect(result.sessionsCompacted).toBe(2);
174
- expect(result.episodicCreated).toBe(2);
175
- expect(result.factsExtracted).toBe(4); // 2 facts per session
176
- });
177
- it("only compacts stale sessions, leaves active ones alone", async () => {
178
- sessionStore.save(testKey, [{ role: "user", content: "stale" }]);
179
- sessionStore.save(testKey2, [{ role: "user", content: "active" }]);
180
- // Only make testKey stale
181
- const longAgo = Date.now() - 10 * 60 * 60 * 1000;
182
- db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
183
- const result = await compactionService.compact({ minIdleMs: 1000 });
184
- expect(result.sessionsCompacted).toBe(1);
185
- expect(result.compactedKeys).toContain("default:user-1:telegram");
186
- // Active session should still exist
187
- const activeSession = sessionStore.load(testKey2);
188
- expect(activeSession).toBeDefined();
189
- });
190
- // ── Pluggable summarizer ─────────────────────────────────────────
191
- it("calls the pluggable summarizer with session messages", async () => {
192
- const tracker = trackingSummarizer();
193
- const service = createCompactionService(db, sessionStore, adapter, tracker);
194
- const messages = [
195
- { role: "user", content: "hello" },
196
- { role: "assistant", content: "hi" },
197
- { role: "user", content: "bye" },
198
- ];
199
- sessionStore.save(testKey, messages);
200
- const longAgo = Date.now() - 10 * 60 * 60 * 1000;
201
- db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
202
- await service.compact({ minIdleMs: 1000 });
203
- expect(tracker.callCount).toBe(1);
204
- });
205
- // ── Tenant scoping ──────────────────────────────────────────────
206
- it("respects tenantId option for scoped compaction", async () => {
207
- const key1 = { tenantId: "tenant-a", userId: "u1", channelId: "ch1" };
208
- const key2 = { tenantId: "tenant-b", userId: "u2", channelId: "ch2" };
209
- sessionStore.save(key1, [{ role: "user", content: "hello" }]);
210
- sessionStore.save(key2, [{ role: "user", content: "world" }]);
211
- const longAgo = Date.now() - 10 * 60 * 60 * 1000;
212
- db.prepare("UPDATE sessions SET updated_at = ?").run(longAgo);
213
- const result = await compactionService.compact({
214
- minIdleMs: 1000,
215
- tenantId: "tenant-a",
216
- });
217
- expect(result.sessionsCompacted).toBe(1);
218
- expect(result.compactedKeys[0]).toContain("tenant-a");
219
- // tenant-b session should still exist
220
- const tenantBSession = sessionStore.load(key2);
221
- expect(tenantBSession).toBeDefined();
222
- });
223
- // ── purgeArchives ────────────────────────────────────────────────
224
- it("purgeArchives removes expired archives", async () => {
225
- sessionStore.save(testKey, [{ role: "user", content: "hello" }]);
226
- const longAgo = Date.now() - 10 * 60 * 60 * 1000;
227
- db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
228
- // Compact with very short retention so archive expires immediately
229
- await compactionService.compact({
230
- minIdleMs: 1000,
231
- archiveRetentionMs: 1, // 1ms retention
232
- });
233
- // Verify archive exists
234
- const beforePurge = db.prepare("SELECT COUNT(*) as cnt FROM archives").get();
235
- expect(beforePurge.cnt).toBe(1);
236
- // Wait a tick to ensure expiry
237
- await new Promise((r) => setTimeout(r, 10));
238
- const purged = compactionService.purgeArchives();
239
- expect(purged).toBe(1);
240
- const afterPurge = db.prepare("SELECT COUNT(*) as cnt FROM archives").get();
241
- expect(afterPurge.cnt).toBe(0);
242
- });
243
- it("purgeArchives does not remove non-expired archives", async () => {
244
- sessionStore.save(testKey, [{ role: "user", content: "hello" }]);
245
- const longAgo = Date.now() - 10 * 60 * 60 * 1000;
246
- db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
247
- // Compact with long retention
248
- await compactionService.compact({
249
- minIdleMs: 1000,
250
- archiveRetentionMs: 7 * 24 * 60 * 60 * 1000, // 7 days
251
- });
252
- const purged = compactionService.purgeArchives();
253
- expect(purged).toBe(0);
254
- const archiveCount = db.prepare("SELECT COUNT(*) as cnt FROM archives").get();
255
- expect(archiveCount.cnt).toBe(1);
256
- });
257
- it("purgeArchives returns 0 when no archives exist", () => {
258
- const purged = compactionService.purgeArchives();
259
- expect(purged).toBe(0);
260
- });
261
- // ── Empty session edge case ────────────────────────────────────
262
- it("handles compaction with empty sessions (0 messages) without calling summarizer", async () => {
263
- // Create a tracking summarizer that should never be called
264
- const tracker = trackingSummarizer();
265
- const service = createCompactionService(db, sessionStore, adapter, tracker);
266
- // Save a session with 0 messages and make it stale
267
- sessionStore.save(testKey, []);
268
- const longAgo = Date.now() - 10 * 60 * 60 * 1000;
269
- db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
270
- const result = await service.compact({ minIdleMs: 1000 });
271
- // Verify compaction completed successfully (no crash)
272
- expect(result.sessionsCompacted).toBe(0);
273
- expect(result.episodicCreated).toBe(0);
274
- expect(result.factsExtracted).toBe(0);
275
- expect(result.compactedKeys).toHaveLength(0);
276
- // Summarizer should never have been called for an empty session
277
- expect(tracker.callCount).toBe(0);
278
- // No episodic or semantic memories should have been created
279
- const episodic = db
280
- .prepare("SELECT * FROM memories WHERE memory_type = 'episodic'")
281
- .all();
282
- expect(episodic).toHaveLength(0);
283
- const semantic = db
284
- .prepare("SELECT * FROM memories WHERE memory_type = 'semantic' AND tags LIKE '%fact%'")
285
- .all();
286
- expect(semantic).toHaveLength(0);
287
- });
288
- // ── Return value ─────────────────────────────────────────────────
289
- it("returns empty result when no sessions are stale", async () => {
290
- const result = await compactionService.compact({ minIdleMs: 1000 });
291
- expect(result).toEqual({
292
- sessionsCompacted: 0,
293
- episodicCreated: 0,
294
- factsExtracted: 0,
295
- compactedKeys: [],
296
- });
297
- });
298
- });
@@ -1,246 +0,0 @@
1
- import { describe, it, expect, beforeEach } from "vitest";
2
- import Database from "better-sqlite3";
3
- import { initContextSchema } from "./context-schema.js";
4
- /** Open an in-memory database with foreign keys enabled and schema initialized. */
5
- function createTestDb() {
6
- const db = new Database(":memory:");
7
- db.pragma("foreign_keys = ON");
8
- initContextSchema(db);
9
- return db;
10
- }
11
- /** Insert a minimal conversation row and return its id. */
12
- function insertConversation(db, id = "conv-1") {
13
- db.prepare(`INSERT INTO ctx_conversations (conversation_id, tenant_id, agent_id, session_key)
14
- VALUES (?, 'tenant-1', 'agent-1', ?)`).run(id, `session-${id}`);
15
- return id;
16
- }
17
- /** Insert a minimal message row and return its message_id. */
18
- function insertMessage(db, conversationId, seq, role = "user", content = "hello") {
19
- const info = db
20
- .prepare(`INSERT INTO ctx_messages (conversation_id, seq, role, content, content_hash, token_count)
21
- VALUES (?, ?, ?, ?, 'hash-' || ?, 1)`)
22
- .run(conversationId, seq, role, content, seq);
23
- return Number(info.lastInsertRowid);
24
- }
25
- /** Insert a minimal summary row and return its summary_id. */
26
- function insertSummary(db, summaryId, conversationId, kind = "leaf") {
27
- db.prepare(`INSERT INTO ctx_summaries (summary_id, conversation_id, kind, content, token_count)
28
- VALUES (?, ?, ?, 'summary content', 10)`).run(summaryId, conversationId, kind);
29
- return summaryId;
30
- }
31
- describe("initContextSchema", () => {
32
- let db;
33
- beforeEach(() => {
34
- db = createTestDb();
35
- });
36
- // --- Group 1: table creation ---
37
- describe("table creation", () => {
38
- it("creates all 9 regular ctx_ tables", () => {
39
- // FTS5 creates shadow tables (e.g. ctx_messages_fts_content, _data, etc.)
40
- // Filter them out by excluding names containing '_fts_' and the FTS5 virtual tables themselves.
41
- const tables = db
42
- .prepare(`SELECT name FROM sqlite_master
43
- WHERE type='table' AND name LIKE 'ctx_%'
44
- AND name NOT LIKE '%_fts%'
45
- ORDER BY name`)
46
- .all();
47
- const names = tables.map((r) => r.name);
48
- expect(names).toEqual([
49
- "ctx_context_items",
50
- "ctx_conversations",
51
- "ctx_expansion_grants",
52
- "ctx_large_files",
53
- "ctx_message_parts",
54
- "ctx_messages",
55
- "ctx_summaries",
56
- "ctx_summary_messages",
57
- "ctx_summary_parents",
58
- ]);
59
- });
60
- it("creates 2 FTS5 virtual tables", () => {
61
- const fts = db
62
- .prepare(`SELECT name FROM sqlite_master
63
- WHERE sql LIKE '%fts5%' AND name LIKE 'ctx_%'
64
- ORDER BY name`)
65
- .all();
66
- const names = fts.map((r) => r.name);
67
- expect(names).toEqual(["ctx_messages_fts", "ctx_summaries_fts"]);
68
- });
69
- it("creates all expected indexes", () => {
70
- const indexes = db
71
- .prepare(`SELECT name FROM sqlite_master
72
- WHERE type='index' AND name LIKE 'ctx_%'
73
- ORDER BY name`)
74
- .all();
75
- const names = indexes.map((r) => r.name);
76
- expect(names).toContain("ctx_messages_conv_seq");
77
- expect(names).toContain("ctx_messages_hash");
78
- expect(names).toContain("ctx_summaries_conv");
79
- expect(names).toContain("ctx_summaries_depth");
80
- expect(names).toContain("ctx_large_files_conv");
81
- expect(names).toContain("ctx_large_files_hash");
82
- expect(names).toContain("ctx_grants_session");
83
- expect(names).toContain("ctx_grants_expires");
84
- });
85
- });
86
- // --- Group 2: idempotency ---
87
- describe("idempotency", () => {
88
- it("calling initContextSchema twice produces no errors", () => {
89
- // db already had initContextSchema called in createTestDb
90
- expect(() => initContextSchema(db)).not.toThrow();
91
- // Verify tables still exist and are usable
92
- const convId = insertConversation(db);
93
- insertMessage(db, convId, 1);
94
- const count = db
95
- .prepare("SELECT COUNT(*) as c FROM ctx_messages")
96
- .get();
97
- expect(count.c).toBe(1);
98
- });
99
- });
100
- // --- Group 3: constraints ---
101
- describe("constraints", () => {
102
- it("ctx_messages role CHECK constraint rejects invalid roles", () => {
103
- const convId = insertConversation(db);
104
- expect(() => {
105
- db.prepare(`INSERT INTO ctx_messages (conversation_id, seq, role, content, content_hash, token_count)
106
- VALUES (?, 1, 'invalid', 'test', 'hash', 1)`).run(convId);
107
- }).toThrow();
108
- });
109
- it("ctx_messages role CHECK allows valid roles", () => {
110
- const convId = insertConversation(db);
111
- for (const [i, role] of ["system", "user", "assistant", "tool"].entries()) {
112
- expect(() => {
113
- db.prepare(`INSERT INTO ctx_messages (conversation_id, seq, role, content, content_hash, token_count)
114
- VALUES (?, ?, ?, 'content', 'hash-' || ?, 1)`).run(convId, i + 1, role, i);
115
- }).not.toThrow();
116
- }
117
- });
118
- it("ctx_messages UNIQUE(conversation_id, seq) prevents duplicates", () => {
119
- const convId = insertConversation(db);
120
- insertMessage(db, convId, 1);
121
- expect(() => insertMessage(db, convId, 1)).toThrow();
122
- });
123
- it("ctx_summaries kind CHECK constraint rejects invalid kinds", () => {
124
- const convId = insertConversation(db);
125
- expect(() => {
126
- db.prepare(`INSERT INTO ctx_summaries (summary_id, conversation_id, kind, content, token_count)
127
- VALUES ('s1', ?, 'invalid', 'content', 10)`).run(convId);
128
- }).toThrow();
129
- });
130
- it("ctx_context_items CHECK enforces item_type exclusivity", () => {
131
- const convId = insertConversation(db);
132
- const msgId = insertMessage(db, convId, 1);
133
- const sumId = insertSummary(db, "sum-1", convId);
134
- // item_type='message' with summary_id set -- should fail
135
- expect(() => {
136
- db.prepare(`INSERT INTO ctx_context_items (conversation_id, ordinal, item_type, message_id, summary_id)
137
- VALUES (?, 1, 'message', ?, ?)`).run(convId, msgId, sumId);
138
- }).toThrow();
139
- // item_type='summary' with message_id set -- should fail
140
- expect(() => {
141
- db.prepare(`INSERT INTO ctx_context_items (conversation_id, ordinal, item_type, message_id, summary_id)
142
- VALUES (?, 2, 'summary', ?, ?)`).run(convId, msgId, sumId);
143
- }).toThrow();
144
- // Valid: message item with only message_id
145
- expect(() => {
146
- db.prepare(`INSERT INTO ctx_context_items (conversation_id, ordinal, item_type, message_id, summary_id)
147
- VALUES (?, 1, 'message', ?, NULL)`).run(convId, msgId);
148
- }).not.toThrow();
149
- // Valid: summary item with only summary_id
150
- expect(() => {
151
- db.prepare(`INSERT INTO ctx_context_items (conversation_id, ordinal, item_type, message_id, summary_id)
152
- VALUES (?, 2, 'summary', NULL, ?)`).run(convId, sumId);
153
- }).not.toThrow();
154
- });
155
- it("ctx_messages AUTOINCREMENT prevents rowid reuse", () => {
156
- const convId = insertConversation(db);
157
- insertMessage(db, convId, 1); // message_id = 1
158
- insertMessage(db, convId, 2); // message_id = 2
159
- const thirdId = insertMessage(db, convId, 3); // message_id = 3
160
- expect(thirdId).toBe(3);
161
- // Delete the last message
162
- db.prepare("DELETE FROM ctx_messages WHERE message_id = ?").run(thirdId);
163
- // Insert a new message -- must get id=4, not 3 (AUTOINCREMENT)
164
- const fourthId = insertMessage(db, convId, 3, "user", "new content");
165
- expect(fourthId).toBe(4);
166
- });
167
- });
168
- // --- Group 4: foreign keys and cascades ---
169
- describe("foreign keys and cascades", () => {
170
- it("deleting a conversation cascades to messages", () => {
171
- const convId = insertConversation(db);
172
- insertMessage(db, convId, 1);
173
- insertMessage(db, convId, 2);
174
- db.prepare("DELETE FROM ctx_conversations WHERE conversation_id = ?").run(convId);
175
- const count = db
176
- .prepare("SELECT COUNT(*) as c FROM ctx_messages")
177
- .get();
178
- expect(count.c).toBe(0);
179
- });
180
- it("deleting a conversation cascades to summaries and context_items", () => {
181
- const convId = insertConversation(db);
182
- const msgId = insertMessage(db, convId, 1);
183
- insertSummary(db, "sum-1", convId);
184
- db.prepare(`INSERT INTO ctx_context_items (conversation_id, ordinal, item_type, message_id, summary_id)
185
- VALUES (?, 1, 'message', ?, NULL)`).run(convId, msgId);
186
- db.prepare("DELETE FROM ctx_conversations WHERE conversation_id = ?").run(convId);
187
- const sumCount = db
188
- .prepare("SELECT COUNT(*) as c FROM ctx_summaries")
189
- .get();
190
- const itemCount = db
191
- .prepare("SELECT COUNT(*) as c FROM ctx_context_items")
192
- .get();
193
- expect(sumCount.c).toBe(0);
194
- expect(itemCount.c).toBe(0);
195
- });
196
- it("cannot delete a message referenced by ctx_summary_messages (RESTRICT)", () => {
197
- const convId = insertConversation(db);
198
- const msgId = insertMessage(db, convId, 1);
199
- insertSummary(db, "sum-1", convId);
200
- db.prepare(`INSERT INTO ctx_summary_messages (summary_id, message_id, ordinal)
201
- VALUES ('sum-1', ?, 0)`).run(msgId);
202
- expect(() => {
203
- db.prepare("DELETE FROM ctx_messages WHERE message_id = ?").run(msgId);
204
- }).toThrow();
205
- });
206
- it("cannot delete a parent summary referenced by ctx_summary_parents (RESTRICT)", () => {
207
- const convId = insertConversation(db);
208
- const parentId = insertSummary(db, "sum-parent", convId, "leaf");
209
- const childId = insertSummary(db, "sum-child", convId, "condensed");
210
- db.prepare(`INSERT INTO ctx_summary_parents (summary_id, parent_summary_id, ordinal)
211
- VALUES (?, ?, 0)`).run(childId, parentId);
212
- expect(() => {
213
- db.prepare("DELETE FROM ctx_summaries WHERE summary_id = ?").run(parentId);
214
- }).toThrow();
215
- });
216
- });
217
- // --- Group 5: FTS5 standalone tables ---
218
- describe("FTS5 standalone tables", () => {
219
- it("can insert and query ctx_messages_fts", () => {
220
- db.prepare("INSERT INTO ctx_messages_fts(rowid, content) VALUES (1, 'the quick brown fox')").run();
221
- const results = db
222
- .prepare("SELECT rowid, content FROM ctx_messages_fts WHERE ctx_messages_fts MATCH 'brown'")
223
- .all();
224
- expect(results).toHaveLength(1);
225
- expect(results[0].rowid).toBe(1);
226
- expect(results[0].content).toBe("the quick brown fox");
227
- });
228
- it("can insert and query ctx_summaries_fts", () => {
229
- db.prepare("INSERT INTO ctx_summaries_fts(summary_id, content) VALUES ('sum-1', 'summary of important events')").run();
230
- const results = db
231
- .prepare("SELECT summary_id, content FROM ctx_summaries_fts WHERE content MATCH 'important'")
232
- .all();
233
- expect(results).toHaveLength(1);
234
- expect(results[0].summary_id).toBe("sum-1");
235
- expect(results[0].content).toBe("summary of important events");
236
- });
237
- it("FTS5 uses porter tokenizer", () => {
238
- // "running" should stem to "run" via porter tokenizer
239
- db.prepare("INSERT INTO ctx_messages_fts(rowid, content) VALUES (1, 'running quickly')").run();
240
- const results = db
241
- .prepare("SELECT rowid FROM ctx_messages_fts WHERE ctx_messages_fts MATCH 'run'")
242
- .all();
243
- expect(results).toHaveLength(1);
244
- });
245
- });
246
- });