@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,160 @@
1
+ /**
2
+ * Knowledge observations: immutable observation versions and the visibility
3
+ * rule that decides which of them a caller may read. A cited observation must
4
+ * satisfy the same rule as a row the reader would have returned.
5
+ *
6
+ * @module knowledge/observations
7
+ */
8
+ import type { DatabaseAdapter } from '../db-manager.js';
9
+ import type { MemoryScopeRef } from '../memory/types.js';
10
+ type ObservationAdapter = Pick<DatabaseAdapter, 'prepare'>;
11
+ type ObservationVisibilityAdapter = Pick<DatabaseAdapter, 'prepare'>;
12
+ export interface ObservationBodyLocation {
13
+ kind: 'raw';
14
+ connectorName: string;
15
+ revisionSourceId: string;
16
+ }
17
+ export interface ObservationVersionInput {
18
+ /**
19
+ * Where this came from -- a namespace, paired with `sourceId` inside it. It was
20
+ * `sourceConnector`; a deployment with no connectors still observes things.
21
+ */
22
+ source: string;
23
+ sourceId: string;
24
+ producerVersionId?: string | null;
25
+ body?: string;
26
+ bodyLocation?: ObservationBodyLocation;
27
+ author?: string | null;
28
+ sourceAt?: number | null;
29
+ observedAt: number;
30
+ contentHash: string;
31
+ metadata?: Record<string, unknown> | null;
32
+ /**
33
+ * What was observed, beyond its body. These lived only on the connector index,
34
+ * which is why a core read had to go there to answer what a memory rests on.
35
+ */
36
+ sourceType?: string | null;
37
+ sourceLocator?: string | null;
38
+ title?: string | null;
39
+ artifactLocator?: string | null;
40
+ artifactTitle?: string | null;
41
+ eventDate?: string | null;
42
+ sourceEntityId?: string | null;
43
+ /**
44
+ * Who may see it. Columns rather than a JSON blob because raw visibility is a
45
+ * WHERE clause as well as a predicate, and the two are pinned against each other.
46
+ */
47
+ channel?: string | null;
48
+ projectId?: string | null;
49
+ tenantId?: string | null;
50
+ memoryScopeKind?: string | null;
51
+ memoryScopeId?: string | null;
52
+ scope?: Record<string, unknown> | null;
53
+ }
54
+ export interface ObservationVersionRecord {
55
+ observationId: string;
56
+ source: string;
57
+ sourceId: string;
58
+ producerVersionId: string | null;
59
+ body: string | null;
60
+ bodyLocation: ObservationBodyLocation | null;
61
+ author: string | null;
62
+ sourceAt: number | null;
63
+ observedAt: number;
64
+ contentHash: string;
65
+ metadata: Record<string, unknown>;
66
+ sourceType: string | null;
67
+ sourceLocator: string | null;
68
+ title: string | null;
69
+ artifactLocator: string | null;
70
+ artifactTitle: string | null;
71
+ eventDate: string | null;
72
+ sourceEntityId: string | null;
73
+ channel: string | null;
74
+ projectId: string | null;
75
+ tenantId: string | null;
76
+ memoryScopeKind: string | null;
77
+ memoryScopeId: string | null;
78
+ scope: Record<string, unknown>;
79
+ }
80
+ export interface OwnerObservationSearchItem {
81
+ observationRef: string;
82
+ source: string;
83
+ sourceId: string;
84
+ author: string | null;
85
+ sourceAt: number | null;
86
+ observedAt: number;
87
+ contentPreview: string;
88
+ metadataPreview: string;
89
+ }
90
+ export interface ObservationBodyReader {
91
+ readVersion(input: ObservationBodyLocation & {
92
+ expectedContentHash: string;
93
+ }): {
94
+ status: 'available';
95
+ body: string;
96
+ contentHash: string;
97
+ } | {
98
+ status: 'version_unavailable';
99
+ reason: 'VERSION_NOT_FOUND' | 'HASH_MISMATCH';
100
+ };
101
+ }
102
+ export type ObservationReadResult = {
103
+ status: 'available';
104
+ observation: ObservationVersionRecord;
105
+ body: string;
106
+ } | {
107
+ status: 'version_unavailable';
108
+ observation: ObservationVersionRecord;
109
+ reason: 'BODY_READER_UNAVAILABLE' | 'VERSION_NOT_FOUND' | 'HASH_MISMATCH';
110
+ } | {
111
+ status: 'not_found';
112
+ };
113
+ export declare function observationVersionId(input: ObservationVersionInput): string;
114
+ export declare function appendObservationVersion(adapter: ObservationAdapter, input: ObservationVersionInput): ObservationVersionRecord;
115
+ /**
116
+ * The observation's source, without reading the rest of it.
117
+ *
118
+ * A caller deciding whether a row is an owner observation needs one column. It
119
+ * asks for one: mapping the whole record would parse a body and a metadata
120
+ * blob it does not use, and a row whose metadata is malformed would then fail
121
+ * loudly where the answer is simply "not yours" — the disclosure the visibility
122
+ * check exists to avoid.
123
+ */
124
+ export declare function getObservationVersionSource(adapter: ObservationAdapter, observationId: string): string | null;
125
+ export declare function getObservationVersion(adapter: ObservationAdapter, observationId: string): ObservationVersionRecord | null;
126
+ export declare function searchOwnerObservationVersions(adapter: ObservationAdapter, input: {
127
+ query: string;
128
+ principalId: string;
129
+ agentId: string;
130
+ connectors?: string[];
131
+ connectorChannels?: Readonly<Record<string, readonly string[]>>;
132
+ fromMs?: number;
133
+ toMs?: number;
134
+ cursor?: string;
135
+ limit?: number;
136
+ }): {
137
+ items: OwnerObservationSearchItem[];
138
+ nextCursor: string | null;
139
+ };
140
+ export declare function readObservationVersion(adapter: ObservationAdapter, observationId: string, reader?: ObservationBodyReader, options?: {
141
+ maxSourceMs?: number | null;
142
+ }): ObservationReadResult;
143
+ export interface ObservationVisibilityAuthority {
144
+ principalId?: string;
145
+ agentId?: string;
146
+ scopes?: readonly MemoryScopeRef[];
147
+ connectors?: readonly string[];
148
+ connectorWideRead?: readonly string[];
149
+ channels?: Readonly<Record<string, readonly string[]>>;
150
+ maxSourceMs?: number | null;
151
+ }
152
+ export interface ObservationVisibilityRow {
153
+ source: unknown;
154
+ scope_json: unknown;
155
+ source_at?: unknown;
156
+ }
157
+ export declare function isObservationVersionVisible(adapter: ObservationVisibilityAdapter, observationId: string, authority: ObservationVisibilityAuthority): boolean;
158
+ export declare function isObservationVisibilityRowVisible(row: ObservationVisibilityRow, authority: ObservationVisibilityAuthority): boolean;
159
+ export {};
160
+ //# sourceMappingURL=observations.d.ts.map
@@ -0,0 +1,449 @@
1
+ "use strict";
2
+ /**
3
+ * Knowledge observations: immutable observation versions and the visibility
4
+ * rule that decides which of them a caller may read. A cited observation must
5
+ * satisfy the same rule as a row the reader would have returned.
6
+ *
7
+ * @module knowledge/observations
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.observationVersionId = observationVersionId;
11
+ exports.appendObservationVersion = appendObservationVersion;
12
+ exports.getObservationVersionSource = getObservationVersionSource;
13
+ exports.getObservationVersion = getObservationVersion;
14
+ exports.searchOwnerObservationVersions = searchOwnerObservationVersions;
15
+ exports.readObservationVersion = readObservationVersion;
16
+ exports.isObservationVersionVisible = isObservationVersionVisible;
17
+ exports.isObservationVisibilityRowVisible = isObservationVisibilityRowVisible;
18
+ const node_crypto_1 = require("node:crypto");
19
+ const canonicalize_js_1 = require("../canonicalize.js");
20
+ function parseObject(value, field) {
21
+ if (typeof value !== 'string') {
22
+ throw new Error(`observation_versions.${field} must be JSON text`);
23
+ }
24
+ let parsed;
25
+ try {
26
+ parsed = JSON.parse(value);
27
+ }
28
+ catch (error) {
29
+ const message = error instanceof Error ? error.message : String(error);
30
+ throw new Error(`observation_versions.${field} is malformed JSON: ${message}`);
31
+ }
32
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
33
+ throw new Error(`observation_versions.${field} must contain a JSON object`);
34
+ }
35
+ return parsed;
36
+ }
37
+ function nullableString(value, field) {
38
+ if (value === null) {
39
+ return null;
40
+ }
41
+ if (typeof value !== 'string') {
42
+ throw new Error(`observation_versions.${field} must be text or null`);
43
+ }
44
+ return value;
45
+ }
46
+ function finiteNumber(value, field, nullable = false) {
47
+ if (nullable && value === null) {
48
+ return null;
49
+ }
50
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
51
+ throw new Error(`observation_versions.${field} must be a finite number${nullable ? ' or null' : ''}`);
52
+ }
53
+ return value;
54
+ }
55
+ function mapRow(row) {
56
+ if (typeof row.observation_id !== 'string' || row.observation_id.trim() === '') {
57
+ throw new Error('observation_versions.observation_id must be nonblank text');
58
+ }
59
+ if (typeof row.source !== 'string' || row.source.trim() === '') {
60
+ throw new Error('observation_versions.source must be nonblank text');
61
+ }
62
+ if (typeof row.source_id !== 'string' || row.source_id.trim() === '') {
63
+ throw new Error('observation_versions.source_id must be nonblank text');
64
+ }
65
+ if (typeof row.content_hash !== 'string' || row.content_hash.trim() === '') {
66
+ throw new Error('observation_versions.content_hash must be nonblank text');
67
+ }
68
+ const body = nullableString(row.body, 'body');
69
+ const bodyLocationJson = nullableString(row.body_location_json, 'body_location_json');
70
+ if ((body === null) === (bodyLocationJson === null)) {
71
+ throw new Error('observation_versions row requires exactly one body source');
72
+ }
73
+ const bodyLocation = bodyLocationJson
74
+ ? parseObject(bodyLocationJson, 'body_location_json')
75
+ : null;
76
+ if (bodyLocation &&
77
+ (bodyLocation.kind !== 'raw' ||
78
+ typeof bodyLocation.connectorName !== 'string' ||
79
+ typeof bodyLocation.revisionSourceId !== 'string' ||
80
+ bodyLocation.connectorName.trim() === '' ||
81
+ bodyLocation.revisionSourceId.trim() === '')) {
82
+ throw new Error('observation_versions.body_location_json has an invalid raw locator');
83
+ }
84
+ return {
85
+ observationId: row.observation_id,
86
+ source: row.source,
87
+ sourceId: row.source_id,
88
+ producerVersionId: nullableString(row.producer_version_id, 'producer_version_id'),
89
+ body,
90
+ bodyLocation: bodyLocation,
91
+ author: nullableString(row.author, 'author'),
92
+ sourceAt: finiteNumber(row.source_at, 'source_at', true),
93
+ observedAt: finiteNumber(row.observed_at, 'observed_at'),
94
+ contentHash: row.content_hash,
95
+ metadata: parseObject(row.metadata_json, 'metadata_json'),
96
+ sourceType: nullableString(row.source_type, 'source_type'),
97
+ sourceLocator: nullableString(row.source_locator, 'source_locator'),
98
+ title: nullableString(row.title, 'title'),
99
+ artifactLocator: nullableString(row.artifact_locator, 'artifact_locator'),
100
+ artifactTitle: nullableString(row.artifact_title, 'artifact_title'),
101
+ eventDate: nullableString(row.event_date, 'event_date'),
102
+ sourceEntityId: nullableString(row.source_entity_id, 'source_entity_id'),
103
+ channel: nullableString(row.channel, 'channel'),
104
+ projectId: nullableString(row.project_id, 'project_id'),
105
+ tenantId: nullableString(row.tenant_id, 'tenant_id'),
106
+ memoryScopeKind: nullableString(row.memory_scope_kind, 'memory_scope_kind'),
107
+ memoryScopeId: nullableString(row.memory_scope_id, 'memory_scope_id'),
108
+ scope: parseObject(row.scope_json, 'scope_json'),
109
+ };
110
+ }
111
+ function sameObservation(actual, input, scope) {
112
+ return (actual.source === input.source &&
113
+ actual.sourceId === input.sourceId &&
114
+ actual.producerVersionId === (input.producerVersionId ?? null) &&
115
+ actual.body === (input.body ?? null) &&
116
+ (0, canonicalize_js_1.canonicalizeJSON)(actual.bodyLocation) === (0, canonicalize_js_1.canonicalizeJSON)(input.bodyLocation ?? null) &&
117
+ actual.author === (input.author ?? null) &&
118
+ actual.sourceAt === (input.sourceAt ?? null) &&
119
+ actual.observedAt === Math.floor(input.observedAt) &&
120
+ actual.contentHash === input.contentHash &&
121
+ (0, canonicalize_js_1.canonicalizeJSON)(actual.metadata) === (0, canonicalize_js_1.canonicalizeJSON)(input.metadata ?? {}) &&
122
+ (0, canonicalize_js_1.canonicalizeJSON)(actual.scope) === (0, canonicalize_js_1.canonicalizeJSON)(scope));
123
+ }
124
+ function observationVersionId(input) {
125
+ const identity = (0, canonicalize_js_1.canonicalizeJSON)({
126
+ source: input.source,
127
+ sourceId: input.sourceId,
128
+ producerVersionId: input.producerVersionId ?? null,
129
+ ...(input.producerVersionId === null || input.producerVersionId === undefined
130
+ ? { contentHash: input.contentHash }
131
+ : {}),
132
+ });
133
+ return `obs_${(0, node_crypto_1.createHash)('sha256').update(identity).digest('hex').slice(0, 24)}`;
134
+ }
135
+ function appendObservationVersion(adapter, input) {
136
+ if ((input.body === undefined) === (input.bodyLocation === undefined)) {
137
+ throw new Error('Observation requires exactly one of body or bodyLocation.');
138
+ }
139
+ if (!Number.isFinite(input.observedAt)) {
140
+ throw new Error('Observation observedAt must be finite.');
141
+ }
142
+ for (const [field, value] of [
143
+ ['source', input.source],
144
+ ['sourceId', input.sourceId],
145
+ ['contentHash', input.contentHash],
146
+ ]) {
147
+ if (!value.trim()) {
148
+ throw new Error(`Observation ${field} must be nonblank.`);
149
+ }
150
+ }
151
+ // The scope bag and the scope columns are one fact in two shapes: the bag is what
152
+ // was stated, the columns are what a WHERE clause can reach. Deriving the columns
153
+ // here, once, is what keeps them from disagreeing -- a caller that fills only one
154
+ // of the two is how they drift apart.
155
+ const scope = input.scope ?? {};
156
+ const fromScope = (key, explicit) => {
157
+ if (explicit !== undefined) {
158
+ return explicit;
159
+ }
160
+ const value = scope[key];
161
+ return typeof value === 'string' && value !== '' ? value : null;
162
+ };
163
+ const channel = fromScope('channel', input.channel);
164
+ const projectId = fromScope('projectId', input.projectId);
165
+ const tenantId = fromScope('tenantId', input.tenantId);
166
+ const memoryScopeKind = fromScope('memoryScopeKind', input.memoryScopeKind);
167
+ const memoryScopeId = fromScope('memoryScopeId', input.memoryScopeId);
168
+ // And the other direction. A caller that named the scope as columns has said the
169
+ // same thing as one that named it in the bag, and the two readers -- a WHERE
170
+ // clause over the columns, a predicate over the bag -- must not disagree about it.
171
+ const scopeBag = { ...scope };
172
+ for (const [key, value] of [
173
+ ['channel', channel],
174
+ ['projectId', projectId],
175
+ ['tenantId', tenantId],
176
+ ['memoryScopeKind', memoryScopeKind],
177
+ ['memoryScopeId', memoryScopeId],
178
+ ]) {
179
+ if (value !== null && scopeBag[key] === undefined) {
180
+ scopeBag[key] = value;
181
+ }
182
+ }
183
+ const id = observationVersionId(input);
184
+ adapter
185
+ .prepare(`INSERT OR IGNORE INTO observation_versions (
186
+ observation_id, source, source_id, producer_version_id, body,
187
+ body_location_json, author, source_at, observed_at, content_hash, metadata_json, scope_json,
188
+ source_type, source_locator, title, artifact_locator, artifact_title, event_date,
189
+ source_entity_id, channel, project_id, tenant_id, memory_scope_kind, memory_scope_id
190
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
191
+ .run(id, input.source, input.sourceId, input.producerVersionId ?? null, input.body ?? null, input.bodyLocation ? (0, canonicalize_js_1.canonicalizeJSON)(input.bodyLocation) : null, input.author ?? null, input.sourceAt ?? null, Math.floor(input.observedAt), input.contentHash, (0, canonicalize_js_1.canonicalizeJSON)(input.metadata ?? {}), (0, canonicalize_js_1.canonicalizeJSON)(scopeBag), input.sourceType ?? null, input.sourceLocator ?? null, input.title ?? null, input.artifactLocator ?? null, input.artifactTitle ?? null, input.eventDate ?? null, input.sourceEntityId ?? null, channel, projectId, tenantId, memoryScopeKind, memoryScopeId);
192
+ const record = getObservationVersion(adapter, id);
193
+ if (!record) {
194
+ throw new Error(`Failed to persist observation ${id}`);
195
+ }
196
+ if (!sameObservation(record, input, scopeBag)) {
197
+ throw new Error(`Observation replay conflict for ${id}`);
198
+ }
199
+ return record;
200
+ }
201
+ /**
202
+ * The observation's source, without reading the rest of it.
203
+ *
204
+ * A caller deciding whether a row is an owner observation needs one column. It
205
+ * asks for one: mapping the whole record would parse a body and a metadata
206
+ * blob it does not use, and a row whose metadata is malformed would then fail
207
+ * loudly where the answer is simply "not yours" — the disclosure the visibility
208
+ * check exists to avoid.
209
+ */
210
+ function getObservationVersionSource(adapter, observationId) {
211
+ const row = adapter
212
+ .prepare('SELECT source FROM observation_versions WHERE observation_id = ? LIMIT 1')
213
+ .get(observationId);
214
+ if (!row) {
215
+ return null;
216
+ }
217
+ if (typeof row.source !== 'string' || row.source.trim() === '') {
218
+ throw new Error('observation_versions.source must be nonblank text');
219
+ }
220
+ return row.source;
221
+ }
222
+ function getObservationVersion(adapter, observationId) {
223
+ const row = adapter
224
+ .prepare('SELECT * FROM observation_versions WHERE observation_id = ? LIMIT 1')
225
+ .get(observationId);
226
+ return row ? mapRow(row) : null;
227
+ }
228
+ function searchOwnerObservationVersions(adapter, input) {
229
+ const query = input.query.trim();
230
+ const limit = Math.min(100, Math.max(1, Math.floor(input.limit ?? 25)));
231
+ const clauses = [
232
+ "(source LIKE 'owner-message:%' OR source LIKE 'owner-result:%')",
233
+ "json_extract(scope_json, '$.visibility') = 'owner'",
234
+ "json_extract(scope_json, '$.principalId') = ?",
235
+ "json_extract(scope_json, '$.agentId') = ?",
236
+ ];
237
+ const params = [input.principalId, input.agentId];
238
+ const connectors = [...new Set((input.connectors ?? []).map((value) => value.trim()))].filter(Boolean);
239
+ if (input.connectors !== undefined && connectors.length === 0) {
240
+ return { items: [], nextCursor: null };
241
+ }
242
+ if (connectors.length > 0) {
243
+ clauses.push(`source IN (${connectors.map(() => '?').join(', ')})`);
244
+ params.push(...connectors);
245
+ }
246
+ if (input.connectorChannels !== undefined) {
247
+ const pairs = Object.entries(input.connectorChannels)
248
+ .map(([connector, values]) => [
249
+ connector.trim(),
250
+ [...new Set(values.map((value) => value.trim()).filter(Boolean))],
251
+ ])
252
+ .filter(([connector, values]) => connector.length > 0 && values.length > 0);
253
+ if (pairs.length === 0) {
254
+ return { items: [], nextCursor: null };
255
+ }
256
+ clauses.push(`(${pairs
257
+ .map(([, values]) => `(source IN (?, ?) AND channel IN (${values.map(() => '?').join(', ')}))`)
258
+ .join(' OR ')})`);
259
+ for (const [connector, values] of pairs) {
260
+ params.push(`owner-message:${connector}`, `owner-result:${connector}`, ...values);
261
+ }
262
+ }
263
+ if (input.fromMs !== undefined) {
264
+ clauses.push('observed_at >= ?');
265
+ params.push(input.fromMs);
266
+ }
267
+ if (input.toMs !== undefined) {
268
+ clauses.push('observed_at <= ?');
269
+ params.push(input.toMs);
270
+ }
271
+ if (query) {
272
+ clauses.push('(instr(body, ?) > 0 OR instr(metadata_json, ?) > 0)');
273
+ params.push(query, query);
274
+ }
275
+ if (input.cursor) {
276
+ let cursor;
277
+ try {
278
+ cursor = JSON.parse(Buffer.from(input.cursor, 'base64url').toString('utf8'));
279
+ }
280
+ catch {
281
+ throw new Error('Invalid owner observation cursor.');
282
+ }
283
+ if (!cursor ||
284
+ typeof cursor !== 'object' ||
285
+ Array.isArray(cursor) ||
286
+ !Number.isFinite(cursor.observedAt) ||
287
+ typeof cursor.observationId !== 'string' ||
288
+ !cursor.observationId) {
289
+ throw new Error('Invalid owner observation cursor.');
290
+ }
291
+ clauses.push('(observed_at < ? OR (observed_at = ? AND observation_id > ?))');
292
+ params.push(cursor.observedAt, cursor.observedAt, cursor.observationId);
293
+ }
294
+ const rows = adapter
295
+ .prepare(`SELECT * FROM observation_versions
296
+ WHERE ${clauses.join(' AND ')}
297
+ ORDER BY observed_at DESC, observation_id ASC
298
+ LIMIT ?`)
299
+ .all(...params, limit + 1);
300
+ const pageRows = rows.slice(0, limit);
301
+ const last = rows.length > limit ? pageRows[pageRows.length - 1] : undefined;
302
+ return {
303
+ items: pageRows.map((row) => {
304
+ const record = mapRow(row);
305
+ const compact = (record.body ?? '').replace(/\s+/g, ' ').trim();
306
+ return {
307
+ observationRef: record.observationId,
308
+ source: record.source,
309
+ sourceId: record.sourceId,
310
+ author: record.author,
311
+ sourceAt: record.sourceAt,
312
+ observedAt: record.observedAt,
313
+ contentPreview: compact.length > 500 ? `${compact.slice(0, 497)}...` : compact,
314
+ metadataPreview: (0, canonicalize_js_1.canonicalizeJSON)(record.metadata).slice(0, 500),
315
+ };
316
+ }),
317
+ nextCursor: last
318
+ ? Buffer.from(JSON.stringify({
319
+ observedAt: Number(last.observed_at),
320
+ observationId: String(last.observation_id),
321
+ })).toString('base64url')
322
+ : null,
323
+ };
324
+ }
325
+ function readObservationVersion(adapter, observationId, reader, options) {
326
+ const observation = getObservationVersion(adapter, observationId);
327
+ if (!observation) {
328
+ return { status: 'not_found' };
329
+ }
330
+ const maxSourceMs = options?.maxSourceMs ?? null;
331
+ if (maxSourceMs !== null) {
332
+ if (!Number.isSafeInteger(maxSourceMs) || maxSourceMs < 0) {
333
+ throw new Error('Observation source ceiling must be a nonnegative epoch millisecond integer');
334
+ }
335
+ if (observation.sourceAt === null || observation.sourceAt > maxSourceMs) {
336
+ return { status: 'not_found' };
337
+ }
338
+ }
339
+ if (observation.body !== null) {
340
+ return { status: 'available', observation, body: observation.body };
341
+ }
342
+ if (!reader || !observation.bodyLocation) {
343
+ return { status: 'version_unavailable', observation, reason: 'BODY_READER_UNAVAILABLE' };
344
+ }
345
+ const result = reader.readVersion({
346
+ ...observation.bodyLocation,
347
+ expectedContentHash: observation.contentHash,
348
+ });
349
+ if (result.status === 'available') {
350
+ return result.contentHash === observation.contentHash
351
+ ? { status: 'available', observation, body: result.body }
352
+ : { status: 'version_unavailable', observation, reason: 'HASH_MISMATCH' };
353
+ }
354
+ return { status: 'version_unavailable', observation, reason: result.reason };
355
+ }
356
+ function parseScopeJson(value) {
357
+ if (typeof value !== 'string') {
358
+ throw new Error('observation_versions.scope_json must be JSON text');
359
+ }
360
+ let parsed;
361
+ try {
362
+ parsed = JSON.parse(value);
363
+ }
364
+ catch (error) {
365
+ const message = error instanceof Error ? error.message : String(error);
366
+ throw new Error(`observation_versions.scope_json is malformed JSON: ${message}`);
367
+ }
368
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
369
+ throw new Error('observation_versions.scope_json must contain a JSON object');
370
+ }
371
+ return parsed;
372
+ }
373
+ function matchesScope(scope, visible) {
374
+ if (visible.kind === 'project') {
375
+ return (scope.projectId === visible.id ||
376
+ (scope.memoryScopeKind === 'project' && scope.memoryScopeId === visible.id));
377
+ }
378
+ if (visible.kind === 'channel') {
379
+ return (scope.channel === visible.id ||
380
+ (scope.memoryScopeKind === 'channel' && scope.memoryScopeId === visible.id));
381
+ }
382
+ return scope.memoryScopeKind === visible.kind && scope.memoryScopeId === visible.id;
383
+ }
384
+ function isObservationVersionVisible(adapter, observationId, authority) {
385
+ const row = adapter
386
+ .prepare(`SELECT scope_json, source, source_at
387
+ FROM observation_versions WHERE observation_id = ?`)
388
+ .get(observationId);
389
+ if (!row) {
390
+ return false;
391
+ }
392
+ return isObservationVisibilityRowVisible(row, authority);
393
+ }
394
+ function isObservationVisibilityRowVisible(row, authority) {
395
+ const principalId = authority.principalId?.trim();
396
+ const agentId = authority.agentId?.trim();
397
+ if (!principalId || !agentId) {
398
+ return false;
399
+ }
400
+ const maxSourceMs = authority.maxSourceMs ?? null;
401
+ if (maxSourceMs !== null) {
402
+ if (!Number.isSafeInteger(maxSourceMs) || maxSourceMs < 0) {
403
+ throw new Error('Observation source ceiling must be a nonnegative epoch millisecond integer');
404
+ }
405
+ const sourceAt = typeof row.source_at === 'number' ? row.source_at : null;
406
+ if (sourceAt === null || sourceAt > maxSourceMs)
407
+ return false;
408
+ }
409
+ if (typeof row.source !== 'string' || !row.source.trim()) {
410
+ throw new Error('observation_versions.source must be nonblank text');
411
+ }
412
+ const ownerConnector = /^owner-(?:message|result):(.+)$/.exec(row.source)?.[1];
413
+ const connector = ownerConnector ?? row.source;
414
+ if (!authority.connectors?.includes(connector)) {
415
+ return false;
416
+ }
417
+ const scope = parseScopeJson(row.scope_json);
418
+ const connectorWideRead = authority.connectorWideRead?.includes(connector) === true;
419
+ if (authority.channels && !connectorWideRead) {
420
+ const visibleChannels = authority.channels[connector];
421
+ const channel = typeof scope.channel === 'string' ? scope.channel : null;
422
+ const normalizedChannel = channel?.startsWith(`${connector}:`)
423
+ ? channel.slice(connector.length + 1)
424
+ : channel;
425
+ if (!visibleChannels ||
426
+ channel === null ||
427
+ (!visibleChannels.includes(channel) &&
428
+ (normalizedChannel === null || !visibleChannels.includes(normalizedChannel)))) {
429
+ return false;
430
+ }
431
+ }
432
+ if (ownerConnector !== undefined || scope.visibility === 'owner') {
433
+ if (ownerConnector === undefined || scope.visibility !== 'owner') {
434
+ throw new Error('owner observation connector and scope visibility are inconsistent');
435
+ }
436
+ return scope.principalId === principalId && scope.agentId === agentId;
437
+ }
438
+ // A current connector/channel grant is the same read authority source.read
439
+ // applies. Imported originals can have no project or memory-scope tag; that
440
+ // absence does not revoke an explicitly granted source read in the graph.
441
+ if (connectorWideRead || authority.channels) {
442
+ return true;
443
+ }
444
+ if (!authority.scopes || authority.scopes.length === 0) {
445
+ return false;
446
+ }
447
+ return authority.scopes.some((visible) => matchesScope(scope, visible));
448
+ }
449
+ //# sourceMappingURL=observations.js.map
@@ -0,0 +1,34 @@
1
+ /**
2
+ * What a search query is asking for.
3
+ *
4
+ * The classifier is a mechanism: an ordered list of patterns, first match wins. The
5
+ * WORDS are not. Two of them named connectors that one deployment happens to run,
6
+ * which told every other product installing this core that asking about that
7
+ * connector is asking about an artifact, whether or not it has one.
8
+ *
9
+ * So the words are injectable. The default below carries only words that name no
10
+ * product: what a file is, what a date is, what asking "why" looks like. A caller
11
+ * with its own domain adds to it.
12
+ */
13
+ export type QuestionType = 'correction' | 'artifact' | 'timeline' | 'status' | 'decision_reason' | 'how_to' | 'unknown';
14
+ export declare const QUESTION_TYPES: readonly QuestionType[];
15
+ /** One question type and the pattern that names it. Order is the precedence. */
16
+ export interface QuestionPattern {
17
+ readonly type: Exclude<QuestionType, 'unknown'>;
18
+ readonly pattern: RegExp;
19
+ }
20
+ export type QuestionVocabulary = readonly QuestionPattern[];
21
+ export declare const DEFAULT_QUESTION_VOCABULARY: QuestionVocabulary;
22
+ /**
23
+ * Declare the words this deployment asks questions with. Call once, at boot.
24
+ *
25
+ * Replaces the default outright rather than merging: a caller that wants the general
26
+ * words plus its own spreads DEFAULT_QUESTION_VOCABULARY itself, and can see in one
27
+ * place what its classifier actually knows.
28
+ */
29
+ export declare function declareQuestionVocabulary(next: QuestionVocabulary): void;
30
+ /** Restore the default. For tests that declared their own. */
31
+ export declare function resetQuestionVocabulary(): void;
32
+ export declare function classifyQuestionType(query: string): QuestionType;
33
+ export declare function isQuestionType(value: string): value is QuestionType;
34
+ //# sourceMappingURL=question-type.d.ts.map