@oscharko-dev/keiko-local-knowledge 0.2.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 (290) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/bounded-document-extraction.d.ts +27 -0
  3. package/dist/bounded-document-extraction.d.ts.map +1 -0
  4. package/dist/bounded-document-extraction.js +214 -0
  5. package/dist/capsule-lifecycle.d.ts +33 -0
  6. package/dist/capsule-lifecycle.d.ts.map +1 -0
  7. package/dist/capsule-lifecycle.js +292 -0
  8. package/dist/capsule-set-lifecycle.d.ts +15 -0
  9. package/dist/capsule-set-lifecycle.d.ts.map +1 -0
  10. package/dist/capsule-set-lifecycle.js +158 -0
  11. package/dist/chunking/chunker-persist.d.ts +36 -0
  12. package/dist/chunking/chunker-persist.d.ts.map +1 -0
  13. package/dist/chunking/chunker-persist.js +74 -0
  14. package/dist/chunking/chunker-runner.d.ts +9 -0
  15. package/dist/chunking/chunker-runner.d.ts.map +1 -0
  16. package/dist/chunking/chunker-runner.js +218 -0
  17. package/dist/chunking/chunker.d.ts +7 -0
  18. package/dist/chunking/chunker.d.ts.map +1 -0
  19. package/dist/chunking/chunker.js +139 -0
  20. package/dist/chunking/citation-mapper.d.ts +4 -0
  21. package/dist/chunking/citation-mapper.d.ts.map +1 -0
  22. package/dist/chunking/citation-mapper.js +180 -0
  23. package/dist/chunking/index.d.ts +6 -0
  24. package/dist/chunking/index.d.ts.map +1 -0
  25. package/dist/chunking/index.js +8 -0
  26. package/dist/chunking/token-estimator.d.ts +3 -0
  27. package/dist/chunking/token-estimator.d.ts.map +1 -0
  28. package/dist/chunking/token-estimator.js +26 -0
  29. package/dist/chunking/types.d.ts +49 -0
  30. package/dist/chunking/types.d.ts.map +1 -0
  31. package/dist/chunking/types.js +26 -0
  32. package/dist/composition.d.ts +57 -0
  33. package/dist/composition.d.ts.map +1 -0
  34. package/dist/composition.js +310 -0
  35. package/dist/conversation/citation-attacher.d.ts +8 -0
  36. package/dist/conversation/citation-attacher.d.ts.map +1 -0
  37. package/dist/conversation/citation-attacher.js +55 -0
  38. package/dist/conversation/citation-excerpts.d.ts +4 -0
  39. package/dist/conversation/citation-excerpts.d.ts.map +1 -0
  40. package/dist/conversation/citation-excerpts.js +41 -0
  41. package/dist/conversation/grounded-answer-runner.d.ts +9 -0
  42. package/dist/conversation/grounded-answer-runner.d.ts.map +1 -0
  43. package/dist/conversation/grounded-answer-runner.js +61 -0
  44. package/dist/conversation/index.d.ts +5 -0
  45. package/dist/conversation/index.d.ts.map +1 -0
  46. package/dist/conversation/index.js +7 -0
  47. package/dist/conversation/model-gateway-answer-generator.d.ts +28 -0
  48. package/dist/conversation/model-gateway-answer-generator.d.ts.map +1 -0
  49. package/dist/conversation/model-gateway-answer-generator.js +105 -0
  50. package/dist/conversation/types.d.ts +35 -0
  51. package/dist/conversation/types.d.ts.map +1 -0
  52. package/dist/conversation/types.js +24 -0
  53. package/dist/discovery/discovery-runner.d.ts +23 -0
  54. package/dist/discovery/discovery-runner.d.ts.map +1 -0
  55. package/dist/discovery/discovery-runner.js +109 -0
  56. package/dist/discovery/extract-progressive.d.ts +17 -0
  57. package/dist/discovery/extract-progressive.d.ts.map +1 -0
  58. package/dist/discovery/extract-progressive.js +522 -0
  59. package/dist/discovery/extract.d.ts +26 -0
  60. package/dist/discovery/extract.d.ts.map +1 -0
  61. package/dist/discovery/extract.js +906 -0
  62. package/dist/discovery/glob.d.ts +10 -0
  63. package/dist/discovery/glob.d.ts.map +1 -0
  64. package/dist/discovery/glob.js +72 -0
  65. package/dist/discovery/index.d.ts +6 -0
  66. package/dist/discovery/index.d.ts.map +1 -0
  67. package/dist/discovery/index.js +8 -0
  68. package/dist/discovery/media-type.d.ts +4 -0
  69. package/dist/discovery/media-type.d.ts.map +1 -0
  70. package/dist/discovery/media-type.js +62 -0
  71. package/dist/discovery/persist.d.ts +63 -0
  72. package/dist/discovery/persist.d.ts.map +1 -0
  73. package/dist/discovery/persist.js +345 -0
  74. package/dist/discovery/test-support.d.ts +16 -0
  75. package/dist/discovery/test-support.d.ts.map +1 -0
  76. package/dist/discovery/test-support.js +127 -0
  77. package/dist/discovery/types.d.ts +63 -0
  78. package/dist/discovery/types.d.ts.map +1 -0
  79. package/dist/discovery/types.js +28 -0
  80. package/dist/discovery/walk.d.ts +12 -0
  81. package/dist/discovery/walk.d.ts.map +1 -0
  82. package/dist/discovery/walk.js +302 -0
  83. package/dist/errors.d.ts +13 -0
  84. package/dist/errors.d.ts.map +1 -0
  85. package/dist/errors.js +22 -0
  86. package/dist/evaluations/dimensions.d.ts +14 -0
  87. package/dist/evaluations/dimensions.d.ts.map +1 -0
  88. package/dist/evaluations/dimensions.js +191 -0
  89. package/dist/evaluations/fixtures.d.ts +18 -0
  90. package/dist/evaluations/fixtures.d.ts.map +1 -0
  91. package/dist/evaluations/fixtures.js +858 -0
  92. package/dist/evaluations/index.d.ts +7 -0
  93. package/dist/evaluations/index.d.ts.map +1 -0
  94. package/dist/evaluations/index.js +10 -0
  95. package/dist/evaluations/report.d.ts +3 -0
  96. package/dist/evaluations/report.d.ts.map +1 -0
  97. package/dist/evaluations/report.js +31 -0
  98. package/dist/evaluations/runner-seed.d.ts +12 -0
  99. package/dist/evaluations/runner-seed.d.ts.map +1 -0
  100. package/dist/evaluations/runner-seed.js +175 -0
  101. package/dist/evaluations/runner.d.ts +8 -0
  102. package/dist/evaluations/runner.d.ts.map +1 -0
  103. package/dist/evaluations/runner.js +205 -0
  104. package/dist/evaluations/scripted-embedding-adapter.d.ts +13 -0
  105. package/dist/evaluations/scripted-embedding-adapter.d.ts.map +1 -0
  106. package/dist/evaluations/scripted-embedding-adapter.js +163 -0
  107. package/dist/evaluations/types.d.ts +116 -0
  108. package/dist/evaluations/types.d.ts.map +1 -0
  109. package/dist/evaluations/types.js +27 -0
  110. package/dist/index.d.ts +23 -0
  111. package/dist/index.d.ts.map +1 -0
  112. package/dist/index.js +41 -0
  113. package/dist/indexing/bounded-indexing.d.ts +41 -0
  114. package/dist/indexing/bounded-indexing.d.ts.map +1 -0
  115. package/dist/indexing/bounded-indexing.js +240 -0
  116. package/dist/indexing/checkpoint-persist.d.ts +8 -0
  117. package/dist/indexing/checkpoint-persist.d.ts.map +1 -0
  118. package/dist/indexing/checkpoint-persist.js +135 -0
  119. package/dist/indexing/checkpoint-resume.d.ts +20 -0
  120. package/dist/indexing/checkpoint-resume.d.ts.map +1 -0
  121. package/dist/indexing/checkpoint-resume.js +50 -0
  122. package/dist/indexing/embedding-batcher.d.ts +3 -0
  123. package/dist/indexing/embedding-batcher.d.ts.map +1 -0
  124. package/dist/indexing/embedding-batcher.js +390 -0
  125. package/dist/indexing/index.d.ts +7 -0
  126. package/dist/indexing/index.d.ts.map +1 -0
  127. package/dist/indexing/index.js +11 -0
  128. package/dist/indexing/job-persist.d.ts +46 -0
  129. package/dist/indexing/job-persist.d.ts.map +1 -0
  130. package/dist/indexing/job-persist.js +157 -0
  131. package/dist/indexing/job-resume.d.ts +4 -0
  132. package/dist/indexing/job-resume.d.ts.map +1 -0
  133. package/dist/indexing/job-resume.js +14 -0
  134. package/dist/indexing/orchestrator.d.ts +3 -0
  135. package/dist/indexing/orchestrator.d.ts.map +1 -0
  136. package/dist/indexing/orchestrator.js +1151 -0
  137. package/dist/indexing/types.d.ts +156 -0
  138. package/dist/indexing/types.d.ts.map +1 -0
  139. package/dist/indexing/types.js +30 -0
  140. package/dist/indexing/vector-persist.d.ts +32 -0
  141. package/dist/indexing/vector-persist.d.ts.map +1 -0
  142. package/dist/indexing/vector-persist.js +105 -0
  143. package/dist/parsers/_internal.d.ts +20 -0
  144. package/dist/parsers/_internal.d.ts.map +1 -0
  145. package/dist/parsers/_internal.js +122 -0
  146. package/dist/parsers/csv-parser.d.ts +3 -0
  147. package/dist/parsers/csv-parser.d.ts.map +1 -0
  148. package/dist/parsers/csv-parser.js +202 -0
  149. package/dist/parsers/docx-parser.d.ts +3 -0
  150. package/dist/parsers/docx-parser.d.ts.map +1 -0
  151. package/dist/parsers/docx-parser.js +390 -0
  152. package/dist/parsers/html-parser.d.ts +3 -0
  153. package/dist/parsers/html-parser.d.ts.map +1 -0
  154. package/dist/parsers/html-parser.js +310 -0
  155. package/dist/parsers/index.d.ts +15 -0
  156. package/dist/parsers/index.d.ts.map +1 -0
  157. package/dist/parsers/index.js +41 -0
  158. package/dist/parsers/json-parser.d.ts +3 -0
  159. package/dist/parsers/json-parser.d.ts.map +1 -0
  160. package/dist/parsers/json-parser.js +192 -0
  161. package/dist/parsers/large-document/capability-discovery.d.ts +27 -0
  162. package/dist/parsers/large-document/capability-discovery.d.ts.map +1 -0
  163. package/dist/parsers/large-document/capability-discovery.js +76 -0
  164. package/dist/parsers/large-document/diagnostics.d.ts +3 -0
  165. package/dist/parsers/large-document/diagnostics.d.ts.map +1 -0
  166. package/dist/parsers/large-document/diagnostics.js +11 -0
  167. package/dist/parsers/large-document/index.d.ts +15 -0
  168. package/dist/parsers/large-document/index.d.ts.map +1 -0
  169. package/dist/parsers/large-document/index.js +10 -0
  170. package/dist/parsers/large-document/legacy-format.d.ts +5 -0
  171. package/dist/parsers/large-document/legacy-format.d.ts.map +1 -0
  172. package/dist/parsers/large-document/legacy-format.js +25 -0
  173. package/dist/parsers/large-document/preflight.d.ts +9 -0
  174. package/dist/parsers/large-document/preflight.d.ts.map +1 -0
  175. package/dist/parsers/large-document/preflight.js +43 -0
  176. package/dist/parsers/large-document/progressive-extraction.d.ts +55 -0
  177. package/dist/parsers/large-document/progressive-extraction.d.ts.map +1 -0
  178. package/dist/parsers/large-document/progressive-extraction.js +123 -0
  179. package/dist/parsers/large-document/progressive-pdf.d.ts +20 -0
  180. package/dist/parsers/large-document/progressive-pdf.d.ts.map +1 -0
  181. package/dist/parsers/large-document/progressive-pdf.js +145 -0
  182. package/dist/parsers/large-document/synthetic-source.d.ts +9 -0
  183. package/dist/parsers/large-document/synthetic-source.d.ts.map +1 -0
  184. package/dist/parsers/large-document/synthetic-source.js +101 -0
  185. package/dist/parsers/large-document/window-builder.d.ts +24 -0
  186. package/dist/parsers/large-document/window-builder.d.ts.map +1 -0
  187. package/dist/parsers/large-document/window-builder.js +75 -0
  188. package/dist/parsers/ocr/index.d.ts +4 -0
  189. package/dist/parsers/ocr/index.d.ts.map +1 -0
  190. package/dist/parsers/ocr/index.js +4 -0
  191. package/dist/parsers/ocr/null-ocr-adapter.d.ts +3 -0
  192. package/dist/parsers/ocr/null-ocr-adapter.d.ts.map +1 -0
  193. package/dist/parsers/ocr/null-ocr-adapter.js +14 -0
  194. package/dist/parsers/ocr/ocr-pipeline-parser.d.ts +8 -0
  195. package/dist/parsers/ocr/ocr-pipeline-parser.d.ts.map +1 -0
  196. package/dist/parsers/ocr/ocr-pipeline-parser.js +147 -0
  197. package/dist/parsers/ocr/types.d.ts +16 -0
  198. package/dist/parsers/ocr/types.d.ts.map +1 -0
  199. package/dist/parsers/ocr/types.js +4 -0
  200. package/dist/parsers/parser-test-fixtures.d.ts +28 -0
  201. package/dist/parsers/parser-test-fixtures.d.ts.map +1 -0
  202. package/dist/parsers/parser-test-fixtures.js +139 -0
  203. package/dist/parsers/pdf-parser.d.ts +43 -0
  204. package/dist/parsers/pdf-parser.d.ts.map +1 -0
  205. package/dist/parsers/pdf-parser.js +388 -0
  206. package/dist/parsers/registry.d.ts +8 -0
  207. package/dist/parsers/registry.d.ts.map +1 -0
  208. package/dist/parsers/registry.js +57 -0
  209. package/dist/parsers/text-parser.d.ts +3 -0
  210. package/dist/parsers/text-parser.d.ts.map +1 -0
  211. package/dist/parsers/text-parser.js +214 -0
  212. package/dist/parsers/types.d.ts +53 -0
  213. package/dist/parsers/types.d.ts.map +1 -0
  214. package/dist/parsers/types.js +21 -0
  215. package/dist/parsers/unsupported-parser.d.ts +4 -0
  216. package/dist/parsers/unsupported-parser.d.ts.map +1 -0
  217. package/dist/parsers/unsupported-parser.js +97 -0
  218. package/dist/parsers/xlsx-parser.d.ts +3 -0
  219. package/dist/parsers/xlsx-parser.d.ts.map +1 -0
  220. package/dist/parsers/xlsx-parser.js +425 -0
  221. package/dist/privacy/audit-emitter.d.ts +5 -0
  222. package/dist/privacy/audit-emitter.d.ts.map +1 -0
  223. package/dist/privacy/audit-emitter.js +93 -0
  224. package/dist/privacy/diagnostic-redactor.d.ts +2 -0
  225. package/dist/privacy/diagnostic-redactor.d.ts.map +1 -0
  226. package/dist/privacy/diagnostic-redactor.js +153 -0
  227. package/dist/privacy/index.d.ts +5 -0
  228. package/dist/privacy/index.d.ts.map +1 -0
  229. package/dist/privacy/index.js +6 -0
  230. package/dist/privacy/retention-applier.d.ts +5 -0
  231. package/dist/privacy/retention-applier.d.ts.map +1 -0
  232. package/dist/privacy/retention-applier.js +88 -0
  233. package/dist/privacy/types.d.ts +98 -0
  234. package/dist/privacy/types.d.ts.map +1 -0
  235. package/dist/privacy/types.js +12 -0
  236. package/dist/qualityIntelligence/capsuleCorpus.d.ts +27 -0
  237. package/dist/qualityIntelligence/capsuleCorpus.d.ts.map +1 -0
  238. package/dist/qualityIntelligence/capsuleCorpus.js +58 -0
  239. package/dist/qualityIntelligence/index.d.ts +3 -0
  240. package/dist/qualityIntelligence/index.d.ts.map +1 -0
  241. package/dist/qualityIntelligence/index.js +5 -0
  242. package/dist/qualityIntelligence/qiHandoff.d.ts +36 -0
  243. package/dist/qualityIntelligence/qiHandoff.d.ts.map +1 -0
  244. package/dist/qualityIntelligence/qiHandoff.js +82 -0
  245. package/dist/retrieval/answer-grounding.d.ts +9 -0
  246. package/dist/retrieval/answer-grounding.d.ts.map +1 -0
  247. package/dist/retrieval/answer-grounding.js +31 -0
  248. package/dist/retrieval/context-pack-assembler.d.ts +24 -0
  249. package/dist/retrieval/context-pack-assembler.d.ts.map +1 -0
  250. package/dist/retrieval/context-pack-assembler.js +50 -0
  251. package/dist/retrieval/index.d.ts +6 -0
  252. package/dist/retrieval/index.d.ts.map +1 -0
  253. package/dist/retrieval/index.js +9 -0
  254. package/dist/retrieval/retrieval-runner.d.ts +10 -0
  255. package/dist/retrieval/retrieval-runner.d.ts.map +1 -0
  256. package/dist/retrieval/retrieval-runner.js +163 -0
  257. package/dist/retrieval/scoped-vector-search.d.ts +24 -0
  258. package/dist/retrieval/scoped-vector-search.d.ts.map +1 -0
  259. package/dist/retrieval/scoped-vector-search.js +864 -0
  260. package/dist/retrieval/types.d.ts +28 -0
  261. package/dist/retrieval/types.d.ts.map +1 -0
  262. package/dist/retrieval/types.js +33 -0
  263. package/dist/section-path-hash.d.ts +3 -0
  264. package/dist/section-path-hash.d.ts.map +1 -0
  265. package/dist/section-path-hash.js +9 -0
  266. package/dist/source-lifecycle.d.ts +14 -0
  267. package/dist/source-lifecycle.d.ts.map +1 -0
  268. package/dist/source-lifecycle.js +155 -0
  269. package/dist/source-routing-validation.d.ts +11 -0
  270. package/dist/source-routing-validation.d.ts.map +1 -0
  271. package/dist/source-routing-validation.js +140 -0
  272. package/dist/store-content-cipher.d.ts +11 -0
  273. package/dist/store-content-cipher.d.ts.map +1 -0
  274. package/dist/store-content-cipher.js +67 -0
  275. package/dist/store-content-encryption.d.ts +12 -0
  276. package/dist/store-content-encryption.d.ts.map +1 -0
  277. package/dist/store-content-encryption.js +275 -0
  278. package/dist/store-paths.d.ts +6 -0
  279. package/dist/store-paths.d.ts.map +1 -0
  280. package/dist/store-paths.js +61 -0
  281. package/dist/store.d.ts +30 -0
  282. package/dist/store.d.ts.map +1 -0
  283. package/dist/store.js +219 -0
  284. package/dist/testing.d.ts +47 -0
  285. package/dist/testing.d.ts.map +1 -0
  286. package/dist/testing.js +170 -0
  287. package/dist/version.d.ts +2 -0
  288. package/dist/version.d.ts.map +1 -0
  289. package/dist/version.js +4 -0
  290. package/package.json +43 -0
@@ -0,0 +1,5 @@
1
+ export type { CapsuleRetentionPolicy, CapsuleAuditEvent, AuditEventSink, RetentionApplyResult, } from "./types.js";
2
+ export { redactDiagnosticMessage } from "./diagnostic-redactor.js";
3
+ export { applyRetentionToCapsule } from "./retention-applier.js";
4
+ export { emitCapsuleAuditEvent, createSqliteAuditSink } from "./audit-emitter.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/privacy/index.ts"],"names":[],"mappings":"AAIA,YAAY,EACV,sBAAsB,EACtB,iBAAiB,EACjB,cAAc,EACd,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,6 @@
1
+ // Public surface of the privacy / retention / audit layer (Epic #189, Issue #201). The
2
+ // package barrel re-exports this module so consumers can import retention and audit
3
+ // helpers from the top-level `@oscharko-dev/keiko-local-knowledge` entry point.
4
+ export { redactDiagnosticMessage } from "./diagnostic-redactor.js";
5
+ export { applyRetentionToCapsule } from "./retention-applier.js";
6
+ export { emitCapsuleAuditEvent, createSqliteAuditSink } from "./audit-emitter.js";
@@ -0,0 +1,5 @@
1
+ import type { KnowledgeCapsuleId } from "@oscharko-dev/keiko-contracts";
2
+ import type { KnowledgeStore } from "../store.js";
3
+ import type { AuditEventSink, CapsuleRetentionPolicy, RetentionApplyResult } from "./types.js";
4
+ export declare function applyRetentionToCapsule(store: KnowledgeStore, capsuleId: KnowledgeCapsuleId, policy: CapsuleRetentionPolicy, now: number, auditSink?: AuditEventSink): RetentionApplyResult;
5
+ //# sourceMappingURL=retention-applier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention-applier.d.ts","sourceRoot":"","sources":["../../src/privacy/retention-applier.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,kBAAkB,EAAqB,MAAM,+BAA+B,CAAC;AAG3F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,KAAK,EAAE,cAAc,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAiE/F,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,kBAAkB,EAC7B,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,cAAc,GACzB,oBAAoB,CA4CtB"}
@@ -0,0 +1,88 @@
1
+ // applyRetentionToCapsule — bounded DELETE that prunes a capsule's vectors / extracted
2
+ // text rows older than the policy window. Pure SQL — no side effects beyond these scoped
3
+ // statements. The `WHERE capsule_id = :capsule_id` clauses are load-bearing: the cascade
4
+ // isolation test in `./retention-applier.test.ts` removes the function's ability to delete
5
+ // cross-capsule rows by relying on those clauses to scope each statement.
6
+ //
7
+ // Schema notes (cf. packages/keiko-contracts/src/local-knowledge-schema.ts):
8
+ // * `vectors.created_at` is the embedding-write timestamp — the right column for the
9
+ // "vector retention" window.
10
+ // * `document_texts` contains raw normalized text and is the row count reported as
11
+ // deletedExtractedTextCount. `parsed_units` has no time column of its own; its cleanup
12
+ // is keyed off `documents.last_extracted_at` via the same subquery. The chunks/vectors
13
+ // that hang off the deleted parsed_units cascade via composite FK (chunks →
14
+ // parsed_units, vectors → chunks).
15
+ // * Both statements are issued inside a single BEGIN/COMMIT so a crash mid-retention
16
+ // cannot leave half the policy applied (matches the source/composition lifecycles).
17
+ // * A missing field on the policy SKIPS the corresponding statement — `undefined` means
18
+ // "retain indefinitely", per the types.ts contract.
19
+ import { KnowledgeStoreError } from "../errors.js";
20
+ const DAY_MS = 86_400_000;
21
+ const DELETE_OLD_VECTORS_SQL = "DELETE FROM vectors WHERE capsule_id = :capsule_id AND created_at < :cutoff";
22
+ const DELETE_OLD_PARSED_UNITS_SQL = "DELETE FROM parsed_units WHERE capsule_id = :capsule_id AND document_id IN " +
23
+ "(SELECT id FROM documents WHERE capsule_id = :capsule_id AND last_extracted_at < :cutoff)";
24
+ const DELETE_OLD_DOCUMENT_TEXTS_SQL = "DELETE FROM document_texts WHERE capsule_id = :capsule_id AND document_id IN " +
25
+ "(SELECT id FROM documents WHERE capsule_id = :capsule_id AND last_extracted_at < :cutoff)";
26
+ function cutoffFor(now, days) {
27
+ return now - days * DAY_MS;
28
+ }
29
+ function parseRetentionDays(field, value) {
30
+ if (value === undefined)
31
+ return undefined;
32
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
33
+ throw new KnowledgeStoreError(`${field} must be a finite non-negative number when set`);
34
+ }
35
+ return value;
36
+ }
37
+ function runDelete(store, sql, capsuleId, cutoff) {
38
+ const stmt = store._internal.db.prepare(sql);
39
+ stmt.run({ capsule_id: capsuleId, cutoff });
40
+ // Read the change count via `SELECT changes()` so the helper is independent of the
41
+ // node:sqlite result-shape; the function is connection-scoped and returns the row
42
+ // count touched by the most recent INSERT/UPDATE/DELETE on that connection.
43
+ const row = store._internal.db.prepare("SELECT changes() AS changes").get();
44
+ return row?.changes ?? 0;
45
+ }
46
+ function sourceIdsForCapsule(store, capsuleId) {
47
+ const rows = store._internal.db
48
+ .prepare("SELECT id FROM capsule_sources WHERE capsule_id = :capsule_id ORDER BY id ASC")
49
+ .all({ capsule_id: capsuleId });
50
+ return rows.map((row) => row.id);
51
+ }
52
+ export function applyRetentionToCapsule(store, capsuleId, policy, now, auditSink) {
53
+ const retainVectorsDays = parseRetentionDays("retainVectorsDays", policy.retainVectorsDays);
54
+ const retainExtractedTextDays = parseRetentionDays("retainExtractedTextDays", policy.retainExtractedTextDays);
55
+ if (retainVectorsDays === undefined && retainExtractedTextDays === undefined) {
56
+ return { capsuleId, deletedVectorCount: 0, deletedExtractedTextCount: 0, appliedAt: now };
57
+ }
58
+ const db = store._internal.db;
59
+ let deletedVectorCount = 0;
60
+ let deletedExtractedTextCount = 0;
61
+ db.exec("BEGIN");
62
+ try {
63
+ if (retainVectorsDays !== undefined) {
64
+ const vectorCutoff = cutoffFor(now, retainVectorsDays);
65
+ deletedVectorCount = runDelete(store, DELETE_OLD_VECTORS_SQL, capsuleId, vectorCutoff);
66
+ }
67
+ if (retainExtractedTextDays !== undefined) {
68
+ const textCutoff = cutoffFor(now, retainExtractedTextDays);
69
+ deletedExtractedTextCount = runDelete(store, DELETE_OLD_DOCUMENT_TEXTS_SQL, capsuleId, textCutoff);
70
+ runDelete(store, DELETE_OLD_PARSED_UNITS_SQL, capsuleId, textCutoff);
71
+ }
72
+ db.exec("COMMIT");
73
+ }
74
+ catch (error) {
75
+ db.exec("ROLLBACK");
76
+ throw error;
77
+ }
78
+ const result = { capsuleId, deletedVectorCount, deletedExtractedTextCount, appliedAt: now };
79
+ auditSink?.emit({
80
+ kind: "retention-applied",
81
+ capsuleId,
82
+ sourceIds: sourceIdsForCapsule(store, capsuleId),
83
+ deletedVectorCount,
84
+ deletedExtractedTextCount,
85
+ occurredAt: now,
86
+ });
87
+ return result;
88
+ }
@@ -0,0 +1,98 @@
1
+ import type { KnowledgeCapsuleId, KnowledgeSourceId } from "@oscharko-dev/keiko-contracts";
2
+ export interface CapsuleRetentionPolicy {
3
+ readonly retainExtractedTextDays?: number;
4
+ readonly retainVectorsDays?: number;
5
+ }
6
+ export interface RetentionApplyResult {
7
+ readonly capsuleId: KnowledgeCapsuleId;
8
+ readonly deletedVectorCount: number;
9
+ readonly deletedExtractedTextCount: number;
10
+ readonly appliedAt: number;
11
+ }
12
+ export type CapsuleAuditEvent = CapsuleCreatedEvent | CapsuleDeletedEvent | CapsuleSourceAddedEvent | CapsuleSourceRemovedEvent | IndexingJobStartedEvent | IndexingJobCompletedEvent | IndexingJobFailedEvent | RetentionAppliedEvent | RetrievalPerformedEvent | AnswerContextAssembledEvent | ModelContextSentEvent;
13
+ export interface CapsuleCreatedEvent {
14
+ readonly kind: "capsule-created";
15
+ readonly capsuleId: KnowledgeCapsuleId;
16
+ readonly occurredAt: number;
17
+ }
18
+ export interface CapsuleDeletedEvent {
19
+ readonly kind: "capsule-deleted";
20
+ readonly capsuleId: KnowledgeCapsuleId;
21
+ readonly occurredAt: number;
22
+ }
23
+ export interface CapsuleSourceAddedEvent {
24
+ readonly kind: "source-added";
25
+ readonly capsuleId: KnowledgeCapsuleId;
26
+ readonly sourceId: KnowledgeSourceId;
27
+ readonly occurredAt: number;
28
+ }
29
+ export interface CapsuleSourceRemovedEvent {
30
+ readonly kind: "source-removed";
31
+ readonly capsuleId: KnowledgeCapsuleId;
32
+ readonly sourceId: KnowledgeSourceId;
33
+ readonly occurredAt: number;
34
+ }
35
+ export interface IndexingJobStartedEvent {
36
+ readonly kind: "indexing-job-started";
37
+ readonly capsuleId: KnowledgeCapsuleId;
38
+ readonly sourceIds: readonly KnowledgeSourceId[];
39
+ readonly jobId: string;
40
+ readonly occurredAt: number;
41
+ }
42
+ export interface IndexingJobCompletedEvent {
43
+ readonly kind: "indexing-job-completed";
44
+ readonly capsuleId: KnowledgeCapsuleId;
45
+ readonly sourceIds: readonly KnowledgeSourceId[];
46
+ readonly jobId: string;
47
+ readonly processedDocuments: number;
48
+ readonly failedDocuments: number;
49
+ readonly occurredAt: number;
50
+ }
51
+ export interface IndexingJobFailedEvent {
52
+ readonly kind: "indexing-job-failed";
53
+ readonly capsuleId: KnowledgeCapsuleId;
54
+ readonly sourceIds: readonly KnowledgeSourceId[];
55
+ readonly jobId: string;
56
+ readonly errorCode: string;
57
+ readonly occurredAt: number;
58
+ }
59
+ export interface RetentionAppliedEvent {
60
+ readonly kind: "retention-applied";
61
+ readonly capsuleId: KnowledgeCapsuleId;
62
+ readonly sourceIds: readonly KnowledgeSourceId[];
63
+ readonly deletedVectorCount: number;
64
+ readonly deletedExtractedTextCount: number;
65
+ readonly occurredAt: number;
66
+ }
67
+ export interface RetrievalPerformedEvent {
68
+ readonly kind: "retrieval-performed";
69
+ readonly capsuleId: KnowledgeCapsuleId;
70
+ readonly sourceIds: readonly KnowledgeSourceId[];
71
+ readonly chunkIds: readonly string[];
72
+ readonly referenceCount: number;
73
+ readonly noEvidence: boolean;
74
+ readonly occurredAt: number;
75
+ }
76
+ export interface AnswerContextAssembledEvent {
77
+ readonly kind: "answer-context-assembled";
78
+ readonly capsuleId: KnowledgeCapsuleId;
79
+ readonly sourceIds: readonly KnowledgeSourceId[];
80
+ readonly chunkIds: readonly string[];
81
+ readonly referenceCount: number;
82
+ readonly citationCount: number;
83
+ readonly occurredAt: number;
84
+ }
85
+ export interface ModelContextSentEvent {
86
+ readonly kind: "model-context-sent";
87
+ readonly capsuleId: KnowledgeCapsuleId;
88
+ readonly sourceIds: readonly KnowledgeSourceId[];
89
+ readonly chunkIds: readonly string[];
90
+ readonly referenceCount: number;
91
+ readonly citationCount: number;
92
+ readonly modelId: string;
93
+ readonly occurredAt: number;
94
+ }
95
+ export interface AuditEventSink {
96
+ readonly emit: (event: CapsuleAuditEvent) => void;
97
+ }
98
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/privacy/types.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAE3F,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAC1C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,yBAAyB,EAAE,MAAM,CAAC;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAOD,MAAM,MAAM,iBAAiB,GACzB,mBAAmB,GACnB,mBAAmB,GACnB,uBAAuB,GACvB,yBAAyB,GACzB,uBAAuB,GACvB,yBAAyB,GACzB,sBAAsB,GACtB,qBAAqB,GACrB,uBAAuB,GACvB,2BAA2B,GAC3B,qBAAqB,CAAC;AAE1B,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,IAAI,EAAE,wBAAwB,CAAC;IACxC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACjD,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,yBAAyB,EAAE,MAAM,CAAC;IAC3C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACjD,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,IAAI,EAAE,0BAA0B,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACjD,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACjD,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAOD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;CACnD"}
@@ -0,0 +1,12 @@
1
+ // Public privacy / retention / audit types for the Local Knowledge Connector (Epic #189,
2
+ // Issue #201). Kept type-only so importing this module costs nothing at runtime. The audit
3
+ // event union deliberately carries ONLY metadata (ids, counts, error codes) — no
4
+ // `excerpt`, `message`, or `text` field that could leak raw extracted content into the
5
+ // audit ledger. Diagnostic-shaped strings (parser errors) flow through `redactDiagnosticMessage`
6
+ // in `./diagnostic-redactor.ts` BEFORE they reach a `parser_diagnostics` row; they are
7
+ // intentionally not part of `CapsuleAuditEvent`.
8
+ //
9
+ // `CapsuleRetentionPolicy` uses optional `readonly` fields so `undefined` (or the absence
10
+ // of the field) means "retain indefinitely". This is enforced by `applyRetentionToCapsule`:
11
+ // a missing field skips the corresponding DELETE entirely.
12
+ export {};
@@ -0,0 +1,27 @@
1
+ import type { CapsuleSetId, KnowledgeCapsuleId } from "@oscharko-dev/keiko-contracts";
2
+ import type { KnowledgeStore } from "../store.js";
3
+ export interface CapsuleDocumentText {
4
+ readonly documentId: string;
5
+ readonly text: string;
6
+ }
7
+ export interface ListCapsuleDocumentTextsOptions {
8
+ /** Maximum number of documents to return (SQL LIMIT). Defaults to 10 000. */
9
+ readonly maxDocuments?: number;
10
+ }
11
+ /**
12
+ * Returns the full normalized text for up to `maxDocuments` indexed documents in a
13
+ * capsule, ordered by document_id for deterministic output. Documents with empty text
14
+ * are skipped. Sync — reads the node:sqlite handle directly like the capsule-lifecycle
15
+ * queries.
16
+ */
17
+ export declare function listCapsuleDocumentTexts(store: KnowledgeStore, capsuleId: string | KnowledgeCapsuleId, options?: ListCapsuleDocumentTextsOptions): readonly CapsuleDocumentText[];
18
+ /**
19
+ * Returns the full corpus text for every member capsule of a capsule-set, concatenated in
20
+ * membership order (ordinal). Each member's documents stay ordered by document_id. An unknown
21
+ * capsule-set yields an empty list, which the QI ingestion layer maps to QI_CAPSULE_UNAVAILABLE.
22
+ * Capsule-sets are logical compositions (no own documents), so this fans out over the members and
23
+ * reuses {@link listCapsuleDocumentTexts} — it does not touch a new storage path (Epic #710,
24
+ * Issue #716/#717). Sync, like the single-capsule reader.
25
+ */
26
+ export declare function listCapsuleSetDocumentTexts(store: KnowledgeStore, capsuleSetId: string | CapsuleSetId): readonly CapsuleDocumentText[];
27
+ //# sourceMappingURL=capsuleCorpus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capsuleCorpus.d.ts","sourceRoot":"","sources":["../../src/qualityIntelligence/capsuleCorpus.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAMD,MAAM,WAAW,+BAA+B;IAC9C,6EAA6E;IAC7E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,MAAM,GAAG,kBAAkB,EACtC,OAAO,GAAE,+BAAoC,GAC5C,SAAS,mBAAmB,EAAE,CAkBhC;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,cAAc,EACrB,YAAY,EAAE,MAAM,GAAG,YAAY,GAClC,SAAS,mBAAmB,EAAE,CAUhC"}
@@ -0,0 +1,58 @@
1
+ // capsuleCorpus.ts — synchronous full-corpus reader for QI ingestion (Epic #710, Issue #717).
2
+ //
3
+ // Reads every indexed document text for a capsule from the `document_texts` table. The QI
4
+ // ingestion pipeline uses this to build atoms directly from the corpus (no retrieval query),
5
+ // so knowledge workers can generate test cases from a connected Local Knowledge connector
6
+ // without needing a search query.
7
+ //
8
+ // SYNC: keiko-local-knowledge uses node:sqlite (synchronous API) throughout; this function
9
+ // follows the same pattern used in capsule-lifecycle.ts (_internal.db.prepare().all()).
10
+ import { getCapsuleSet } from "../capsule-set-lifecycle.js";
11
+ // Safe default: caps the in-process document load to prevent unbounded memory
12
+ // consumption on large capsules. Callers may override with a smaller value.
13
+ const DEFAULT_MAX_DOCUMENTS = 10_000;
14
+ /**
15
+ * Returns the full normalized text for up to `maxDocuments` indexed documents in a
16
+ * capsule, ordered by document_id for deterministic output. Documents with empty text
17
+ * are skipped. Sync — reads the node:sqlite handle directly like the capsule-lifecycle
18
+ * queries.
19
+ */
20
+ export function listCapsuleDocumentTexts(store, capsuleId, options = {}) {
21
+ const limit = options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS;
22
+ const sql = "SELECT document_id, normalized_text FROM document_texts WHERE capsule_id = ? ORDER BY document_id LIMIT ?";
23
+ const rows = store._internal.db.prepare(sql).all(String(capsuleId), limit);
24
+ const cipher = store._internal.contentCipher;
25
+ const result = [];
26
+ for (const row of rows) {
27
+ const documentId = row.document_id;
28
+ const stored = row.normalized_text;
29
+ if (typeof documentId !== "string" || typeof stored !== "string")
30
+ continue;
31
+ // Decrypt at the store boundary so QI ingestion always receives plaintext corpus text.
32
+ const text = cipher.openText(stored);
33
+ if (text.length > 0) {
34
+ result.push({ documentId, text });
35
+ }
36
+ }
37
+ return result;
38
+ }
39
+ /**
40
+ * Returns the full corpus text for every member capsule of a capsule-set, concatenated in
41
+ * membership order (ordinal). Each member's documents stay ordered by document_id. An unknown
42
+ * capsule-set yields an empty list, which the QI ingestion layer maps to QI_CAPSULE_UNAVAILABLE.
43
+ * Capsule-sets are logical compositions (no own documents), so this fans out over the members and
44
+ * reuses {@link listCapsuleDocumentTexts} — it does not touch a new storage path (Epic #710,
45
+ * Issue #716/#717). Sync, like the single-capsule reader.
46
+ */
47
+ export function listCapsuleSetDocumentTexts(store, capsuleSetId) {
48
+ const set = getCapsuleSet(store, capsuleSetId);
49
+ if (set === undefined)
50
+ return [];
51
+ const result = [];
52
+ for (const capsuleId of set.capsuleIds) {
53
+ for (const doc of listCapsuleDocumentTexts(store, capsuleId)) {
54
+ result.push(doc);
55
+ }
56
+ }
57
+ return result;
58
+ }
@@ -0,0 +1,3 @@
1
+ export { buildCapsuleSourceEnvelopes, QiHandoffError, type BuildCapsuleEnvelopesInput, type QiHandoffErrorCode, } from "./qiHandoff.js";
2
+ export { listCapsuleDocumentTexts, listCapsuleSetDocumentTexts, type CapsuleDocumentText, } from "./capsuleCorpus.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/qualityIntelligence/index.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,2BAA2B,EAC3B,cAAc,EACd,KAAK,0BAA0B,EAC/B,KAAK,kBAAkB,GACxB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,wBAAwB,EACxB,2BAA2B,EAC3B,KAAK,mBAAmB,GACzB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,5 @@
1
+ // Public barrel for the local-knowledge → Quality Intelligence handoff (Epic #270,
2
+ // Issue #278). Pure adapter sub-namespace; re-exports the QI capsule-envelope builder
3
+ // and the full-corpus reader added in Epic #710 (Issue #717).
4
+ export { buildCapsuleSourceEnvelopes, QiHandoffError, } from "./qiHandoff.js";
5
+ export { listCapsuleDocumentTexts, listCapsuleSetDocumentTexts, } from "./capsuleCorpus.js";
@@ -0,0 +1,36 @@
1
+ import type { RetrievalReference } from "@oscharko-dev/keiko-contracts";
2
+ import { QualityIntelligence } from "@oscharko-dev/keiko-contracts";
3
+ type CapsuleEnvelope = QualityIntelligence.QualityIntelligenceLocalKnowledgeCapsuleEnvelope;
4
+ export type QiHandoffErrorCode = "EMPTY_REFERENCE" | "EMPTY_HASH_TABLE" | "INVALID_INTEGRITY_HASH" | "INVALID_REGISTERED_AT" | "MISSING_INTEGRITY_HASH";
5
+ export declare class QiHandoffError extends Error {
6
+ readonly code: QiHandoffErrorCode;
7
+ constructor(code: QiHandoffErrorCode, message: string);
8
+ }
9
+ export interface BuildCapsuleEnvelopesInput {
10
+ /** ISO 8601 UTC timestamp the consumer captured for envelope provenance. */
11
+ readonly registeredAt: string;
12
+ /** Retrieval references from `keiko-local-knowledge` for a single capsule query. */
13
+ readonly references: readonly RetrievalReference[];
14
+ /**
15
+ * SHA-256 hex digest table keyed by the citation chunkId. The adapter rejects any
16
+ * reference whose chunkId has no matching digest with `MISSING_INTEGRITY_HASH`.
17
+ */
18
+ readonly integrityHashByChunkId: Readonly<Record<string, string>>;
19
+ /**
20
+ * Caller-pre-validated id prefix. Each envelope id is `${idPrefix}:${chunkId}`. The
21
+ * contract validator (asQualityIntelligenceSourceEnvelopeId) rejects forbidden
22
+ * fragments — callers must guarantee the prefix passes those rules.
23
+ */
24
+ readonly idPrefix: string;
25
+ }
26
+ /**
27
+ * Convert a list of `RetrievalReference` into local-knowledge-capsule envelopes. Pure.
28
+ *
29
+ * Each envelope's `localRef` is the citation chunkId (an opaque local-knowledge
30
+ * identifier, never a URL or a path). The envelope display label combines the
31
+ * capsule's `safeDisplayName` with the chunkId so the audit ledger has a
32
+ * non-secret descriptor.
33
+ */
34
+ export declare const buildCapsuleSourceEnvelopes: (input: BuildCapsuleEnvelopesInput) => readonly CapsuleEnvelope[];
35
+ export {};
36
+ //# sourceMappingURL=qiHandoff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qiHandoff.d.ts","sourceRoot":"","sources":["../../src/qualityIntelligence/qiHandoff.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEpE,KAAK,eAAe,GAAG,mBAAmB,CAAC,gDAAgD,CAAC;AAG5F,MAAM,MAAM,kBAAkB,GAC1B,iBAAiB,GACjB,kBAAkB,GAClB,wBAAwB,GACxB,uBAAuB,GACvB,wBAAwB,CAAC;AAE7B,qBAAa,cAAe,SAAQ,KAAK;IACvC,SAAgB,IAAI,EAAE,kBAAkB,CAAC;gBAC7B,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM;CAKtD;AA4BD,MAAM,WAAW,0BAA0B;IACzC,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,oFAAoF;IACpF,QAAQ,CAAC,UAAU,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACnD;;;OAGG;IACH,QAAQ,CAAC,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAClE;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,GACtC,OAAO,0BAA0B,KAChC,SAAS,eAAe,EAoC1B,CAAC"}
@@ -0,0 +1,82 @@
1
+ // Local Knowledge → Quality Intelligence handoff adapter (Epic #270, Issue #278).
2
+ //
3
+ // Pure adapter that turns an existing local-knowledge `RetrievalReference` list (the
4
+ // result of a capsule retrieval) into a list of QI source envelopes with
5
+ // `kind: "local-knowledge-capsule"`. The QI ingestion pipeline then plans / reconciles
6
+ // these envelopes alongside repo-context envelopes through the same source-mix surface.
7
+ //
8
+ // Pure: no IO, no clock reads. The caller supplies a deterministic `registeredAt`
9
+ // timestamp and a per-citation SHA-256 integrity hash. The adapter reuses ONLY
10
+ // existing local-knowledge / contract types — it does not add a new retrieval port.
11
+ //
12
+ // Structurally inspired by Test Intelligence reference (TI) capsule handoffs, but the
13
+ // envelope shape is anchored on the QI contracts surface
14
+ // (@oscharko-dev/keiko-contracts/qualityIntelligence).
15
+ import { QualityIntelligence } from "@oscharko-dev/keiko-contracts";
16
+ const { asQualityIntelligenceSourceEnvelopeId } = QualityIntelligence;
17
+ export class QiHandoffError extends Error {
18
+ code;
19
+ constructor(code, message) {
20
+ super(`[${code}] ${message}`);
21
+ this.name = "QiHandoffError";
22
+ this.code = code;
23
+ }
24
+ }
25
+ const HEX64 = /^[0-9a-f]{64}$/u;
26
+ const ISO_8601 = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?Z$/u;
27
+ const assertHash = (hash) => {
28
+ if (!HEX64.test(hash)) {
29
+ throw new QiHandoffError("INVALID_INTEGRITY_HASH", "integrityHashSha256Hex must be 64 lowercase hex chars");
30
+ }
31
+ };
32
+ const assertRegisteredAt = (timestamp) => {
33
+ if (!ISO_8601.test(timestamp)) {
34
+ throw new QiHandoffError("INVALID_REGISTERED_AT", "registeredAt must be ISO 8601 UTC (e.g. 2026-06-05T00:00:00Z)");
35
+ }
36
+ };
37
+ const clampLabel = (label) => {
38
+ if (label.length <= 256)
39
+ return label;
40
+ return `${label.slice(0, 253)}...`;
41
+ };
42
+ /**
43
+ * Convert a list of `RetrievalReference` into local-knowledge-capsule envelopes. Pure.
44
+ *
45
+ * Each envelope's `localRef` is the citation chunkId (an opaque local-knowledge
46
+ * identifier, never a URL or a path). The envelope display label combines the
47
+ * capsule's `safeDisplayName` with the chunkId so the audit ledger has a
48
+ * non-secret descriptor.
49
+ */
50
+ export const buildCapsuleSourceEnvelopes = (input) => {
51
+ if (input.references.length === 0) {
52
+ throw new QiHandoffError("EMPTY_REFERENCE", "references list must not be empty");
53
+ }
54
+ if (Object.keys(input.integrityHashByChunkId).length === 0) {
55
+ throw new QiHandoffError("EMPTY_HASH_TABLE", "integrityHashByChunkId must not be empty");
56
+ }
57
+ assertRegisteredAt(input.registeredAt);
58
+ const envelopes = [];
59
+ for (const reference of input.references) {
60
+ const chunkId = reference.chunkId;
61
+ const hash = input.integrityHashByChunkId[chunkId];
62
+ if (typeof hash !== "string") {
63
+ throw new QiHandoffError("MISSING_INTEGRITY_HASH", `No integrity hash supplied for chunkId "${chunkId}"`);
64
+ }
65
+ assertHash(hash);
66
+ const id = asQualityIntelligenceSourceEnvelopeId(`${input.idPrefix}:${chunkId}`);
67
+ const safeName = reference.citation.safeDisplayName;
68
+ const displayLabel = clampLabel(`local-knowledge:${safeName}#${chunkId}`);
69
+ envelopes.push({
70
+ id,
71
+ kind: "local-knowledge-capsule",
72
+ displayLabel,
73
+ provenance: {
74
+ origin: `local-knowledge-capsule:${reference.capsuleId}`,
75
+ registeredAt: input.registeredAt,
76
+ integrityHashSha256Hex: hash,
77
+ },
78
+ localRef: chunkId,
79
+ });
80
+ }
81
+ return envelopes;
82
+ };
@@ -0,0 +1,9 @@
1
+ import type { CapsuleAnswerGroundingPolicy, RetrievalReference } from "@oscharko-dev/keiko-contracts";
2
+ export type GroundingDecisionReason = "allowed" | "require-citations-rejected" | "no-evidence-stated";
3
+ export interface GroundingDecision {
4
+ readonly allow: boolean;
5
+ readonly reason: GroundingDecisionReason;
6
+ readonly noEvidence: boolean;
7
+ }
8
+ export declare function validateAnswerGrounding(references: readonly RetrievalReference[], policy: CapsuleAnswerGroundingPolicy): GroundingDecision;
9
+ //# sourceMappingURL=answer-grounding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"answer-grounding.d.ts","sourceRoot":"","sources":["../../src/retrieval/answer-grounding.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACV,4BAA4B,EAC5B,kBAAkB,EACnB,MAAM,+BAA+B,CAAC;AAEvC,MAAM,MAAM,uBAAuB,GAC/B,SAAS,GACT,4BAA4B,GAC5B,oBAAoB,CAAC;AAEzB,MAAM,WAAW,iBAAiB;IAIhC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC;IAKzC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,SAAS,kBAAkB,EAAE,EACzC,MAAM,EAAE,4BAA4B,GACnC,iBAAiB,CAYnB"}
@@ -0,0 +1,31 @@
1
+ // Answer-grounding policy decision (Epic #189, Issue #199). Pure function over the
2
+ // retrieval result + the capsule's configured `answerGroundingPolicy`. Lives at the
3
+ // boundary between retrieval and the future #200 Conversation Center integration: the
4
+ // runner uses it to decide whether to release retrieved evidence to the LLM grounding
5
+ // prompt, and the UI layer reads `noEvidence` / `reason` to phrase the user-visible
6
+ // message.
7
+ //
8
+ // Three policies (matching `CapsuleAnswerGroundingPolicy` in contracts):
9
+ // * "require-citations" — empty refs MUST block; no answer can fire.
10
+ // * "require-citations-or-state-no-evidence" — empty refs are allowed *if* the caller
11
+ // surfaces a "no evidence" message; the decision marks `noEvidence: true` so the
12
+ // caller knows it has to.
13
+ // * "best-effort" — empty refs are allowed; the LLM may answer without citations.
14
+ //
15
+ // `validateAnswerGrounding` is deliberately a *function*, not a method on a capsule or a
16
+ // store call. No IO, no allocation beyond the returned record. Tests pin every branch.
17
+ export function validateAnswerGrounding(references, policy) {
18
+ const hasReferences = references.length > 0;
19
+ if (policy === "require-citations") {
20
+ if (hasReferences)
21
+ return { allow: true, reason: "allowed", noEvidence: false };
22
+ return { allow: false, reason: "require-citations-rejected", noEvidence: true };
23
+ }
24
+ if (policy === "require-citations-or-state-no-evidence") {
25
+ if (hasReferences)
26
+ return { allow: true, reason: "allowed", noEvidence: false };
27
+ return { allow: true, reason: "no-evidence-stated", noEvidence: true };
28
+ }
29
+ // best-effort
30
+ return { allow: true, reason: "allowed", noEvidence: !hasReferences };
31
+ }
@@ -0,0 +1,24 @@
1
+ import type { CitationReference, KnowledgeCapsuleId, KnowledgeSourceId, RetrievalReference } from "@oscharko-dev/keiko-contracts";
2
+ export declare const LOCAL_KNOWLEDGE_GROUNDED_CONTEXT_PACK_VERSION: "1";
3
+ export interface LocalKnowledgeGroundedContextScope {
4
+ readonly capsuleIds: readonly KnowledgeCapsuleId[];
5
+ readonly sourceIds: readonly KnowledgeSourceId[];
6
+ readonly capsuleCount: number;
7
+ readonly sourceCount: number;
8
+ }
9
+ export interface LocalKnowledgeGroundedContextCounts {
10
+ readonly totalReferences: number;
11
+ readonly distinctCapsules: number;
12
+ readonly distinctSources: number;
13
+ }
14
+ export interface LocalKnowledgeGroundedContextPack {
15
+ readonly schemaVersion: typeof LOCAL_KNOWLEDGE_GROUNDED_CONTEXT_PACK_VERSION;
16
+ readonly scope: LocalKnowledgeGroundedContextScope;
17
+ readonly citations: readonly CitationReference[];
18
+ readonly counts: LocalKnowledgeGroundedContextCounts;
19
+ }
20
+ export interface AssembleGroundedContextOptions {
21
+ readonly reserved?: never;
22
+ }
23
+ export declare function assembleGroundedContext(references: readonly RetrievalReference[], _options?: AssembleGroundedContextOptions): LocalKnowledgeGroundedContextPack;
24
+ //# sourceMappingURL=context-pack-assembler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-pack-assembler.d.ts","sourceRoot":"","sources":["../../src/retrieval/context-pack-assembler.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,+BAA+B,CAAC;AAIvC,eAAO,MAAM,6CAA6C,EAAG,GAAY,CAAC;AAE1E,MAAM,WAAW,kCAAkC;IAKjD,QAAQ,CAAC,UAAU,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACnD,QAAQ,CAAC,SAAS,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACjD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,mCAAmC;IAClD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CAAC,aAAa,EAAE,OAAO,6CAA6C,CAAC;IAC7E,QAAQ,CAAC,KAAK,EAAE,kCAAkC,CAAC;IAInD,QAAQ,CAAC,SAAS,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACjD,QAAQ,CAAC,MAAM,EAAE,mCAAmC,CAAC;CACtD;AAED,MAAM,WAAW,8BAA8B;IAI7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC;CAC3B;AAED,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,SAAS,kBAAkB,EAAE,EACzC,QAAQ,GAAE,8BAAmC,GAC5C,iCAAiC,CAkBnC"}
@@ -0,0 +1,50 @@
1
+ // Grounded context-pack assembler (Epic #189, Issue #199). Converts a list of
2
+ // `RetrievalReference` (the ranked output of `searchVectorsForScope`) into a
3
+ // `LocalKnowledgeGroundedContextPack` — a deliberately metadata-only projection that the
4
+ // future #200 Conversation Center integration hands to the LLM grounding prompt and the
5
+ // answer surface UI.
6
+ //
7
+ // This pack is structurally distinct from the connected-context layer's
8
+ // `GroundedAnswerContextPackSummary` (in `@oscharko-dev/keiko-contracts/bff-wire`): that
9
+ // one is shaped around `ConnectedContextPack`, scope kinds (workspace-root, directory,
10
+ // files), exploration usage / budget, and uncertainty. Local Knowledge has a different
11
+ // runtime model — capsule-set composition, citation-per-chunk — and a different privacy
12
+ // contract (we never carry raw text in this PR; `outputMode === "raw"` lands in #200
13
+ // where the workspace FS port is wired and we can read the parsed_unit's character
14
+ // span from disk). Keeping the two pack types distinct prevents accidental cross-wiring
15
+ // when the BFF layer eventually surfaces both.
16
+ //
17
+ // The pack is pure data — no IO, no allocation beyond the returned record + its
18
+ // citation array. Sorting and de-duplication are stable on `chunkId` so the pack is
19
+ // byte-identical across runs with the same inputs (load-bearing for the audit ledger).
20
+ // Bumped when the wire shape changes. Consumers (audit ledger, future #200 wire) pin
21
+ // against this literal so an unrecognised pack is rejected at the boundary.
22
+ export const LOCAL_KNOWLEDGE_GROUNDED_CONTEXT_PACK_VERSION = "1";
23
+ export function assembleGroundedContext(references, _options = {}) {
24
+ const capsuleIds = collectSortedUnique(references.map((r) => r.capsuleId));
25
+ const sourceIds = collectSortedUnique(references.map((r) => r.citation.sourceId));
26
+ return {
27
+ schemaVersion: LOCAL_KNOWLEDGE_GROUNDED_CONTEXT_PACK_VERSION,
28
+ scope: {
29
+ capsuleIds,
30
+ sourceIds,
31
+ capsuleCount: capsuleIds.length,
32
+ sourceCount: sourceIds.length,
33
+ },
34
+ citations: references.map((r) => r.citation),
35
+ counts: {
36
+ totalReferences: references.length,
37
+ distinctCapsules: capsuleIds.length,
38
+ distinctSources: sourceIds.length,
39
+ },
40
+ };
41
+ }
42
+ function collectSortedUnique(values) {
43
+ const seen = new Map();
44
+ for (const value of values) {
45
+ const key = String(value);
46
+ if (!seen.has(key))
47
+ seen.set(key, value);
48
+ }
49
+ return [...seen.values()].sort((a, b) => String(a).localeCompare(String(b)));
50
+ }
@@ -0,0 +1,6 @@
1
+ export { runLocalKnowledgeRetrieval, type RetrievalDependencies } from "./retrieval-runner.js";
2
+ export { searchVectorsForScope, toScopeInput, type RetrievalScopeInput, type SearchOptions, type SearchOutcome, } from "./scoped-vector-search.js";
3
+ export { assembleGroundedContext, LOCAL_KNOWLEDGE_GROUNDED_CONTEXT_PACK_VERSION, type AssembleGroundedContextOptions, type LocalKnowledgeGroundedContextCounts, type LocalKnowledgeGroundedContextPack, type LocalKnowledgeGroundedContextScope, } from "./context-pack-assembler.js";
4
+ export { validateAnswerGrounding, type GroundingDecision, type GroundingDecisionReason, } from "./answer-grounding.js";
5
+ export { DEFAULT_RETRIEVAL_TOP_K, MAX_RETRIEVAL_TOP_K, RetrievalError, type RetrievalErrorCode, type RetrievalNoEvidenceReason, type RetrievalQuery, type RetrievalResult, } from "./types.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/retrieval/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,0BAA0B,EAAE,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAE/F,OAAO,EACL,qBAAqB,EACrB,YAAY,EACZ,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,uBAAuB,EACvB,6CAA6C,EAC7C,KAAK,8BAA8B,EACnC,KAAK,mCAAmC,EACxC,KAAK,iCAAiC,EACtC,KAAK,kCAAkC,GACxC,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,GAC7B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,uBAAuB,EACvB,mBAAmB,EACnB,cAAc,EACd,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACnB,KAAK,eAAe,GACrB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,9 @@
1
+ // Public surface of the retrieval layer (Epic #189, Issue #199). Composed by the
2
+ // package barrel in ../index.ts; consumers outside this package never import from this
3
+ // subdirectory directly (ADR-0019 direction rule 3e + the trust-8 test-support naming
4
+ // convention).
5
+ export { runLocalKnowledgeRetrieval } from "./retrieval-runner.js";
6
+ export { searchVectorsForScope, toScopeInput, } from "./scoped-vector-search.js";
7
+ export { assembleGroundedContext, LOCAL_KNOWLEDGE_GROUNDED_CONTEXT_PACK_VERSION, } from "./context-pack-assembler.js";
8
+ export { validateAnswerGrounding, } from "./answer-grounding.js";
9
+ export { DEFAULT_RETRIEVAL_TOP_K, MAX_RETRIEVAL_TOP_K, RetrievalError, } from "./types.js";
@@ -0,0 +1,10 @@
1
+ import type { OpenAIEmbeddingAdapter } from "@oscharko-dev/keiko-model-gateway";
2
+ import type { KnowledgeStore } from "../store.js";
3
+ import { type RetrievalQuery, type RetrievalResult } from "./types.js";
4
+ export interface RetrievalDependencies {
5
+ readonly store: KnowledgeStore;
6
+ readonly embeddingAdapter: OpenAIEmbeddingAdapter;
7
+ readonly signal?: AbortSignal;
8
+ }
9
+ export declare function runLocalKnowledgeRetrieval(deps: RetrievalDependencies, query: RetrievalQuery): Promise<RetrievalResult>;
10
+ //# sourceMappingURL=retrieval-runner.d.ts.map