@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
@@ -0,0 +1,808 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.JudgmentError = void 0;
7
+ exports.judgmentRecordId = judgmentRecordId;
8
+ exports.admittedScopeIds = admittedScopeIds;
9
+ exports.boundScopeIdsFor = boundScopeIdsFor;
10
+ exports.appendJudgment = appendJudgment;
11
+ exports.createJudgmentWriter = createJudgmentWriter;
12
+ exports.mapTwinEdgeRow = mapTwinEdgeRow;
13
+ exports.insertTwinEdge = insertTwinEdge;
14
+ exports.getTwinEdge = getTwinEdge;
15
+ exports.listTwinEdgesForRefs = listTwinEdgesForRefs;
16
+ const node_crypto_1 = __importDefault(require("node:crypto"));
17
+ const db_manager_js_1 = require("../db-manager.js");
18
+ const canonicalize_js_1 = require("../canonicalize.js");
19
+ const twin_edge_types_js_1 = require("./twin-edge-types.js");
20
+ const event_store_js_1 = require("../memory/event-store.js");
21
+ const record_identity_js_1 = require("../registry/record-identity.js");
22
+ class JudgmentError extends Error {
23
+ code;
24
+ constructor(code, message) {
25
+ super(message);
26
+ this.code = code;
27
+ this.name = 'JudgmentError';
28
+ }
29
+ }
30
+ exports.JudgmentError = JudgmentError;
31
+ function commandHash(command) {
32
+ return node_crypto_1.default.createHash('sha256').update((0, canonicalize_js_1.canonicalizeJSON)(command)).digest('hex');
33
+ }
34
+ function recordIdForCommand(command) {
35
+ return judgmentRecordId(command.commandId);
36
+ }
37
+ /** Deterministic record id a command will be bound to — lets adapters build
38
+ * projections (timeline events, identity bindings) before the write commits. */
39
+ function judgmentRecordId(commandId) {
40
+ return `judgment_${node_crypto_1.default.createHash('sha256').update(commandId).digest('hex').slice(0, 24)}`;
41
+ }
42
+ function commitmentIdForCommand(command) {
43
+ return `commitment_${node_crypto_1.default
44
+ .createHash('sha256')
45
+ .update(command.commandId)
46
+ .digest('hex')
47
+ .slice(0, 24)}`;
48
+ }
49
+ function requireText(value, field) {
50
+ if (typeof value !== 'string' || value.trim() === '') {
51
+ throw new JudgmentError('INVALID_COMMAND', `${field} must be nonblank`);
52
+ }
53
+ }
54
+ function scopeIdFor(scope) {
55
+ return `scope_${scope.kind}_${Buffer.from(scope.id).toString('base64url')}`;
56
+ }
57
+ function scopeKey(scope) {
58
+ return `${scope.kind}\0${scope.id}`;
59
+ }
60
+ /** Scopes the caller was admitted to; bounds what the command may reference.
61
+ * Exported for the sibling source-ingest command path; not part of the public API. */
62
+ function admittedScopeIds(access) {
63
+ const seen = new Set();
64
+ return access.scopes.map((scope) => {
65
+ requireText(scope.kind, 'scope kind');
66
+ requireText(scope.id, 'scope id');
67
+ const key = scopeKey(scope);
68
+ if (seen.has(key)) {
69
+ throw new JudgmentError('INVALID_SCOPE', 'Access scopes must be unique');
70
+ }
71
+ seen.add(key);
72
+ return scopeIdFor(scope);
73
+ });
74
+ }
75
+ /**
76
+ * Scopes bound to the new record. An explicit `scopes: []` declares an
77
+ * unscoped record (legacy parity); an omitted field inherits the access scope.
78
+ * Exported for the sibling source-ingest command path; not part of the public API.
79
+ */
80
+ function boundScopeIdsFor(access, command) {
81
+ const scopes = command.scopes ?? access.scopes;
82
+ if (command.scopes === undefined && scopes.length === 0) {
83
+ throw new JudgmentError('INVALID_SCOPE', 'At least one judgment scope is required');
84
+ }
85
+ const admitted = new Set(access.scopes.map(scopeKey));
86
+ const seen = new Set();
87
+ return scopes.map((scope) => {
88
+ // A kind is nonblank text. Which kinds exist is the writer's statement; this
89
+ // compared against a list the core kept, so a consumer whose world had a kind
90
+ // of its own could not append a judgment at all.
91
+ requireText(scope.kind, 'scope kind');
92
+ requireText(scope.id, 'scope id');
93
+ const key = scopeKey(scope);
94
+ if (seen.has(key)) {
95
+ throw new JudgmentError('INVALID_SCOPE', 'Judgment scopes must be unique');
96
+ }
97
+ seen.add(key);
98
+ if (!admitted.has(key)) {
99
+ throw new JudgmentError('SCOPE_DENIED', 'Judgment scope is outside the admitted access');
100
+ }
101
+ return scopeIdFor(scope);
102
+ });
103
+ }
104
+ function validateBounds(command) {
105
+ for (const [name, value] of [
106
+ ['appliesFrom', command.appliesFrom],
107
+ ['appliesUntil', command.appliesUntil],
108
+ ]) {
109
+ if (value !== undefined && (!Number.isFinite(value) || value < 0)) {
110
+ throw new JudgmentError('INVALID_TIME', `${name} must be a finite nonnegative epoch`);
111
+ }
112
+ }
113
+ if (command.appliesFrom !== undefined &&
114
+ command.appliesUntil !== undefined &&
115
+ command.appliesFrom > command.appliesUntil) {
116
+ throw new JudgmentError('INVALID_TIME', 'appliesFrom cannot be after appliesUntil');
117
+ }
118
+ }
119
+ const AMEND_FIELDS = [
120
+ 'outcome',
121
+ 'failureReason',
122
+ 'limitation',
123
+ 'status',
124
+ 'confidence',
125
+ 'durationDays',
126
+ 'supersedes',
127
+ 'supersededBy',
128
+ ];
129
+ /** Public-save parity fields carried by the command are validated up front so a
130
+ * malformed command fails before any write or embedder call. */
131
+ function validateCommandFields(command) {
132
+ if (command.confidence !== undefined &&
133
+ (!Number.isFinite(command.confidence) || command.confidence < 0 || command.confidence > 1)) {
134
+ throw new JudgmentError('INVALID_COMMAND', 'confidence must be a number between 0 and 1');
135
+ }
136
+ if (command.eventDate !== undefined && command.eventDate !== null) {
137
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(command.eventDate) ||
138
+ Number.isNaN(new Date(command.eventDate).getTime())) {
139
+ throw new JudgmentError('INVALID_TIME', `eventDate must be ISO 8601 YYYY-MM-DD (got: ${command.eventDate})`);
140
+ }
141
+ }
142
+ if (command.eventDatetime !== undefined && command.eventDatetime !== null) {
143
+ if (!Number.isFinite(command.eventDatetime) || command.eventDatetime <= 0) {
144
+ throw new JudgmentError('INVALID_TIME', `eventDatetime must be a positive millisecond timestamp (got: ${command.eventDatetime})`);
145
+ }
146
+ }
147
+ if (command.recordedAt !== undefined &&
148
+ (!Number.isFinite(command.recordedAt) || command.recordedAt < 0)) {
149
+ throw new JudgmentError('INVALID_TIME', 'recordedAt must be a finite nonnegative epoch');
150
+ }
151
+ if (command.sourceRefs !== undefined &&
152
+ (!Array.isArray(command.sourceRefs) ||
153
+ command.sourceRefs.some((ref) => typeof ref !== 'string' || ref.length === 0))) {
154
+ throw new JudgmentError('INVALID_COMMAND', 'sourceRefs must be an array of nonblank strings');
155
+ }
156
+ for (const amendment of command.amends ?? []) {
157
+ if (amendment.target?.kind !== 'memory') {
158
+ throw new JudgmentError('INVALID_COMMAND', 'amends targets must be memory references');
159
+ }
160
+ requireText(amendment.target.id, 'amends target id');
161
+ if (!AMEND_FIELDS.some((field) => field in amendment)) {
162
+ throw new JudgmentError('INVALID_COMMAND', 'amends requires at least one projection field to set');
163
+ }
164
+ }
165
+ for (const edge of command.projections?.decisionEdges ?? []) {
166
+ if (edge.fromId !== undefined) {
167
+ requireText(edge.fromId, 'projection edge source');
168
+ }
169
+ requireText(edge.targetId, 'projection edge target');
170
+ requireText(edge.relationship, 'projection edge relationship');
171
+ }
172
+ }
173
+ function referenceExists(adapter, reference, admittedScopeIds) {
174
+ if (reference.kind === 'memory') {
175
+ const row = adapter
176
+ .prepare(`SELECT (SELECT COUNT(*) FROM memory_scope_bindings b WHERE b.memory_id = d.id) AS bindings
177
+ FROM decisions d WHERE d.id = ?`)
178
+ .get(reference.id);
179
+ if (!row)
180
+ return false;
181
+ // A record with no scope bindings has no partition boundary to violate.
182
+ if (row.bindings === 0)
183
+ return true;
184
+ if (admittedScopeIds.length === 0)
185
+ return false;
186
+ const placeholders = admittedScopeIds.map(() => '?').join(', ');
187
+ return (adapter
188
+ .prepare(`SELECT 1 FROM decisions d
189
+ JOIN memory_scope_bindings b ON b.memory_id = d.id
190
+ WHERE d.id = ? AND b.scope_id IN (${placeholders}) LIMIT 1`)
191
+ .get(reference.id, ...admittedScopeIds) !== undefined);
192
+ }
193
+ if (reference.kind === 'registry') {
194
+ const node = adapter.prepare('SELECT 1 FROM registry_nodes WHERE id = ?').get(reference.id) !== undefined;
195
+ if (!node)
196
+ return false;
197
+ const bindings = adapter
198
+ .prepare('SELECT scope_kind, scope_id FROM registry_scope_bindings WHERE node_id = ?')
199
+ .all(reference.id);
200
+ return (bindings.length === 0 ||
201
+ bindings.some((binding) => admittedScopeIds.includes(`scope_${binding.scope_kind}_${Buffer.from(binding.scope_id).toString('base64url')}`)));
202
+ }
203
+ if (reference.kind === 'observation') {
204
+ return (adapter
205
+ .prepare('SELECT 1 FROM observation_versions WHERE observation_id = ?')
206
+ .get(reference.id) !== undefined);
207
+ }
208
+ if (reference.kind === 'edge') {
209
+ return (adapter.prepare('SELECT 1 FROM twin_edges WHERE edge_id = ?').get(reference.id) !== undefined);
210
+ }
211
+ return false;
212
+ }
213
+ function validateLinks(adapter, links, admittedScopeIds) {
214
+ const keys = new Set();
215
+ for (const link of links) {
216
+ requireText(link.relation, 'link relation');
217
+ requireText(link.target.id, 'link target id');
218
+ const attrs = link.attrs ?? {};
219
+ const key = (0, canonicalize_js_1.canonicalizeJSON)({
220
+ relation: link.relation,
221
+ target: link.target,
222
+ role: attrs.role ?? null,
223
+ slot: attrs.slot ?? null,
224
+ });
225
+ if (keys.has(key)) {
226
+ throw new JudgmentError('DUPLICATE_LINK', 'A judgment cannot repeat the same relation target slot');
227
+ }
228
+ keys.add(key);
229
+ if (!referenceExists(adapter, link.target, admittedScopeIds)) {
230
+ // Echo only the caller's own input: an unavailable id reads the same whether wrong or outside scope.
231
+ throw new JudgmentError('REFERENCE_NOT_FOUND', `A judgment reference is unavailable: ${link.target.kind} ${link.target.id}`);
232
+ }
233
+ }
234
+ }
235
+ function validateAmends(adapter, amends, admittedScopeIds) {
236
+ for (const amendment of amends) {
237
+ if (!referenceExists(adapter, amendment.target, admittedScopeIds)) {
238
+ throw new JudgmentError('REFERENCE_NOT_FOUND', 'An amendment target is unavailable');
239
+ }
240
+ }
241
+ }
242
+ function toColumnText(value) {
243
+ if (value === undefined || value === null)
244
+ return null;
245
+ return Array.isArray(value) ? JSON.stringify(value) : value;
246
+ }
247
+ const AMEND_COLUMN_MAP = {
248
+ outcome: 'outcome',
249
+ failureReason: 'failure_reason',
250
+ limitation: 'limitation',
251
+ status: 'status',
252
+ confidence: 'confidence',
253
+ durationDays: 'duration_days',
254
+ supersedes: 'supersedes',
255
+ supersededBy: 'superseded_by',
256
+ };
257
+ function applyAmendment(adapter, amendment, now) {
258
+ const sets = ['updated_at = ?'];
259
+ const params = [now];
260
+ for (const field of AMEND_FIELDS) {
261
+ if (field in amendment) {
262
+ sets.push(`${AMEND_COLUMN_MAP[field]} = ?`);
263
+ params.push(amendment[field] ?? null);
264
+ }
265
+ }
266
+ adapter
267
+ .prepare(`UPDATE decisions SET ${sets.join(', ')} WHERE id = ?`)
268
+ .run(...params, amendment.target.id);
269
+ }
270
+ function applyProjections(adapter, command, recordId, effectiveScopes, now) {
271
+ const projections = command.projections;
272
+ if (!projections)
273
+ return;
274
+ if (projections.recordIdentity) {
275
+ (0, record_identity_js_1.writeRecordIdentity)(adapter, {
276
+ recordId,
277
+ itemId: projections.recordIdentity.itemId,
278
+ actors: projections.recordIdentity.actors ?? [],
279
+ scopes: effectiveScopes,
280
+ });
281
+ }
282
+ for (const targetId of projections.supersedeTargets ?? []) {
283
+ adapter
284
+ .prepare("UPDATE decisions SET superseded_by = ?, status = 'superseded', updated_at = ? WHERE id = ?")
285
+ .run(recordId, now, targetId);
286
+ }
287
+ const edgeInsert = adapter.prepare(`INSERT INTO decision_edges
288
+ (from_id, to_id, relationship, reason, weight, created_at, created_by, approved_by_user)
289
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
290
+ for (const edge of projections.decisionEdges ?? []) {
291
+ edgeInsert.run(edge.fromId ?? recordId, edge.targetId, edge.relationship, edge.reason ?? null, edge.weight ?? 1, now, edge.createdBy ?? 'user', edge.approvedByUser ?? 1);
292
+ }
293
+ }
294
+ function parseReceipt(value) {
295
+ let parsed;
296
+ try {
297
+ parsed = JSON.parse(value);
298
+ }
299
+ catch (error) {
300
+ throw new Error('judgment_commands.receipt_json is malformed', { cause: error });
301
+ }
302
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
303
+ throw new Error('judgment_commands.receipt_json must contain an object');
304
+ }
305
+ return parsed;
306
+ }
307
+ function assertReplay(adapter, command, access, hash) {
308
+ const binding = adapter
309
+ .prepare('SELECT principal_id, action, payload_hash, receipt_key FROM command_bindings WHERE command_id = ?')
310
+ .get(command.commandId);
311
+ if (!binding)
312
+ return null;
313
+ if (binding.principal_id !== access.principalId ||
314
+ binding.action !== 'judgment.append' ||
315
+ binding.payload_hash !== hash) {
316
+ throw new JudgmentError('COMMAND_CONFLICT', 'The command id is already bound to another request');
317
+ }
318
+ const row = adapter
319
+ .prepare('SELECT receipt_json FROM judgment_commands WHERE command_id = ?')
320
+ .get(command.commandId);
321
+ if (!row) {
322
+ throw new Error('Command binding has no judgment receipt');
323
+ }
324
+ return parseReceipt(row.receipt_json);
325
+ }
326
+ function edgeId(commandId, index, relation, target) {
327
+ return `edge_${node_crypto_1.default
328
+ .createHash('sha256')
329
+ .update((0, canonicalize_js_1.canonicalizeJSON)({ commandId, index, relation, target }))
330
+ .digest('hex')
331
+ .slice(0, 24)}`;
332
+ }
333
+ function insertLink(adapter, command, recordId, link, index, access, now) {
334
+ const id = edgeId(command.commandId, index, link.relation, link.target);
335
+ const attrs = link.attrs ?? {};
336
+ const contentHash = node_crypto_1.default
337
+ .createHash('sha256')
338
+ .update((0, canonicalize_js_1.canonicalizeJSON)({ id, recordId, link }))
339
+ .digest();
340
+ insertTwinEdge(adapter, {
341
+ edge_id: id,
342
+ edge_type: link.relation,
343
+ subject_ref: { kind: 'memory', id: recordId },
344
+ object_ref: link.target,
345
+ relation_attrs: attrs,
346
+ confidence: 1.0,
347
+ source: access.edgeSource ?? 'agent',
348
+ agent_id: Object.hasOwn(command, 'agentId') ? (command.agentId ?? undefined) : access.agentId,
349
+ model_run_id: command.modelRunId ?? undefined,
350
+ reason_text: typeof link.attrs?.reason === 'string' ? link.attrs.reason : (command.reasoning ?? undefined),
351
+ evidence_refs: command.replaces?.length ? command.replaces : undefined,
352
+ content_hash: contentHash,
353
+ created_at: now,
354
+ });
355
+ return id;
356
+ }
357
+ function workPatch(value) {
358
+ return value?.set ?? {};
359
+ }
360
+ async function appendJudgmentOnAdapter(adapter, command, access, embedder) {
361
+ requireText(command.commandId, 'commandId');
362
+ requireText(command.topic, 'topic');
363
+ requireText(command.summary, 'summary');
364
+ requireText(access.principalId, 'principalId');
365
+ requireText(access.agentId, 'agentId');
366
+ if (command.recordKind !== 'judgment' && command.recordKind !== 'commitment') {
367
+ throw new JudgmentError('INVALID_COMMAND', 'recordKind is invalid');
368
+ }
369
+ if ((command.recordKind === 'commitment') !== (command.work !== undefined)) {
370
+ throw new JudgmentError('INVALID_COMMAND', 'Commitment records require a work assignment');
371
+ }
372
+ validateBounds(command);
373
+ validateCommandFields(command);
374
+ const admittedScopeIdList = admittedScopeIds(access);
375
+ const boundScopeIdList = boundScopeIdsFor(access, command);
376
+ const effectiveScopes = command.scopes ?? [...access.scopes];
377
+ const effectiveCommand = command.scopes ? command : { ...command, scopes: [...access.scopes] };
378
+ const hash = commandHash(effectiveCommand);
379
+ const replay = assertReplay(adapter, command, access, hash);
380
+ if (replay) {
381
+ validateLinks(adapter, command.links ?? [], admittedScopeIdList);
382
+ for (const replacement of command.replaces ?? []) {
383
+ if (!referenceExists(adapter, { kind: 'memory', id: replacement.id }, admittedScopeIdList)) {
384
+ throw new JudgmentError('REFERENCE_NOT_FOUND', 'A replacement target is unavailable');
385
+ }
386
+ }
387
+ validateAmends(adapter, command.amends ?? [], admittedScopeIdList);
388
+ return replay;
389
+ }
390
+ validateLinks(adapter, command.links ?? [], admittedScopeIdList);
391
+ validateAmends(adapter, command.amends ?? [], admittedScopeIdList);
392
+ const recordId = recordIdForCommand(command);
393
+ const now = Date.now();
394
+ const domainNow = command.recordedAt ?? now;
395
+ const embedding = embedder
396
+ ? await embedder.embed(`${command.topic}\n${command.summary}`, 'passage')
397
+ : null;
398
+ const edgeIds = [];
399
+ let workReceipt;
400
+ let replayedReceipt = null;
401
+ const transaction = adapter.transactionImmediate
402
+ ? adapter.transactionImmediate.bind(adapter)
403
+ : adapter.transaction.bind(adapter);
404
+ transaction(() => {
405
+ const bindingResult = adapter
406
+ .prepare(`INSERT OR IGNORE INTO command_bindings
407
+ (command_id, principal_id, action, payload_hash, receipt_kind, receipt_key, created_at)
408
+ VALUES (?, ?, 'judgment.append', ?, 'judgment', ?, ?)`)
409
+ .run(command.commandId, access.principalId, hash, recordId, now);
410
+ if (bindingResult.changes === 0) {
411
+ replayedReceipt = assertReplay(adapter, command, access, hash);
412
+ if (!replayedReceipt) {
413
+ throw new Error('Command binding was not readable after a conflict-free insert');
414
+ }
415
+ return;
416
+ }
417
+ const decisionRowId = (0, db_manager_js_1.insertPreparedDecision)(adapter, {
418
+ id: recordId,
419
+ topic: command.topic,
420
+ decision: command.summary,
421
+ reasoning: command.reasoning ?? null,
422
+ outcome: command.outcome ?? null,
423
+ failure_reason: command.failureReason ?? null,
424
+ limitation: command.limitation ?? null,
425
+ user_involvement: command.record?.userInvolvement ?? null,
426
+ session_id: command.record?.sessionId ?? null,
427
+ supersedes: command.replaces?.[0]?.id ?? command.record?.supersedes ?? null,
428
+ refined_from: command.record?.refinedFrom ?? null,
429
+ confidence: command.confidence ?? 0.5,
430
+ created_at: domainNow,
431
+ updated_at: now,
432
+ needs_validation: command.record?.needsValidation ?? 0,
433
+ trust_context: command.record?.trustContext ?? null,
434
+ evidence: toColumnText(command.evidence),
435
+ alternatives: toColumnText(command.alternatives),
436
+ risks: command.risks ?? null,
437
+ event_date: command.eventDate ?? null,
438
+ event_datetime: command.eventDatetime ?? null,
439
+ agent_id: Object.hasOwn(command, 'agentId') ? (command.agentId ?? null) : access.agentId,
440
+ model_run_id: command.modelRunId ?? null,
441
+ envelope_hash: command.envelopeHash ?? null,
442
+ gateway_call_id: command.gatewayCallId ?? null,
443
+ source_refs_json: command.sourceRefs ? (0, canonicalize_js_1.canonicalizeJSON)(command.sourceRefs) : null,
444
+ provenance_json: command.provenance ? (0, canonicalize_js_1.canonicalizeJSON)(command.provenance) : null,
445
+ }, embedding);
446
+ if (!Number.isSafeInteger(decisionRowId) || decisionRowId <= 0) {
447
+ throw new Error('Judgment decision row was not inserted');
448
+ }
449
+ adapter
450
+ .prepare(`UPDATE decisions
451
+ SET record_kind = ?, payload_json = ?, applies_from = ?, applies_until = ?,
452
+ kind = COALESCE(?, kind), status = COALESCE(?, status),
453
+ summary = COALESCE(?, summary), is_static = COALESCE(?, is_static)
454
+ WHERE id = ?`)
455
+ .run(command.recordKind, (0, canonicalize_js_1.canonicalizeJSON)(command.payload ?? {}), command.appliesFrom ?? null, command.appliesUntil ?? null, command.record?.kind ?? null, command.record?.status ?? null,
456
+ // Never leave summary NULL: legacy readers (evolution candidates,
457
+ // recall) assume it is text. The command summary is the authored
458
+ // summary when the record does not carry a distinct one.
459
+ command.record?.summary ?? command.summary, command.record?.isStatic ?? null, recordId);
460
+ for (const [index, scopeId] of boundScopeIdList.entries()) {
461
+ const scope = effectiveScopes[index];
462
+ (0, db_manager_js_1.ensureMemoryScope)(adapter, scope.kind, scope.id);
463
+ adapter
464
+ .prepare('INSERT INTO memory_scope_bindings (memory_id, scope_id, is_primary) VALUES (?, ?, ?)')
465
+ .run(recordId, scopeId, index === 0 ? 1 : 0);
466
+ }
467
+ for (const [index, link] of (command.links ?? []).entries()) {
468
+ edgeIds.push(insertLink(adapter, command, recordId, link, index, access, now));
469
+ }
470
+ for (const replacement of command.replaces ?? []) {
471
+ if (!referenceExists(adapter, { kind: 'memory', id: replacement.id }, admittedScopeIdList)) {
472
+ throw new JudgmentError('REFERENCE_NOT_FOUND', 'A replacement target is unavailable');
473
+ }
474
+ adapter
475
+ .prepare("UPDATE decisions SET superseded_by = ?, status = 'superseded', updated_at = ? WHERE id = ?")
476
+ .run(recordId, domainNow, replacement.id);
477
+ edgeIds.push(insertLink(adapter, { ...command, links: [] }, recordId, {
478
+ relation: 'supersedes',
479
+ target: { kind: 'memory', id: replacement.id },
480
+ attrs: { reason: replacement.reason },
481
+ }, edgeIds.length, access, now));
482
+ }
483
+ for (const amendment of command.amends ?? []) {
484
+ if (!referenceExists(adapter, amendment.target, admittedScopeIdList)) {
485
+ throw new JudgmentError('REFERENCE_NOT_FOUND', 'An amendment target is unavailable');
486
+ }
487
+ applyAmendment(adapter, amendment, domainNow);
488
+ }
489
+ applyProjections(adapter, command, recordId, effectiveScopes, domainNow);
490
+ // A commitment's own clock is domain time, like the record's: `updated_at`
491
+ // is what the board sorts and filters on, so a caller that states when a
492
+ // revision happened must see that time on the row, not the wall clock the
493
+ // write happened to land on.
494
+ if (command.recordKind === 'commitment' && command.work) {
495
+ const work = command.work;
496
+ if (work.operation === 'create') {
497
+ const commitmentId = commitmentIdForCommand(command);
498
+ const imported = work.imported;
499
+ if (imported !== undefined) {
500
+ if (!Number.isSafeInteger(imported.rowId) || imported.rowId <= 0) {
501
+ throw new JudgmentError('INVALID_COMMAND', `imported.rowId must be a positive integer (got: ${imported.rowId})`);
502
+ }
503
+ if (imported.revision !== undefined &&
504
+ (!Number.isSafeInteger(imported.revision) || imported.revision <= 0)) {
505
+ throw new JudgmentError('INVALID_COMMAND', `imported.revision must be a positive integer (got: ${imported.revision})`);
506
+ }
507
+ if (imported.createdAt !== undefined &&
508
+ (!Number.isFinite(imported.createdAt) || imported.createdAt < 0)) {
509
+ throw new JudgmentError('INVALID_COMMAND', `imported.createdAt must be a finite nonnegative epoch (got: ${imported.createdAt})`);
510
+ }
511
+ }
512
+ const revision = imported?.revision ?? 1;
513
+ const createdAt = imported?.createdAt ?? domainNow;
514
+ if (imported !== undefined) {
515
+ // An imported row keeps the task id it already had; sqlite_sequence
516
+ // moves with the explicit insert, so work first stated here still
517
+ // takes the next number after it.
518
+ adapter
519
+ .prepare(`INSERT INTO commitments
520
+ (row_id, commitment_id, current_revision, head_record_id, withdrawn, created_at, updated_at, agent_id, model_run_id)
521
+ VALUES (?, ?, ?, ?, 0, ?, ?, ?, ?)`)
522
+ .run(imported.rowId, commitmentId, revision, recordId, createdAt, domainNow, access.agentId, command.modelRunId ?? null);
523
+ }
524
+ else {
525
+ adapter
526
+ .prepare(`INSERT INTO commitments
527
+ (commitment_id, current_revision, head_record_id, withdrawn, created_at, updated_at, agent_id, model_run_id)
528
+ VALUES (?, ?, ?, 0, ?, ?, ?, ?)`)
529
+ .run(commitmentId, revision, recordId, createdAt, domainNow, access.agentId, command.modelRunId ?? null);
530
+ }
531
+ adapter
532
+ .prepare(`INSERT INTO commitment_assignments
533
+ (commitment_id, revision, record_id, operation, set_json, clear_json, applies_from, applies_until, created_at, agent_id, model_run_id)
534
+ VALUES (?, ?, ?, 'create', ?, ?, ?, ?, ?, ?, ?)`)
535
+ .run(commitmentId, revision, recordId, (0, canonicalize_js_1.canonicalizeJSON)(workPatch(work)), (0, canonicalize_js_1.canonicalizeJSON)(work.clear ?? []), command.eventDatetime ?? null, command.appliesUntil ?? null, domainNow, access.agentId, command.modelRunId ?? null);
536
+ workReceipt = { commitmentId, revision };
537
+ }
538
+ else {
539
+ const current = adapter
540
+ .prepare('SELECT current_revision, withdrawn FROM commitments WHERE commitment_id = ?')
541
+ .get(work.commitmentId);
542
+ if (!current) {
543
+ throw new JudgmentError('REFERENCE_NOT_FOUND', `Commitment is unavailable: ${work.commitmentId}`);
544
+ }
545
+ // Only revise may omit the revision (it appends to the head); withdraw always states it.
546
+ if ((work.expectedRevision !== undefined || work.operation === 'withdraw') &&
547
+ current.current_revision !== work.expectedRevision) {
548
+ throw new JudgmentError('STALE_REVISION', 'Commitment revision is stale');
549
+ }
550
+ if (work.operation === 'revise' && current.withdrawn === 1) {
551
+ throw new JudgmentError('COMMITMENT_WITHDRAWN', 'Withdrawn commitments cannot be revised');
552
+ }
553
+ const revision = current.current_revision + 1;
554
+ adapter
555
+ .prepare(`INSERT INTO commitment_assignments
556
+ (commitment_id, revision, record_id, operation, set_json, clear_json, applies_from, applies_until, created_at, agent_id, model_run_id)
557
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
558
+ .run(work.commitmentId, revision, recordId, work.operation, (0, canonicalize_js_1.canonicalizeJSON)(workPatch(work)), (0, canonicalize_js_1.canonicalizeJSON)(work.clear ?? []), command.eventDatetime ?? null, command.appliesUntil ?? null, domainNow, access.agentId, command.modelRunId ?? null);
559
+ adapter
560
+ .prepare('UPDATE commitments SET current_revision = ?, head_record_id = ?, withdrawn = ?, updated_at = ?, agent_id = ?, model_run_id = ? WHERE commitment_id = ?')
561
+ .run(revision, recordId, work.operation === 'withdraw' ? 1 : 0, domainNow, access.agentId, command.modelRunId ?? null, work.commitmentId);
562
+ workReceipt = { commitmentId: work.commitmentId, revision };
563
+ }
564
+ }
565
+ (0, event_store_js_1.insertMemoryEventInTransaction)(adapter, {
566
+ event_type: command.event?.eventType ?? 'save',
567
+ actor: command.event?.actor ?? `actor:${access.principalId}`,
568
+ source_turn_id: command.event?.sourceTurnId,
569
+ memory_id: recordId,
570
+ topic: command.topic,
571
+ scope_refs: effectiveScopes,
572
+ evidence_refs: command.event?.evidenceRefs,
573
+ reason: command.event?.reason ?? 'agent judgment command',
574
+ created_at: domainNow,
575
+ });
576
+ const receipt = {
577
+ status: 'committed',
578
+ recordId,
579
+ commandId: command.commandId,
580
+ edgeIds,
581
+ watermark: now,
582
+ ...(workReceipt ? { work: workReceipt } : {}),
583
+ diagnostics: [],
584
+ };
585
+ adapter
586
+ .prepare(`INSERT INTO judgment_commands (command_id, record_id, committed_watermark, receipt_json, created_at)
587
+ VALUES (?, ?, ?, ?, ?)`)
588
+ .run(command.commandId, recordId, receipt.watermark, (0, canonicalize_js_1.canonicalizeJSON)(receipt), now);
589
+ });
590
+ if (replayedReceipt) {
591
+ return replayedReceipt;
592
+ }
593
+ // Sync the adapter's status cache for every row whose projection columns were
594
+ // touched: the new record, superseded targets, and amended outcome rows.
595
+ refreshDecisionStatusCaches(adapter, [
596
+ recordId,
597
+ ...(command.replaces ?? []).map((replacement) => replacement.id),
598
+ ...(command.amends ?? []).map((amendment) => amendment.target.id),
599
+ ]);
600
+ return {
601
+ status: 'committed',
602
+ recordId,
603
+ commandId: command.commandId,
604
+ edgeIds,
605
+ watermark: now,
606
+ ...(workReceipt ? { work: workReceipt } : {}),
607
+ diagnostics: [],
608
+ };
609
+ }
610
+ function refreshDecisionStatusCaches(adapter, memoryIds) {
611
+ if (!adapter.refreshDecisionStatusCache)
612
+ return;
613
+ const stmt = adapter.prepare('SELECT rowid FROM decisions WHERE id = ?');
614
+ for (const memoryId of new Set(memoryIds)) {
615
+ const row = stmt.get(memoryId);
616
+ if (row) {
617
+ adapter.refreshDecisionStatusCache(row.rowid);
618
+ }
619
+ }
620
+ }
621
+ async function appendJudgment(command, access, options) {
622
+ // The command boundary no longer reaches the process-global store: the
623
+ // caller names the database this judgment commits to.
624
+ if (!options?.adapter) {
625
+ throw new JudgmentError('INVALID_COMMAND', 'appendJudgment requires an explicit adapter; the process-global store is not a write path');
626
+ }
627
+ return appendJudgmentOnAdapter(options.adapter, command, access, options.embedder);
628
+ }
629
+ function createJudgmentWriter(options) {
630
+ return {
631
+ appendJudgment: (command, access) => appendJudgmentOnAdapter(options.adapter, command, access, options.embedder),
632
+ };
633
+ }
634
+ const EDGE_TYPE_SET = new Set(twin_edge_types_js_1.TWIN_EDGE_TYPES);
635
+ const REF_KIND_SET = new Set(twin_edge_types_js_1.TWIN_REF_KINDS);
636
+ const SOURCE_SET = new Set(twin_edge_types_js_1.TWIN_EDGE_SOURCES);
637
+ function nullableString(value) {
638
+ return typeof value === 'string' && value.length > 0 ? value : null;
639
+ }
640
+ function requireNonEmptyString(value, field) {
641
+ if (typeof value !== 'string' || value.length === 0) {
642
+ throw new Error(`twin_edges.${field} must be a non-empty string`);
643
+ }
644
+ return value;
645
+ }
646
+ function normalizeEdgeType(value) {
647
+ if (typeof value === 'string' && EDGE_TYPE_SET.has(value)) {
648
+ return value;
649
+ }
650
+ throw new Error(`Unsupported twin edge type: ${String(value)}`);
651
+ }
652
+ function normalizeSource(value) {
653
+ if (typeof value === 'string' && SOURCE_SET.has(value)) {
654
+ return value;
655
+ }
656
+ throw new Error(`Unsupported twin edge source: ${String(value)}`);
657
+ }
658
+ function normalizeRef(ref, field) {
659
+ if (!ref || typeof ref !== 'object') {
660
+ throw new Error(`${field} must be a TwinRef`);
661
+ }
662
+ if (!REF_KIND_SET.has(ref.kind)) {
663
+ throw new Error(`${field}.kind is unsupported: ${String(ref.kind)}`);
664
+ }
665
+ return {
666
+ kind: ref.kind,
667
+ id: requireNonEmptyString(ref.id, `${field}.id`),
668
+ };
669
+ }
670
+ function parseJsonField(value, field, edgeId) {
671
+ if (value === null || value === undefined) {
672
+ return null;
673
+ }
674
+ const text = String(value);
675
+ if (text.length === 0) {
676
+ return null;
677
+ }
678
+ try {
679
+ return JSON.parse(text);
680
+ }
681
+ catch (error) {
682
+ const message = error instanceof Error ? error.message : String(error);
683
+ throw new Error(`Invalid twin_edges.${field} for ${edgeId}: ${message}`);
684
+ }
685
+ }
686
+ function toBuffer(value) {
687
+ if (Buffer.isBuffer(value)) {
688
+ return value;
689
+ }
690
+ if (value instanceof Uint8Array) {
691
+ return Buffer.from(value);
692
+ }
693
+ throw new Error('twin_edges.content_hash must be a 32-byte Buffer');
694
+ }
695
+ function mapTwinEdgeRow(row) {
696
+ const edgeId = String(row.edge_id);
697
+ return {
698
+ edge_id: edgeId,
699
+ edge_type: normalizeEdgeType(row.edge_type),
700
+ subject_ref: {
701
+ kind: String(row.subject_kind),
702
+ id: String(row.subject_id),
703
+ },
704
+ object_ref: {
705
+ kind: String(row.object_kind),
706
+ id: String(row.object_id),
707
+ },
708
+ relation_attrs_json: nullableString(row.relation_attrs_json),
709
+ relation_attrs: parseJsonField(row.relation_attrs_json, 'relation_attrs_json', edgeId),
710
+ confidence: Number(row.confidence),
711
+ source: normalizeSource(row.source),
712
+ agent_id: nullableString(row.agent_id),
713
+ model_run_id: nullableString(row.model_run_id),
714
+ envelope_hash: nullableString(row.envelope_hash),
715
+ human_actor_id: nullableString(row.human_actor_id),
716
+ human_actor_role: nullableString(row.human_actor_role),
717
+ authority_scope_json: nullableString(row.authority_scope_json),
718
+ authority_scope: parseJsonField(row.authority_scope_json, 'authority_scope_json', edgeId),
719
+ reason_classification: nullableString(row.reason_classification),
720
+ reason_text: nullableString(row.reason_text),
721
+ evidence_refs_json: nullableString(row.evidence_refs_json),
722
+ evidence_refs: parseJsonField(row.evidence_refs_json, 'evidence_refs_json', edgeId),
723
+ request_idempotency_key: nullableString(row.request_idempotency_key),
724
+ edge_idempotency_key: nullableString(row.edge_idempotency_key),
725
+ content_hash: toBuffer(row.content_hash),
726
+ created_at: Number(row.created_at),
727
+ };
728
+ }
729
+ function jsonColumn(value) {
730
+ if (value === null || value === undefined) {
731
+ return null;
732
+ }
733
+ return typeof value === 'string' ? value : (0, canonicalize_js_1.canonicalizeJSON)(value);
734
+ }
735
+ /**
736
+ * The single write boundary for twin_edges. Both former write sites (the
737
+ * alias_of edge inside the alias write and the judgment-command link insert)
738
+ * now resolve their columns into a TwinEdgeInsert and call here, inside the
739
+ * caller's transaction. The row is read back so callers get the stored record.
740
+ */
741
+ function insertTwinEdge(adapter, input) {
742
+ adapter
743
+ .prepare(`
744
+ INSERT INTO twin_edges (
745
+ edge_id, edge_type, subject_kind, subject_id, object_kind, object_id,
746
+ relation_attrs_json, confidence, source, agent_id, model_run_id, envelope_hash,
747
+ human_actor_id, human_actor_role, authority_scope_json, reason_classification,
748
+ reason_text, evidence_refs_json, request_idempotency_key, edge_idempotency_key,
749
+ content_hash, created_at
750
+ )
751
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
752
+ `)
753
+ .run(input.edge_id, input.edge_type, input.subject_ref.kind, input.subject_ref.id, input.object_ref.kind, input.object_ref.id, jsonColumn(input.relation_attrs), input.confidence ?? 1, input.source, input.agent_id ?? null, input.model_run_id ?? null, input.envelope_hash ?? null, input.human_actor_id ?? null, input.human_actor_role ?? null, jsonColumn(input.authority_scope_json), input.reason_classification ?? null, input.reason_text ?? null, jsonColumn(input.evidence_refs), input.request_idempotency_key ?? null, input.edge_idempotency_key ?? null, input.content_hash, input.created_at);
754
+ const record = getTwinEdge(adapter, input.edge_id);
755
+ if (!record) {
756
+ throw new Error(`Twin edge was not written: ${input.edge_id}`);
757
+ }
758
+ return record;
759
+ }
760
+ function getTwinEdge(adapter, edgeId) {
761
+ const row = adapter.prepare('SELECT * FROM twin_edges WHERE edge_id = ?').get(edgeId);
762
+ return row ? mapTwinEdgeRow(row) : null;
763
+ }
764
+ function listTwinEdgesForRefs(adapter, refs, page) {
765
+ const normalizedRefs = refs.map((ref, index) => normalizeRef(ref, `refs[${index}]`));
766
+ if (normalizedRefs.length === 0) {
767
+ return [];
768
+ }
769
+ const clauses = [];
770
+ const params = [];
771
+ for (const ref of normalizedRefs) {
772
+ clauses.push('(subject_kind = ? AND subject_id = ?)');
773
+ params.push(ref.kind, ref.id);
774
+ clauses.push('(object_kind = ? AND object_id = ?)');
775
+ params.push(ref.kind, ref.id);
776
+ }
777
+ const where = [`(${clauses.join(' OR ')})`];
778
+ if (page?.edgeTypes?.length) {
779
+ where.push(`edge_type IN (${page.edgeTypes.map(() => '?').join(', ')})`);
780
+ params.push(...page.edgeTypes);
781
+ }
782
+ if (typeof page?.startMs === 'number') {
783
+ where.push('created_at >= ?');
784
+ params.push(page.startMs);
785
+ }
786
+ if (typeof page?.asOfMs === 'number') {
787
+ where.push('created_at <= ?');
788
+ params.push(page.asOfMs);
789
+ }
790
+ if (page?.after) {
791
+ where.push('(created_at < ? OR (created_at = ? AND edge_id > ?))');
792
+ params.push(page.after.createdAt, page.after.createdAt, page.after.edgeId);
793
+ }
794
+ if (page && (!Number.isSafeInteger(page.limit) || page.limit < 1)) {
795
+ throw new Error('Twin edge scan limit must be a positive safe integer');
796
+ }
797
+ const rows = adapter
798
+ .prepare(`
799
+ SELECT *
800
+ FROM twin_edges
801
+ WHERE ${where.join(' AND ')}
802
+ ORDER BY created_at ${page ? 'DESC' : 'ASC'}, edge_id ASC
803
+ ${page ? 'LIMIT ?' : ''}
804
+ `)
805
+ .all(...params, ...(page ? [page.limit] : []));
806
+ return rows.map(mapTwinEdgeRow);
807
+ }
808
+ //# sourceMappingURL=judgments.js.map