@jungjaehoon/mama-core 2.4.1 → 4.0.0

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 (371) hide show
  1. package/README.md +76 -239
  2. package/db/migrations/030-case-first-memory-substrate.sql +13 -98
  3. package/db/migrations/034-add-connector-event-scope-columns.sql +9 -13
  4. package/db/migrations/039-add-connector-event-operator-ingest-seq.sql +9 -95
  5. package/db/migrations/062-refresh-connector-event-sequences.sql +9 -113
  6. package/db/migrations/063-refresh-legacy-connector-event-updates.sql +9 -32
  7. package/db/migrations/067-add-connector-event-source-entity-id.sql +9 -13
  8. package/db/migrations/069-create-registry-nodes.sql +52 -0
  9. package/db/migrations/070-record-identity.sql +35 -0
  10. package/db/migrations/071-service-operation-origins.sql +187 -0
  11. package/db/migrations/072-observation-versions.sql +29 -0
  12. package/db/migrations/073-registry-corrections.sql +45 -0
  13. package/db/migrations/074-work-graph-ref-kinds.sql +39 -0
  14. package/db/migrations/075-agent-judgments.sql +58 -0
  15. package/db/migrations/076-scoped-checkpoint-bindings.sql +15 -0
  16. package/db/migrations/077-legacy-record-kind.sql +6 -0
  17. package/db/migrations/078-source-ingest-commands.sql +18 -0
  18. package/db/migrations/079-twin-edge-relations.sql +43 -0
  19. package/db/migrations/080-restore-duration-days.sql +7 -0
  20. package/db/migrations/081-mailbox.sql +61 -0
  21. package/db/migrations/082-commitments-row-id.sql +12 -0
  22. package/db/migrations/083-observation-is-the-evidence.sql +49 -0
  23. package/db/migrations/084-scope-kind-is-not-the-cores-to-list.sql +34 -0
  24. package/db/migrations/085-a-migration-has-a-source.sql +37 -0
  25. package/db/migrations/086-the-mailbox-is-generic.sql +110 -0
  26. package/db/migrations/087-mailbox-payload.sql +5 -0
  27. package/db/migrations/088-native-input-journal.sql +14 -0
  28. package/db/migrations/089-native-turn-receipt-lookup.sql +3 -0
  29. package/db/migrations/090-model-run-native-inputs.sql +29 -0
  30. package/db/migrations/091-native-turn-results.sql +11 -0
  31. package/db/migrations/092-restore-decision-fts.sql +34 -0
  32. package/db/migrations/093-native-turn-primary-kind.sql +14 -0
  33. package/db/migrations/094-commitment-run-provenance.sql +9 -0
  34. package/db/migrations/095-model-run-native-input-identity.sql +39 -0
  35. package/db/migrations/096-drop-connector-event-index-fts.sql +9 -0
  36. package/db/migrations/097-native-input-json-validity.sql +37 -0
  37. package/db/migrations/098-workflow-memory-kind.sql +105 -0
  38. package/dist/action-contracts.d.ts +212 -0
  39. package/dist/action-contracts.js +10 -0
  40. package/dist/api/catalog.d.ts +118 -0
  41. package/dist/api/catalog.js +1756 -0
  42. package/dist/api/dispatch.d.ts +76 -0
  43. package/dist/api/dispatch.js +372 -0
  44. package/dist/canonicalize.d.ts +2 -2
  45. package/dist/canonicalize.js +3 -3
  46. package/dist/client/client.d.ts +33 -0
  47. package/dist/client/client.js +123 -0
  48. package/dist/client/ipc.d.ts +81 -0
  49. package/dist/client/ipc.js +314 -0
  50. package/dist/db-adapter/base-adapter.d.ts +6 -66
  51. package/dist/db-adapter/base-adapter.js +6 -6
  52. package/dist/db-adapter/index.d.ts +5 -5
  53. package/dist/db-adapter/index.js +2 -5
  54. package/dist/db-adapter/node-sqlite-adapter.d.ts +85 -15
  55. package/dist/db-adapter/node-sqlite-adapter.js +1968 -570
  56. package/dist/db-manager.d.ts +53 -97
  57. package/dist/db-manager.js +121 -603
  58. package/dist/embedding/embedder.d.ts +132 -0
  59. package/dist/embedding/embedder.js +299 -0
  60. package/dist/identity/principal-repository.d.ts +2 -2
  61. package/dist/index.d.ts +43 -58
  62. package/dist/index.js +97 -145
  63. package/dist/{context-compile/channel-grant.d.ts → knowledge/access.d.ts} +12 -17
  64. package/dist/knowledge/access.js +663 -0
  65. package/dist/knowledge/case-errors.d.ts +31 -0
  66. package/dist/knowledge/case-errors.js +40 -0
  67. package/dist/{cases/search-rollup.d.ts → knowledge/case-search-rollup.d.ts} +5 -4
  68. package/dist/{cases/search-rollup.js → knowledge/case-search-rollup.js} +6 -8
  69. package/dist/{cases/store.d.ts → knowledge/case-store.d.ts} +3 -6
  70. package/dist/{cases/store.js → knowledge/case-store.js} +7 -42
  71. package/dist/{cases/timeline-range.d.ts → knowledge/case-timeline-range.d.ts} +3 -3
  72. package/dist/{cases/timeline-range.js → knowledge/case-timeline-range.js} +47 -25
  73. package/dist/{cases/types.d.ts → knowledge/case-types.d.ts} +8 -2
  74. package/dist/{cases/types.js → knowledge/case-types.js} +1 -1
  75. package/dist/knowledge/commitments.d.ts +188 -0
  76. package/dist/knowledge/commitments.js +370 -0
  77. package/dist/knowledge/decision-edges.d.ts +70 -0
  78. package/dist/knowledge/decision-edges.js +123 -0
  79. package/dist/{agent-graph/types.d.ts → knowledge/graph-query.d.ts} +80 -70
  80. package/dist/knowledge/graph-query.js +1585 -0
  81. package/dist/knowledge/identity.d.ts +16 -0
  82. package/dist/knowledge/identity.js +22 -0
  83. package/dist/knowledge/index.d.ts +47 -0
  84. package/dist/knowledge/index.js +90 -0
  85. package/dist/knowledge/judgments.d.ts +118 -0
  86. package/dist/knowledge/judgments.js +808 -0
  87. package/dist/knowledge/observations.d.ts +160 -0
  88. package/dist/knowledge/observations.js +449 -0
  89. package/dist/knowledge/question-type.d.ts +34 -0
  90. package/dist/knowledge/question-type.js +70 -0
  91. package/dist/{search → knowledge}/ranker-features.js +1 -1
  92. package/dist/{search → knowledge}/search-quality.d.ts +0 -1
  93. package/dist/knowledge/search.d.ts +38 -0
  94. package/dist/knowledge/search.js +91 -0
  95. package/dist/knowledge/source-ingest.d.ts +65 -0
  96. package/dist/knowledge/source-ingest.js +174 -0
  97. package/dist/{edges/types.d.ts → knowledge/twin-edge-types.d.ts} +31 -4
  98. package/dist/{edges/types.js → knowledge/twin-edge-types.js} +13 -2
  99. package/dist/knowledge/work-dates.d.ts +49 -0
  100. package/dist/knowledge/work-dates.js +120 -0
  101. package/dist/mama-api.d.ts +114 -778
  102. package/dist/mama-api.js +313 -2461
  103. package/dist/memory/api.d.ts +264 -24
  104. package/dist/memory/api.js +1633 -843
  105. package/dist/memory/bootstrap-builder.d.ts +2 -1
  106. package/dist/memory/bootstrap-builder.js +5 -5
  107. package/dist/memory/channel-summary-state-store.d.ts +3 -2
  108. package/dist/memory/channel-summary-state-store.js +53 -51
  109. package/dist/memory/channel-summary-store.d.ts +5 -3
  110. package/dist/memory/channel-summary-store.js +3 -8
  111. package/dist/memory/dashboard-read.d.ts +46 -0
  112. package/dist/memory/dashboard-read.js +94 -0
  113. package/dist/memory/event-store.d.ts +3 -3
  114. package/dist/memory/event-store.js +3 -10
  115. package/dist/memory/finding-store.d.ts +3 -2
  116. package/dist/memory/finding-store.js +2 -7
  117. package/dist/memory/graph-read.d.ts +52 -0
  118. package/dist/memory/graph-read.js +130 -0
  119. package/dist/memory/judgment-types.d.ts +394 -0
  120. package/dist/memory/judgment-types.js +16 -0
  121. package/dist/memory/provenance-audit.d.ts +5 -2
  122. package/dist/memory/provenance-audit.js +8 -14
  123. package/dist/memory/provenance-live.d.ts +141 -0
  124. package/dist/memory/provenance-live.js +409 -0
  125. package/dist/memory/provenance-query.d.ts +5 -4
  126. package/dist/memory/provenance-query.js +21 -29
  127. package/dist/memory/provenance-resolver.d.ts +191 -0
  128. package/dist/memory/provenance-resolver.js +155 -0
  129. package/dist/memory/provenance.d.ts +1 -11
  130. package/dist/memory/provenance.js +2 -33
  131. package/dist/memory/recall-sanitize.d.ts +55 -0
  132. package/dist/memory/recall-sanitize.js +144 -0
  133. package/dist/memory/secret-filter.d.ts +32 -0
  134. package/dist/memory/secret-filter.js +126 -0
  135. package/dist/memory/truth-store.d.ts +2 -1
  136. package/dist/memory/truth-store.js +1 -4
  137. package/dist/memory/types.d.ts +74 -24
  138. package/dist/memory/types.js +75 -5
  139. package/dist/memory/write-adapters.d.ts +98 -0
  140. package/dist/memory/write-adapters.js +242 -0
  141. package/dist/operations/owner-action-effects.d.ts +53 -0
  142. package/dist/operations/owner-action-effects.js +96 -0
  143. package/dist/provenance/source-ref.d.ts +1 -1
  144. package/dist/provenance/source-ref.js +0 -2
  145. package/dist/registry/corrections.d.ts +31 -0
  146. package/dist/registry/corrections.js +332 -0
  147. package/dist/registry/record-identity.d.ts +49 -0
  148. package/dist/registry/record-identity.js +133 -0
  149. package/dist/registry/store.d.ts +101 -0
  150. package/dist/registry/store.js +395 -0
  151. package/dist/registry/types.d.ts +28 -0
  152. package/dist/registry/types.js +5 -0
  153. package/dist/relevance-scorer.js +2 -2
  154. package/dist/runtime/agent-event-bus.d.ts +50 -0
  155. package/dist/runtime/agent-event-bus.js +101 -0
  156. package/dist/runtime/drivers/claude-cli-wrapper.d.ts +155 -0
  157. package/dist/runtime/drivers/claude-cli-wrapper.js +392 -0
  158. package/dist/runtime/drivers/cli-arg-redaction.d.ts +2 -0
  159. package/dist/runtime/drivers/cli-arg-redaction.js +18 -0
  160. package/dist/runtime/drivers/cli-secret-redaction.d.ts +17 -0
  161. package/dist/runtime/drivers/cli-secret-redaction.js +126 -0
  162. package/dist/runtime/drivers/codex-app-server-process.d.ts +277 -0
  163. package/dist/runtime/drivers/codex-app-server-process.js +2342 -0
  164. package/dist/runtime/drivers/codex-auxiliary-tools.d.ts +17 -0
  165. package/dist/runtime/drivers/codex-auxiliary-tools.js +267 -0
  166. package/dist/runtime/drivers/codex-home.d.ts +20 -0
  167. package/dist/runtime/drivers/codex-home.js +674 -0
  168. package/dist/runtime/drivers/codex-thread-registry.d.ts +63 -0
  169. package/dist/runtime/drivers/codex-thread-registry.js +433 -0
  170. package/dist/runtime/drivers/persistent-cli-adapter.d.ts +160 -0
  171. package/dist/runtime/drivers/persistent-cli-adapter.js +396 -0
  172. package/dist/runtime/drivers/persistent-cli-process.d.ts +424 -0
  173. package/dist/runtime/drivers/persistent-cli-process.js +1543 -0
  174. package/dist/runtime/drivers/types.d.ts +456 -0
  175. package/dist/runtime/drivers/types.js +110 -0
  176. package/dist/runtime/mailbox.d.ts +223 -0
  177. package/dist/runtime/mailbox.js +582 -0
  178. package/dist/runtime/model-run-store.d.ts +28 -0
  179. package/dist/{model-runs/store.js → runtime/model-run-store.js} +81 -41
  180. package/dist/{model-runs/types.d.ts → runtime/model-run-types.d.ts} +33 -4
  181. package/dist/{model-runs/types.js → runtime/model-run-types.js} +1 -1
  182. package/dist/runtime/native-effect-observer.d.ts +22 -0
  183. package/dist/runtime/native-effect-observer.js +103 -0
  184. package/dist/runtime/native-input-journal.d.ts +46 -0
  185. package/dist/runtime/native-input-journal.js +308 -0
  186. package/dist/runtime/native-prompt.d.ts +118 -0
  187. package/dist/runtime/native-prompt.js +496 -0
  188. package/dist/runtime/native-session.d.ts +10 -0
  189. package/dist/{agent-graph/index.js → runtime/native-session.js} +9 -6
  190. package/dist/runtime/native-tool-trace-observer.d.ts +5 -0
  191. package/dist/runtime/native-tool-trace-observer.js +65 -0
  192. package/dist/runtime/native-turn.d.ts +272 -0
  193. package/dist/runtime/native-turn.js +1041 -0
  194. package/dist/runtime/operations.d.ts +236 -0
  195. package/dist/runtime/operations.js +565 -0
  196. package/dist/runtime/post-tool-handler.d.ts +74 -0
  197. package/dist/runtime/post-tool-handler.js +137 -0
  198. package/dist/runtime/prompt-layers.d.ts +126 -0
  199. package/dist/runtime/prompt-layers.js +239 -0
  200. package/dist/runtime/runtime-process.d.ts +102 -0
  201. package/dist/runtime/runtime-process.js +206 -0
  202. package/dist/runtime/runtime.d.ts +278 -0
  203. package/dist/runtime/runtime.js +563 -0
  204. package/dist/runtime/session-pool.d.ts +176 -0
  205. package/dist/runtime/session-pool.js +369 -0
  206. package/dist/runtime/stimulus-payload.d.ts +5 -0
  207. package/dist/runtime/stimulus-payload.js +50 -0
  208. package/dist/runtime/subagent-bridge.d.ts +24 -0
  209. package/dist/{agent-graph/types.js → runtime/subagent-bridge.js} +1 -1
  210. package/dist/runtime/text-completion.d.ts +22 -0
  211. package/dist/{agent-situation/types.js → runtime/text-completion.js} +1 -1
  212. package/dist/runtime/token-estimator.d.ts +25 -0
  213. package/dist/runtime/token-estimator.js +75 -0
  214. package/dist/runtime/tool-trace-store.d.ts +22 -0
  215. package/dist/{model-runs → runtime}/tool-trace-store.js +46 -27
  216. package/dist/runtime/trace-summary.d.ts +5 -0
  217. package/dist/runtime/trace-summary.js +74 -0
  218. package/dist/runtime/turn-text.d.ts +23 -0
  219. package/dist/runtime/turn-text.js +41 -0
  220. package/dist/storage/database.d.ts +67 -0
  221. package/dist/storage/database.js +158 -0
  222. package/package.json +20 -34
  223. package/dist/agent-graph/alias-write.d.ts +0 -3
  224. package/dist/agent-graph/alias-write.js +0 -234
  225. package/dist/agent-graph/entity-resolve.d.ts +0 -3
  226. package/dist/agent-graph/entity-resolve.js +0 -271
  227. package/dist/agent-graph/errors.d.ts +0 -4
  228. package/dist/agent-graph/errors.js +0 -11
  229. package/dist/agent-graph/graph-query.d.ts +0 -5
  230. package/dist/agent-graph/graph-query.js +0 -360
  231. package/dist/agent-graph/index.d.ts +0 -6
  232. package/dist/agent-situation/builder.d.ts +0 -8
  233. package/dist/agent-situation/builder.js +0 -239
  234. package/dist/agent-situation/cache-key.d.ts +0 -4
  235. package/dist/agent-situation/cache-key.js +0 -64
  236. package/dist/agent-situation/index.d.ts +0 -7
  237. package/dist/agent-situation/index.js +0 -28
  238. package/dist/agent-situation/packet-store.d.ts +0 -27
  239. package/dist/agent-situation/packet-store.js +0 -228
  240. package/dist/agent-situation/ranking-policy.d.ts +0 -18
  241. package/dist/agent-situation/ranking-policy.js +0 -75
  242. package/dist/agent-situation/source-readers.d.ts +0 -52
  243. package/dist/agent-situation/source-readers.js +0 -300
  244. package/dist/agent-situation/types.d.ts +0 -195
  245. package/dist/cases/wiki-page-index.d.ts +0 -37
  246. package/dist/cases/wiki-page-index.js +0 -201
  247. package/dist/config-loader.d.ts +0 -68
  248. package/dist/config-loader.js +0 -239
  249. package/dist/connectors/event-index.d.ts +0 -24
  250. package/dist/connectors/event-index.js +0 -245
  251. package/dist/connectors/raw-query.d.ts +0 -40
  252. package/dist/connectors/raw-query.js +0 -412
  253. package/dist/connectors/types.d.ts +0 -117
  254. package/dist/connectors/types.js +0 -3
  255. package/dist/context-compile/boundary-defaults.d.ts +0 -12
  256. package/dist/context-compile/boundary-defaults.js +0 -105
  257. package/dist/context-compile/channel-grant.js +0 -66
  258. package/dist/context-compile/compiler-policy.d.ts +0 -39
  259. package/dist/context-compile/compiler-policy.js +0 -147
  260. package/dist/context-compile/compiler.d.ts +0 -18
  261. package/dist/context-compile/compiler.js +0 -404
  262. package/dist/context-compile/index.d.ts +0 -10
  263. package/dist/context-compile/index.js +0 -26
  264. package/dist/context-compile/packet-store.d.ts +0 -8
  265. package/dist/context-compile/packet-store.js +0 -239
  266. package/dist/context-compile/ref.d.ts +0 -7
  267. package/dist/context-compile/ref.js +0 -102
  268. package/dist/context-compile/source-readers.d.ts +0 -70
  269. package/dist/context-compile/source-readers.js +0 -840
  270. package/dist/context-compile/types.d.ts +0 -155
  271. package/dist/context-compile/types.js +0 -11
  272. package/dist/context-compile/visibility.d.ts +0 -21
  273. package/dist/context-compile/visibility.js +0 -224
  274. package/dist/decision-tracker.d.ts +0 -206
  275. package/dist/decision-tracker.js +0 -481
  276. package/dist/edges/ref-validation.d.ts +0 -7
  277. package/dist/edges/ref-validation.js +0 -321
  278. package/dist/edges/store.d.ts +0 -8
  279. package/dist/edges/store.js +0 -129
  280. package/dist/embedding-client.d.ts +0 -28
  281. package/dist/embedding-client.js +0 -53
  282. package/dist/embedding-server/index.d.ts +0 -67
  283. package/dist/embedding-server/index.js +0 -405
  284. package/dist/embedding-server/mobile/auth.d.ts +0 -44
  285. package/dist/embedding-server/mobile/auth.js +0 -150
  286. package/dist/embedding-server/mobile/daemon.d.ts +0 -129
  287. package/dist/embedding-server/mobile/daemon.js +0 -313
  288. package/dist/embedding-server/mobile/output-parser.d.ts +0 -115
  289. package/dist/embedding-server/mobile/output-parser.js +0 -241
  290. package/dist/embedding-server/mobile/session-api.d.ts +0 -57
  291. package/dist/embedding-server/mobile/session-api.js +0 -261
  292. package/dist/embedding-server/mobile/session-manager.d.ts +0 -138
  293. package/dist/embedding-server/mobile/session-manager.js +0 -378
  294. package/dist/embedding-server/mobile/websocket-handler.d.ts +0 -137
  295. package/dist/embedding-server/mobile/websocket-handler.js +0 -486
  296. package/dist/embeddings.d.ts +0 -68
  297. package/dist/embeddings.js +0 -244
  298. package/dist/entities/audit-metrics.d.ts +0 -68
  299. package/dist/entities/audit-metrics.js +0 -124
  300. package/dist/entities/candidate-generator.d.ts +0 -8
  301. package/dist/entities/candidate-generator.js +0 -24
  302. package/dist/entities/entity-linked-decision-counts.d.ts +0 -2
  303. package/dist/entities/entity-linked-decision-counts.js +0 -29
  304. package/dist/entities/entity-list.d.ts +0 -22
  305. package/dist/entities/entity-list.js +0 -3
  306. package/dist/entities/entity-orphan-list.d.ts +0 -17
  307. package/dist/entities/entity-orphan-list.js +0 -3
  308. package/dist/entities/entity-search.d.ts +0 -20
  309. package/dist/entities/entity-search.js +0 -3
  310. package/dist/entities/errors.d.ts +0 -55
  311. package/dist/entities/errors.js +0 -100
  312. package/dist/entities/lineage-store.d.ts +0 -28
  313. package/dist/entities/lineage-store.js +0 -204
  314. package/dist/entities/normalization.d.ts +0 -14
  315. package/dist/entities/normalization.js +0 -63
  316. package/dist/entities/policy-store.d.ts +0 -8
  317. package/dist/entities/policy-store.js +0 -75
  318. package/dist/entities/policy-types.d.ts +0 -72
  319. package/dist/entities/policy-types.js +0 -59
  320. package/dist/entities/projection.d.ts +0 -8
  321. package/dist/entities/projection.js +0 -82
  322. package/dist/entities/provenance-query.d.ts +0 -32
  323. package/dist/entities/provenance-query.js +0 -3
  324. package/dist/entities/read-identity.d.ts +0 -31
  325. package/dist/entities/read-identity.js +0 -264
  326. package/dist/entities/recall-bridge.d.ts +0 -5
  327. package/dist/entities/recall-bridge.js +0 -171
  328. package/dist/entities/resolution-engine.d.ts +0 -8
  329. package/dist/entities/resolution-engine.js +0 -3
  330. package/dist/entities/rollback-preview.d.ts +0 -35
  331. package/dist/entities/rollback-preview.js +0 -3
  332. package/dist/entities/source-locator.d.ts +0 -3
  333. package/dist/entities/source-locator.js +0 -32
  334. package/dist/entities/store.d.ts +0 -69
  335. package/dist/entities/store.js +0 -544
  336. package/dist/entities/types.d.ts +0 -166
  337. package/dist/entities/types.js +0 -30
  338. package/dist/memory/extraction-prompt.d.ts +0 -4
  339. package/dist/memory/extraction-prompt.js +0 -89
  340. package/dist/memory/scope-backfill.d.ts +0 -14
  341. package/dist/memory/scope-backfill.js +0 -3
  342. package/dist/memory/scope-store.d.ts +0 -4
  343. package/dist/memory/scope-store.js +0 -9
  344. package/dist/memory-store.d.ts +0 -87
  345. package/dist/memory-store.js +0 -78
  346. package/dist/model-runs/store.d.ts +0 -13
  347. package/dist/model-runs/tool-trace-store.d.ts +0 -8
  348. package/dist/notification-manager.d.ts +0 -7
  349. package/dist/notification-manager.js +0 -12
  350. package/dist/outcome-tracker.d.ts +0 -151
  351. package/dist/outcome-tracker.js +0 -271
  352. package/dist/query-intent.d.ts +0 -23
  353. package/dist/query-intent.js +0 -114
  354. package/dist/runtime/trusted-provenance.d.ts +0 -3
  355. package/dist/runtime/trusted-provenance.js +0 -6
  356. package/dist/search/question-type.d.ts +0 -5
  357. package/dist/search/question-type.js +0 -41
  358. package/dist/test-utils.d.ts +0 -80
  359. package/dist/test-utils.js +0 -218
  360. package/dist/tier-validator.d.ts +0 -43
  361. package/dist/tier-validator.js +0 -159
  362. package/dist/time-formatter.d.ts +0 -25
  363. package/dist/time-formatter.js +0 -93
  364. /package/dist/{search → knowledge}/feedback-store.d.ts +0 -0
  365. /package/dist/{search → knowledge}/feedback-store.js +0 -0
  366. /package/dist/{search → knowledge}/ranker-features.d.ts +0 -0
  367. /package/dist/{search → knowledge}/ranker-rescore.d.ts +0 -0
  368. /package/dist/{search → knowledge}/ranker-rescore.js +0 -0
  369. /package/dist/{search → knowledge}/ranker-trainer.d.ts +0 -0
  370. /package/dist/{search → knowledge}/ranker-trainer.js +0 -0
  371. /package/dist/{search → knowledge}/search-quality.js +0 -0
@@ -2,10 +2,9 @@
2
2
  /**
3
3
  * MAMA Database Manager (SQLite-only)
4
4
  *
5
- * SQLite-exclusive database interface for MAMA Plugin.
6
- * Uses better-sqlite3 for local storage.
5
+ * SQLite-exclusive database interface. Uses better-sqlite3 for local storage.
7
6
  *
8
- * PostgreSQL support is only available in the legacy mcp-server repository.
7
+ * There is no PostgreSQL path.
9
8
  *
10
9
  * Features:
11
10
  * - WAL mode for better concurrency
@@ -18,43 +17,36 @@
18
17
  * @date 2026-02-01
19
18
  * @source-of-truth packages/mama-core/src/db-manager.js (mama-core)
20
19
  */
21
- var __importDefault = (this && this.__importDefault) || function (mod) {
22
- return (mod && mod.__esModule) ? mod : { "default": mod };
23
- };
24
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.openDatabase = exports.isTestMode = exports.declareProductionDatabasePath = exports.clearProductionDatabasePaths = exports.assertTestProcessIsNotUsingRealDb = void 0;
25
22
  exports.assertEmbeddingSchemeCurrent = assertEmbeddingSchemeCurrent;
26
23
  exports.initDB = initDB;
27
- exports.assertTestProcessIsNotUsingRealDb = assertTestProcessIsNotUsingRealDb;
28
24
  exports.getDB = getDB;
29
25
  exports.getAdapter = getAdapter;
26
+ exports.buildMemoryScopeId = buildMemoryScopeId;
30
27
  exports.closeDB = closeDB;
31
28
  exports.ensureMemoryScope = ensureMemoryScope;
32
- exports.insertEmbedding = insertEmbedding;
33
- exports.vectorSearch = vectorSearch;
34
- exports.insertDecisionWithEmbedding = insertDecisionWithEmbedding;
35
- exports.queryDecisionGraph = queryDecisionGraph;
36
- exports.querySemanticEdges = querySemanticEdges;
37
- exports.queryVectorSearch = queryVectorSearch;
29
+ exports.prepareDecisionEmbedding = prepareDecisionEmbedding;
30
+ exports.insertPreparedDecision = insertPreparedDecision;
38
31
  exports.updateDecisionOutcome = updateDecisionOutcome;
39
- exports.getPreparedStmt = getPreparedStmt;
40
32
  exports.getDbPath = getDbPath;
41
33
  exports.resetDBState = resetDBState;
42
- exports.isTestMode = isTestMode;
43
- exports.fts5Search = fts5Search;
44
- const path_1 = __importDefault(require("path"));
45
- const os_1 = __importDefault(require("os"));
46
34
  const debug_logger_js_1 = require("./debug-logger.js");
47
- const progress_indicator_js_1 = require("./progress-indicator.js");
48
- const index_js_1 = require("./db-adapter/index.js");
49
- const embeddings_js_1 = require("./embeddings.js");
50
- // Database adapter instance (singleton)
51
- let dbAdapter = null;
52
- let dbConnection = null;
53
- let isInitialized = false;
54
- let initializingPromise = null; // Single-flight guard for concurrent callers
35
+ const database_js_1 = require("./storage/database.js");
36
+ var database_js_2 = require("./storage/database.js");
37
+ Object.defineProperty(exports, "assertTestProcessIsNotUsingRealDb", { enumerable: true, get: function () { return database_js_2.assertTestProcessIsNotUsingRealDb; } });
38
+ Object.defineProperty(exports, "clearProductionDatabasePaths", { enumerable: true, get: function () { return database_js_2.clearProductionDatabasePaths; } });
39
+ Object.defineProperty(exports, "declareProductionDatabasePath", { enumerable: true, get: function () { return database_js_2.declareProductionDatabasePath; } });
40
+ Object.defineProperty(exports, "isTestMode", { enumerable: true, get: function () { return database_js_2.isTestMode; } });
41
+ var database_js_3 = require("./storage/database.js");
42
+ Object.defineProperty(exports, "openDatabase", { enumerable: true, get: function () { return database_js_3.openDatabase; } });
43
+ const embedder_js_1 = require("./embedding/embedder.js");
44
+ // One process-wide handle for callers that have not been given a database of
45
+ // their own yet. `openDatabase` owns the lifetime; this module only holds the
46
+ // handle and hands its adapter out.
47
+ let handle = null;
48
+ let openingPromise = null; // Single-flight guard for concurrent callers
55
49
  // Migration directory (moved to src/db/migrations for M1.2)
56
- const MIGRATIONS_DIR = path_1.default.join(__dirname, '..', 'db', 'migrations');
57
- const REAL_USER_DB_PATH = path_1.default.join(os_1.default.homedir(), '.claude', 'mama-memory.db');
58
50
  /**
59
51
  * Initialize SQLite database adapter and connect
60
52
  *
@@ -96,13 +88,13 @@ function assertEmbeddingSchemeCurrent(adapter) {
96
88
  // embedding_meta missing (pre-042). runMigrations creates it, so this path
97
89
  // only hits if migrations did not run; fall through to the vector check.
98
90
  }
99
- if (scheme === embeddings_js_1.EMBEDDING_PREFIX_SCHEME)
91
+ if (scheme === embedder_js_1.EMBEDDING_PREFIX_SCHEME)
100
92
  return; // current
101
93
  const legacyVectors = countRows(adapter, 'embeddings') + countRows(adapter, 'wiki_page_embeddings');
102
94
  if (legacyVectors > 0) {
103
- const dbPath = resolveAdapterDbPath(adapter) ?? '<db>';
95
+ const dbPath = (0, database_js_1.resolveAdapterDbPath)(adapter) ?? '<db>';
104
96
  throw new Error(`[embedding-guard] ${dbPath} holds ${legacyVectors} vectors written WITHOUT the e5 ` +
105
- `query/passage prefix (scheme='${scheme}', code requires '${embeddings_js_1.EMBEDDING_PREFIX_SCHEME}'). ` +
97
+ `query/passage prefix (scheme='${scheme}', code requires '${embedder_js_1.EMBEDDING_PREFIX_SCHEME}'). ` +
106
98
  `Cosine search would be non-discriminative. Re-embed before starting:\n` +
107
99
  ` MAMA_DB_PATH="${dbPath}" node packages/mama-core/scripts/re-embed-migration.mjs`);
108
100
  }
@@ -110,112 +102,48 @@ function assertEmbeddingSchemeCurrent(adapter) {
110
102
  try {
111
103
  adapter
112
104
  .prepare("INSERT OR REPLACE INTO embedding_meta (key, value) VALUES ('embedding_prefix_scheme', ?)")
113
- .run(embeddings_js_1.EMBEDDING_PREFIX_SCHEME);
105
+ .run(embedder_js_1.EMBEDDING_PREFIX_SCHEME);
114
106
  }
115
107
  catch {
116
108
  // best-effort; if embedding_meta truly missing, nothing to guard yet
117
109
  }
118
110
  }
119
- async function initDB() {
120
- assertTestProcessIsNotUsingRealDb();
121
- // Already initialized - return immediately
122
- if (isInitialized) {
123
- return dbConnection;
124
- }
125
- // Single-flight guard: If initialization is in progress, wait for it
126
- if (initializingPromise) {
127
- return initializingPromise;
128
- }
129
- // Start initialization and store promise for concurrent callers
130
- initializingPromise = (async () => {
131
- try {
132
- (0, progress_indicator_js_1.logSearching)('Initializing database...');
133
- // Create SQLite adapter
134
- dbAdapter = (0, index_js_1.createAdapter)();
135
- assertTestProcessIsNotUsingRealDb(resolveAdapterDbPath(dbAdapter), 'adapter.getDbPath()');
136
- // Connect to database
137
- dbConnection = await dbAdapter.connect();
138
- // Run migrations (includes 012-create-checkpoints-table.sql)
139
- await dbAdapter.runMigrations(MIGRATIONS_DIR);
140
- // Reload vector cache after migrations (new tables/rows may now exist)
141
- if (typeof dbAdapter.reloadVectorCache === 'function') {
142
- dbAdapter.reloadVectorCache();
143
- }
144
- assertEmbeddingSchemeCurrent(dbAdapter); // fail loud on legacy vectors + new code
145
- isInitialized = true;
146
- (0, debug_logger_js_1.info)(`[db-manager] Database initialized (${dbAdapter.constructor.name})`);
147
- (0, progress_indicator_js_1.logComplete)('Database ready');
148
- return dbConnection;
149
- }
150
- catch (error) {
151
- // Clear state on failure so retry is possible
152
- initializingPromise = null;
153
- dbAdapter = null;
154
- dbConnection = null;
155
- isInitialized = false;
156
- const message = error instanceof Error ? error.message : String(error);
157
- throw new Error(`Failed to initialize database: ${message}`);
158
- }
159
- })();
160
- return initializingPromise;
161
- }
162
- function assertTestProcessIsNotUsingRealDb(effectivePath, effectivePathSource = 'adapter') {
163
- if (!isDatabaseBoundaryTestMode()) {
164
- return;
165
- }
166
- const configuredPaths = [
167
- { name: 'MAMA_DB_PATH', value: process.env.MAMA_DB_PATH },
168
- { name: 'MAMA_DATABASE_PATH', value: process.env.MAMA_DATABASE_PATH },
169
- ];
170
- if (effectivePath) {
171
- configuredPaths.push({ name: effectivePathSource, value: effectivePath });
172
- }
173
- for (const configuredPath of configuredPaths) {
174
- if (!configuredPath.value) {
175
- continue;
176
- }
177
- const resolvedPath = path_1.default.resolve(expandHomePath(configuredPath.value));
178
- if (resolvedPath === path_1.default.resolve(REAL_USER_DB_PATH)) {
179
- throw new Error(`[db-boundary] Refusing to initDB against real DB ${REAL_USER_DB_PATH} ` +
180
- `from a test process (${configuredPath.name}=${configuredPath.value}). ` +
181
- 'Set MAMA_DB_PATH to a temporary path before initDB.');
182
- }
183
- }
184
- }
185
- function resolveAdapterDbPath(adapter) {
186
- if (typeof adapter.getDbPath === 'function') {
187
- return adapter.getDbPath();
188
- }
189
- return adapter.dbPath;
190
- }
191
- function isDatabaseBoundaryTestMode() {
192
- return isTestMode();
193
- }
194
- function expandHomePath(value) {
195
- const home = os_1.default.homedir();
196
- if (value === '~') {
197
- return home;
198
- }
199
- if (value.startsWith('~/')) {
200
- return path_1.default.join(home, value.slice(2));
201
- }
202
- return value.replaceAll('${HOME}', home);
111
+ async function initDB(options = {}) {
112
+ if (handle) {
113
+ return handle.connection;
114
+ }
115
+ if (openingPromise) {
116
+ return (await openingPromise).connection;
117
+ }
118
+ // The handle is stored inside the chain, before `openingPromise` is cleared.
119
+ // Assigning it after the await left one microtask in which both guards read
120
+ // null, and a concurrent caller landing there would open a second adapter and
121
+ // run migrations again on the same file.
122
+ openingPromise = (0, database_js_1.openDatabase)(options.migrations ? { migrations: options.migrations } : {})
123
+ .then((opened) => {
124
+ handle = opened;
125
+ return opened;
126
+ })
127
+ .finally(() => {
128
+ openingPromise = null;
129
+ });
130
+ return (await openingPromise).connection;
203
131
  }
204
132
  /**
205
133
  * Get database connection (singleton pattern)
206
134
  *
207
135
  * Returns better-sqlite3 Database instance
208
136
  *
209
- * Note: Synchronous for backward compatibility with memory-store.js
137
+ * Note: Synchronous because callers assume a resolved adapter, not a promise.
210
138
  * Will throw if database not initialized
211
139
  *
212
140
  * @returns SQLite database connection
213
141
  */
214
142
  function getDB() {
215
- if (!dbConnection) {
143
+ if (!handle) {
216
144
  throw new Error('Database not initialized. Call await initDB() first.');
217
145
  }
218
- return dbConnection;
146
+ return handle.connection;
219
147
  }
220
148
  /**
221
149
  * Get database adapter instance
@@ -225,10 +153,10 @@ function getDB() {
225
153
  * @returns Adapter instance
226
154
  */
227
155
  function getAdapter() {
228
- if (!dbAdapter) {
156
+ if (!handle) {
229
157
  throw new Error('Database adapter not initialized. Call await initDB() first.');
230
158
  }
231
- return dbAdapter;
159
+ return handle.adapter;
232
160
  }
233
161
  function buildMemoryScopeId(kind, externalId) {
234
162
  return `scope_${kind}_${Buffer.from(externalId).toString('base64url')}`;
@@ -239,17 +167,19 @@ function buildMemoryScopeId(kind, externalId) {
239
167
  * Call this on process exit
240
168
  */
241
169
  async function closeDB() {
242
- if (dbAdapter) {
243
- await dbAdapter.disconnect();
244
- dbAdapter = null;
245
- dbConnection = null;
246
- isInitialized = false;
247
- initializingPromise = null; // Clear to allow re-initialization
170
+ if (handle) {
171
+ await handle.close();
172
+ handle = null;
173
+ openingPromise = null; // Clear to allow re-initialization
248
174
  (0, debug_logger_js_1.info)('[db-manager] Database connection closed');
249
175
  }
250
176
  }
251
- async function ensureMemoryScope(kind, externalId) {
252
- const adapter = getAdapter();
177
+ /**
178
+ * The single write boundary for memory_scopes. Callers inside a transaction
179
+ * pass their adapter through; the public wrapper resolves the global adapter
180
+ * for the call sites that predate the adapter-taking boundary.
181
+ */
182
+ function ensureMemoryScope(adapter, kind, externalId) {
253
183
  const id = buildMemoryScopeId(kind, externalId);
254
184
  adapter
255
185
  .prepare(`
@@ -259,388 +189,54 @@ async function ensureMemoryScope(kind, externalId) {
259
189
  .run(id, kind, externalId);
260
190
  return id;
261
191
  }
262
- /**
263
- * Insert embedding into vector search table
264
- *
265
- * Stores embedding in the embeddings table for vector similarity search
266
- *
267
- * @param decisionRowid - SQLite rowid
268
- * @param embedding - 1024-dim embedding vector
269
- */
270
- async function insertEmbedding(decisionRowid, embedding) {
271
- const adapter = getAdapter();
272
- try {
273
- adapter.insertEmbedding(decisionRowid, embedding);
274
- }
275
- catch (error) {
276
- const message = error instanceof Error ? error.message : String(error);
277
- // Graceful degradation: Log warning but don't fail
278
- (0, debug_logger_js_1.error)(`[db-manager] Failed to insert embedding (vector search unavailable): ${message}`);
279
- }
280
- }
281
- /**
282
- * Perform vector similarity search
283
- *
284
- * Returns empty array if vector search not available (no keyword fallback)
285
- *
286
- * @param queryEmbedding - Query embedding (1024-dim)
287
- * @param limit - Max results to return (default: 5)
288
- * @param threshold - Minimum similarity threshold (default: 0.7)
289
- * @returns Array of decisions with similarity scores, or empty array
290
- */
291
- async function vectorSearch(queryEmbedding, limit = 5, threshold = 0.7, topicPrefix, excludeStatuses) {
292
- const adapter = getAdapter();
293
- try {
294
- // Brute-force cosine similarity over all embeddings (with optional topic/status pre-filter)
295
- const results = await adapter.vectorSearch(queryEmbedding, limit * 3, topicPrefix, excludeStatuses);
296
- if (!results || results.length === 0) {
297
- return []; // No keyword fallback - fast fail
298
- }
299
- const stmt = adapter.prepare(`SELECT * FROM decisions WHERE rowid = ?`);
300
- const decisions = [];
301
- for (const row of results) {
302
- const decision = stmt.get(row.rowid);
303
- if (!decision) {
304
- continue;
305
- }
306
- const similarity = row.similarity ?? Math.max(0, 1.0 - (row.distance ?? 1));
307
- const distance = row.distance ?? Math.max(0, 1.0 - similarity);
308
- if (similarity >= threshold) {
309
- decisions.push({
310
- ...decision,
311
- distance,
312
- similarity,
313
- });
314
- }
315
- if (decisions.length >= limit) {
316
- break;
317
- }
318
- }
319
- return decisions;
320
- }
321
- catch (error) {
322
- const message = error instanceof Error ? error.message : String(error);
323
- (0, debug_logger_js_1.error)(`[db-manager] Vector search failed: ${message}`);
324
- return []; // No keyword fallback - fast fail
325
- }
326
- }
327
- /**
328
- * Insert decision with embedding
329
- *
330
- * Combined operation: Insert decision + Generate embedding + Insert embedding
331
- * SQLite-only implementation
332
- *
333
- * @param decision - Decision object
334
- * @returns Decision ID
335
- */
336
- async function insertDecisionWithEmbedding(decision) {
337
- const adapter = getAdapter();
338
- const { generateEnhancedEmbedding } = await import('./embeddings.js');
339
- // Validate event_date before any DB operations
340
- if (decision.event_date !== null && decision.event_date !== undefined) {
341
- const d = decision.event_date;
342
- if (!/^\d{4}-\d{2}-\d{2}$/.test(d) || isNaN(new Date(d).getTime())) {
343
- throw new Error(`Invalid event_date: must be ISO 8601 YYYY-MM-DD (got: ${d})`);
344
- }
345
- }
346
- if (decision.event_datetime !== null && decision.event_datetime !== undefined) {
347
- if (typeof decision.event_datetime !== 'number' ||
192
+ /** Generate the optional vector before opening a synchronous SQLite transaction. */
193
+ async function prepareDecisionEmbedding(decision) {
194
+ if (decision.event_date !== null &&
195
+ decision.event_date !== undefined &&
196
+ (!/^\d{4}-\d{2}-\d{2}$/.test(decision.event_date) ||
197
+ Number.isNaN(new Date(decision.event_date).getTime()))) {
198
+ throw new Error(`Invalid event_date: must be ISO 8601 YYYY-MM-DD (got: ${decision.event_date})`);
199
+ }
200
+ if (decision.event_datetime !== null &&
201
+ decision.event_datetime !== undefined &&
202
+ (typeof decision.event_datetime !== 'number' ||
348
203
  !Number.isFinite(decision.event_datetime) ||
349
- decision.event_datetime <= 0) {
350
- throw new Error(`Invalid event_datetime: must be a positive millisecond timestamp (got: ${decision.event_datetime})`);
351
- }
352
- }
353
- try {
354
- // Generate embedding BEFORE transaction (required for SQLite's sync transaction)
355
- // Note: Redact topic for privacy - only log length
356
- (0, debug_logger_js_1.info)(`[db-manager] Generating embedding for decision (topic length: ${decision.topic?.length || 0})`);
357
- let embedding = null;
358
- try {
359
- // e5 passage role (default) - stored vector
360
- embedding = await generateEnhancedEmbedding({
361
- topic: decision.topic,
362
- decision: decision.decision,
363
- reasoning: decision.reasoning || undefined,
364
- outcome: decision.outcome || undefined,
365
- confidence: decision.confidence,
366
- });
367
- (0, debug_logger_js_1.info)(`[db-manager] Embedding generated: ${embedding ? embedding.length : 'null'} dimensions`);
368
- }
369
- catch (embGenErr) {
370
- // Non-fatal: save decision without embedding (e.g. ONNX model unavailable on CI)
371
- const message = embGenErr instanceof Error ? embGenErr.message : String(embGenErr);
372
- (0, debug_logger_js_1.error)(`[db-manager] ⚠️ Embedding generation failed, saving without vector: ${message}`);
373
- }
374
- // SQLite: Synchronous transaction including embedding
375
- adapter.transaction(() => {
376
- // Prepare INSERT statement
377
- const stmt = adapter.prepare(`
378
- INSERT INTO decisions (
379
- id, topic, decision, reasoning,
380
- outcome, failure_reason, limitation,
381
- user_involvement, session_id,
382
- supersedes, superseded_by, refined_from,
383
- confidence, created_at, updated_at,
384
- needs_validation, validation_attempts, last_validated_at, usage_count,
385
- trust_context, usage_success, usage_failure, time_saved,
386
- evidence, alternatives, risks, event_date, event_datetime,
387
- agent_id, model_run_id, envelope_hash, gateway_call_id, source_refs_json, provenance_json
388
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
389
- `);
390
- const insertResult = stmt.run(decision.id, decision.topic, decision.decision, decision.reasoning || null, decision.outcome || null, decision.failure_reason || null, decision.limitation || null, decision.user_involvement || null, decision.session_id || null, decision.supersedes || null, decision.superseded_by || null, decision.refined_from ? JSON.stringify(decision.refined_from) : null, decision.confidence !== undefined ? decision.confidence : 0.5,
391
- // IMPORTANT: All timestamps are stored in milliseconds (Date.now()).
392
- // The schema DEFAULT uses unixepoch() (seconds) but is never used
393
- // since all inserts go through this function which always provides ms.
394
- decision.created_at || Date.now(), decision.updated_at || Date.now(), decision.needs_validation !== undefined ? decision.needs_validation : 0, decision.validation_attempts || 0, decision.last_validated_at || null, decision.usage_count || 0, decision.trust_context || null, decision.usage_success || 0, decision.usage_failure || 0, decision.time_saved || 0, decision.evidence || null, decision.alternatives || null, decision.risks || null, decision.event_date || null, decision.event_datetime ?? null, decision.agent_id ?? null, decision.model_run_id ?? null, decision.envelope_hash ?? null, decision.gateway_call_id ?? null, decision.source_refs_json ?? null, decision.provenance_json ?? null);
395
- const rowid = Number(insertResult.lastInsertRowid);
396
- // Insert embedding in same transaction to ensure rowid matching
397
- (0, debug_logger_js_1.info)(`[db-manager] Vector search enabled: ${adapter.vectorSearchEnabled}`);
398
- if (adapter.vectorSearchEnabled && embedding) {
399
- try {
400
- (0, debug_logger_js_1.info)(`[db-manager] Inserting embedding for rowid: ${rowid}`);
401
- adapter.insertEmbedding(rowid, embedding);
402
- (0, debug_logger_js_1.info)(`[db-manager] ✅ Embedding inserted successfully`);
403
- }
404
- catch (embErr) {
405
- const message = embErr instanceof Error ? embErr.message : String(embErr);
406
- // Log but don't fail transaction if embedding fails
407
- (0, debug_logger_js_1.error)(`[db-manager] ❌ Embedding insert failed: ${message}`);
408
- }
409
- }
410
- else {
411
- (0, debug_logger_js_1.info)(`[db-manager] ⚠️ Vector search disabled, skipping embedding`);
412
- }
413
- return rowid;
414
- });
415
- if (process.env.MAMA_DEBUG) {
416
- (0, debug_logger_js_1.info)(`[db-manager] Decision stored: ${decision.id}`);
417
- }
418
- return decision.id;
419
- }
420
- catch (error) {
421
- const message = error instanceof Error ? error.message : String(error);
422
- throw new Error(`Failed to insert decision with embedding: ${message}`);
423
- }
204
+ decision.event_datetime <= 0)) {
205
+ throw new Error(`Invalid event_datetime: must be a positive millisecond timestamp (got: ${decision.event_datetime})`);
206
+ }
207
+ const { generateEnhancedEmbedding, isForceTier3Enabled } = await import('./embedding/embedder.js');
208
+ if (isForceTier3Enabled()) {
209
+ // Tier-3 is an explicit no-vector mode, not an embedder failure.
210
+ return null;
211
+ }
212
+ // Failure-first: an embedder error aborts the write instead of committing a
213
+ // decision row without its required vector.
214
+ return await generateEnhancedEmbedding({
215
+ topic: decision.topic,
216
+ decision: decision.decision,
217
+ reasoning: decision.reasoning || undefined,
218
+ outcome: decision.outcome || undefined,
219
+ confidence: decision.confidence,
220
+ });
424
221
  }
425
- /**
426
- * Query decision graph for topic
427
- *
428
- * Recursive CTE to traverse supersedes chain
429
- * SQLite implementation using WITH RECURSIVE
430
- *
431
- * @param topic - Decision topic to query
432
- * @returns Array of decisions (ordered by recency)
433
- */
434
- async function queryDecisionGraph(topic) {
435
- const adapter = getAdapter();
436
- try {
437
- // Story 014.14 Fix: Prioritize exact topic match over fuzzy matching
438
- // First try exact match, then fallback to fuzzy if no results
439
- // Try exact match first
440
- let stmt = adapter.prepare(`
441
- WITH RECURSIVE decision_chain AS (
442
- -- Base case: Get current decision (not superseded)
443
- SELECT * FROM decisions
444
- WHERE topic = ? AND superseded_by IS NULL
445
-
446
- UNION ALL
447
-
448
- -- Recursive case: Get previous decisions
449
- SELECT d.* FROM decisions d
450
- JOIN decision_chain dc ON d.id = dc.supersedes
451
- )
452
- SELECT * FROM decision_chain
453
- ORDER BY created_at DESC
454
- `);
455
- let decisions = stmt.all(topic);
456
- // If no exact match, try fuzzy matching as fallback
457
- if (decisions.length === 0) {
458
- const topicKeyword = topic.split('_')[0];
459
- stmt = adapter.prepare(`
460
- WITH RECURSIVE decision_chain AS (
461
- -- Base case: Get current decision (not superseded)
462
- SELECT * FROM decisions
463
- WHERE topic LIKE ? || '%' AND superseded_by IS NULL
464
-
465
- UNION ALL
466
-
467
- -- Recursive case: Get previous decisions
468
- SELECT d.* FROM decisions d
469
- JOIN decision_chain dc ON d.id = dc.supersedes
470
- )
471
- SELECT * FROM decision_chain
472
- ORDER BY created_at DESC
473
- `);
474
- decisions = stmt.all(topicKeyword);
475
- }
476
- // Join with decision_edges to include relationships
477
- // Prepare statement once outside loop for performance
478
- const edgesStmt = adapter.prepare(`
479
- SELECT * FROM decision_edges
480
- WHERE from_id = ?
481
- AND (approved_by_user = 1 OR approved_by_user IS NULL)
482
- `);
483
- for (const decision of decisions) {
484
- decision.edges = edgesStmt.all(decision.id);
485
- // Parse refined_from JSON if exists
486
- if (decision.refined_from) {
487
- try {
488
- decision.refined_from =
489
- typeof decision.refined_from === 'string'
490
- ? JSON.parse(decision.refined_from)
491
- : decision.refined_from;
492
- }
493
- catch {
494
- decision.refined_from = [];
495
- }
496
- }
497
- }
498
- return decisions;
499
- }
500
- catch (error) {
501
- const message = error instanceof Error ? error.message : String(error);
502
- throw new Error(`Decision graph query failed: ${message}`);
503
- }
504
- }
505
- /**
506
- * Query semantic edges for a list of decisions
507
- *
508
- * Returns both outgoing (from_id) and incoming (to_id) edges
509
- * for refines and contradicts relationships
510
- *
511
- * @param decisionIds - Decision IDs to query edges for
512
- * @returns Categorized edges
513
- */
514
- async function querySemanticEdges(decisionIds) {
515
- const adapter = getAdapter();
516
- if (!decisionIds || decisionIds.length === 0) {
517
- return {
518
- refines: [],
519
- refined_by: [],
520
- contradicts: [],
521
- contradicted_by: [],
522
- // Story 2.1: Extended edge types
523
- builds_on: [],
524
- built_on_by: [],
525
- debates: [],
526
- debated_by: [],
527
- synthesizes: [],
528
- synthesized_by: [],
529
- };
530
- }
531
- try {
532
- // Build placeholders for IN clause
533
- const placeholders = decisionIds.map(() => '?').join(',');
534
- // Story 2.1: Include new edge types in query
535
- const edgeTypes = ['refines', 'contradicts', 'builds_on', 'debates', 'synthesizes'];
536
- const edgeTypePlaceholders = edgeTypes.map(() => '?').join(',');
537
- // Query outgoing edges (from_id = decision)
538
- const outgoingStmt = adapter.prepare(`
539
- SELECT e.*, d.topic, d.decision, d.confidence, d.created_at
540
- FROM decision_edges e
541
- JOIN decisions d ON e.to_id = d.id
542
- WHERE e.from_id IN (${placeholders})
543
- AND e.relationship IN (${edgeTypePlaceholders})
544
- AND (e.approved_by_user = 1 OR e.approved_by_user IS NULL)
545
- ORDER BY e.created_at DESC
546
- `);
547
- const outgoingEdges = outgoingStmt.all(...decisionIds, ...edgeTypes);
548
- // Query incoming edges (to_id = decision)
549
- const incomingStmt = adapter.prepare(`
550
- SELECT e.*, d.topic, d.decision, d.confidence, d.created_at
551
- FROM decision_edges e
552
- JOIN decisions d ON e.from_id = d.id
553
- WHERE e.to_id IN (${placeholders})
554
- AND e.relationship IN (${edgeTypePlaceholders})
555
- AND (e.approved_by_user = 1 OR e.approved_by_user IS NULL)
556
- ORDER BY e.created_at DESC
557
- `);
558
- const incomingEdges = incomingStmt.all(...decisionIds, ...edgeTypes);
559
- // Categorize edges (original + v1.3 extended)
560
- const refines = outgoingEdges.filter((e) => e.relationship === 'refines');
561
- const refined_by = incomingEdges.filter((e) => e.relationship === 'refines');
562
- const contradicts = outgoingEdges.filter((e) => e.relationship === 'contradicts');
563
- const contradicted_by = incomingEdges.filter((e) => e.relationship === 'contradicts');
564
- // Story 2.1: New edge type categories
565
- const builds_on = outgoingEdges.filter((e) => e.relationship === 'builds_on');
566
- const built_on_by = incomingEdges.filter((e) => e.relationship === 'builds_on');
567
- const debates = outgoingEdges.filter((e) => e.relationship === 'debates');
568
- const debated_by = incomingEdges.filter((e) => e.relationship === 'debates');
569
- const synthesizes = outgoingEdges.filter((e) => e.relationship === 'synthesizes');
570
- const synthesized_by = incomingEdges.filter((e) => e.relationship === 'synthesizes');
571
- return {
572
- refines,
573
- refined_by,
574
- contradicts,
575
- contradicted_by,
576
- builds_on,
577
- built_on_by,
578
- debates,
579
- debated_by,
580
- synthesizes,
581
- synthesized_by,
582
- };
583
- }
584
- catch (error) {
585
- const message = error instanceof Error ? error.message : String(error);
586
- throw new Error(`Semantic edges query failed: ${message}`);
587
- }
588
- }
589
- /**
590
- * Query vector search with time window and threshold
591
- *
592
- * Story 014.14: AC #1 - Vector Search for Related Decisions
593
- *
594
- * @param params - Search parameters
595
- * @returns Results with similarity scores and decision data
596
- */
597
- async function queryVectorSearch(params) {
598
- const { query, limit = 10, threshold = 0.75, timeWindow = 90 * 24 * 60 * 60 * 1000 } = params;
599
- const adapter = getAdapter();
600
- const { generateEmbedding } = await import('./embeddings.js');
601
- try {
602
- // Generate embedding for query
603
- const embedding = await generateEmbedding(query, 'query');
604
- const cutoffTime = Date.now() - timeWindow;
605
- const candidates = await adapter.vectorSearch(embedding, limit * 5);
606
- if (!candidates || candidates.length === 0) {
607
- return [];
608
- }
609
- const stmt = adapter.prepare(`SELECT * FROM decisions WHERE rowid = ?`);
610
- const results = [];
611
- for (const candidate of candidates) {
612
- const decision = stmt.get(candidate.rowid);
613
- if (!decision) {
614
- continue;
615
- }
616
- if (decision.created_at < cutoffTime) {
617
- continue;
618
- }
619
- const similarity = candidate.similarity ?? Math.max(0, 1 - (candidate.distance ?? 1));
620
- const distance = candidate.distance ?? Math.max(0, 1 - similarity);
621
- if (similarity < threshold) {
622
- continue;
623
- }
624
- // Filter out superseded decisions unless explicitly requested
625
- if (!params.includeSuperseded && decision.superseded_by) {
626
- continue;
627
- }
628
- results.push({
629
- ...decision,
630
- similarity,
631
- distance,
632
- });
633
- if (results.length >= limit) {
634
- break;
635
- }
636
- }
637
- return results;
638
- }
639
- catch (error) {
640
- const message = error instanceof Error ? error.message : String(error);
641
- (0, debug_logger_js_1.error)(`[db-manager] queryVectorSearch failed: ${message}`);
642
- return []; // Return empty array on error (graceful degradation)
222
+ /** Insert one decision and its already-prepared vector inside the caller's transaction. */
223
+ function insertPreparedDecision(adapter, decision, embedding) {
224
+ const stmt = adapter.prepare(`
225
+ INSERT INTO decisions (
226
+ id, topic, decision, reasoning, outcome, failure_reason, limitation,
227
+ user_involvement, session_id, supersedes, superseded_by, refined_from,
228
+ confidence, created_at, updated_at, needs_validation, validation_attempts,
229
+ last_validated_at, usage_count, trust_context, usage_success, usage_failure,
230
+ time_saved, evidence, alternatives, risks, event_date, event_datetime,
231
+ agent_id, model_run_id, envelope_hash, gateway_call_id, source_refs_json, provenance_json
232
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
233
+ `);
234
+ const result = stmt.run(decision.id, decision.topic, decision.decision, decision.reasoning || null, decision.outcome || null, decision.failure_reason || null, decision.limitation || null, decision.user_involvement || null, decision.session_id || null, decision.supersedes || null, decision.superseded_by || null, decision.refined_from ? JSON.stringify(decision.refined_from) : null, decision.confidence ?? 0.5, decision.created_at || Date.now(), decision.updated_at || Date.now(), decision.needs_validation ?? 0, decision.validation_attempts || 0, decision.last_validated_at || null, decision.usage_count || 0, decision.trust_context || null, decision.usage_success || 0, decision.usage_failure || 0, decision.time_saved || 0, decision.evidence || null, decision.alternatives || null, decision.risks || null, decision.event_date || null, decision.event_datetime ?? null, decision.agent_id ?? null, decision.model_run_id ?? null, decision.envelope_hash ?? null, decision.gateway_call_id ?? null, decision.source_refs_json ?? null, decision.provenance_json ?? null);
235
+ const rowid = Number(result.lastInsertRowid);
236
+ if (embedding) {
237
+ adapter.insertEmbedding(rowid, embedding);
643
238
  }
239
+ return rowid;
644
240
  }
645
241
  /**
646
242
  * Update decision outcome
@@ -649,20 +245,18 @@ async function queryVectorSearch(params) {
649
245
  * @param outcomeData - Outcome data
650
246
  */
651
247
  async function updateDecisionOutcome(decisionId, outcomeData) {
652
- const adapter = getAdapter();
653
248
  try {
654
- const stmt = adapter.prepare(`
655
- UPDATE decisions
656
- SET
657
- outcome = ?,
658
- failure_reason = ?,
659
- limitation = ?,
660
- duration_days = ?,
661
- confidence = COALESCE(?, confidence),
662
- updated_at = ?
663
- WHERE id = ?
664
- `);
665
- stmt.run(outcomeData.outcome ?? null, outcomeData.failure_reason ?? null, outcomeData.limitation ?? null, outcomeData.duration_days ?? null, outcomeData.confidence !== undefined ? outcomeData.confidence : null, Date.now(), decisionId);
249
+ // Append-only: the outcome change is an authored judgment record; the
250
+ // maintained decisions projection columns move in the same transaction.
251
+ // Dynamic import avoids a module cycle with memory/write-adapters.
252
+ const { appendOutcomeAmendment } = await import('./memory/write-adapters.js');
253
+ await appendOutcomeAmendment(decisionId, {
254
+ outcome: outcomeData.outcome ?? null,
255
+ failureReason: outcomeData.failure_reason ?? null,
256
+ limitation: outcomeData.limitation ?? null,
257
+ confidence: outcomeData.confidence ?? null,
258
+ durationDays: outcomeData.duration_days ?? null,
259
+ }, { adapter: getAdapter() });
666
260
  (0, debug_logger_js_1.info)(`[db-manager] Decision outcome updated: ${decisionId} → ${outcomeData.outcome}`);
667
261
  }
668
262
  catch (error) {
@@ -670,58 +264,16 @@ async function updateDecisionOutcome(decisionId, outcomeData) {
670
264
  throw new Error(`Failed to update decision outcome: ${message}`);
671
265
  }
672
266
  }
673
- /**
674
- * Get prepared statement
675
- *
676
- * For backward compatibility with memory-store.js
677
- * Returns a compatibility shim that proxies to adapter.prepare()
678
- *
679
- * @param sql - SQL statement
680
- * @returns Statement-like object with run/get/all methods
681
- */
682
- function getPreparedStmt(sql) {
683
- if (!dbAdapter) {
684
- (0, debug_logger_js_1.warn)('[db-manager] getPreparedStmt() called before initialization');
685
- // Return no-op object for feature detection (won't throw)
686
- return {
687
- run: () => ({ changes: 0, lastInsertRowid: 0 }),
688
- get: () => null,
689
- all: () => [],
690
- };
691
- }
692
- // Proxy to adapter.prepare() for actual usage
693
- try {
694
- return dbAdapter.prepare(sql);
695
- }
696
- catch (error) {
697
- const message = error instanceof Error ? error.message : String(error);
698
- (0, debug_logger_js_1.warn)(`[db-manager] getPreparedStmt() failed: ${message}`);
699
- // Return no-op object on error (graceful degradation)
700
- return {
701
- run: () => ({ changes: 0, lastInsertRowid: 0 }),
702
- get: () => null,
703
- all: () => [],
704
- };
705
- }
706
- }
707
267
  /**
708
268
  * Get database file path
709
269
  *
710
270
  * @returns Actual database path or 'Not initialized'
711
271
  */
712
272
  function getDbPath() {
713
- if (!dbAdapter) {
273
+ if (!handle) {
714
274
  return 'Not initialized';
715
275
  }
716
- // Use adapter's getDbPath method if available, fallback to description
717
- if (typeof dbAdapter.getDbPath === 'function') {
718
- return dbAdapter.getDbPath();
719
- }
720
- // Fallback: try to get path from adapter properties
721
- if (dbAdapter.dbPath) {
722
- return dbAdapter.dbPath;
723
- }
724
- return `${dbAdapter.constructor.name} (path unavailable)`;
276
+ return handle.dbPath;
725
277
  }
726
278
  // Note: Removed auto-registered SIGINT/SIGTERM handlers that called process.exit(0)
727
279
  // This was causing issues with host cleanup in parent processes.
@@ -737,54 +289,20 @@ function getDbPath() {
737
289
  */
738
290
  function resetDBState(options = {}) {
739
291
  const { disconnect = true } = options;
740
- if (disconnect && dbAdapter) {
292
+ if (disconnect && handle) {
741
293
  try {
742
- dbAdapter.disconnect();
294
+ handle.adapter.disconnect();
743
295
  }
744
296
  catch {
745
297
  // Ignore disconnect errors during reset
746
298
  }
747
299
  }
748
- dbAdapter = null;
749
- dbConnection = null;
750
- isInitialized = false;
751
- initializingPromise = null;
300
+ handle = null;
301
+ openingPromise = null;
752
302
  }
753
303
  /**
754
304
  * Check if running in test mode
755
305
  *
756
306
  * Returns true if MAMA_TEST_MODE, VITEST, or NODE_ENV=test env vars are set
757
307
  */
758
- function isTestMode() {
759
- return Boolean(process.env.MAMA_TEST_MODE || process.env.VITEST || process.env.NODE_ENV === 'test');
760
- }
761
- /**
762
- * FTS5 keyword search on decisions table.
763
- * Returns matching decision IDs with BM25 rank scores.
764
- */
765
- async function fts5Search(query, limit = 10) {
766
- const adapter = getAdapter();
767
- // Check if FTS5 table exists (swallow errors — table may not exist yet)
768
- let tableCheck;
769
- try {
770
- tableCheck = adapter
771
- .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='decisions_fts'")
772
- .get();
773
- }
774
- catch {
775
- return [];
776
- }
777
- if (!tableCheck)
778
- return [];
779
- // Query execution — let errors propagate to the caller
780
- const stmt = adapter.prepare(`
781
- SELECT d.id, rank
782
- FROM decisions_fts
783
- JOIN decisions d ON decisions_fts.rowid = d.rowid
784
- WHERE decisions_fts MATCH ?
785
- ORDER BY rank
786
- LIMIT ?
787
- `);
788
- return stmt.all(query, limit);
789
- }
790
308
  //# sourceMappingURL=db-manager.js.map