dsh-data-domain-knowledge 0.1.0-alpha.9 → 0.1.1-alpha.2

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 (438) hide show
  1. package/README.md +118 -567
  2. package/cordis.patch.yml +5 -418
  3. package/eval/README.md +19 -8
  4. package/eval/benchmark-extraction-format.mjs +20 -0
  5. package/eval/evaluate-retrieval.mjs +17 -8
  6. package/eval/queries.example.jsonl +1 -0
  7. package/lib/client.js +4181 -3239
  8. package/lib/contract-envelope.d.ts +91 -0
  9. package/lib/contract-envelope.js +106 -0
  10. package/lib/contract-layout.d.ts +130 -0
  11. package/lib/contract-layout.js +138 -0
  12. package/lib/contract-refs.d.ts +39 -0
  13. package/lib/contract-refs.js +9 -0
  14. package/lib/contract-version.d.ts +311 -0
  15. package/lib/contract-version.js +270 -0
  16. package/lib/contract-wire.d.ts +147 -0
  17. package/lib/contract-wire.js +99 -0
  18. package/lib/contracts.d.ts +12 -0
  19. package/lib/contracts.js +12 -0
  20. package/lib/doc-page.js +2 -2
  21. package/lib/pages.js +3329 -1296
  22. package/lib/types/agent-behavior-rules.d.ts +10 -0
  23. package/lib/types/agent-behavior-rules.d.ts.map +1 -0
  24. package/lib/types/agent-behavior-rules.js +28 -0
  25. package/lib/types/agent-behavior-rules.js.map +1 -0
  26. package/lib/types/axis-registry.d.ts +37 -5
  27. package/lib/types/axis-registry.d.ts.map +1 -1
  28. package/lib/types/axis-registry.js +234 -26
  29. package/lib/types/axis-registry.js.map +1 -1
  30. package/lib/types/card-organize.d.ts +27 -12
  31. package/lib/types/card-organize.d.ts.map +1 -1
  32. package/lib/types/card-organize.js +58 -73
  33. package/lib/types/card-organize.js.map +1 -1
  34. package/lib/types/compiler/embedding-identity.d.ts +3 -0
  35. package/lib/types/compiler/embedding-identity.d.ts.map +1 -0
  36. package/lib/types/compiler/embedding-identity.js +24 -0
  37. package/lib/types/compiler/embedding-identity.js.map +1 -0
  38. package/lib/types/compiler/snapshot.d.ts +16 -0
  39. package/lib/types/compiler/snapshot.d.ts.map +1 -0
  40. package/lib/types/compiler/snapshot.js +2 -0
  41. package/lib/types/compiler/snapshot.js.map +1 -0
  42. package/lib/types/config-schema.d.ts +6 -3
  43. package/lib/types/config-schema.d.ts.map +1 -1
  44. package/lib/types/config-schema.js +6 -2
  45. package/lib/types/config-schema.js.map +1 -1
  46. package/lib/types/convert/host.d.ts +6 -5
  47. package/lib/types/convert/host.d.ts.map +1 -1
  48. package/lib/types/convert/host.js +58 -18
  49. package/lib/types/convert/host.js.map +1 -1
  50. package/lib/types/convert/ingest.d.ts +10 -1
  51. package/lib/types/convert/ingest.d.ts.map +1 -1
  52. package/lib/types/convert/ingest.js +68 -9
  53. package/lib/types/convert/ingest.js.map +1 -1
  54. package/lib/types/derive-contract.d.ts +3 -0
  55. package/lib/types/derive-contract.d.ts.map +1 -0
  56. package/lib/types/derive-contract.js +15 -0
  57. package/lib/types/derive-contract.js.map +1 -0
  58. package/lib/types/diagnosis/cards.d.ts +7 -3
  59. package/lib/types/diagnosis/cards.d.ts.map +1 -1
  60. package/lib/types/diagnosis/cards.js +13 -8
  61. package/lib/types/diagnosis/cards.js.map +1 -1
  62. package/lib/types/diagnosis/run.d.ts +4 -13
  63. package/lib/types/diagnosis/run.d.ts.map +1 -1
  64. package/lib/types/diagnosis/run.js +26 -16
  65. package/lib/types/diagnosis/run.js.map +1 -1
  66. package/lib/types/diagnosis/schema.js +2 -2
  67. package/lib/types/diagnosis/schema.js.map +1 -1
  68. package/lib/types/diagnosis/seed-source.d.ts +1 -1
  69. package/lib/types/diagnosis/seed-source.d.ts.map +1 -1
  70. package/lib/types/diagnosis/seed-source.js +5 -4
  71. package/lib/types/diagnosis/seed-source.js.map +1 -1
  72. package/lib/types/diagnosis/seed.d.ts +4 -4
  73. package/lib/types/diagnosis/seed.d.ts.map +1 -1
  74. package/lib/types/diagnosis/seed.js +6 -8
  75. package/lib/types/diagnosis/seed.js.map +1 -1
  76. package/lib/types/diagnosis/store.d.ts +24 -18
  77. package/lib/types/diagnosis/store.d.ts.map +1 -1
  78. package/lib/types/diagnosis/store.js +130 -65
  79. package/lib/types/diagnosis/store.js.map +1 -1
  80. package/lib/types/diagnosis/types.d.ts +47 -15
  81. package/lib/types/diagnosis/types.d.ts.map +1 -1
  82. package/lib/types/diagnosis/types.js.map +1 -1
  83. package/lib/types/environment/dsh-python.d.ts +77 -0
  84. package/lib/types/environment/dsh-python.d.ts.map +1 -0
  85. package/lib/types/environment/dsh-python.js +139 -0
  86. package/lib/types/environment/dsh-python.js.map +1 -0
  87. package/lib/types/environment/install.d.ts +15 -0
  88. package/lib/types/environment/install.d.ts.map +1 -0
  89. package/lib/types/environment/install.js +297 -0
  90. package/lib/types/environment/install.js.map +1 -0
  91. package/lib/types/environment/manifest.d.ts +208 -0
  92. package/lib/types/environment/manifest.d.ts.map +1 -0
  93. package/lib/types/environment/manifest.js +26 -0
  94. package/lib/types/environment/manifest.js.map +1 -0
  95. package/lib/types/environment/model-catalog.d.ts +223 -0
  96. package/lib/types/environment/model-catalog.d.ts.map +1 -0
  97. package/lib/types/environment/model-catalog.js +129 -0
  98. package/lib/types/environment/model-catalog.js.map +1 -0
  99. package/lib/types/environment/process.d.ts +10 -0
  100. package/lib/types/environment/process.d.ts.map +1 -0
  101. package/lib/types/environment/process.js +54 -0
  102. package/lib/types/environment/process.js.map +1 -0
  103. package/lib/types/environment/selection.d.ts +9 -0
  104. package/lib/types/environment/selection.d.ts.map +1 -0
  105. package/lib/types/environment/selection.js +29 -0
  106. package/lib/types/environment/selection.js.map +1 -0
  107. package/lib/types/environment/service.d.ts +64 -0
  108. package/lib/types/environment/service.d.ts.map +1 -0
  109. package/lib/types/environment/service.js +424 -0
  110. package/lib/types/environment/service.js.map +1 -0
  111. package/lib/types/environment/types.d.ts +56 -0
  112. package/lib/types/environment/types.d.ts.map +1 -0
  113. package/lib/types/environment/types.js +2 -0
  114. package/lib/types/environment/types.js.map +1 -0
  115. package/lib/types/events.d.ts +76 -0
  116. package/lib/types/events.d.ts.map +1 -0
  117. package/lib/types/events.js +52 -0
  118. package/lib/types/events.js.map +1 -0
  119. package/lib/types/extract-contract.d.ts +3 -0
  120. package/lib/types/extract-contract.d.ts.map +1 -0
  121. package/lib/types/extract-contract.js +41 -0
  122. package/lib/types/extract-contract.js.map +1 -0
  123. package/lib/types/extract-model.d.ts +5 -0
  124. package/lib/types/extract-model.d.ts.map +1 -1
  125. package/lib/types/extract-model.js.map +1 -1
  126. package/lib/types/extract-table-contract.d.ts +3 -0
  127. package/lib/types/extract-table-contract.d.ts.map +1 -0
  128. package/lib/types/extract-table-contract.js +27 -0
  129. package/lib/types/extract-table-contract.js.map +1 -0
  130. package/lib/types/extract-table.d.ts +8 -0
  131. package/lib/types/extract-table.d.ts.map +1 -0
  132. package/lib/types/extract-table.js +158 -0
  133. package/lib/types/extract-table.js.map +1 -0
  134. package/lib/types/extraction-runs.d.ts +30 -6
  135. package/lib/types/extraction-runs.d.ts.map +1 -1
  136. package/lib/types/extraction-runs.js +69 -8
  137. package/lib/types/extraction-runs.js.map +1 -1
  138. package/lib/types/fs-atomic.d.ts +0 -7
  139. package/lib/types/fs-atomic.d.ts.map +1 -1
  140. package/lib/types/fs-atomic.js +17 -2
  141. package/lib/types/fs-atomic.js.map +1 -1
  142. package/lib/types/images.d.ts +26 -0
  143. package/lib/types/images.d.ts.map +1 -0
  144. package/lib/types/images.js +196 -0
  145. package/lib/types/images.js.map +1 -0
  146. package/lib/types/index.d.ts +2 -2
  147. package/lib/types/index.d.ts.map +1 -1
  148. package/lib/types/index.js +598 -209
  149. package/lib/types/index.js.map +1 -1
  150. package/lib/types/intent/engine.d.ts +12 -4
  151. package/lib/types/intent/engine.d.ts.map +1 -1
  152. package/lib/types/intent/engine.js +43 -5
  153. package/lib/types/intent/engine.js.map +1 -1
  154. package/lib/types/intent/host.d.ts +5 -8
  155. package/lib/types/intent/host.d.ts.map +1 -1
  156. package/lib/types/intent/host.js +56 -11
  157. package/lib/types/intent/host.js.map +1 -1
  158. package/lib/types/intent/scope.d.ts +25 -0
  159. package/lib/types/intent/scope.d.ts.map +1 -0
  160. package/lib/types/intent/scope.js +116 -0
  161. package/lib/types/intent/scope.js.map +1 -0
  162. package/lib/types/intent/tools.d.ts +24 -20
  163. package/lib/types/intent/tools.d.ts.map +1 -1
  164. package/lib/types/intent/tools.js +65 -23
  165. package/lib/types/intent/tools.js.map +1 -1
  166. package/lib/types/intent/types.d.ts +11 -4
  167. package/lib/types/intent/types.d.ts.map +1 -1
  168. package/lib/types/knowledge-bases.d.ts +11 -5
  169. package/lib/types/knowledge-bases.d.ts.map +1 -1
  170. package/lib/types/knowledge-bases.js +52 -20
  171. package/lib/types/knowledge-bases.js.map +1 -1
  172. package/lib/types/knowledge-contract.d.ts +32 -0
  173. package/lib/types/knowledge-contract.d.ts.map +1 -0
  174. package/lib/types/knowledge-contract.js +116 -0
  175. package/lib/types/knowledge-contract.js.map +1 -0
  176. package/lib/types/knowledge.d.ts +31 -9
  177. package/lib/types/knowledge.d.ts.map +1 -1
  178. package/lib/types/knowledge.js +137 -60
  179. package/lib/types/knowledge.js.map +1 -1
  180. package/lib/types/layout.d.ts +10 -29
  181. package/lib/types/layout.d.ts.map +1 -1
  182. package/lib/types/layout.js +17 -36
  183. package/lib/types/layout.js.map +1 -1
  184. package/lib/types/materialize-snapshot.d.ts +49 -0
  185. package/lib/types/materialize-snapshot.d.ts.map +1 -0
  186. package/lib/types/materialize-snapshot.js +2 -0
  187. package/lib/types/materialize-snapshot.js.map +1 -0
  188. package/lib/types/migrate.d.ts +11 -0
  189. package/lib/types/migrate.d.ts.map +1 -1
  190. package/lib/types/migrate.js +83 -2
  191. package/lib/types/migrate.js.map +1 -1
  192. package/lib/types/model-text.d.ts +58 -1
  193. package/lib/types/model-text.d.ts.map +1 -1
  194. package/lib/types/model-text.js +85 -30
  195. package/lib/types/model-text.js.map +1 -1
  196. package/lib/types/oplog.d.ts.map +1 -1
  197. package/lib/types/oplog.js +7 -0
  198. package/lib/types/oplog.js.map +1 -1
  199. package/lib/types/organize-contract.d.ts +18 -0
  200. package/lib/types/organize-contract.d.ts.map +1 -0
  201. package/lib/types/organize-contract.js +82 -0
  202. package/lib/types/organize-contract.js.map +1 -0
  203. package/lib/types/pipeline/engine.d.ts +66 -0
  204. package/lib/types/pipeline/engine.d.ts.map +1 -0
  205. package/lib/types/pipeline/engine.js +352 -0
  206. package/lib/types/pipeline/engine.js.map +1 -0
  207. package/lib/types/pipeline/store.d.ts +73 -0
  208. package/lib/types/pipeline/store.d.ts.map +1 -0
  209. package/lib/types/pipeline/store.js +143 -0
  210. package/lib/types/pipeline/store.js.map +1 -0
  211. package/lib/types/pipeline/types.d.ts +102 -0
  212. package/lib/types/pipeline/types.d.ts.map +1 -0
  213. package/lib/types/pipeline/types.js +5 -0
  214. package/lib/types/pipeline/types.js.map +1 -0
  215. package/lib/types/present.d.ts +2 -0
  216. package/lib/types/present.d.ts.map +1 -1
  217. package/lib/types/present.js +9 -4
  218. package/lib/types/present.js.map +1 -1
  219. package/lib/types/preset-ids.d.ts +4 -0
  220. package/lib/types/preset-ids.d.ts.map +1 -0
  221. package/lib/types/preset-ids.js +6 -0
  222. package/lib/types/preset-ids.js.map +1 -0
  223. package/lib/types/query-analysis.d.ts +13 -1
  224. package/lib/types/query-analysis.d.ts.map +1 -1
  225. package/lib/types/query-analysis.js +62 -6
  226. package/lib/types/query-analysis.js.map +1 -1
  227. package/lib/types/recall-rank.d.ts +3 -3
  228. package/lib/types/recall-rank.d.ts.map +1 -1
  229. package/lib/types/recall-rank.js +4 -4
  230. package/lib/types/recall-rank.js.map +1 -1
  231. package/lib/types/retention.d.ts +39 -0
  232. package/lib/types/retention.d.ts.map +1 -0
  233. package/lib/types/retention.js +42 -0
  234. package/lib/types/retention.js.map +1 -0
  235. package/lib/types/retrieval/vector-index.d.ts +32 -0
  236. package/lib/types/retrieval/vector-index.d.ts.map +1 -0
  237. package/lib/types/retrieval/vector-index.js +15 -0
  238. package/lib/types/retrieval/vector-index.js.map +1 -0
  239. package/lib/types/retrieval/visibility.d.ts +6 -0
  240. package/lib/types/retrieval/visibility.d.ts.map +1 -0
  241. package/lib/types/retrieval/visibility.js +11 -0
  242. package/lib/types/retrieval/visibility.js.map +1 -0
  243. package/lib/types/runtime.d.ts +272 -27
  244. package/lib/types/runtime.d.ts.map +1 -1
  245. package/lib/types/runtime.js +2751 -537
  246. package/lib/types/runtime.js.map +1 -1
  247. package/lib/types/settings-schema.d.ts +17 -15
  248. package/lib/types/settings-schema.d.ts.map +1 -1
  249. package/lib/types/settings-schema.js +50 -47
  250. package/lib/types/settings-schema.js.map +1 -1
  251. package/lib/types/settings-volatile.d.ts +4 -4
  252. package/lib/types/settings-volatile.js +4 -4
  253. package/lib/types/source-evidence.d.ts +14 -0
  254. package/lib/types/source-evidence.d.ts.map +1 -0
  255. package/lib/types/source-evidence.js +36 -0
  256. package/lib/types/source-evidence.js.map +1 -0
  257. package/lib/types/store/axis-references.d.ts +5 -0
  258. package/lib/types/store/axis-references.d.ts.map +1 -0
  259. package/lib/types/store/axis-references.js +106 -0
  260. package/lib/types/store/axis-references.js.map +1 -0
  261. package/lib/types/store/derived.d.ts +12 -1
  262. package/lib/types/store/derived.d.ts.map +1 -1
  263. package/lib/types/store/derived.js +71 -12
  264. package/lib/types/store/derived.js.map +1 -1
  265. package/lib/types/store/knowledge-db.d.ts +31 -1
  266. package/lib/types/store/knowledge-db.d.ts.map +1 -1
  267. package/lib/types/store/knowledge-db.js +252 -18
  268. package/lib/types/store/knowledge-db.js.map +1 -1
  269. package/lib/types/store/migrate.d.ts.map +1 -1
  270. package/lib/types/store/migrate.js +17 -6
  271. package/lib/types/store/migrate.js.map +1 -1
  272. package/lib/types/trajectory-types.d.ts +172 -0
  273. package/lib/types/trajectory-types.d.ts.map +1 -0
  274. package/lib/types/trajectory-types.js +36 -0
  275. package/lib/types/trajectory-types.js.map +1 -0
  276. package/lib/types/types.d.ts +59 -27
  277. package/lib/types/types.d.ts.map +1 -1
  278. package/lib/types/types.js +6 -15
  279. package/lib/types/types.js.map +1 -1
  280. package/lib/types/wiki-finalize.d.ts +16 -0
  281. package/lib/types/wiki-finalize.d.ts.map +1 -1
  282. package/lib/types/wiki-finalize.js +26 -0
  283. package/lib/types/wiki-finalize.js.map +1 -1
  284. package/lib/types/wiki-materialize.d.ts +4 -0
  285. package/lib/types/wiki-materialize.d.ts.map +1 -1
  286. package/lib/types/wiki-materialize.js +21 -7
  287. package/lib/types/wiki-materialize.js.map +1 -1
  288. package/lib/types/wiki-trial-types.d.ts +87 -0
  289. package/lib/types/wiki-trial-types.d.ts.map +1 -1
  290. package/lib/types/wiki-trial-types.js +0 -16
  291. package/lib/types/wiki-trial-types.js.map +1 -1
  292. package/lib/types/wiki-trial.d.ts +40 -16
  293. package/lib/types/wiki-trial.d.ts.map +1 -1
  294. package/lib/types/wiki-trial.js +337 -224
  295. package/lib/types/wiki-trial.js.map +1 -1
  296. package/package.json +29 -20
  297. package/python/prepare-models.py +19 -0
  298. package/python/requirements.txt +8 -6
  299. package/scripts/doctor.mjs +13 -19
  300. package/scripts/environment.mjs +29 -0
  301. package/scripts/setup-embedding.sh +15 -5
  302. package/src/agent-behavior-rules.ts +37 -0
  303. package/src/axis-registry.ts +234 -27
  304. package/src/card-organize.ts +69 -75
  305. package/src/client/AgentBehaviorRulesCard.tsx +65 -0
  306. package/src/client/AttributionEventGuide.tsx +41 -39
  307. package/src/client/AxisRegistrySection.tsx +204 -164
  308. package/src/client/CompileDialogs.tsx +44 -29
  309. package/src/client/DeleteDocumentDialog.tsx +36 -5
  310. package/src/client/DirectoryPicker.tsx +0 -10
  311. package/src/client/EnvironmentSetup.tsx +243 -0
  312. package/src/client/ExpansionGraph.tsx +13 -7
  313. package/src/client/ExtractionTasksPage.tsx +578 -0
  314. package/src/client/GuideArchitectureCanvas.tsx +470 -0
  315. package/src/client/ImportFilesDialog.tsx +2 -13
  316. package/src/client/IntentLogic.tsx +2 -2
  317. package/src/client/IntentTab.tsx +118 -47
  318. package/src/client/KnowledgeBaseTab.tsx +277 -197
  319. package/src/client/KnowledgeLayerSection.tsx +24 -6
  320. package/src/client/KnowledgePanel.tsx +58 -93
  321. package/src/client/KnowledgeSettingsDrawer.tsx +90 -0
  322. package/src/client/ListPager.tsx +15 -5
  323. package/src/client/MaterializeTrajectoryView.tsx +124 -0
  324. package/src/client/NodeSettings.tsx +210 -89
  325. package/src/client/NormalizeTrajectoryView.tsx +136 -0
  326. package/src/client/PipelineNotice.tsx +26 -0
  327. package/src/client/PreviewSections.tsx +118 -51
  328. package/src/client/RecallPanel.tsx +89 -85
  329. package/src/client/TabGuide.tsx +159 -184
  330. package/src/client/TrialReportView.tsx +391 -214
  331. package/src/client/TypeDefinitionDrawer.tsx +150 -23
  332. package/src/client/WikiCardPreview.tsx +62 -16
  333. package/src/client/WikiFlowPanel.tsx +4 -4
  334. package/src/client/arch/NodeLogicPanel.tsx +3 -4
  335. package/src/client/arch/model.ts +6 -9
  336. package/src/client/architecture.tsx +23 -33
  337. package/src/client/attribution/AttributionGraph.tsx +10 -6
  338. package/src/client/attribution/AttributionTab.tsx +136 -84
  339. package/src/client/attribution/CaseView.tsx +3 -1
  340. package/src/client/attribution/EventView.tsx +108 -0
  341. package/src/client/attribution/RunView.tsx +2 -2
  342. package/src/client/attribution/StageChrome.tsx +34 -3
  343. package/src/client/attribution/ViewSwitch.tsx +20 -5
  344. package/src/client/attribution/graph-options.ts +47 -39
  345. package/src/client/attribution/model.ts +17 -10
  346. package/src/client/config-scope.ts +3 -3
  347. package/src/client/graph/palette.ts +165 -0
  348. package/src/client/index.ts +49 -42
  349. package/src/client/pages.css +459 -135
  350. package/src/client/pages.tsx +37 -9
  351. package/src/client/problem-domains.ts +1 -1
  352. package/src/client/session-remote.ts +9 -0
  353. package/src/client/styles.css +205 -62
  354. package/src/client/wiki-trial.ts +44 -3
  355. package/src/compiler/embedding-identity.ts +21 -0
  356. package/src/compiler/snapshot.ts +17 -0
  357. package/src/config-schema.ts +8 -3
  358. package/src/convert/host.ts +56 -21
  359. package/src/convert/ingest.ts +74 -10
  360. package/src/derive-contract.ts +14 -0
  361. package/src/diagnosis/cards.ts +13 -8
  362. package/src/diagnosis/run.ts +27 -17
  363. package/src/diagnosis/schema.ts +2 -2
  364. package/src/diagnosis/seed-source.ts +5 -4
  365. package/src/diagnosis/seed.ts +6 -8
  366. package/src/diagnosis/store.ts +138 -69
  367. package/src/diagnosis/types.ts +34 -15
  368. package/src/environment/dsh-python.ts +156 -0
  369. package/src/environment/install.ts +244 -0
  370. package/src/environment/manifest.ts +28 -0
  371. package/src/environment/model-catalog.ts +147 -0
  372. package/src/environment/process.ts +54 -0
  373. package/src/environment/selection.ts +29 -0
  374. package/src/environment/service.ts +348 -0
  375. package/src/environment/types.ts +45 -0
  376. package/src/events.ts +108 -0
  377. package/src/extract-contract.ts +42 -0
  378. package/src/extract-model.ts +10 -1
  379. package/src/extract-table-contract.ts +26 -0
  380. package/src/extract-table.ts +132 -0
  381. package/src/extraction-runs.ts +70 -11
  382. package/src/fs-atomic.ts +19 -2
  383. package/src/images.ts +203 -0
  384. package/src/index.ts +553 -205
  385. package/src/intent/README.md +8 -6
  386. package/src/intent/engine.ts +44 -6
  387. package/src/intent/host.ts +59 -18
  388. package/src/intent/scope.ts +125 -0
  389. package/src/intent/tools.ts +70 -28
  390. package/src/intent/types.ts +11 -4
  391. package/src/knowledge-bases.ts +56 -20
  392. package/src/knowledge-contract.ts +114 -0
  393. package/src/knowledge.ts +138 -69
  394. package/src/layout.ts +17 -37
  395. package/src/materialize-snapshot.ts +46 -0
  396. package/src/migrate.ts +75 -2
  397. package/src/model-text.ts +109 -4
  398. package/src/oplog.ts +7 -0
  399. package/src/organize-contract.ts +83 -0
  400. package/src/pipeline/engine.ts +323 -0
  401. package/src/pipeline/store.ts +147 -0
  402. package/src/pipeline/types.ts +93 -0
  403. package/src/present.ts +10 -4
  404. package/src/preset-ids.ts +6 -0
  405. package/src/python/embedding_worker.py +66 -2
  406. package/src/query-analysis.ts +68 -8
  407. package/src/recall-rank.ts +5 -5
  408. package/src/retention.ts +76 -0
  409. package/src/retrieval/vector-index.ts +45 -0
  410. package/src/retrieval/visibility.ts +14 -0
  411. package/src/runtime.ts +2708 -567
  412. package/src/settings-schema.ts +11 -8
  413. package/src/settings-volatile.ts +4 -4
  414. package/src/source-evidence.ts +37 -0
  415. package/src/store/axis-references.ts +96 -0
  416. package/src/store/derived.ts +70 -12
  417. package/src/store/knowledge-db.ts +248 -17
  418. package/src/store/migrate.ts +16 -6
  419. package/src/trajectory-types.ts +142 -0
  420. package/src/types.ts +61 -35
  421. package/src/wiki-finalize.ts +28 -0
  422. package/src/wiki-materialize.ts +25 -6
  423. package/src/wiki-trial-types.ts +83 -1
  424. package/src/wiki-trial.ts +357 -185
  425. package/lib/types/intent/preset-tools.d.ts +0 -37
  426. package/lib/types/intent/preset-tools.d.ts.map +0 -1
  427. package/lib/types/intent/preset-tools.js +0 -45
  428. package/lib/types/intent/preset-tools.js.map +0 -1
  429. package/preset/tita-data-agent/agent.cordis.yml +0 -394
  430. package/preset/tita-data-agent/preset.yml +0 -3
  431. package/scripts/install-agent-preset.mjs +0 -92
  432. package/scripts/sync-agent-preset.mjs +0 -188
  433. package/src/client/AgentPresetCard.tsx +0 -247
  434. package/src/client/agent-preset.ts +0 -121
  435. package/src/client/convert/ConvertPanel.tsx +0 -583
  436. package/src/client/convert/styles.css +0 -138
  437. package/src/client/convert/styles.ts +0 -49
  438. package/src/intent/preset-tools.ts +0 -48
@@ -31,8 +31,10 @@ import { createHash } from 'node:crypto'
31
31
  import { mkdir } from 'node:fs/promises'
32
32
  import { dirname } from 'node:path'
33
33
  import type { SqliteDatabase, SqliteStatement } from '../fts.js'
34
+ import { applyRetention } from '../retention.js'
34
35
  import { KNOWLEDGE_DB_FILE } from '../layout.js'
35
36
  import { cardCoordinateKey, legacyCardStorageKey, type CardCoordinate, type KnowledgeCard, type SourceRaw } from '../types.js'
37
+ import type { EventEntity } from '../events.js'
36
38
 
37
39
  /** 知识库里那个文件的名字。定义在 `layout.ts`(布局的唯一真源),这里只是本模块的别名。 */
38
40
  export { KNOWLEDGE_DB_FILE }
@@ -236,7 +238,32 @@ function looksLikeAssetRef(ref: string): boolean {
236
238
  return /(?:^|\/)_?assets\//.test(ref)
237
239
  }
238
240
 
239
- /** 表结构。建库与迁移共用一份,避免两处 DDL 漂移。 */
241
+ /** Event 与坐标 WikiCard 之间的表结构。 */
242
+ const EVENT_SCHEMA_SQL = `
243
+ CREATE TABLE IF NOT EXISTS event (
244
+ id TEXT PRIMARY KEY,
245
+ identity_key TEXT NOT NULL UNIQUE,
246
+ identity_type TEXT NOT NULL,
247
+ external_id TEXT,
248
+ object_id TEXT NOT NULL DEFAULT '',
249
+ action_id TEXT NOT NULL DEFAULT '',
250
+ scene_id TEXT NOT NULL DEFAULT '',
251
+ occurred_at TEXT,
252
+ payload TEXT NOT NULL,
253
+ created_at TEXT NOT NULL,
254
+ updated_at TEXT NOT NULL
255
+ );
256
+ CREATE INDEX IF NOT EXISTS event_coordinate_idx ON event(object_id, action_id, scene_id);
257
+ CREATE INDEX IF NOT EXISTS event_occurred_at_idx ON event(occurred_at);
258
+ CREATE TABLE IF NOT EXISTS card_event (
259
+ card_id TEXT NOT NULL,
260
+ event_id TEXT NOT NULL,
261
+ ordinal INTEGER NOT NULL DEFAULT 0,
262
+ PRIMARY KEY (card_id, event_id)
263
+ );
264
+ CREATE INDEX IF NOT EXISTS card_event_event_idx ON card_event(event_id);
265
+ `
266
+
240
267
  function schemaSql(): string {
241
268
  return `
242
269
  CREATE TABLE IF NOT EXISTS blob (
@@ -310,6 +337,8 @@ CREATE TABLE IF NOT EXISTS card_knowledge (
310
337
  );
311
338
  CREATE INDEX IF NOT EXISTS card_knowledge_knowledge_idx ON card_knowledge(knowledge_id);
312
339
 
340
+ ${EVENT_SCHEMA_SQL}
341
+
313
342
  CREATE TABLE IF NOT EXISTS revision (
314
343
  entity_kind TEXT NOT NULL,
315
344
  entity_id TEXT NOT NULL,
@@ -417,15 +446,25 @@ function rebuildCardTableForSchemaMigration(db: SqliteDatabase): void {
417
446
  head_version, created_at, updated_at, object_id, action_id, scene_id)
418
447
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
419
448
  `)
449
+ /** 已经用掉的落位键。同坐标多行时后面那些要退回身份键(见下面的说明)。 */
450
+ const used = new Set<string>()
420
451
  for (const row of rows) {
421
452
  const entityId = String(row.entity_id ?? '')
422
453
  const objectId = String(row.object_id ?? '')
423
454
  const actionId = String(row.action_id ?? '')
424
455
  const sceneId = String(row.scene_id ?? '')
425
456
  const coordinate = coordinateOfRow({ object_id: objectId, action_id: actionId, scene_id: sceneId })
426
- const key = coordinate === undefined
457
+ let key = coordinate === undefined
427
458
  ? legacyCardStorageKey(entityId)
428
459
  : cardCoordinateKey(coordinate)
460
+ /**
461
+ * 同一坐标在旧表里可能有多行(那时还没有坐标唯一约束)。`coordinate_key` 是主键,
462
+ * 一旦冲突整次迁移就回滚 —— 而迁移在打开库时跑,于是**每次启动都打不开这个库**,
463
+ * 也没有修复路径。落在后面的那一行退回按 `entityId` 生成的身份键:两张卡都留住,
464
+ * 坐标冲突交给既有的审计路径去报。`entity_id` 在旧表里是唯一的,所以这个键不会重。
465
+ */
466
+ if (used.has(key)) key = legacyCardStorageKey(entityId)
467
+ used.add(key)
429
468
  insert.run(
430
469
  key,
431
470
  entityId,
@@ -587,6 +626,8 @@ interface ColumnInfo {
587
626
  name: string
588
627
  notNull: boolean
589
628
  hasDefault: boolean
629
+ /** 主键里的序号(1 起);0 = 不是主键的一部分。判「是不是同一行」要用它。 */
630
+ pk: number
590
631
  }
591
632
 
592
633
  /**
@@ -685,7 +726,7 @@ export class KnowledgeTx {
685
726
  * 会把「只改备注」「只补图」那几条路径变成一次静默的关联丢失 —— 它们本来就不该
686
727
  * 碰落位(那件事只有编译知道)。
687
728
  */
688
- putCard(card: KnowledgeCard, frontmatter: Record<string, unknown>, journal: Journal, placements?: CardKnowledgeRow[]): WriteOutcome {
729
+ putCard(card: KnowledgeCard, frontmatter: Record<string, unknown>, journal: Journal, placements?: CardKnowledgeRow[], allowCoordinateMove = false): WriteOutcome {
689
730
  const outcome = this.putEntity('card', card.entityId, card.content, frontmatter, journal, () => {
690
731
  const existing = this.raw('SELECT coordinate_key, object_id, action_id, scene_id FROM card WHERE entity_id = ?').get(card.entityId) as Record<string, unknown> | undefined
691
732
  /*
@@ -698,7 +739,7 @@ export class KnowledgeTx {
698
739
  : cardCoordinateKey(storedCoordinate)
699
740
  const oldKey = typeof existing?.coordinate_key === 'string' ? existing.coordinate_key : undefined
700
741
  if (existing !== undefined) {
701
- if (oldKey !== undefined && oldKey !== coordinateKey && !oldKey.startsWith('legacy:')) {
742
+ if (!allowCoordinateMove && oldKey !== undefined && oldKey !== coordinateKey && !oldKey.startsWith('legacy:')) {
702
743
  throw new Error(`CARD_COORDINATE_IMMUTABLE: ${card.entityId} 已绑定坐标,不能直接改主键`)
703
744
  }
704
745
  this.raw(`UPDATE card SET coordinate_key = ?, entity_type = ?, title = ?, status = ?, owner = ?,
@@ -739,6 +780,7 @@ export class KnowledgeTx {
739
780
  /** 删掉一张卡时它的关联行一起走(**代码责任**,这里没有外键)。 */
740
781
  dropPlacements(cardId: string): void {
741
782
  this.raw('DELETE FROM card_knowledge WHERE card_id = ?').run(cardId)
783
+ this.raw('DELETE FROM card_event WHERE card_id = ?').run(cardId)
742
784
  }
743
785
 
744
786
  /**
@@ -804,7 +846,7 @@ export class KnowledgeTx {
804
846
  * 下一次编译会把这批行当成「这张卡引用的知识」,而那张卡已经不存在了:表现是
805
847
  * 对账里冒出一批 `missingCard`,以及一条知识的「属于哪张卡」指向 404。
806
848
  */
807
- if (kind === 'card') this.raw('DELETE FROM card_knowledge WHERE card_id = ?').run(id)
849
+ if (kind === 'card') this.dropPlacements(id)
808
850
  this.manifest.set(`${kind}/${id}`, 0)
809
851
  }
810
852
 
@@ -821,6 +863,9 @@ export class KnowledgeTx {
821
863
  * 短事务。异步化只会把调用方的顺序打散,换不到任何东西。
822
864
  */
823
865
  export class KnowledgeDb {
866
+ private transactionSequence = 0
867
+ /** 距离上一次保留裁剪又提交了几次写事务。 */
868
+ private writesSincePrune = 0
824
869
  private constructor(private readonly db: SqliteDatabase, readonly file: string) {}
825
870
 
826
871
  /**
@@ -849,7 +894,6 @@ export class KnowledgeDb {
849
894
  return { ok: false, code: 'OPEN_FAILED', message: `打开失败:${cause instanceof Error ? cause.message : String(cause)}` }
850
895
  }
851
896
  try {
852
- db.exec('PRAGMA journal_mode = WAL')
853
897
  const owner = readPragma(db, 'application_id')
854
898
  const version = readPragma(db, 'user_version')
855
899
  /*
@@ -873,11 +917,12 @@ export class KnowledgeDb {
873
917
  * 先开的那个模块就会让另一个模块判成「这不是知识库」—— 表现是 `addSource` 报
874
918
  * 知识库不可用,而文件明明好好的。
875
919
  */
876
- const ownTables = ['knowledge', 'knowledge_sources', 'axis_entities', 'axis_aliases']
920
+ const ownTables = ['knowledge', 'knowledge_sources', 'axis_entities', 'axis_aliases', 'event', 'card_event']
877
921
  const hasUserTables = userTableCount(db) > 0
878
- const fresh = version === 0 && !hasUserTables
879
- const unmarkedKnowledgeDb = version === 0 && ownTables.some(name => tableExists(db, name))
922
+ const fresh = owner === 0 && version === 0 && !hasUserTables
923
+ const unmarkedKnowledgeDb = owner === 0 && version === 0 && ownTables.some(name => tableExists(db, name))
880
924
  if (fresh) {
925
+ db.exec('PRAGMA journal_mode = WAL')
881
926
  db.exec(schemaSql())
882
927
  db.exec(`PRAGMA application_id = ${KNOWLEDGE_DB_APPLICATION_ID}`)
883
928
  db.exec(`PRAGMA user_version = ${KNOWLEDGE_DB_SCHEMA}`)
@@ -891,9 +936,12 @@ export class KnowledgeDb {
891
936
  db.close()
892
937
  return { ok: false, code: 'SCHEMA_TOO_NEW', message: `知识库的 schema 是 ${version},本版本只认到 ${KNOWLEDGE_DB_SCHEMA}:${file}` }
893
938
  }
939
+ db.exec('PRAGMA journal_mode = WAL')
894
940
  if (version < KNOWLEDGE_DB_SCHEMA) {
895
941
  migrateSchema(db, version)
896
942
  }
943
+ // 确保知识库包含 Event 实体表和 WikiCard 聚合关系表。
944
+ db.exec(EVENT_SCHEMA_SQL)
897
945
  return { ok: true, db: new KnowledgeDb(db, file) }
898
946
  } catch (cause) {
899
947
  try { db.close() } catch { /* already closed */ }
@@ -902,11 +950,16 @@ export class KnowledgeDb {
902
950
  }
903
951
 
904
952
  close(): void {
953
+ // 关库前收一次尾:否则「最后一次裁剪之后新增的行」会一直留到下次打开。
954
+ this.pruneRetention()
905
955
  try { this.db.close() } catch { /* already closed */ }
906
956
  }
907
957
 
908
958
  raw(sql: string): SqliteStatement { return this.db.prepare(sql) }
909
959
 
960
+ /** Repositories share this validated connection; only its owner closes it. */
961
+ connection(): SqliteDatabase { return this.db }
962
+
910
963
  /**
911
964
  * 一次操作 = 一个事务 = 一行 `tree` + 若干行 `revision`。
912
965
  *
@@ -920,7 +973,8 @@ export class KnowledgeDb {
920
973
  transact<T>(entry: TreeEntry, fn: (tx: KnowledgeTx) => T): T {
921
974
  const tx = new KnowledgeTx(this)
922
975
  const at = entry.at ?? new Date().toISOString()
923
- this.db.exec('BEGIN')
976
+ const savepoint = `knowledge_op_${++this.transactionSequence}`
977
+ this.db.exec(`SAVEPOINT ${savepoint}`)
924
978
  try {
925
979
  /*
926
980
  * `INSERT OR REPLACE`:`op_id` 是**幂等键**。
@@ -933,14 +987,66 @@ export class KnowledgeDb {
933
987
  .run(entry.opId, at, entry.actor, entry.summary, '{}')
934
988
  const result = fn(tx)
935
989
  this.raw('UPDATE tree SET manifest = ? WHERE op_id = ?').run(tx.manifestJson(), entry.opId)
936
- this.db.exec('COMMIT')
990
+ this.db.exec(`RELEASE ${savepoint}`)
991
+ /**
992
+ * 裁剪**每 50 次写入**做一次,不是每次。
993
+ *
994
+ * 每次都做的话,一次卡片写入要额外跑一遍 `ROW_NUMBER()` 按实体分区的窗口(水位以上
995
+ * 收敛到约 20 版/实体,5000 个实体就是十万行排序)外加一次 blob 引用集比对 ——
996
+ * 那是把维护成本摊到每一次交互上。每 50 次一次,界面上感觉不到,库也照样有界。
997
+ */
998
+ if (++this.writesSincePrune >= 50) {
999
+ this.writesSincePrune = 0
1000
+ this.pruneRetention()
1001
+ }
937
1002
  return result
938
1003
  } catch (cause) {
939
- try { this.db.exec('ROLLBACK') } catch { /* rollback of a dead tx */ }
1004
+ try { this.db.exec(`ROLLBACK TO ${savepoint}; RELEASE ${savepoint}`) } catch { /* rollback of a dead tx */ }
940
1005
  throw cause
941
1006
  }
942
1007
  }
943
1008
 
1009
+ /**
1010
+ * 保留上限(0070)。
1011
+ *
1012
+ * 文档里写了保留策略,代码里一条 `DELETE` 都没有。这里补上**新数据**的上限,
1013
+ * 且只裁「水位以上」的行 —— 升级那一刻已有的历史一行不动(见 `retention.ts`)。
1014
+ *
1015
+ * - `revision`:机器版本(`auto`)每个实体留最近 20 版;**命名版本永不裁**
1016
+ * (`named` 是人对内容做的决定,保留策略里它是永久的)。
1017
+ * - `pipeline_extractions`:抽取检查点同时是复用缓存,每份来源留最近 5 份;
1018
+ * `pipeline_sources` 指着的那两份(当前抽取 / 当前归一)绝不删 ——
1019
+ * 删了会让那份来源显示成「还没抽取」。
1020
+ * - `blob`:水位以上、已经没有任何 revision 引用的才删。水位以下的 blob 一律留着,
1021
+ * 因为老版本可能还在引用它们。
1022
+ * - `tree`:操作台账,与处理日志同一量级。
1023
+ */
1024
+ private pruneRetention(): void {
1025
+ /*
1026
+ * 每一张都要先问在不在:`pipeline_extractions` 与 `pipeline_sources` 是**管线模块**
1027
+ * 懒建的(`PipelineStore` 的构造函数),而 knowledge.db 可能被别的模块先打开、
1028
+ * 也可能根本没跑过管线。少了这一步,这里会以「no such table」把**每一次写入**都打挂。
1029
+ *
1030
+ * 整个方法包在 try 里还有一层理由:裁剪是**尽力而为**的维护动作,它绝不能成为
1031
+ * 一次已提交写入的失败原因。真出错时宁可不裁(库继续变大),也不让用户写不进去。
1032
+ */
1033
+ try {
1034
+ if (tableExists(this.db, 'revision')) {
1035
+ applyRetention(this.db, 'revision', { table: 'revision', key: 'rowid', keep: 20, where: "kind = 'auto'", groupBy: "entity_kind || '/' || entity_id" })
1036
+ }
1037
+ if (tableExists(this.db, 'pipeline_extractions') && tableExists(this.db, 'pipeline_sources')) {
1038
+ applyRetention(this.db, 'pipeline_extractions', {
1039
+ table: 'pipeline_extractions', key: 'rowid', keep: 5, groupBy: 'source_id',
1040
+ where: "id NOT IN (SELECT extracted_id FROM pipeline_sources WHERE extracted_id IS NOT NULL) AND id NOT IN (SELECT normalized_id FROM pipeline_sources WHERE normalized_id IS NOT NULL)",
1041
+ })
1042
+ }
1043
+ if (tableExists(this.db, 'blob') && tableExists(this.db, 'revision')) {
1044
+ applyRetention(this.db, 'blob', { table: 'blob', key: 'rowid', keep: 0, where: 'hash NOT IN (SELECT blob_hash FROM revision)' })
1045
+ }
1046
+ if (tableExists(this.db, 'tree')) applyRetention(this.db, 'tree', { table: 'tree', key: 'rowid', keep: 2000 })
1047
+ } catch { /* 裁剪是尽力而为:不让它成为写入失败的原因 */ }
1048
+ }
1049
+
944
1050
  /** 记一次**没有内容变更**的操作(迁移收尾、审核结论这一类)。 */
945
1051
  recordOperation(entry: TreeEntry, manifest: Record<string, number> = {}): void {
946
1052
  this.raw('INSERT OR REPLACE INTO tree(op_id, at, actor, summary, manifest) VALUES (?, ?, ?, ?, ?)')
@@ -1008,6 +1114,93 @@ export class KnowledgeDb {
1008
1114
  sources(): SourceRecord[] { return this.readEntities('source') as SourceRecord[] }
1009
1115
  cards(): CardRecord[] { return this.readEntities('card') as CardRecord[] }
1010
1116
 
1117
+ /** Knowledge 的同层事实实体。事件正文是结构化 JSON,不经过 WikiCard 的内容版本表。 */
1118
+ events(): EventEntity[] {
1119
+ const rows = this.raw('SELECT payload FROM event ORDER BY occurred_at DESC, id').all() as Array<{ payload?: unknown }>
1120
+ const out: EventEntity[] = []
1121
+ for (const row of rows) {
1122
+ if (typeof row.payload !== 'string') continue
1123
+ try {
1124
+ const parsed: unknown = JSON.parse(row.payload)
1125
+ if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) out.push({ ...(parsed as EventEntity), kind: 'case', version: (parsed as EventEntity).version ?? 1 })
1126
+ } catch { /* 单条坏记录不阻断其他 Event 的读取 */ }
1127
+ }
1128
+ return out
1129
+ }
1130
+
1131
+ event(id: string): EventEntity | undefined {
1132
+ const row = this.raw('SELECT payload FROM event WHERE id = ?').get(id) as { payload?: unknown } | undefined
1133
+ if (typeof row?.payload !== 'string') return undefined
1134
+ try {
1135
+ const parsed: unknown = JSON.parse(row.payload)
1136
+ return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed) ? { ...(parsed as EventEntity), kind: 'case', version: (parsed as EventEntity).version ?? 1 } : undefined
1137
+ } catch { return undefined }
1138
+ }
1139
+
1140
+ writeEvent(event: EventEntity): void {
1141
+ const previous = this.event(event.id)
1142
+ const contentOf = (value: EventEntity): string => JSON.stringify({ ...value, kind: 'case', version: undefined, createdAt: undefined, updatedAt: undefined })
1143
+ event.kind = 'case'
1144
+ const latest = this.raw("SELECT MAX(version) AS v FROM revision WHERE entity_kind='case' AND entity_id=?").get(event.id) as { v?: number } | undefined
1145
+ event.version = previous && contentOf(previous) === contentOf(event) ? previous.version ?? 1 : Math.max(previous?.version ?? 0, latest?.v ?? 0) + 1
1146
+ const sql = this.connection()
1147
+ sql.exec('SAVEPOINT write_case')
1148
+ try {
1149
+ this.raw(`INSERT INTO event(id, identity_key, identity_type, external_id, object_id, action_id, scene_id, occurred_at, payload, created_at, updated_at)
1150
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1151
+ ON CONFLICT(id) DO UPDATE SET
1152
+ identity_key = excluded.identity_key,
1153
+ identity_type = excluded.identity_type,
1154
+ external_id = excluded.external_id,
1155
+ object_id = excluded.object_id,
1156
+ action_id = excluded.action_id,
1157
+ scene_id = excluded.scene_id,
1158
+ occurred_at = excluded.occurred_at,
1159
+ payload = excluded.payload,
1160
+ updated_at = excluded.updated_at`)
1161
+ .run(
1162
+ event.id, event.identityKey, event.identityType, event.externalId,
1163
+ event.coordinateIds.objectId, event.coordinateIds.actionId, event.coordinateIds.sceneId,
1164
+ event.occurredAt, JSON.stringify(event), event.createdAt, event.updatedAt,
1165
+ )
1166
+ const alreadySaved = this.raw("SELECT 1 FROM revision WHERE entity_kind='case' AND entity_id=? AND version=?").get(event.id, event.version)
1167
+ if (!alreadySaved) {
1168
+ const body = JSON.stringify(event)
1169
+ const blobHash = createHash('sha256').update(body).digest('hex')
1170
+ this.raw('INSERT OR IGNORE INTO blob(hash,body,bytes) VALUES (?,?,?)').run(blobHash, body, Buffer.byteLength(body))
1171
+ this.raw("INSERT OR IGNORE INTO revision(entity_kind,entity_id,version,blob_hash,actor,kind,summary,at,parent_version,frontmatter) VALUES ('case',?,?,?,?,?,?,?,?,?)")
1172
+ .run(event.id, event.version, blobHash, 'compiler', 'auto', '保存 Case 过程与证据', event.updatedAt, previous?.version ?? null, '{}')
1173
+ }
1174
+ sql.exec('RELEASE write_case')
1175
+ } catch (cause) { sql.exec('ROLLBACK TO write_case; RELEASE write_case'); throw cause }
1176
+ }
1177
+
1178
+ caseVersion(id: string, version: number): EventEntity | undefined {
1179
+ const row = this.raw("SELECT b.body FROM revision r JOIN blob b ON b.hash=r.blob_hash WHERE r.entity_kind='case' AND r.entity_id=? AND r.version=?").get(id, version) as { body?: unknown } | undefined
1180
+ if (typeof row?.body !== 'string') return undefined
1181
+ try { return JSON.parse(row.body) as EventEntity } catch { return undefined }
1182
+ }
1183
+
1184
+ removeEvent(id: string): void {
1185
+ this.raw('DELETE FROM card_event WHERE event_id = ?').run(id)
1186
+ this.raw('DELETE FROM event WHERE id = ?').run(id)
1187
+ }
1188
+
1189
+ cardEventIds(cardId: string): string[] {
1190
+ return (this.raw('SELECT event_id FROM card_event WHERE card_id = ? ORDER BY ordinal, event_id').all(cardId) as Array<{ event_id?: unknown }>)
1191
+ .flatMap(row => typeof row.event_id === 'string' ? [row.event_id] : [])
1192
+ }
1193
+
1194
+ setCardEvents(cardId: string, eventIds: string[]): boolean {
1195
+ const next = [...new Set(eventIds)].sort()
1196
+ const current = this.cardEventIds(cardId)
1197
+ if (JSON.stringify(current) === JSON.stringify(next)) return false
1198
+ this.raw('DELETE FROM card_event WHERE card_id = ?').run(cardId)
1199
+ const insert = this.raw('INSERT INTO card_event(card_id, event_id, ordinal) VALUES (?, ?, ?)')
1200
+ next.forEach((eventId, ordinal) => insert.run(cardId, eventId, ordinal))
1201
+ return true
1202
+ }
1203
+
1011
1204
  source(id: string): SourceRecord | undefined {
1012
1205
  return (this.readEntities('source', id) as SourceRecord[])[0]
1013
1206
  }
@@ -1184,12 +1377,13 @@ export class KnowledgeDb {
1184
1377
  * 名字拼成语法错误。
1185
1378
  */
1186
1379
  private columnsOf(schema: 'main' | 'legacy', table: string): ColumnInfo[] {
1187
- const rows = this.raw('SELECT name, "notnull" AS not_null, dflt_value AS dflt FROM pragma_table_info(?, ?)')
1380
+ const rows = this.raw('SELECT name, "notnull" AS not_null, dflt_value AS dflt, pk FROM pragma_table_info(?, ?)')
1188
1381
  .all(table, schema) as Array<Record<string, unknown>>
1189
1382
  return rows.map(row => ({
1190
1383
  name: String(row.name ?? ''),
1191
1384
  notNull: Number(row.not_null ?? 0) === 1,
1192
1385
  hasDefault: row.dflt !== null && row.dflt !== undefined,
1386
+ pk: Number(row.pk ?? 0),
1193
1387
  }))
1194
1388
  }
1195
1389
 
@@ -1266,7 +1460,8 @@ export class KnowledgeDb {
1266
1460
  const mainNames = new Set(mainColumns.map(column => column.name))
1267
1461
  const legacyNames = new Set(legacyColumns.map(column => column.name))
1268
1462
  const shared = legacyColumns.filter(column => mainNames.has(column.name)).map(column => column.name)
1269
- const dropped = legacyColumns.filter(column => !mainNames.has(column.name)).map(column => column.name)
1463
+ const dropped = legacyColumns.filter(column => !mainNames.has(column.name)
1464
+ && !((name === 'knowledge_sources' || name === 'source') && column.name === 'source_type')).map(column => column.name)
1270
1465
  const required = mainColumns.filter(column => !legacyNames.has(column.name) && column.notNull && !column.hasDefault)
1271
1466
  if (dropped.length > 0) problems.push(`${name} 的这几列在目标表里没有,内容搬不过来:${dropped.join('、')}`)
1272
1467
  if (required.length > 0) {
@@ -1276,11 +1471,47 @@ export class KnowledgeDb {
1276
1471
  if (shared.length === 0) { problems.push(`${name} 与目标表没有同名的列,行搬不动`); continue }
1277
1472
  const columns = shared.map(quoteIdentifier).join(', ')
1278
1473
  const expected = Number((this.db.prepare(`SELECT count(*) AS c FROM legacy.${quoteIdentifier(name)}`).get() as { c?: unknown } | undefined)?.c ?? 0)
1279
- this.db.exec(`INSERT OR IGNORE INTO main.${quoteIdentifier(name)} (${columns}) SELECT ${columns} FROM legacy.${quoteIdentifier(name)}`)
1474
+ /**
1475
+ * 「有没有丢行」要按**三个账**算(0070):`already`(插入**之前**主键就已存在)、
1476
+ * `inserted`(本次真的插进去的)、以及两者之外的 `unaccounted`
1477
+ * —— 被唯一索引 / 非空 / 检查约束挡掉的那些才是真的丢。
1478
+ *
1479
+ * **顺序很要紧**:`already` 必须在 `INSERT` 之前数。插完再数会把本次插入的行
1480
+ * 也算进去,重复计数恰好把丢行掩盖成 0(试过,就是这样漏掉一行的)。
1481
+ *
1482
+ * 原来那条 `copied < expected`(拿目标**总行数**比旧文件行数)则**永远不成立** ——
1483
+ * 每一次主键冲突都对应一行早就存在 —— 于是丢行之后调用方还会把旧库删掉。
1484
+ */
1485
+ const keyColumns = this.columnsOf('main', name).filter(column => column.pk > 0).map(column => column.name)
1486
+ const comparable = shared.filter(column => !keyColumns.includes(column))
1487
+ const already = keyColumns.length === 0 ? 0 : Number((this.db.prepare(
1488
+ `SELECT COUNT(*) AS c FROM legacy.${quoteIdentifier(name)} l WHERE EXISTS (
1489
+ SELECT 1 FROM main.${quoteIdentifier(name)} m WHERE ${keyColumns.map(column => `m.${quoteIdentifier(column)} IS l.${quoteIdentifier(column)}`).join(' AND ')}
1490
+ )`,
1491
+ ).get() as { c?: unknown } | undefined)?.c ?? 0)
1492
+ const run = this.db.prepare(`INSERT OR IGNORE INTO main.${quoteIdentifier(name)} (${columns}) SELECT ${columns} FROM legacy.${quoteIdentifier(name)}`).run() as { changes?: unknown } | undefined
1493
+ const inserted = Number(run?.changes ?? 0)
1280
1494
  const copied = Number((this.db.prepare(`SELECT count(*) AS c FROM main.${quoteIdentifier(name)}`).get() as { c?: unknown } | undefined)?.c ?? 0)
1281
1495
  tables[name] = copied
1282
- // 目标里原本可能已经有行(重跑、或者两个来源都写过),所以判据是「不少于」。
1283
- if (copied < expected) problems.push(`${name} 只拷进 ${copied} 行,原有 ${expected} 行`)
1496
+ if (keyColumns.length === 0) {
1497
+ if (inserted < expected) problems.push(`${name} 只拷进 ${inserted} 行(旧文件 ${expected} 行):这张表没有主键,无法逐行核对,旧文件不删`)
1498
+ } else {
1499
+ const unaccounted = expected - already - inserted
1500
+ if (unaccounted > 0) problems.push(`${name} 有 ${unaccounted} 行没能进来(主键之外还有唯一索引 / 非空 / 检查约束挡住的行):旧文件不删`)
1501
+ /**
1502
+ * 纯主键表(例如 `entity_asset(entity_kind, entity_id, ref)`)没有可比的非键列:
1503
+ * 主键相同就是同一行。重跑时报「无法比对内容」是误报,会让旧文件永久留在磁盘上。
1504
+ */
1505
+ if (comparable.length > 0) {
1506
+ const onEq = keyColumns.map(column => `m.${quoteIdentifier(column)} = l.${quoteIdentifier(column)}`).join(' AND ')
1507
+ // `IS NOT` 而不是 `<>`:NULL 与任何值都不相等,用 `<>` 会把「一边是 NULL」漏判。
1508
+ const differs = Number((this.db.prepare(
1509
+ `SELECT COUNT(*) AS c FROM legacy.${quoteIdentifier(name)} l JOIN main.${quoteIdentifier(name)} m ON ${onEq}
1510
+ WHERE ${comparable.map(column => `m.${quoteIdentifier(column)} IS NOT l.${quoteIdentifier(column)}`).join(' OR ')}`,
1511
+ ).get() as { c?: unknown } | undefined)?.c ?? 0)
1512
+ if (differs > 0) problems.push(`${name} 有 ${differs} 行与目标库主键相同但内容不同:旧文件不删`)
1513
+ }
1514
+ }
1284
1515
  } catch (cause) {
1285
1516
  problems.push(`${name} 的行没拷成:${cause instanceof Error ? cause.message : String(cause)}`)
1286
1517
  }
@@ -35,7 +35,7 @@
35
35
  import { existsSync } from 'node:fs'
36
36
  import { cp, mkdir, readFile, readdir, rename, rm, writeFile } from 'node:fs/promises'
37
37
  import { basename, dirname, join, relative } from 'node:path'
38
- import { ASSETS_DIR, SOURCE_RAW_DIR } from '../layout.js'
38
+ import { ASSETS_DIR, PUBLISHED_IMAGE_MARKER, SOURCE_RAW_DIR } from '../layout.js'
39
39
  import type { KnowledgeCard, SourceRaw } from '../types.js'
40
40
  import type { KnowledgeDb } from './knowledge-db.js'
41
41
 
@@ -240,7 +240,17 @@ export async function migrateKnowledgeTree(input: MigrationInput): Promise<Migra
240
240
  })
241
241
  }
242
242
  db.transact({ opId: sourceOp(opId, 'source', sourceId), actor: 'migration', summary: '迁移来源', at: record.updatedAt }, tx => {
243
- tx.putSource(record, parsed.frontmatter, { actor: 'migration', kind: 'named', at: record.createdAt })
243
+ const frontmatter = { ...parsed.frontmatter }
244
+ const origin = typeof record.metadata.source_url === 'string' ? record.metadata.source_url.trim() : ''
245
+ if (origin) {
246
+ frontmatter.resource ??= origin
247
+ frontmatter.sources ??= [{ id: 'origin', resource: origin, title: record.title }]
248
+ }
249
+ const parser = typeof record.metadata.parser === 'string' ? record.metadata.parser.trim() : ''
250
+ if (parser && typeof parsed.frontmatter.created_at === 'string') {
251
+ frontmatter.generated ??= { by: parser.startsWith('convert/') ? parser : `convert/${parser}`, at: parsed.frontmatter.created_at }
252
+ }
253
+ tx.putSource(record, frontmatter, { actor: 'migration', kind: 'named', at: record.createdAt })
244
254
  })
245
255
  report.sources += 1
246
256
  }
@@ -290,7 +300,7 @@ export async function migrateKnowledgeTree(input: MigrationInput): Promise<Migra
290
300
  const to = join(kbRoot, ASSETS_DIR, bucket)
291
301
  report.assetBuckets += 1
292
302
  if (dryRun) {
293
- report.assetFiles += (await listFiles(from, '')).length
303
+ report.assetFiles += (await listFiles(from, '')).filter(path => basename(path) !== PUBLISHED_IMAGE_MARKER).length
294
304
  continue
295
305
  }
296
306
  try {
@@ -306,7 +316,7 @@ export async function migrateKnowledgeTree(input: MigrationInput): Promise<Migra
306
316
  */
307
317
  if (!existsSync(to)) await rename(from, to)
308
318
  else await mergeAssetBucket(from, to)
309
- report.assetFiles += (await listFiles(to, '')).length
319
+ report.assetFiles += (await listFiles(to, '')).filter(path => basename(path) !== PUBLISHED_IMAGE_MARKER).length
310
320
  } catch (cause) {
311
321
  problems.push(`搬不动图片桶 ${bucket}:${cause instanceof Error ? cause.message : String(cause)}`)
312
322
  }
@@ -515,7 +525,7 @@ export async function exportKnowledgeTree(input: {
515
525
  for (const item of db.sources()) {
516
526
  if (only !== undefined && item.record.sourceId !== only) continue
517
527
  const path = join(targetRoot, LEGACY_SOURCE, sourceName(item.record.title, item.record.sourceId))
518
- if (await write(path, serialize(item.frontmatter, item.record.content))) sources += 1
528
+ if (await write(path, serialize({ ...item.frontmatter, source_id: item.record.sourceId }, item.record.content))) sources += 1
519
529
  }
520
530
  for (const item of db.cards()) {
521
531
  const path = join(targetRoot, LEGACY_WIKI, item.card.entityType, cardName(item.card.entityId))
@@ -555,7 +565,7 @@ async function copyContentTree(from: string, to: string, overwrite: boolean): Pr
555
565
  if (!overwrite && existsSync(target)) continue
556
566
  await mkdir(dirname(target), { recursive: true })
557
567
  await cp(source, target, { force: true })
558
- copied += 1
568
+ if (basename(source) !== PUBLISHED_IMAGE_MARKER) copied += 1
559
569
  }
560
570
  return copied
561
571
  }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * 三段轨迹的**传输形状**(抽取 / 归一 / 物化)。
3
+ *
4
+ * 抽取那段本来就有报告(`TrialReport`,见 `wiki-trial-types.ts`),形状没动。这一份只补
5
+ * 新分开的两段 —— 它们此前**没有轨迹**:只有任务里一段流式输出和一张结果表,跑完就查不到
6
+ * 「这条知识是怎么从模型给的名字变成库里的坐标的」「这张卡是按什么排出来的」。
7
+ *
8
+ * ## 为什么这些类型住在浏览器安全的一层
9
+ *
10
+ * 服务端按这个形状写,客户端按同一个形状渲染。写两份就会漂。它们不 import 任何
11
+ * `node:*`,所以可以直接被两边引用 —— 这与 `pipeline/types.ts` 是同一条规矩。
12
+ * (`client/wiki-trial.ts` 里那份手写镜像之所以必须手写,是因为它需要引用的服务端模块
13
+ * import 了 `node:fs`;这里没有那个问题,所以不重复。)
14
+ *
15
+ * ## 一份轨迹对应「一次工作」,不是一次任务
16
+ *
17
+ * - 抽取:一份来源 × 一次运行。
18
+ * - 归一:一份来源 × 一次运行(复用的那一次不落轨迹 —— 那一次什么都没做)。
19
+ * - 物化:一个卡片分组 × 一次运行。
20
+ *
21
+ * 粒度定在这里,是因为这三件事的失败与复用手忙脚乱的程度不同:一份文档抽取失败不影响
22
+ * 另一份;一张卡物化失败也不该让同批其它卡的轨迹看起来像没跑。
23
+ */
24
+
25
+ /** 轨迹属于哪一段。与 `PipelineStage` 的前三段同形(`all` 不是轨迹)。 */
26
+ export type TrajectoryStage = 'extract' | 'normalize' | 'materialize'
27
+
28
+ /** 三个名字只在这里写一次:页标题、列表徽标、日志都用它。 */
29
+ export const TRAJECTORY_STAGE_LABEL: Record<TrajectoryStage, string> = {
30
+ extract: '抽取轨迹',
31
+ normalize: '归一轨迹',
32
+ materialize: '物化轨迹',
33
+ }
34
+
35
+ /** 挤在行内时用的短名(列表徽标、筛选器)。 */
36
+ export const TRAJECTORY_STAGE_SHORT: Record<TrajectoryStage, string> = {
37
+ extract: '抽取',
38
+ normalize: '归一',
39
+ materialize: '物化',
40
+ }
41
+
42
+ /** 每条轨迹都要有的那段公共头。 */
43
+ export interface TrajectoryHead {
44
+ at: string
45
+ /** 模型接缝当时通不通。抽取以外的两段也可能根本没有模型调用。 */
46
+ model: { ok: boolean; why?: string; provider?: string; model?: string; reasoningEffort?: string }
47
+ warnings: string[]
48
+ }
49
+
50
+ /** 坐标三个分量在轨迹里只作为**展示文本**出现(真源是轴注册表的 id)。 */
51
+ export type TrajectoryAxisKind = 'object' | 'action' | 'scene'
52
+
53
+ /**
54
+ * 归一的坐标解析结论。
55
+ *
56
+ * `outcome` 的取值与 `TrialAxis.outcome` 同源:命中已有实体 / 新建候选 / 不唯一没猜 /
57
+ * 只做了文档内归一(`no-registry`)。
58
+ */
59
+ export interface NormalizeAxisRow {
60
+ kind: TrajectoryAxisKind
61
+ /** 文档里的写法。 */
62
+ name: string
63
+ outcome: 'matched' | 'new' | 'ambiguous' | 'no-registry' | string
64
+ /** 落到的实体 id(新建的是候选 id)。歧义时没有。 */
65
+ id?: string
66
+ /** 判成这个结果的理由 / 途径,模型或规则给的。 */
67
+ how?: string
68
+ aliases: string[]
69
+ parentMention?: string
70
+ }
71
+
72
+ /** 一条写进库的 Knowledge 在轨迹里的投影。 */
73
+ export interface NormalizeFactRow {
74
+ text: string
75
+ /** 「对象 · 动作 · 场景」的展示文本。 */
76
+ coordinate: string
77
+ /** 几条出处。 */
78
+ sources: number
79
+ /** 坐标仍有歧义:事实与出处都留着,但暂不进任何卡片分组。 */
80
+ unresolved: boolean
81
+ }
82
+
83
+ /** 归一轨迹:一份来源的 mention 名字 → 库内坐标,以及随之写入的事实。 */
84
+ export interface NormalizeTrajectoryReport extends TrajectoryHead {
85
+ stage: 'normalize'
86
+ source: { id: string; title: string; version: number }
87
+ /** 依据哪一条抽取记录做的归一。 */
88
+ extractionId: string
89
+ /** 归一之后存下的指纹(下一份文档归一能不能复用,看它)。 */
90
+ fingerprint: string
91
+ /** 模型这次给了多少个坐标提及。 */
92
+ mentions: number
93
+ /**
94
+ * 解析结论的分布。`mechanical` = 这一轮没接注册表,只做了文档内归一
95
+ * (三个分量各自计数,所以它是「多少条提及只做了机械归一」而不是一个布尔)。
96
+ */
97
+ counts: { matched: number; created: number; ambiguous: number; mechanical: number }
98
+ axes: NormalizeAxisRow[]
99
+ knowledge: { saved: number; merged: number; skipped: number; failures: number; unresolved: number }
100
+ events: { saved: number; merged: number; skipped: number }
101
+ rows: NormalizeFactRow[]
102
+ /** 已有坐标有没有下发进提示词,以及发了多少(0068)。 */
103
+ axesContext?: { injected: number; totals: number; truncated: number; selection?: string }
104
+ }
105
+
106
+ /** 物化轨迹里的一节。 */
107
+ export interface MaterializeSectionRow {
108
+ label: string
109
+ count: number
110
+ }
111
+
112
+ /** 物化轨迹:一个卡片分组 → 一张卡,以及它是怎么排出来的。 */
113
+ export interface MaterializeTrajectoryReport extends TrajectoryHead {
114
+ stage: 'materialize'
115
+ group: { key: string; title: string; coordinate: string; state: string; fingerprint: string }
116
+ action: 'created' | 'updated' | 'withdrawn'
117
+ card: {
118
+ id: string
119
+ title: string
120
+ status: string
121
+ /** 正文规模,字符数。 */
122
+ characters: number
123
+ /** 进卡的知识条数 / 事件数 / 图数。 */
124
+ knowledge: number
125
+ events: number
126
+ images: number
127
+ sections: MaterializeSectionRow[]
128
+ }
129
+ /**
130
+ * 卡片组织用了没有。
131
+ *
132
+ * `used: false` = 这一库没开卡片组织(`cardOrganize` 关着),正文是按坐标机械拼的;
133
+ * 关着不是失败,但两件事的正文长得不一样,报告必须分得清。
134
+ */
135
+ organize: { used: boolean; origin?: 'model' | 'fallback'; layoutHash?: string }
136
+ /** 进卡的每一条知识(带出处数),让人对着原文核。 */
137
+ rows: Array<{ id: string; text: string; sources: number }>
138
+ /** 这一组的来源文档。 */
139
+ sources: string[]
140
+ /** 正文里可以指向的其它卡片,以及为什么(`coordinate` / `relation` / `source`)。 */
141
+ related: Array<{ title: string; reason: string }>
142
+ }