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
@@ -1,8 +1,7 @@
1
1
  import { useCallback, useEffect, useRef, useState, useSyncExternalStore, type ReactNode } from 'react'
2
2
  import { Button, Field, Input, SelectMenu, Switch, cn } from '@tita-agent/ui/components'
3
- import { AlertCircle, Check, LoaderCircle, SlidersHorizontal } from '@tita-agent/ui/icons'
4
- import { DirectoryPickerField } from './DirectoryPicker.tsx'
5
- import { InfoHint } from './InfoHint.tsx'
3
+ import { AlertCircle, Check, ChevronDown, FolderOpen, LoaderCircle, SlidersHorizontal } from '@tita-agent/ui/icons'
4
+ import type { SessionRemote } from './session-remote.ts'
6
5
 
7
6
  /**
8
7
  * 方案级设置。
@@ -42,16 +41,22 @@ export interface SettingsScopeLike {
42
41
  unset?(field: string): Promise<void>
43
42
  }
44
43
 
45
- /** 设置组。编译模板是系统固定契约,不作为用户设置暴露。 */
46
- export type SettingsNode = 'storage' | 'build' | 'recall' | 'context'
44
+ /** 设置分组。编译模板是系统固定契约,不作为用户设置暴露。 */
45
+ export type SettingsNode = 'storage' | 'build' | 'recall'
47
46
 
48
47
  interface FieldSpec {
49
48
  key: string
50
49
  label: string
51
- kind: 'text' | 'number' | 'select' | 'toggle' | 'picker'
50
+ kind: 'text' | 'number' | 'select' | 'toggle' | 'location'
52
51
  fallback: string | number | boolean
53
52
  hint?: string
54
53
  options?: Array<{ value: string; label: string }>
54
+ min?: number
55
+ max?: number
56
+ step?: number
57
+ /** Low-level tuning stays out of the default view. */
58
+ advanced?: boolean
59
+ visibleWhen?: { key: string; value: string }
55
60
  /** Comma-separated array field: shown as text, saved as `string[]`. */
56
61
  list?: boolean
57
62
  }
@@ -59,8 +64,6 @@ interface FieldSpec {
59
64
  interface NodeSpec {
60
65
  scope: 'knowledge'
61
66
  title: string
62
- /** 说明,收进标题旁的 `?`。给了才渲染 —— 存储那一组 2026-10-06 起不给(见下)。 */
63
- description?: string
64
67
  fields: FieldSpec[]
65
68
  }
66
69
 
@@ -76,55 +79,42 @@ export const NODE_SETTINGS: Record<SettingsNode, NodeSpec> = {
76
79
  */
77
80
  storage: {
78
81
  scope: 'knowledge',
79
- title: '存储设置',
82
+ title: '数据位置',
80
83
  fields: [
81
- { key: 'agentRoot', label: 'Tita Agent 根路径', kind: 'picker', fallback: '' },
84
+ { key: 'agentRoot', label: 'Tita Agent 数据目录', kind: 'location', fallback: '' },
82
85
  ],
83
86
  },
84
87
  // 0025:`compile` 设置组已撤。卡片编译模板是系统固定契约,不是用户设置项。
85
88
  build: {
86
89
  scope: 'knowledge',
87
- title: '构建设置',
88
- description: '召回片段的向量编码方式与构建所用的 Python 环境。模型不可用时会降级到关键词路。',
90
+ title: '向量运行设置',
89
91
  fields: [
90
- { key: 'embeddingProvider', label: 'Embedding Provider', kind: 'select', fallback: 'wemm-python', options: [{ value: 'wemm-python', label: 'Python 本地模型' }, { value: 'ollama', label: 'Ollama(文本)' }, { value: 'none', label: '关闭(FTS fallback)' }] },
91
- { key: 'embeddingDevice', label: '运行设备', kind: 'select', fallback: 'auto', options: [{ value: 'auto', label: '自动(优先 MPS)' }, { value: 'mps', label: 'Apple MPS' }, { value: 'cpu', label: 'CPU' }] },
92
- { key: 'embeddingModel', label: 'Embedding Model', kind: 'text', fallback: '', hint: '本地模型目录,或一个 HuggingFace 模型名' },
93
- { key: 'embeddingBaseUrl', label: 'Embedding 地址', kind: 'text', fallback: 'http://127.0.0.1:11434' },
94
- { key: 'embeddingDimension', label: '向量维度', kind: 'number', fallback: 512 },
95
- { key: 'embeddingBatchSize', label: 'Embedding 批大小', kind: 'number', fallback: 8 },
96
- { key: 'embeddingTimeoutMs', label: '编码超时(毫秒)', kind: 'number', fallback: 120000 },
97
- { key: 'python', label: 'Python', kind: 'text', fallback: '', hint: '解释器路径;留空用根路径下那个本地环境' },
92
+ { key: 'embeddingProvider', label: '向量来源', kind: 'select', fallback: 'wemm-python', options: [{ value: 'wemm-python', label: '本地模型' }, { value: 'ollama', label: 'Ollama' }, { value: 'none', label: '关闭向量' }] },
93
+ { key: 'embeddingModel', label: 'Ollama 模型名称', kind: 'text', fallback: '', hint: '填写 Ollama 中已安装的模型名称。', visibleWhen: { key: 'embeddingProvider', value: 'ollama' } },
94
+ { key: 'embeddingBaseUrl', label: 'Ollama 地址', kind: 'text', fallback: 'http://127.0.0.1:11434', visibleWhen: { key: 'embeddingProvider', value: 'ollama' } },
95
+ { key: 'embeddingDevice', label: '运行设备', kind: 'select', fallback: 'auto', advanced: true, options: [{ value: 'auto', label: '自动' }, { value: 'mps', label: 'Apple MPS' }, { value: 'cpu', label: 'CPU' }] },
96
+ { key: 'embeddingDimension', label: '向量维度', kind: 'number', fallback: 512, min: 1, step: 1, advanced: true, hint: '必须与所选模型输出维度一致;修改后需要重建向量索引。' },
97
+ { key: 'embeddingBatchSize', label: '编码批大小', kind: 'number', fallback: 8, min: 1, max: 64, step: 1, advanced: true },
98
+ { key: 'embeddingTimeoutMs', label: '编码超时(毫秒)', kind: 'number', fallback: 120000, min: 1000, max: 600000, step: 1000, advanced: true },
98
99
  ],
99
100
  },
100
101
  recall: {
101
102
  scope: 'knowledge',
102
- title: '召回设置',
103
- description: 'Coordinate、关键词 BM25、向量三路各自生成 Section 候选,融合(RRF)后统一重排。知识图谱不参与候选生成。',
103
+ title: '检索与上下文',
104
104
  fields: [
105
- { key: 'recallTopK', label: '最终条数(hits)', kind: 'number', fallback: 10, hint: '1–50,默认 10。这是给模型的最终条数,不是召回深度' },
106
- { key: 'recallCandidatePool', label: '候选池深度', kind: 'number', fallback: 50, hint: '1–200,默认 50。融合与重排都作用在它上面 —— 太小则重排救不回排在窗口外的那条' },
107
- { key: 'recallMode', label: '默认召回路线', kind: 'select', fallback: 'hybrid', options: [{ value: 'hybrid', label: 'Hybrid(Coordinate + 关键词 + 向量)' }, { value: 'vector', label: '仅向量' }, { value: 'fts', label: '仅关键词' }] },
108
- { key: 'keywordThreshold', label: '关键词阈值', kind: 'number', fallback: 0.3, hint: '0–1,默认 0.3。归一化 BM25 的相对阈值:挡得住弱命中,挡不住「整批都弱」' },
109
- { key: 'vectorThreshold', label: '向量阈值(余弦)', kind: 'number', fallback: 0.15, hint: '−1–1,默认 0.15。**绝对**阈值,弃答主要靠它' },
110
- ],
111
- },
112
- context: {
113
- scope: 'knowledge',
114
- title: '重排设置',
115
- description: 'BERT cross-encoder 对**候选池**精排,低于阈值的一律丢弃,再取上下文条数。阈值是整条链路里唯一跨查询可比的相关性判据,所以「弃答」主要靠它。',
116
- fields: [
117
- { key: 'contextTopN', label: '上下文 TopN', kind: 'number', fallback: 5, hint: '1–recallTopK,默认 5。同一张 wiki 最多 2 条,其余名额留给别的卡片' },
118
- { key: 'rerankThreshold', label: '重排阈值', kind: 'number', fallback: 0.2, hint: '−10–10,默认 0.2。原始 cross-encoder 分(与 WeKnora 同刻度):低于它的不进上下文' },
119
- { key: 'rerankProvider', label: 'Rerank Provider', kind: 'select', fallback: 'bert-python', options: [{ value: 'bert-python', label: 'BERT Python(本地)' }, { value: 'none', label: '关闭(按融合分数)' }] },
120
- { key: 'rerankModel', label: 'Rerank Model', kind: 'text', fallback: '', hint: '本地模型目录,或一个 HuggingFace 模型名' },
121
- { key: 'rerankTimeoutMs', label: '重排超时(毫秒)', kind: 'number', fallback: 60000 },
105
+ { key: 'recallTopK', label: '最终召回片段数', kind: 'number', fallback: 10, min: 1, max: 50, step: 1, hint: '默认 10 条;低于重排阈值的片段会被过滤。' },
106
+ { key: 'contextTopN', label: '进入上下文的卡片数', kind: 'number', fallback: 5, min: 1, max: 50, step: 1, hint: '默认 5 张命中卡片;知识图谱的一跳扩展不占这个名额。' },
107
+ { key: 'keywordThreshold', label: '关键词阈值', kind: 'number', fallback: 0.3, min: 0, max: 1, step: 0.01, advanced: true, hint: '0–1 的归一化 BM25 分数;适合过滤弱命中,不能单独判断整批结果是否相关。' },
108
+ { key: 'vectorThreshold', label: '向量阈值', kind: 'number', fallback: 0.5, min: -1, max: 1, step: 0.01, advanced: true, hint: '原始余弦分数。默认 0.5 是当前本地模型与语料的起点,换模型后应按召回诊断校准。' },
109
+ { key: 'rerankThreshold', label: '重排最低分', kind: 'number', fallback: 0.2, min: -10, max: 10, step: 0.1, advanced: true },
110
+ { key: 'rerankTimeoutMs', label: '重排超时(毫秒)', kind: 'number', fallback: 60000, min: 1000, max: 600000, step: 1000, advanced: true },
111
+ { key: 'contextMaxChars', label: '上下文字符上限', kind: 'number', fallback: 20000, min: 0, max: 1000000, step: 1000, hint: '0 表示不限', advanced: true },
122
112
  ],
123
113
  },
124
114
  }
125
115
 
126
116
  /** 知识库那一组设置组的**展示顺序**。抽屉按它渲染。 */
127
- export const KNOWLEDGE_SETTINGS_ORDER: SettingsNode[] = ['storage', 'build', 'recall', 'context']
117
+ export const KNOWLEDGE_SETTINGS_ORDER: SettingsNode[] = ['storage', 'build', 'recall']
128
118
 
129
119
  /**
130
120
  * 卡片头上的「参数」入口(0034)。
@@ -174,9 +164,36 @@ function readField(spec: FieldSpec, value: Record<string, unknown> | undefined):
174
164
  return String(raw)
175
165
  }
176
166
 
167
+ /**
168
+ * 一个数字字段的草稿能不能落盘。返回 `null` = 能,否则是人话的原因。
169
+ *
170
+ * 这里挡的是两个具体的坏值,它们都不是「用户想存这个数」,而是**那一瞬间的输入状态**:
171
+ *
172
+ * - `Number('') === 0`:清空「向量维度」会把 0 写进设置,而 0 是被 `min` 挡掉的非法值;
173
+ * - `Number('abc') === NaN`:`JSON.stringify(NaN)` 是 `null`,存下去是一份坏设置。
174
+ *
175
+ * 所以非法文本**留在草稿里**(人看到自己敲的东西、右面有一句「值无效」),只是不写。
176
+ * 这比「敲一半就被改写」温和:中间态是人打字时的一个正常阶段,不是错误。
177
+ */
178
+ function fieldErrorOf(spec: FieldSpec, value: string | boolean): string | undefined {
179
+ if (spec.kind !== 'number') return undefined
180
+ const text = String(value).trim()
181
+ if (text === '') return '值无效:请填写一个数字'
182
+ const parsed = Number(text)
183
+ if (!Number.isFinite(parsed)) return '值无效:请填写一个数字'
184
+ if (spec.min !== undefined && parsed < spec.min) return `值无效:不能小于 ${spec.min}`
185
+ if (spec.max !== undefined && parsed > spec.max) return `值无效:不能大于 ${spec.max}`
186
+ return undefined
187
+ }
188
+
189
+ /** 数字字段只在**合法**时才写。非法时返回 `undefined`,上层跳过这一次写入。 */
177
190
  function writeField(spec: FieldSpec, draft: string | boolean): unknown {
178
191
  if (spec.kind === 'toggle') return Boolean(draft)
179
- if (spec.kind === 'number') return Number(draft)
192
+ if (spec.kind === 'number') {
193
+ const error = fieldErrorOf(spec, draft)
194
+ if (error !== undefined) return undefined
195
+ return Number(String(draft).trim())
196
+ }
180
197
  if (spec.list) return String(draft).split(',').map(item => item.trim()).filter(Boolean)
181
198
  return String(draft)
182
199
  }
@@ -187,20 +204,20 @@ function needsDebounce(spec: FieldSpec): boolean {
187
204
  }
188
205
 
189
206
  function TextField({ spec, value, onChange, disabled, resolved }: { spec: FieldSpec; value: string; onChange: (value: string) => void; disabled: boolean; resolved?: string }) {
190
- /* 两条说明各自成行:`Field` 的说明位是一个 span,两条挤在一行会读成一句。 */
191
- const hint = <>
192
- {spec.hint ? <span className="block">{spec.hint}</span> : null}
193
- {/* 留空 = 自动。占位符显示**解析后**的真实路径 —— 否则「自动」等于没说。 */}
194
- {resolved ? <span className="block">留空 = 自动:<code>{resolved}</code></span> : null}
195
- </>
196
- return <Field label={spec.label} hint={hint}>
207
+ const invalid = fieldErrorOf(spec, value)
208
+ return <Field label={spec.label} hint={spec.hint}>
197
209
  <Input
198
210
  type={spec.kind === 'number' ? 'number' : 'text'}
199
211
  value={value}
200
212
  disabled={disabled}
213
+ min={spec.min}
214
+ max={spec.max}
215
+ step={spec.step}
201
216
  {...(resolved ? { placeholder: resolved } : {})}
202
217
  onChange={event => onChange(event.target.value)}
203
218
  />
219
+ {/* 非法草稿**留在输入框里**,只在旁边说一句「值无效、不会写」—— 见 `fieldErrorOf`。 */}
220
+ {invalid && <span className="tita-settingInvalid" role="status">{invalid}</span>}
204
221
  </Field>
205
222
  }
206
223
 
@@ -240,30 +257,99 @@ function ToggleField({ spec, value, onChange, disabled }: { spec: FieldSpec; val
240
257
  </Field>
241
258
  }
242
259
 
260
+ /**
261
+ * 固定的数据目录:只读展示实际路径,打开动作交给 DSH 的 session 能力。
262
+ */
263
+ function StorageLocationField({ label, path, session, onError, loadError, onRetry }: {
264
+ label: string
265
+ path?: string
266
+ session?: SessionRemote['session']
267
+ onError: (message: string | null) => void
268
+ /** `/knowledge/layout` 的读取失败。它发生在这个字段上,所以报在这个字段旁边。 */
269
+ loadError?: string | null
270
+ /** 重读一次布局。不给人这条路的话,一次瞬时失败会永久停在一句占位文字上。 */
271
+ onRetry?: () => void
272
+ }): ReactNode {
273
+ const [opening, setOpening] = useState(false)
274
+
275
+ const open = async (): Promise<void> => {
276
+ if (!path) return
277
+ onError(null)
278
+ if (!session || typeof session.openWorkspacePath !== 'function') {
279
+ onError('宿主没有提供打开目录的能力,请手动打开下面显示的路径。')
280
+ return
281
+ }
282
+ setOpening(true)
283
+ try {
284
+ const result = await session.openWorkspacePath({ path, action: 'reveal' })
285
+ if (!result.ok) onError(result.message ?? '宿主拒绝打开这个目录,请手动打开下面显示的路径。')
286
+ } catch (cause) {
287
+ onError(`${cause instanceof Error ? cause.message : String(cause)} 请手动打开下面显示的路径。`)
288
+ } finally {
289
+ setOpening(false)
290
+ }
291
+ }
292
+
293
+ return <Field as="div" label={label}>
294
+ <div className="tita-pickerRow">
295
+ {/* 读不到布局时占位文字要跟着变:原来的「正在读取数据目录…」在失败之后
296
+ 是一句**永远为真**的话,而它掩盖了「已经不读了」这个事实。 */}
297
+ <Input readOnly value={path ?? ''} placeholder={loadError ? '未能读取数据目录' : '正在读取数据目录…'} aria-label={label} />
298
+ <Button variant="outline" onClick={() => void open()} disabled={!path || opening}>
299
+ {opening ? <LoaderCircle size={14} className="tita-spin" /> : <FolderOpen size={14} />}
300
+ {opening ? '正在打开…' : '打开目录'}
301
+ </Button>
302
+ </div>
303
+ {loadError && <p className="tita-settingsRetry" role="alert">
304
+ <AlertCircle size={13} />
305
+ <span>读取数据目录失败:{loadError}</span>
306
+ {onRetry && <Button variant="ghost" size="sm" onClick={onRetry}>重新读取</Button>}
307
+ </p>}
308
+ </Field>
309
+ }
310
+
243
311
  /** 写入状态。`saved` 只短暂出现,`error` 留到下一次改动。 */
244
312
  type SaveState = 'idle' | 'saving' | 'saved' | 'error'
245
313
 
246
- export function NodeSettingsPanel({ node, knowledge, pickerKind = null, resolved = {}, highlight = false }: {
314
+ export function NodeSettingsPanel({ node, knowledge, agentRoot, session, resolved = {}, highlight = false, showAdvanced = false, advancedLabel, layoutError = null, onRetryLayout }: {
247
315
  node: SettingsNode
248
316
  knowledge?: SettingsScopeLike
249
- /** 宿主提供哪种目录选择交互;`null` 表示没挂目录选择器。 */
250
- pickerKind?: string | null
317
+ /** `/knowledge/layout` 返回的固定 Agent 根目录。 */
318
+ agentRoot?: string
319
+ /** DSH 提供的系统文件管理器能力,只用于打开固定目录。 */
320
+ session?: SessionRemote['session']
251
321
  /** 字段键 → 「留空时实际会用什么」。0029:家当路径空着就是跟着根路径走。 */
252
322
  resolved?: Record<string, string>
253
323
  /** 由卡片头的「参数」chip 定位过来的那一块:一次性强调 + 接管焦点。 */
254
324
  highlight?: boolean
325
+ /** Show the model and retrieval tuning fields. */
326
+ showAdvanced?: boolean
327
+ /** Keep low-frequency fields within this group, behind a named disclosure. */
328
+ advancedLabel?: string
329
+ /** `/knowledge/layout` 读取失败时的原话;只有「数据位置」那一块用得到。 */
330
+ layoutError?: string | null
331
+ /** 重读一次布局。 */
332
+ onRetryLayout?: () => void
255
333
  }): ReactNode {
256
334
  const spec = NODE_SETTINGS[node]
257
335
  const scope = knowledge
258
336
  const state = useScope(knowledge)
259
337
  const [draft, setDraft] = useState<Record<string, string | boolean>>({})
260
338
  const [saveState, setSaveState] = useState<SaveState>('idle')
339
+ const [advancedOpen, setAdvancedOpen] = useState(false)
261
340
  const [error, setError] = useState<string | null>(null)
262
- /** 待落地的防抖写入,按字段键索引。 */
263
- const timers = useRef(new Map<string, ReturnType<typeof setTimeout>>())
341
+ /**
342
+ * 待落地的防抖写入,按字段键索引。
343
+ *
344
+ * 除了计时器,它还记着**那次要写的值**:卸载时那个 `setTimeout` 不会再回来,
345
+ * 而草稿 state 在那一刻不一定已经渲染过(`setDraft` 是异步的)—— 从 timer 里读
346
+ * 值才是「用户最后敲进去的东西」的唯一可靠来源。
347
+ */
348
+ const pending = useRef(new Map<string, { timer: ReturnType<typeof setTimeout>; field: FieldSpec; value: string | boolean }>())
264
349
  /** 正在进行的写入数。用它挡住「写完 → revision 变 → 重置草稿」把输入抹掉。 */
265
350
  const inflight = useRef(0)
266
351
  const savedTimer = useRef<ReturnType<typeof setTimeout> | null>(null)
352
+ const disabled = !state.writable || state.status !== 'ready'
267
353
 
268
354
  useEffect(() => {
269
355
  // 有写入在飞的时候不重置草稿:那一刻 `state.value` 还是旧值,重置会把用户刚
@@ -272,21 +358,16 @@ export function NodeSettingsPanel({ node, knowledge, pickerKind = null, resolved
272
358
  setDraft(Object.fromEntries(spec.fields.map(field => [field.key, readField(field, state.value)])))
273
359
  }, [node, state.revision, state.value])
274
360
 
275
- // 卸载时把还没落地的防抖写入丢掉并清计时器:一个已经离开的面板不该再写设置。
276
- useEffect(() => () => {
277
- for (const timer of timers.current.values()) clearTimeout(timer)
278
- timers.current.clear()
279
- if (savedTimer.current) clearTimeout(savedTimer.current)
280
- }, [])
281
-
282
- const disabled = !state.writable || state.status !== 'ready'
283
-
284
361
  const persist = useCallback(async (field: FieldSpec, raw: string | boolean): Promise<void> => {
285
362
  if (!scope || disabled) return
363
+ const value = writeField(field, raw)
364
+ // 非法草稿跳过这一次写入(见 `fieldErrorOf`)。它不是失败,也不该报错 ——
365
+ // 只是「这一笔现在还不能写」,草稿留在输入框里等人把它改对。
366
+ if (value === undefined) return
286
367
  inflight.current += 1
287
368
  setSaveState('saving'); setError(null)
288
369
  try {
289
- await scope.set(field.key, writeField(field, raw))
370
+ await scope.set(field.key, value)
290
371
  setSaveState('saved')
291
372
  if (savedTimer.current) clearTimeout(savedTimer.current)
292
373
  // 「已保存」只是一个确认,不是一条状态:两秒后收回,免得它常驻成一个
@@ -300,18 +381,35 @@ export function NodeSettingsPanel({ node, knowledge, pickerKind = null, resolved
300
381
  }
301
382
  }, [scope, disabled])
302
383
 
384
+ // 卸载时把还没落地的防抖写入**冲出去**,而不是丢掉:用户敲完立刻按 Esc 是常规操作,
385
+ // 而丢掉的表现是「输入框里的值过一会儿自己变回去了」,中间连一个保存指示都没有。
386
+ // 冲出去就是替那一个 `setTimeout` 把事做完。卸载后 `persist` 里的 setState 会被
387
+ // React 忽略(它已经不在树上了),写入本身照旧发出去。
388
+ useEffect(() => () => {
389
+ for (const { timer, field, value } of pending.current.values()) {
390
+ clearTimeout(timer)
391
+ void persist(field, value)
392
+ }
393
+ pending.current.clear()
394
+ if (savedTimer.current) clearTimeout(savedTimer.current)
395
+ }, []) // eslint-disable-line react-hooks/exhaustive-deps
396
+
303
397
  const update = (field: FieldSpec, value: string | boolean): void => {
304
398
  setDraft(current => ({ ...current, [field.key]: value }))
305
399
  if (!needsDebounce(field)) {
306
400
  void persist(field, value)
307
401
  return
308
402
  }
309
- const existing = timers.current.get(field.key)
310
- if (existing) clearTimeout(existing)
311
- timers.current.set(field.key, setTimeout(() => {
312
- timers.current.delete(field.key)
313
- void persist(field, value)
314
- }, 600))
403
+ const existing = pending.current.get(field.key)
404
+ if (existing) clearTimeout(existing.timer)
405
+ pending.current.set(field.key, {
406
+ field,
407
+ value,
408
+ timer: setTimeout(() => {
409
+ pending.current.delete(field.key)
410
+ void persist(field, value)
411
+ }, 600),
412
+ })
315
413
  }
316
414
 
317
415
  const meta = state.status === 'loading'
@@ -323,12 +421,45 @@ export function NodeSettingsPanel({ node, knowledge, pickerKind = null, resolved
323
421
  : saveState === 'saved'
324
422
  ? '已保存'
325
423
  : saveState === 'error'
326
- ? '未保存'
327
- : state.mode === 'host' ? '改动即生效' : '当前会话'
424
+ ? '保存失败'
425
+ : ''
426
+
427
+ const visibleFields = spec.fields.filter(field => {
428
+ if (!field.visibleWhen) return true
429
+ const dependency = spec.fields.find(item => item.key === field.visibleWhen?.key)
430
+ const value = draft[field.visibleWhen.key] ?? (dependency ? readField(dependency, state.value) : undefined)
431
+ return value === field.visibleWhen.value
432
+ })
433
+
434
+ const fields = visibleFields.filter(field => showAdvanced || !field.advanced)
435
+ const advancedFields = visibleFields.filter(field => field.advanced)
328
436
 
329
437
  const anchor = settingsAnchorId(node)
330
438
  const titleId = `${anchor}-title`
331
439
 
440
+ const renderField = (field: FieldSpec): ReactNode => field.kind === 'toggle'
441
+ ? <ToggleField key={field.key} spec={field} value={Boolean(draft[field.key])} onChange={value => update(field, value)} disabled={disabled} />
442
+ : field.kind === 'select'
443
+ ? <SelectField key={field.key} spec={field} value={String(draft[field.key] ?? '')} onChange={value => update(field, value)} disabled={disabled} />
444
+ : field.kind === 'location'
445
+ ? <StorageLocationField
446
+ key={field.key}
447
+ label={field.label}
448
+ path={agentRoot}
449
+ session={session}
450
+ onError={setError}
451
+ loadError={layoutError}
452
+ {...(onRetryLayout ? { onRetry: onRetryLayout } : {})}
453
+ />
454
+ : <TextField
455
+ key={field.key}
456
+ spec={field}
457
+ value={String(draft[field.key] ?? '')}
458
+ onChange={value => update(field, value)}
459
+ disabled={disabled}
460
+ {...(resolved[field.key] && !(field.key === 'embeddingModel' && draft.embeddingProvider === 'ollama') ? { resolved: resolved[field.key]! } : {})}
461
+ />
462
+
332
463
  return <section
333
464
  id={anchor}
334
465
  className={cn('tita-settingsSection', highlight && 'tita-settingsSectionOn')}
@@ -339,9 +470,7 @@ export function NodeSettingsPanel({ node, knowledge, pickerKind = null, resolved
339
470
  aria-labelledby={titleId}
340
471
  >
341
472
  <div className="tita-settingsSectionHeader">
342
- {/* 说明从标题下面挪到图标后面:常驻文字会把控件挤出首屏,而说明本身有价值。
343
- 见 `InfoHint`。 */}
344
- <div><h3 id={titleId}>{spec.title}{spec.description && <InfoHint text={spec.description} />}</h3></div>
473
+ <div><h3 id={titleId}>{spec.title}</h3></div>
345
474
  <div className={`tita-settingMeta${saveState === 'error' ? ' tita-settingMetaBad' : ''}`} role="status" aria-live="polite">
346
475
  {saveState === 'saving' && <LoaderCircle size={11} className="tita-spin" />}
347
476
  {saveState === 'saved' && <Check size={11} />}
@@ -350,21 +479,13 @@ export function NodeSettingsPanel({ node, knowledge, pickerKind = null, resolved
350
479
  </div>
351
480
  {error && <div className="tita-settingsError" role="alert"><AlertCircle size={13} />{error}</div>}
352
481
  <div className="tita-settingsGrid">
353
- {spec.fields.map(field => field.kind === 'toggle'
354
- ? <ToggleField key={field.key} spec={field} value={Boolean(draft[field.key])} onChange={value => update(field, value)} disabled={disabled} />
355
- : field.kind === 'select'
356
- ? <SelectField key={field.key} spec={field} value={String(draft[field.key] ?? '')} onChange={value => update(field, value)} disabled={disabled} />
357
- : field.kind === 'picker'
358
- ? <DirectoryPickerField
359
- key={field.key}
360
- label={field.label}
361
- value={String(draft[field.key] ?? '')}
362
- disabled={disabled}
363
- pickerKind={pickerKind}
364
- onError={setError}
365
- onChange={value => update(field, value)}
366
- />
367
- : <TextField key={field.key} spec={field} value={String(draft[field.key] ?? '')} onChange={value => update(field, value)} disabled={disabled} {...(resolved[field.key] ? { resolved: resolved[field.key]! } : {})} />)}
482
+ {fields.map(renderField)}
368
483
  </div>
484
+ {!showAdvanced && advancedLabel && advancedFields.length > 0 && <div className="mt-5 border-t border-tita-line pt-3">
485
+ <Button variant="ghost" size="sm" className="-ml-2" aria-expanded={advancedOpen} aria-controls={`${anchor}-advanced`} onClick={() => setAdvancedOpen(value => !value)}>
486
+ <ChevronDown size={14} className={advancedOpen ? 'rotate-180' : ''} />{advancedLabel}
487
+ </Button>
488
+ {advancedOpen && <div id={`${anchor}-advanced`} className="tita-settingsGrid mt-4">{advancedFields.map(renderField)}</div>}
489
+ </div>}
369
490
  </section>
370
491
  }
@@ -0,0 +1,136 @@
1
+ /**
2
+ * 归一轨迹的渲染(2026-10-18)。
3
+ *
4
+ * 归一这一段原来只留下两个东西:任务里一段会滚走的流式输出,和最终写进事实表的那些行。
5
+ * 中间那一步 ——「模型给的这些名字里,哪些命中了已有实体、哪些新建了候选、哪些因为不唯一
6
+ * 没猜」—— 跑完就查不到了。而它恰好是坐标错了之后唯一能复盘的东西:卡名错了,根在这里。
7
+ *
8
+ * 所以这一页只回答三个问题,按这个顺序:
9
+ *
10
+ * 1. **这次归一动了多少**(提及数、命中/新建/歧义、写进多少条事实)。
11
+ * 2. **每个名字落到了哪里**(名字 → 实体 id,以及凭什么)。
12
+ * 3. **写进去的事实长什么样**(正文、坐标、几条出处、哪几条还悬着)。
13
+ *
14
+ * 样式复用抽取报告那一套(`.tita-trial*`):三份轨迹是同一件事的三种粒度,
15
+ * 长得不一样会让人以为是三个不同的功能。
16
+ */
17
+
18
+ import { type ReactNode } from 'react'
19
+ import { TriangleAlert } from '@tita-agent/ui/icons'
20
+ import { cn } from '@tita-agent/ui/components'
21
+ import { styles } from './styles.ts'
22
+ import type { NormalizeTrajectoryReport } from '../trajectory-types.js'
23
+
24
+ /** 名字最多摊这么多行。再多就该看总体结论,不该在页面上滚。 */
25
+ const AXIS_CAP = 200
26
+ const FACT_CAP = 200
27
+
28
+ const OUTCOME_LABEL: Record<string, string> = {
29
+ matched: '命中已有',
30
+ new: '新建候选',
31
+ ambiguous: '不唯一,没猜',
32
+ 'no-registry': '只做文档内归一',
33
+ }
34
+
35
+ export function NormalizeTrajectory({ report }: { report: NormalizeTrajectoryReport }): ReactNode {
36
+ const axes = report.axes ?? []
37
+ const rows = report.rows ?? []
38
+ const counts = report.counts ?? { matched: 0, created: 0, ambiguous: 0, mechanical: 0 }
39
+ const modelOk = report.model?.ok !== false
40
+ return <div className={styles.trialReport}>
41
+ <section className={styles.trialOverview} aria-labelledby="normalize-overview-title">
42
+ <div className={styles.trialSectionHead}>
43
+ <h2 id="normalize-overview-title">结果概览</h2>
44
+ <span>{report.knowledge?.unresolved ? `${report.knowledge.unresolved} 条坐标未决` : modelOk ? '已完成' : '需检查'}</span>
45
+ </div>
46
+ <dl className={styles.trialOverviewStats}>
47
+ <div><dt>坐标提及</dt><dd>{report.mentions ?? axes.length}</dd></div>
48
+ <div><dt>命中 / 新建</dt><dd>{counts.matched} / {counts.created}<small>已有实体 / 候选</small></dd></div>
49
+ <div><dt>歧义未决</dt><dd>{counts.ambiguous}<small>不猜,留给人</small></dd></div>
50
+ <div><dt>写入 Knowledge</dt><dd>{report.knowledge?.saved ?? 0}<small>另并入 {report.knowledge?.merged ?? 0} 条</small></dd></div>
51
+ <div><dt>写入 Event</dt><dd>{report.events?.saved ?? 0}<small>另并入 {report.events?.merged ?? 0} 个</small></dd></div>
52
+ </dl>
53
+ <p className={styles.trialOverviewTitle}>
54
+ <strong>{report.source?.title || '(来源已删除)'}</strong>
55
+ <span>v{report.source?.version ?? '?'} · 依据抽取记录 {report.extractionId || '—'}</span>
56
+ </p>
57
+ </section>
58
+
59
+ {!modelOk && <p className={styles.trialError} role="alert">
60
+ <TriangleAlert size={12} />{report.model?.why ?? '归一没有跑通'} · 这一份文档的事实没有写完。
61
+ </p>}
62
+
63
+ {report.warnings.length > 0 && <section className={styles.trialSection} aria-labelledby="normalize-warnings-title">
64
+ <div className={styles.trialSectionHead}>
65
+ <h2 id="normalize-warnings-title">警告</h2>
66
+ <span>{report.warnings.length} 条</span>
67
+ </div>
68
+ <ul className={styles.trialCandidates}>{report.warnings.map((warning, index) => <li key={index}>
69
+ {/* 不加装饰图标:这一段就叫「警告」,一条黄三角再标一次是同一句话说两遍。
70
+ 而且 `trialCandidates > li` 是上下两行的栅格 —— 图标单占一行会让正文掉到第三行。 */}
71
+ <span className={styles.trialCandName}>{warning}</span>
72
+ </li>)}</ul>
73
+ </section>}
74
+
75
+ <section className={styles.trialSection} aria-labelledby="normalize-axes-title">
76
+ <div className={styles.trialSectionHead}>
77
+ <h2 id="normalize-axes-title">坐标解析</h2>
78
+ <span>{axes.length} 个名字{counts.mechanical > 0 ? ` · ${counts.mechanical} 个只做了机械归一` : ''}</span>
79
+ </div>
80
+ {axes.length === 0
81
+ ? <p className={styles.trialEmpty}>这次归一没有解析任何坐标名。</p>
82
+ : <ul className={styles.trialItems}>{axes.slice(0, AXIS_CAP).map((axis, index) => <li
83
+ key={`${axis.kind}-${axis.name}-${index}`}
84
+ className={axis.outcome === 'matched' ? styles.trialItem : `${styles.trialItem} ${styles.trialItemWarn}`}
85
+ >
86
+ <span className={styles.trialItemRef}>{axis.name}</span>
87
+ <span className={styles.trialItemArrow} aria-hidden="true">→</span>
88
+ <span className={styles.trialItemValue}>
89
+ {(OUTCOME_LABEL[axis.outcome] ?? axis.outcome)}{axis.id ? ` ${axis.id}` : ''}
90
+ </span>
91
+ <span className={styles.trialItemNote}>
92
+ {KIND_LABEL[axis.kind] ?? axis.kind}{axis.how ? ` · ${axis.how}` : ''}{axis.parentMention ? ` · 作用域:${axis.parentMention}` : ''}
93
+ </span>
94
+ </li>)}</ul>}
95
+ {axes.length > AXIS_CAP && <p className={styles.trialItemsMore}>另有 {axes.length - AXIS_CAP} 个名字未展开。</p>}
96
+ </section>
97
+
98
+ <section className={styles.trialSection} aria-labelledby="normalize-facts-title">
99
+ <div className={styles.trialSectionHead}>
100
+ <h2 id="normalize-facts-title">写入的事实</h2>
101
+ <span>{rows.length} 条{report.knowledge?.unresolved ? ` · ${report.knowledge.unresolved} 条坐标未决` : ''}</span>
102
+ </div>
103
+ {rows.length === 0
104
+ ? <p className={styles.trialEmpty}>这次归一没有写入 Knowledge。</p>
105
+ : <ul className={styles.trialOutputList}>{rows.slice(0, FACT_CAP).map((row, index) => <li key={index} className={styles.trialOutputItem}>
106
+ <div className={styles.trialCandHead}>
107
+ <span className={cn(styles.trialCandName, row.unresolved && styles.trialCandBad)}>{row.text}</span>
108
+ </div>
109
+ <div className={styles.trialOutputMeta}>
110
+ {row.coordinate} · {row.sources} 条出处
111
+ {row.unresolved && <strong>坐标未决</strong>}
112
+ </div>
113
+ </li>)}</ul>}
114
+ {rows.length > FACT_CAP && <p className={styles.trialMore}>另有 {rows.length - FACT_CAP} 条未展开。</p>}
115
+ </section>
116
+
117
+ <section className={styles.trialSection} aria-labelledby="normalize-fingerprint-title">
118
+ <div className={styles.trialSectionHead}>
119
+ <h2 id="normalize-fingerprint-title">续跑信息</h2>
120
+ <span>指纹没变,下一次归一会直接复用这一份</span>
121
+ </div>
122
+ <dl className={styles.trialRows}>
123
+ <div className={styles.trialRow}><dt>归一指纹</dt><dd>{report.fingerprint || '—'}</dd></div>
124
+ <div className={styles.trialRow}>
125
+ <dt>已下发坐标</dt>
126
+ <dd>{report.axesContext === undefined
127
+ ? '这次没有下发(没接注册表,或库里还没有实体)'
128
+ : `${report.axesContext.injected} / ${report.axesContext.totals} 条${report.axesContext.truncated > 0 ? ` · 未下发 ${report.axesContext.truncated} 条` : ''}${report.axesContext.selection === 'relevant' ? ' · 当前文档相关候选' : report.axesContext.selection === 'full' ? ' · 全集' : ''}`}</dd>
129
+ </div>
130
+ <div className={styles.trialRow}><dt>模型</dt><dd>{report.model?.model ? `${report.model.provider ? `${report.model.provider} / ` : ''}${report.model.model}${report.model.reasoningEffort ? ` · ${report.model.reasoningEffort}` : ''}` : '未记录'}</dd></div>
131
+ </dl>
132
+ </section>
133
+ </div>
134
+ }
135
+
136
+ const KIND_LABEL: Record<string, string> = { object: '对象', action: '动作', scene: '场景' }
@@ -0,0 +1,26 @@
1
+ import { useEffect, useState } from 'react'
2
+ import { Button } from '@tita-agent/ui/components'
3
+ import type { PipelineStatus } from '../pipeline/types.js'
4
+ import { requestJson } from './request.ts'
5
+
6
+ export function PipelineNotice({ kb, revision, onOpen }: { kb: string; revision?: string; onOpen: () => void }) {
7
+ const [status, setStatus] = useState<PipelineStatus>()
8
+ useEffect(() => {
9
+ let active = true
10
+ const refresh = (): void => { void requestJson<PipelineStatus>(`/knowledge/pipeline?kb=${encodeURIComponent(kb)}`).then(result => { if (active && result.data) setStatus(result.data) }).catch(() => undefined) }
11
+ refresh()
12
+ window.addEventListener('focus', refresh)
13
+ return () => { active = false; window.removeEventListener('focus', refresh) }
14
+ }, [kb, revision])
15
+ if (!status) return null
16
+ const pending = status.pendingNew + status.pendingUpdated + status.pendingWithdrawn
17
+ const parts = [
18
+ status.sources.filter(source => source.state === 'pending' || source.state === 'outdated').length,
19
+ status.sources.filter(source => source.state === 'extracted').length,
20
+ ]
21
+ if (!parts.some(Boolean) && !pending && !status.indexPending) return null
22
+ return <div className="mx-3 flex min-w-0 items-center gap-2 border-b border-tita-line py-2 text-xs text-tita-ink-muted" role="status">
23
+ <span className="min-w-0 flex-1 leading-relaxed">{[parts[0] ? `${parts[0]} 份待抽取` : '', parts[1] ? `${parts[1]} 份待归一` : '', pending ? `${pending} 张 WikiCard 待物化` : '', status.indexPending ? '索引待更新' : ''].filter(Boolean).join(' · ')}</span>
24
+ <Button size="sm" variant="ghost" onClick={onOpen}>处理</Button>
25
+ </div>
26
+ }