dsh-data-domain-knowledge 0.1.1-alpha.2 → 0.1.1-alpha.4

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 (252) hide show
  1. package/README.md +33 -16
  2. package/lib/client.js +1042 -2097
  3. package/lib/contract-refs.d.ts +0 -1
  4. package/lib/doc-page.js +1 -1
  5. package/lib/pages.js +2489 -1060
  6. package/lib/types/agent-behavior-rules.d.ts.map +1 -1
  7. package/lib/types/agent-behavior-rules.js +3 -2
  8. package/lib/types/agent-behavior-rules.js.map +1 -1
  9. package/lib/types/axis-contract.js +1 -1
  10. package/lib/types/axis-contract.js.map +1 -1
  11. package/lib/types/card-organize.d.ts +2 -1
  12. package/lib/types/card-organize.d.ts.map +1 -1
  13. package/lib/types/card-organize.js +5 -3
  14. package/lib/types/card-organize.js.map +1 -1
  15. package/lib/types/config-schema.d.ts +1 -1
  16. package/lib/types/config-schema.js +1 -1
  17. package/lib/types/convert/host.d.ts +2 -3
  18. package/lib/types/convert/host.d.ts.map +1 -1
  19. package/lib/types/convert/host.js +11 -2
  20. package/lib/types/convert/host.js.map +1 -1
  21. package/lib/types/convert/ingest.d.ts +14 -20
  22. package/lib/types/convert/ingest.d.ts.map +1 -1
  23. package/lib/types/convert/ingest.js +8 -5
  24. package/lib/types/convert/ingest.js.map +1 -1
  25. package/lib/types/diagnosis/store.d.ts +2 -2
  26. package/lib/types/diagnosis/store.d.ts.map +1 -1
  27. package/lib/types/diagnosis/store.js +10 -2
  28. package/lib/types/diagnosis/store.js.map +1 -1
  29. package/lib/types/diagnosis/types.d.ts +1 -1
  30. package/lib/types/diagnosis/types.js +1 -1
  31. package/lib/types/extract-model.d.ts +3 -0
  32. package/lib/types/extract-model.d.ts.map +1 -1
  33. package/lib/types/extract-model.js.map +1 -1
  34. package/lib/types/images.js +1 -1
  35. package/lib/types/index.d.ts +0 -4
  36. package/lib/types/index.d.ts.map +1 -1
  37. package/lib/types/index.js +99 -91
  38. package/lib/types/index.js.map +1 -1
  39. package/lib/types/knowledge-bases.d.ts +3 -0
  40. package/lib/types/knowledge-bases.d.ts.map +1 -1
  41. package/lib/types/knowledge-bases.js +22 -2
  42. package/lib/types/knowledge-bases.js.map +1 -1
  43. package/lib/types/knowledge-contract.d.ts +6 -0
  44. package/lib/types/knowledge-contract.d.ts.map +1 -1
  45. package/lib/types/knowledge-contract.js +6 -2
  46. package/lib/types/knowledge-contract.js.map +1 -1
  47. package/lib/types/layout.d.ts +9 -26
  48. package/lib/types/layout.d.ts.map +1 -1
  49. package/lib/types/layout.js +10 -28
  50. package/lib/types/layout.js.map +1 -1
  51. package/lib/types/materialize-snapshot.d.ts +5 -0
  52. package/lib/types/materialize-snapshot.d.ts.map +1 -1
  53. package/lib/types/migrate.d.ts +3 -1
  54. package/lib/types/migrate.d.ts.map +1 -1
  55. package/lib/types/migrate.js +3 -1
  56. package/lib/types/migrate.js.map +1 -1
  57. package/lib/types/model-text.d.ts +7 -0
  58. package/lib/types/model-text.d.ts.map +1 -1
  59. package/lib/types/model-text.js +31 -9
  60. package/lib/types/model-text.js.map +1 -1
  61. package/lib/types/organize-contract.d.ts.map +1 -1
  62. package/lib/types/organize-contract.js +2 -1
  63. package/lib/types/organize-contract.js.map +1 -1
  64. package/lib/types/pipeline/engine.d.ts +28 -4
  65. package/lib/types/pipeline/engine.d.ts.map +1 -1
  66. package/lib/types/pipeline/engine.js +68 -15
  67. package/lib/types/pipeline/engine.js.map +1 -1
  68. package/lib/types/pipeline/progress.d.ts +12 -0
  69. package/lib/types/pipeline/progress.d.ts.map +1 -0
  70. package/lib/types/pipeline/progress.js +37 -0
  71. package/lib/types/pipeline/progress.js.map +1 -0
  72. package/lib/types/pipeline/resumable-extract.d.ts +45 -0
  73. package/lib/types/pipeline/resumable-extract.d.ts.map +1 -0
  74. package/lib/types/pipeline/resumable-extract.js +262 -0
  75. package/lib/types/pipeline/resumable-extract.js.map +1 -0
  76. package/lib/types/pipeline/store.d.ts +8 -2
  77. package/lib/types/pipeline/store.d.ts.map +1 -1
  78. package/lib/types/pipeline/store.js +27 -5
  79. package/lib/types/pipeline/store.js.map +1 -1
  80. package/lib/types/pipeline/types.d.ts +56 -0
  81. package/lib/types/pipeline/types.d.ts.map +1 -1
  82. package/lib/types/pipeline/types.js +18 -1
  83. package/lib/types/pipeline/types.js.map +1 -1
  84. package/lib/types/present.d.ts +1 -1
  85. package/lib/types/present.js +1 -1
  86. package/lib/types/preset-ids.d.ts +0 -1
  87. package/lib/types/preset-ids.d.ts.map +1 -1
  88. package/lib/types/preset-ids.js +0 -3
  89. package/lib/types/preset-ids.js.map +1 -1
  90. package/lib/types/query-analysis.d.ts +0 -5
  91. package/lib/types/query-analysis.d.ts.map +1 -1
  92. package/lib/types/query-analysis.js +0 -16
  93. package/lib/types/query-analysis.js.map +1 -1
  94. package/lib/types/recall-delivery.d.ts +36 -0
  95. package/lib/types/recall-delivery.d.ts.map +1 -0
  96. package/lib/types/recall-delivery.js +16 -0
  97. package/lib/types/recall-delivery.js.map +1 -0
  98. package/lib/types/retrieval/scope.d.ts +17 -0
  99. package/lib/types/retrieval/scope.d.ts.map +1 -0
  100. package/lib/types/retrieval/scope.js +11 -0
  101. package/lib/types/retrieval/scope.js.map +1 -0
  102. package/lib/types/runtime.d.ts +75 -14
  103. package/lib/types/runtime.d.ts.map +1 -1
  104. package/lib/types/runtime.js +454 -198
  105. package/lib/types/runtime.js.map +1 -1
  106. package/lib/types/settings-volatile.d.ts +0 -7
  107. package/lib/types/settings-volatile.d.ts.map +1 -1
  108. package/lib/types/settings-volatile.js +0 -7
  109. package/lib/types/settings-volatile.js.map +1 -1
  110. package/lib/types/source-identity.d.ts +34 -0
  111. package/lib/types/source-identity.d.ts.map +1 -0
  112. package/lib/types/source-identity.js +85 -0
  113. package/lib/types/source-identity.js.map +1 -0
  114. package/lib/types/source-read.d.ts +18 -0
  115. package/lib/types/source-read.d.ts.map +1 -0
  116. package/lib/types/source-read.js +49 -0
  117. package/lib/types/source-read.js.map +1 -0
  118. package/lib/types/store/axis-references.d.ts.map +1 -1
  119. package/lib/types/store/axis-references.js +5 -3
  120. package/lib/types/store/axis-references.js.map +1 -1
  121. package/lib/types/store/knowledge-db.d.ts +5 -29
  122. package/lib/types/store/knowledge-db.d.ts.map +1 -1
  123. package/lib/types/store/knowledge-db.js +57 -2
  124. package/lib/types/store/knowledge-db.js.map +1 -1
  125. package/lib/types/store/source-identity.d.ts +8 -0
  126. package/lib/types/store/source-identity.d.ts.map +1 -0
  127. package/lib/types/store/source-identity.js +56 -0
  128. package/lib/types/store/source-identity.js.map +1 -0
  129. package/lib/types/types.d.ts +46 -5
  130. package/lib/types/types.d.ts.map +1 -1
  131. package/lib/types/types.js +6 -3
  132. package/lib/types/types.js.map +1 -1
  133. package/lib/types/wiki-availability.d.ts +11 -0
  134. package/lib/types/wiki-availability.d.ts.map +1 -0
  135. package/lib/types/wiki-availability.js +39 -0
  136. package/lib/types/wiki-availability.js.map +1 -0
  137. package/lib/types/wiki-finalize.d.ts +4 -2
  138. package/lib/types/wiki-finalize.d.ts.map +1 -1
  139. package/lib/types/wiki-finalize.js +9 -27
  140. package/lib/types/wiki-finalize.js.map +1 -1
  141. package/lib/types/wiki-links.d.ts +35 -0
  142. package/lib/types/wiki-links.d.ts.map +1 -0
  143. package/lib/types/wiki-links.js +129 -0
  144. package/lib/types/wiki-links.js.map +1 -0
  145. package/lib/types/wiki-trial-types.d.ts +3 -0
  146. package/lib/types/wiki-trial-types.d.ts.map +1 -1
  147. package/lib/types/wiki-trial.d.ts +3 -3
  148. package/lib/types/wiki-trial.d.ts.map +1 -1
  149. package/lib/types/wiki-trial.js +14 -3
  150. package/lib/types/wiki-trial.js.map +1 -1
  151. package/package.json +16 -16
  152. package/scripts/doctor.mjs +0 -7
  153. package/scripts/setup-embedding.sh +0 -0
  154. package/src/agent-behavior-rules.ts +3 -2
  155. package/src/axis-contract.ts +1 -1
  156. package/src/card-organize.ts +5 -3
  157. package/src/client/AttributionEventGuide.tsx +3 -3
  158. package/src/client/AxisMatrix.tsx +86 -19
  159. package/src/client/AxisRegistrySection.tsx +1 -1
  160. package/src/client/CompileSettingsView.tsx +473 -0
  161. package/src/client/DirectoryPicker.tsx +0 -2
  162. package/src/client/ExtractionTasksPage.tsx +557 -423
  163. package/src/client/GuideArchitectureCanvas.tsx +5 -12
  164. package/src/client/ImportFilesDialog.tsx +1 -0
  165. package/src/client/InfoHint.tsx +1 -1
  166. package/src/client/KnowledgeBaseTab.tsx +141 -148
  167. package/src/client/KnowledgePanel.tsx +16 -9
  168. package/src/client/OperationLogTab.tsx +1 -1
  169. package/src/client/PipelineRunView.tsx +173 -0
  170. package/src/client/RecallPanel.tsx +5 -16
  171. package/src/client/TabGuide.tsx +3 -7
  172. package/src/client/WikiFlowPanel.tsx +10 -11
  173. package/src/client/arch/NodeLogicPanel.tsx +3 -3
  174. package/src/client/arch/model.ts +5 -113
  175. package/src/client/architecture.css +1 -1
  176. package/src/client/architecture.tsx +13 -27
  177. package/src/client/attribution/AttributionTab.tsx +141 -10
  178. package/src/client/pages.css +305 -180
  179. package/src/client/pages.tsx +7 -12
  180. package/src/client/styles.css +15 -96
  181. package/src/config-schema.ts +1 -1
  182. package/src/convert/host.ts +13 -5
  183. package/src/convert/ingest.ts +20 -7
  184. package/src/diagnosis/store.ts +7 -2
  185. package/src/diagnosis/types.ts +1 -1
  186. package/src/extract-model.ts +3 -0
  187. package/src/images.ts +1 -1
  188. package/src/index.ts +92 -97
  189. package/src/knowledge-bases.ts +23 -3
  190. package/src/knowledge-contract.ts +7 -2
  191. package/src/layout.ts +10 -32
  192. package/src/materialize-snapshot.ts +1 -0
  193. package/src/migrate.ts +3 -1
  194. package/src/model-text.ts +33 -11
  195. package/src/organize-contract.ts +2 -1
  196. package/src/pipeline/engine.ts +85 -22
  197. package/src/pipeline/progress.ts +40 -0
  198. package/src/pipeline/resumable-extract.ts +233 -0
  199. package/src/pipeline/store.ts +28 -6
  200. package/src/pipeline/types.ts +47 -2
  201. package/src/present.ts +1 -1
  202. package/src/preset-ids.ts +0 -4
  203. package/src/query-analysis.ts +0 -20
  204. package/src/recall-delivery.ts +19 -0
  205. package/src/retrieval/scope.ts +10 -0
  206. package/src/runtime.ts +412 -206
  207. package/src/settings-volatile.ts +0 -7
  208. package/src/source-identity.ts +85 -0
  209. package/src/source-read.ts +47 -0
  210. package/src/store/axis-references.ts +5 -3
  211. package/src/store/knowledge-db.ts +54 -2
  212. package/src/store/source-identity.ts +42 -0
  213. package/src/types.ts +18 -5
  214. package/src/wiki-availability.ts +37 -0
  215. package/src/wiki-finalize.ts +9 -25
  216. package/src/wiki-links.ts +120 -0
  217. package/src/wiki-trial-types.ts +3 -2
  218. package/src/wiki-trial.ts +17 -6
  219. package/lib/types/intent/engine.d.ts +0 -122
  220. package/lib/types/intent/engine.d.ts.map +0 -1
  221. package/lib/types/intent/engine.js +0 -488
  222. package/lib/types/intent/engine.js.map +0 -1
  223. package/lib/types/intent/host.d.ts +0 -68
  224. package/lib/types/intent/host.d.ts.map +0 -1
  225. package/lib/types/intent/host.js +0 -249
  226. package/lib/types/intent/host.js.map +0 -1
  227. package/lib/types/intent/scope.d.ts +0 -25
  228. package/lib/types/intent/scope.d.ts.map +0 -1
  229. package/lib/types/intent/scope.js +0 -116
  230. package/lib/types/intent/scope.js.map +0 -1
  231. package/lib/types/intent/store.d.ts +0 -33
  232. package/lib/types/intent/store.d.ts.map +0 -1
  233. package/lib/types/intent/store.js +0 -239
  234. package/lib/types/intent/store.js.map +0 -1
  235. package/lib/types/intent/tools.d.ts +0 -62
  236. package/lib/types/intent/tools.d.ts.map +0 -1
  237. package/lib/types/intent/tools.js +0 -130
  238. package/lib/types/intent/tools.js.map +0 -1
  239. package/lib/types/intent/types.d.ts +0 -170
  240. package/lib/types/intent/types.d.ts.map +0 -1
  241. package/lib/types/intent/types.js +0 -56
  242. package/lib/types/intent/types.js.map +0 -1
  243. package/src/client/IntentLogic.tsx +0 -192
  244. package/src/client/IntentTab.tsx +0 -343
  245. package/src/client/TypeDefinitionDrawer.tsx +0 -195
  246. package/src/intent/README.md +0 -42
  247. package/src/intent/engine.ts +0 -515
  248. package/src/intent/host.ts +0 -315
  249. package/src/intent/scope.ts +0 -125
  250. package/src/intent/store.ts +0 -218
  251. package/src/intent/tools.ts +0 -154
  252. package/src/intent/types.ts +0 -229
@@ -1,5 +1,5 @@
1
1
  /**
2
- * 通用整页:**抽取试跑 / 抽取轨迹 / 编译和召回链路 / 意图的逻辑 / Agent 方案说明**。
2
+ * 通用整页:**抽取试跑 / 抽取轨迹 / 编译和召回链路 / Agent 方案说明**。
3
3
  *
4
4
  * 这些页面共用一个 bundle、一条宿主路由(`/knowledge/pages/<...>`),因为它们是同一种东西:
5
5
  * 「在面板里放不下、也不该挤进 380px 侧栏」的内容。**它们开在真的浏览器窗口里** ——
@@ -15,9 +15,8 @@
15
15
  * - **抽取轨迹**:同一份理由的另一面 —— 报告几十 KB,2026-10-06 从抽屉里就地展开
16
16
  * 改成开整页(用户点名「点击要在浏览器打开」)。
17
17
  * - **编译和召回链路**:按架构说明 SourceRaw 如何变成 Wiki,以及一次 Query 如何变成 Agent Context。
18
- * - **意图的逻辑**:同上,讲的是词表为什么这么设计。
19
18
  *
20
- * 说明类内容原来住在表单里(编译流程对话框的第二个页签、意图页的第二个页签)。抽成
19
+ * 说明类内容原来住在表单里(编译流程对话框的说明页)。抽成
21
20
  * 整页之后,**每个容器只装一种东西**:浮层装要动手的,整页装要读的。
22
21
  *
23
22
  * ## 地址形状
@@ -26,7 +25,6 @@
26
25
  * /knowledge/pages/trial?kb=<知识库 id> 抽取试跑
27
26
  * /knowledge/pages/run?kb=<库 id>&run=<轨迹 id> 一次抽取的完整报告
28
27
  * /knowledge/pages/help/compile 编译和召回链路
29
- * /knowledge/pages/help/intent 意图的逻辑
30
28
  * /knowledge/pages/help/attribution 智能归因事件
31
29
  * /knowledge/pages/help/overview?node=... TitaAgent 完整架构和逻辑说明
32
30
  * ```
@@ -40,7 +38,6 @@ import '@tita-agent/ui/styles.css'
40
38
  import './styles.css'
41
39
  import './pages.css'
42
40
  import { AttributionEventGuide } from './AttributionEventGuide.tsx'
43
- import { IntentLogic } from './IntentLogic.tsx'
44
41
  import { TabGuidePage } from './TabGuide.tsx'
45
42
  import { WikiFlowPanel } from './WikiFlowPanel.tsx'
46
43
  import { WikiTrialRun, type TrialDocumentOption } from './WikiTrialRun.tsx'
@@ -59,7 +56,7 @@ type PageRoute =
59
56
  | { kind: 'trial'; kb: string }
60
57
  | { kind: 'run'; kb: string; run: string }
61
58
  | { kind: 'tasks'; kb: string }
62
- | { kind: 'help'; topic: 'intent' | 'compile' | 'attribution' | 'overview'; node?: string }
59
+ | { kind: 'help'; topic: 'compile' | 'attribution' | 'overview'; node?: string }
63
60
  | { kind: 'unknown' }
64
61
 
65
62
  /**
@@ -78,7 +75,7 @@ function routeOf(location: Location): PageRoute {
78
75
  if (parts[0] === 'help' && parts[1] === 'overview') {
79
76
  return { kind: 'help', topic: 'overview', node: params.get('node') ?? undefined }
80
77
  }
81
- if (parts[0] === 'help' && (parts[1] === 'intent' || parts[1] === 'compile' || parts[1] === 'attribution')) {
78
+ if (parts[0] === 'help' && (parts[1] === 'compile' || parts[1] === 'attribution')) {
82
79
  return { kind: 'help', topic: parts[1] }
83
80
  }
84
81
  return { kind: 'unknown' }
@@ -250,15 +247,14 @@ function App(): ReactNode {
250
247
 
251
248
  if (route.kind === 'run') return <RunPage kb={route.kb} runId={route.run} />
252
249
 
253
- if (route.kind === 'tasks') return <PageShell title="知识处理" column="tasks"><ExtractionTasksPage kb={route.kb} /></PageShell>
250
+ if (route.kind === 'tasks') {
251
+ return <PageShell title="知识处理" meta="从来源到检索索引,一条可核对的流水线" column="tasks"><ExtractionTasksPage kb={route.kb} /></PageShell>
252
+ }
254
253
 
255
254
  if (route.kind === 'help') {
256
255
  if (route.topic === 'overview') {
257
256
  return <PageShell title="TitaAgent 完整架构" meta="从理解请求到持续改进的三层闭环" column="guide"><TabGuidePage initialNode={route.node} /></PageShell>
258
257
  }
259
- if (route.topic === 'intent') {
260
- return <PageShell title="意图的逻辑" meta="这套词表为什么这样设计" column="help"><IntentLogic /></PageShell>
261
- }
262
258
  if (route.topic === 'compile') {
263
259
  return <PageShell title="编译和召回链路" meta="信息编译为知识,然后才值得被召回使用" column="help"><WikiFlowPanel /></PageShell>
264
260
  }
@@ -272,7 +268,6 @@ function App(): ReactNode {
272
268
  <code>/knowledge/pages/run?kb=&lt;知识库 id&gt;&amp;run=&lt;轨迹 id&gt;</code> · 一次抽取的报告<br />
273
269
  <code>/knowledge/pages/tasks?kb=&lt;知识库 id&gt;</code> · 知识处理与运行记录<br />
274
270
  <code>/knowledge/pages/help/compile</code> · 编译和召回链路<br />
275
- <code>/knowledge/pages/help/intent</code> · 意图的逻辑<br />
276
271
  <code>/knowledge/pages/help/attribution</code> · 智能归因事件<br />
277
272
  <code>/knowledge/pages/help/overview?node=knowledge</code> · 五个 Tab 的方案说明</p>
278
273
  <p>它们都由面板上的入口打开,不需要手写。</p>
@@ -672,7 +672,7 @@ html[data-platform='darwin'] [data-window-drag] [data-conversation-tabs] > [role
672
672
  同一个图标有两种含义时,光标是零成本的那条线索。 */
673
673
  .tita-infoHintAction { cursor: pointer; }
674
674
  .tita-infoHintAction:hover { color: var(--tita-ink); }
675
- /* 说明靠右(2026-10-05):知识库与意图两条工具栏同一条规矩 —— 动作一组靠左,
675
+ /* 说明靠右(2026-10-05):知识库工具栏的排版规矩 —— 动作一组靠左,
676
676
  `?` 自己站在最右端。`margin-left: auto` 在换行时也成立:它落到第二行,仍然右对齐。 */
677
677
  .tita-kbToolbarHint { flex: 0 0 auto; margin-left: auto; }
678
678
  /* 标题里的说明入口(设置小节同理):与标题文字留一格气,
@@ -852,7 +852,7 @@ html[data-platform='darwin'] [data-window-drag] [data-conversation-tabs] > [role
852
852
  /*
853
853
  * 行右侧的次要事实(类型 · 时间 / N 个来源 / N 张)。
854
854
  *
855
- * 0042 补的定义:这个类名被五处用着(卡片行、原文抽屉的编译产物、意图任务、
855
+ * 0042 补的定义:这个类名被五处用着(卡片行、原文抽屉的编译产物、
856
856
  * 类型定义、卡片类型编辑器)却**一直没有样式** —— 于是它只传递了一个名字,
857
857
  * 右对齐与「比主文字轻」全靠父级的 flex 碰运气。一次定义到位。
858
858
  */
@@ -1014,91 +1014,11 @@ html[data-platform='darwin'] [data-window-drag] [data-conversation-tabs] > [role
1014
1014
  .tita-kbInlineImage { margin: 10px 0; text-align: center; }
1015
1015
  .tita-kbInlineImage img { display: block; width: auto; max-width: 100%; max-height: 360px; margin: 0 auto; object-fit: contain; }
1016
1016
  .tita-kbPre { max-height: 320px; margin: 0; border: 1px solid var(--tita-line); border-radius: 9px; background: var(--tita-surface); color: var(--tita-ink); padding: 10px; overflow: auto; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10.5px; line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere; }
1017
- /* 文本视图的 `<pre>` 自带 max-height(它同时被意图 / 类型定义两处用),进了正文块之后
1017
+ /* 文本视图的 `<pre>` 自带 max-height(类型定义视图也会用),进了正文块之后
1018
1018
  由外层那一层负责滚 —— 同一件事不画两条滚动条。 */
1019
1019
  .tita-drawerBody .tita-kbPre { max-height: none; border: 0; border-radius: 0; background: transparent; padding: 0; }
1020
1020
 
1021
- /* ------------------------------------------------------ intent (session audit) */
1022
- .tita-intentTop { display: flex; min-width: 0; align-items: flex-start; justify-content: space-between; gap: 12px; border-bottom: 1px solid var(--tita-line); padding-bottom: 12px; }
1023
- .tita-intentTop h2 { margin: 0; color: var(--tita-ink); font-size: 17px; font-weight: 780; letter-spacing: -.025em; line-height: 1.3; }
1024
- .tita-intentTop p { max-width: 320px; margin: 5px 0 0; color: var(--tita-ink-muted); font-size: 11px; line-height: 1.6; }
1025
- .tita-intentControls { display: grid; gap: 8px; border-bottom: 1px solid var(--tita-line); padding-bottom: 12px; }
1026
- .tita-intentPrimaryFilter { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; min-width: 0; align-items: center; gap: 7px; }
1027
- .tita-intentFilterHeading { display: grid; min-width: 42px; gap: 1px; }
1028
- .tita-intentFilterHeading small { color: var(--tita-primary); font-size: 9px; font-weight: 700; white-space: nowrap; }
1029
- .tita-intentFilterCount { color: var(--tita-ink-faint); font-size: 10px; font-variant-numeric: tabular-nums; white-space: nowrap; }
1030
- .tita-intentSecondaryFilters { display: grid; grid-template-columns: auto minmax(0, .7fr) auto minmax(0, 1.3fr) auto; min-width: 0; align-items: center; gap: 7px; }
1031
- .tita-intentControlLabel { color: var(--tita-ink-muted); font-size: 11px; white-space: nowrap; }
1032
- .tita-intentFilterSummary { display: flex; min-width: 0; flex-wrap: wrap; align-items: center; gap: 5px 7px; border-radius: 8px; background: var(--tita-surface-muted); padding: 6px 8px; color: var(--tita-ink-faint); font-size: 10px; line-height: 1.4; }
1033
- .tita-intentFilterSummary > span { color: var(--tita-ink-muted); font-weight: 650; }
1034
- .tita-intentSectionHead { display: flex; min-width: 0; align-items: flex-start; justify-content: space-between; gap: 12px; }
1035
- .tita-intentSectionHead h3 { margin: 0; color: var(--tita-ink); font-size: 13.5px; font-weight: 760; }
1036
- .tita-intentSectionSubhead { margin: 3px 0 0; color: var(--tita-ink-faint); font-size: 10px; line-height: 1.45; }
1037
- .tita-intentSectionHead > time, .tita-intentSectionCount { flex: 0 0 auto; color: var(--tita-ink-faint); font-size: 10px; font-variant-numeric: tabular-nums; }
1038
- .tita-intentEyebrow { display: inline-flex; align-items: center; gap: 5px; color: var(--tita-ink-faint); font-size: 9.5px; font-weight: 750; letter-spacing: .08em; text-transform: uppercase; }
1039
- .tita-intentInterpretation { display: grid; gap: 11px; border-color: var(--tita-line-strong); padding: 14px 15px; }
1040
- .tita-intentUserBlock { min-width: 0; border: 1px solid var(--tita-line); border-radius: 9px; background: var(--tita-surface-muted); padding: 10px 11px; }
1041
- .tita-intentStepLabel { display: block; color: var(--tita-ink-faint); font-size: 10px; font-weight: 700; }
1042
- .tita-intentUserInput { margin: 5px 0 0; color: var(--tita-ink); font-size: 12px; line-height: 1.65; overflow-wrap: anywhere; }
1043
- .tita-intentVerdict { min-width: 0; border: 1px solid var(--tita-primary); border-radius: 9px; background: var(--tita-primary-soft); padding: 11px; }
1044
- .tita-intentVerdictHead { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
1045
- .tita-intentResultTitle { margin: 5px 0 0; color: var(--tita-ink); font-size: 15px; font-weight: 780; letter-spacing: -.02em; line-height: 1.35; overflow-wrap: anywhere; }
1046
- .tita-intentResultText { margin: 4px 0 0; color: var(--tita-ink-muted); font-size: 11px; line-height: 1.6; }
1047
- .tita-intentResultTags { display: flex; min-width: 0; flex-wrap: wrap; align-items: center; gap: 5px; margin-top: 8px; }
1048
- .tita-intentLowConfidence { color: var(--tita-tone-amber); font-size: 10px; font-weight: 650; }
1049
- .tita-intentMetaGrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
1050
- .tita-intentMetaCell { display: grid; min-width: 0; gap: 3px; border-top: 1px solid var(--tita-line); padding-top: 8px; }
1051
- .tita-intentMetaCell span { color: var(--tita-ink-faint); font-size: 9.5px; }
1052
- .tita-intentMetaCell strong { color: var(--tita-ink-muted); font-size: 10.5px; font-weight: 650; overflow-wrap: anywhere; }
1053
- .tita-intentDiagnostics { border-top: 1px solid var(--tita-line); padding-top: 9px; }
1054
- .tita-intentDiagnostics summary { color: var(--tita-ink-faint); font-size: 10px; cursor: pointer; list-style-position: inside; }
1055
- .tita-intentDiagnostics summary:hover { color: var(--tita-ink-muted); }
1056
- .tita-intentDiagnosticsBody { display: grid; gap: 5px; margin: 8px 0 0 15px; color: var(--tita-ink-faint); font-size: 10px; line-height: 1.5; }
1057
- .tita-intentDiagnosticsBody code { border-radius: 4px; background: var(--tita-surface-muted); padding: 1px 4px; color: var(--tita-tone-violet); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 9.5px; }
1058
- .tita-intentEmptyCard { display: grid; justify-items: center; gap: 6px; padding: 25px 15px; text-align: center; }
1059
- .tita-intentEmptyCard > svg { color: var(--tita-ink-faint); }
1060
- .tita-intentEmptyCard strong { color: var(--tita-ink); font-size: 12px; }
1061
- .tita-intentEmptyCard p, .tita-intentEmpty { margin: 0; color: var(--tita-ink-faint); font-size: 11px; line-height: 1.6; }
1062
- .tita-intentHistory { border-top: 1px solid var(--tita-line); padding-top: 13px; }
1063
- .tita-intentHistoryList { display: grid; margin: 10px 0 0; padding: 0; list-style: none; }
1064
- .tita-intentHistoryItem { display: grid; min-width: 0; gap: 4px; border-bottom: 1px solid var(--tita-line); padding: 10px 0 11px; }
1065
- .tita-intentHistoryItem:first-child { padding-top: 0; }
1066
- .tita-intentHistoryItem:last-child { border-bottom: 0; }
1067
- .tita-intentHistoryPrimary { display: flex; min-width: 0; align-items: baseline; justify-content: space-between; gap: 9px; }
1068
- .tita-intentHistoryPrimary strong { min-width: 0; color: var(--tita-ink); font-size: 12px; font-weight: 720; overflow-wrap: anywhere; }
1069
- .tita-intentHistoryPrimary time { display: inline-flex; flex: 0 0 auto; align-items: center; gap: 4px; color: var(--tita-ink-muted); font-size: 10px; font-variant-numeric: tabular-nums; }
1070
- .tita-intentHistoryMeta { display: flex; min-width: 0; flex-wrap: wrap; align-items: center; gap: 4px 9px; color: var(--tita-ink-faint); font-size: 10px; }
1071
- .tita-intentHistoryMeta span + span::before { content: '·'; margin-right: 9px; color: var(--tita-line-strong); }
1072
- .tita-intentHistoryWarning { color: var(--tita-tone-amber); }
1073
- .tita-intentHistoryItem p { margin: 0; color: var(--tita-ink-muted); font-size: 11px; line-height: 1.55; overflow-wrap: anywhere; }
1074
- .tita-intentFooter { display: flex; flex-wrap: wrap; gap: 5px 12px; border-top: 1px solid var(--tita-line); padding-top: 10px; color: var(--tita-ink-faint); font-size: 10px; }
1075
- .tita-intentFooter span + span::before { content: '·'; margin-right: 12px; color: var(--tita-line-strong); }
1076
-
1077
- /* 「意图的逻辑」:说明性内容,读的是排版而不是控件。它现在是一份整页
1078
- (`/knowledge/pages/help/intent`),所以这几条规则同时服务面板与那一页。 */
1079
- .tita-intentDocList { display: grid; margin: 3px 0 0; padding-left: 18px; gap: 5px; color: var(--tita-ink); font-size: 11.5px; line-height: 1.75; }
1080
- .tita-intentDocList b { color: var(--tita-ink); }
1081
- .tita-intentDocList code, .tita-kbHint code { border-radius: 4px; background: var(--tita-surface-muted); padding: 0 4px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10.5px; }
1082
- .tita-intentDocGrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 8px; margin-top: 10px; }
1083
- .tita-intentDocGrid > div { display: grid; gap: 3px; border: 1px solid var(--tita-line); border-radius: 9px; background: var(--tita-surface); padding: 9px 11px; }
1084
- .tita-intentDocGrid strong { color: var(--tita-ink); font-size: 11.5px; }
1085
- .tita-intentDocGrid span { color: var(--tita-ink-faint); font-size: 10.5px; line-height: 1.6; }
1086
- .tita-intentDocTableWrap { overflow-x: auto; margin-top: 8px; }
1087
- .tita-intentDocTable { width: 100%; border-collapse: collapse; font-size: 11px; }
1088
- .tita-intentDocTable th { border-bottom: 1px solid var(--tita-line); color: var(--tita-ink-muted); padding: 6px 8px; font-weight: 700; text-align: left; white-space: nowrap; }
1089
- .tita-intentDocTable td { border-bottom: 1px solid var(--tita-surface-muted); color: var(--tita-ink); padding: 6px 8px; line-height: 1.65; vertical-align: top; }
1090
- .tita-intentDocTable td:first-child code { border-radius: 4px; background: var(--tita-surface-muted); padding: 1px 4px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10.5px; }
1091
- .tita-intentDocTable td:nth-child(2) { color: var(--tita-tone-green); text-align: center; }
1092
- .tita-intentPaperList { display: grid; margin: 8px 0 0; padding: 0; list-style: none; gap: 10px; }
1093
- .tita-intentPaperList > li { border: 1px solid var(--tita-line); border-radius: 8px; background: var(--tita-surface); padding: 9px 11px; }
1094
- .tita-intentPaperList a { color: var(--tita-primary); font-size: 12px; font-weight: 640; text-decoration: none; }
1095
- .tita-intentPaperList a:hover { text-decoration: underline; }
1096
- .tita-intentPaperMeta { display: block; margin-top: 2px; color: var(--tita-ink-faint); font-size: 10px; }
1097
- .tita-intentPaperList p { margin: 6px 0 0; color: var(--tita-ink-muted); font-size: 11px; line-height: 1.75; }
1098
- .tita-intentPaperNote { color: var(--tita-ink-faint) !important; font-size: 10.5px !important; }
1099
- .tita-intentOasLegend { display: flex; flex-wrap: wrap; align-items: center; gap: 5px 12px; margin-top: 9px; color: var(--tita-ink-faint); font-size: 10px; }
1100
- .tita-intentOasLegend span { display: inline-flex; align-items: center; gap: 4px; }
1101
- .tita-intentOasLegend svg { color: var(--tita-primary); }
1021
+ .tita-kbHint code { border-radius: 4px; background: var(--tita-surface-muted); padding: 0 4px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10.5px; }
1102
1022
 
1103
1023
  /* Wiki 编译与召回流程:总览图 + 当前选中节点说明。 */
1104
1024
  .tita-wikiFlowSection + .tita-wikiFlowSection { margin-top: 30px; border-top: 1px solid var(--tita-line); padding-top: 24px; }
@@ -1414,6 +1334,7 @@ html[data-platform='darwin'] [data-window-drag] [data-conversation-tabs] > [role
1414
1334
  .tita-trialMatrixGrid tbody th > .tita-trialMatrixAxisPick { margin-bottom: 3px; }
1415
1335
  .tita-trialMatrixObjectHead { text-align: center; }
1416
1336
  .tita-trialMatrixLoose { display: block; width: 100%; max-width: 100%; margin: 0; border: 1px solid transparent; border-radius: 5px; color: var(--tita-ink-faint); padding: 0 3px; font: inherit; font-size: 9px; font-weight: 500; line-height: 1.5; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1337
+ .tita-trialMatrixLooseStack { display: grid; min-width: 0; justify-items: center; gap: 1px; }
1417
1338
  .tita-trialMatrixLooseEmpty { display: block; height: 14px; }
1418
1339
  .tita-trialMatrixLoosePick { border-color: var(--tita-line); background: none; cursor: pointer; }
1419
1340
  .tita-trialMatrixLoosePick:hover { border-color: var(--tita-line-strong); color: var(--tita-ink-muted); }
@@ -1450,6 +1371,16 @@ html[data-platform='darwin'] [data-window-drag] [data-conversation-tabs] > [role
1450
1371
  .tita-trialMatrixCellPick:focus-visible { outline: 2px solid var(--tita-focus-ring); outline-offset: 0; }
1451
1372
  .tita-trialMatrixCellPickOn { border-color: var(--tita-primary); background: var(--tita-primary-soft); color: var(--tita-primary); }
1452
1373
  .tita-trialMatrixCellValue { font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums; }
1374
+ .tita-trialMatrixCellStack { display: grid; min-width: 0; justify-items: center; gap: 1px; }
1375
+ .tita-trialMatrixCellStack .tita-trialMatrixCellPick,
1376
+ .tita-trialMatrixCellStack .tita-trialMatrixCellStatic { min-height: 20px; }
1377
+ .tita-trialMatrixBreakdown { display: flex; min-width: 0; flex-wrap: wrap; justify-content: center; gap: 2px; }
1378
+ .tita-trialMatrixBreakdownItem { display: inline-flex; align-items: center; justify-content: center; border: 1px solid transparent; border-radius: 4px; color: var(--tita-ink-muted); padding: 0 2px; font-size: 8px; font-weight: 650; line-height: 1.35; font-variant-numeric: tabular-nums; white-space: nowrap; }
1379
+ .tita-trialMatrixBreakdownPick { background: transparent; cursor: pointer; }
1380
+ .tita-trialMatrixBreakdownPick:hover { border-color: var(--tita-line-strong); background: var(--tita-surface-muted); color: var(--tita-ink); }
1381
+ .tita-trialMatrixBreakdownPick:focus-visible { outline: 2px solid var(--tita-focus-ring); outline-offset: 1px; }
1382
+ .tita-trialMatrixBreakdownOn { border-color: var(--tita-primary); background: var(--tita-primary-soft); color: var(--tita-primary); }
1383
+ .tita-trialMatrixBreakdownOn:hover { background: var(--tita-primary-soft); color: var(--tita-primary); }
1453
1384
  .tita-trialMatrixGroups { display: grid; gap: 0; }
1454
1385
  .tita-trialMatrixGroupLine { display: block; min-width: 0; }
1455
1386
  .tita-trialMatrixGroupLine + .tita-trialMatrixGroupLine { border-top: 1px dashed var(--tita-line); }
@@ -2049,8 +1980,6 @@ button.tita-trialMatrixGroup:focus-visible { outline: 2px solid var(--tita-focus
2049
1980
  .tita-recallAnalysisTitle > svg { flex: 0 0 auto; color: var(--tita-tone-violet); }
2050
1981
  .tita-recallAnalysisTitle h4 { margin: 0; color: var(--tita-ink); font-size: 11.5px; font-weight: 720; }
2051
1982
  .tita-recallAnalysisTitle span { display: block; margin-top: 1px; color: var(--tita-ink-faint); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 9.5px; }
2052
- .tita-recallAnalysisIntent { display: inline-flex; flex: 0 0 auto; align-items: baseline; gap: 5px; border-radius: 999px; background: var(--tita-tone-violet-tint); color: var(--tita-tone-violet); padding: 3px 8px; font-size: 10.5px; font-weight: 700; }
2053
- .tita-recallAnalysisIntent b { color: var(--tita-ink-faint); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 9px; font-weight: 600; }
2054
1983
  .tita-recallAnalysisAxes { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 7px; }
2055
1984
  .tita-recallAnalysisAxis { display: grid; min-width: 0; gap: 5px; border: 1px solid var(--tita-line); border-radius: 8px; background: var(--tita-surface); padding: 7px 8px; }
2056
1985
  .tita-recallAnalysisAxisHead { display: flex; align-items: center; gap: 5px; color: var(--tita-ink-muted); font-size: 10px; }
@@ -2161,16 +2090,6 @@ button.tita-trialMatrixGroup:focus-visible { outline: 2px solid var(--tita-focus
2161
2090
  .tita-hitSnippet { margin: 0; color: var(--tita-ink-muted); font-size: 11px; line-height: 1.6; overflow-wrap: anywhere; }
2162
2091
  .tita-hitFoot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; color: var(--tita-ink-faint); font-size: 10px; }
2163
2092
 
2164
- /* 意图:从最近记录里归纳出的分布(act / 分类 / 低置信度)。 */
2165
- .tita-distList { display: grid; gap: 7px; margin: 10px 0 0; }
2166
- .tita-distRow { display: grid; grid-template-columns: minmax(56px, 96px) minmax(0, 1fr) 34px; align-items: center; gap: 8px; }
2167
- .tita-distRow > span { overflow: hidden; color: var(--tita-ink-muted); font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
2168
- .tita-distBar { height: 7px; overflow: hidden; border-radius: 999px; background: var(--tita-surface-muted); }
2169
- .tita-distBar > i { display: block; height: 100%; border-radius: 999px; background: var(--tita-tone-violet); }
2170
- .tita-distBarFts > i { background: var(--tita-primary); }
2171
- .tita-distBarGraph > i { background: var(--tita-tone-green); }
2172
- .tita-distCount { color: var(--tita-ink-faint); font-size: 10.5px; text-align: right; }
2173
-
2174
2093
  /* 记忆:真实落盘计数(04_memory)与「示意」卡片分开摆,避免把占位数字读成真实数据。 */
2175
2094
  .tita-memoryReal { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 14px; }
2176
2095
 
@@ -9,7 +9,7 @@
9
9
  * (`{op:'set', path:['knowledge','recallTopK']}`)。
10
10
  *
11
11
  * `knowledge` / `convert` 沿用原来的配置边界;`behaviorRules` 保存可关闭的系统提示词
12
- * 扩展开关。会话意图审计仍使用固定内置契约,不属于 entry config。
12
+ * 扩展开关。
13
13
  *
14
14
  * 「单独一个模块」的理由和 `settings-schema.ts` 一样,
15
15
  * 是**可测**:`tests/settings-schema.test.mjs` 要能 import 这个 schema 并把它解析
@@ -91,14 +91,13 @@ export interface ConvertHostDeps {
91
91
  * 0.1.7 起设置不再是命名空间,而是随 `apply(ctx, config)` 进来的 entry config,
92
92
  * 所以这里由调用方把那一组传下来。省略时用 `DEFAULT_CONVERT_CONFIG`。
93
93
  *
94
- * 允许给**函数**的理由与意图那组相同(见 `intent/host.ts` 的 `IntentHostDeps.config`):
95
- * 字段是 volatile,解包是一次快照,而 volatile 改动不会重载插件。
94
+ * 给函数可响应设置热更新:字段是 volatile,解包是一次快照,而 volatile 改动不会重载插件。
96
95
  */
97
96
  config?: Partial<ConvertPluginConfig> | (() => Partial<ConvertPluginConfig>)
98
97
  }
99
98
 
100
99
  /**
101
- * `applyConvert` 交给组合根的那一个把手,与 `intent/host.ts` 的 `IntentHost` 同形。
100
+ * `applyConvert` 交给组合根的那一个把手。
102
101
  *
103
102
  * 转换这一组也有 volatile 字段(`maxConcurrent` / `maxInputBytes` / `retentionDays` /
104
103
  * `toolWaitMs` / `ingestEnabled`),其中一个解包漏了就是沉默的错误:`maxInputBytes`
@@ -131,7 +130,7 @@ function lineageOf(args: Args, exec?: ToolRunContext): Record<string, string> |
131
130
  const sessionId = typeof args.session_key === 'string' && args.session_key.trim() ? args.session_key.trim() : exec?.agent?.session.header.id
132
131
  if (sessionId) lineage.sessionId = sessionId
133
132
  const fields: Array<[string, string]> = [
134
- ['turnId', 'turn_id'], ['intentId', 'intent_id'], ['taskId', 'task_id'],
133
+ ['turnId', 'turn_id'], ['taskId', 'task_id'],
135
134
  ['subtaskId', 'subtask_id'], ['parentSubtaskId', 'parent_subtask_id'], ['runId', 'run_id'], ['traceId', 'trace_id'], ['domain', 'domain'],
136
135
  ]
137
136
  for (const [target, source] of fields) if (typeof args[source] === 'string' && args[source].trim()) lineage[target] = args[source].trim().slice(0, 160)
@@ -506,7 +505,10 @@ export function applyConvert(ctx: Context, deps: ConvertHostDeps): ConvertHost {
506
505
  convert_task_id: { type: 'string', required: true },
507
506
  kb: { type: 'string', description: '目标知识库 id;省略则写入默认知识库' },
508
507
  title: { type: 'string' },
509
- source_id: { type: 'string', description: '写进这一条已有的 SourceRaw(同一实体的新版本)。省略则按 external_id / 内容算一个新来源 —— 给一份「已经录进库的文档重新转换」时应当传它,否则会多出一条来源。' },
508
+ source_id: { type: 'string', description: '导入前分配的稳定 Source Raw ID;重试沿用,更新已有来源时明确传入其 ID。' },
509
+ source_url: { type: 'string', description: '明确的原始访问 URL,保留参数和 fragment。网页文件内自带 canonical 地址不会自动作为身份。' },
510
+ source_identity: { type: 'json', description: '{system, scope, document_id, document_type?, url?};链接也可传 {url, scope?}。' },
511
+ allow_duplicate_title: { type: 'boolean', description: '显式允许同名独立来源;URL 或平台 ID 已匹配时复用已有来源。' },
510
512
  plan_only: { type: 'boolean' },
511
513
  session_key: { type: 'string' },
512
514
  task_id: { type: 'string' },
@@ -533,6 +535,9 @@ export function applyConvert(ctx: Context, deps: ConvertHostDeps): ConvertHost {
533
535
  knowledgeRoot: target.knowledgeRoot,
534
536
  ...(typeof args.title === 'string' && args.title.trim() ? { title: args.title.trim() } : {}),
535
537
  ...(typeof args.source_id === 'string' && args.source_id.trim() ? { sourceId: args.source_id.trim() } : {}),
538
+ ...(args.source_url === undefined ? {} : { sourceUrl: args.source_url }),
539
+ ...(args.source_identity === undefined ? {} : { sourceIdentity: args.source_identity }),
540
+ ...(args.allow_duplicate_title === true ? { allowDuplicateTitle: true } : {}),
536
541
  planOnly: args.plan_only === true,
537
542
  ...(lineageOf(args, exec) ? { lineage: lineageOf(args, exec)! } : {}),
538
543
  })
@@ -588,6 +593,9 @@ export function applyConvert(ctx: Context, deps: ConvertHostDeps): ConvertHost {
588
593
  knowledgeRoot: target.knowledgeRoot,
589
594
  ...(typeof body.title === 'string' && body.title.trim() ? { title: body.title.trim() } : {}),
590
595
  ...(typeof body.source_id === 'string' && body.source_id.trim() ? { sourceId: body.source_id.trim() } : {}),
596
+ ...(body.source_url === undefined ? {} : { sourceUrl: body.source_url }),
597
+ ...(body.source_identity === undefined ? {} : { sourceIdentity: body.source_identity }),
598
+ ...(body.allow_duplicate_title === true ? { allowDuplicateTitle: true } : {}),
591
599
  planOnly: body.plan_only === true,
592
600
  ...(body.lineage && typeof body.lineage === 'object' ? { lineage: body.lineage as Record<string, string> } : {}),
593
601
  })
@@ -1,3 +1,4 @@
1
+ import type { SourceIdentity, SourceIdentityInput } from '../source-identity.js'
1
2
  /**
2
3
  * The knowledge-ingestion seam.
3
4
  *
@@ -46,6 +47,9 @@ export interface IngestTarget {
46
47
  externalId?: string
47
48
  metadata?: Record<string, unknown>
48
49
  sourceId?: string
50
+ sourceUrl?: string
51
+ sourceIdentity?: SourceIdentityInput
52
+ allowDuplicateTitle?: boolean
49
53
  lineage?: LineageContext
50
54
  /** OKF v0.2 溯源信息(0048):写成 front matter 的顶层键,不塞进 metadata。 */
51
55
  provenance?: SourceProvenance
@@ -55,7 +59,7 @@ export interface IngestTarget {
55
59
  * `path` 是可选的:0062 v2 起正文进库,没有对应的文件了。调用方只能依赖 `sourceId`
56
60
  * —— 那本来就是稳定身份,而路径从来不是(0051 起文件名跟着标题走)。
57
61
  */
58
- data: { sourceId: string; path?: string }
62
+ data: { sourceId: string; path?: string; reusedSource?: boolean; sourceIdentity?: SourceIdentity[]; candidates?: string[] }
59
63
  error?: { code?: string; message?: string; suggestion?: string } | null
60
64
  }>
61
65
  }
@@ -71,12 +75,15 @@ export interface IngestOptions {
71
75
  /**
72
76
  * 写进**哪一条** SourceRaw(0070)。
73
77
  *
74
- * 省略时 `addSource` 按 `external_id ?? content` 算一个新 id —— 于是「把一份已经录进库
78
+ * 省略时按持久转换任务 ID 派生稳定的来源 ID(不使用正文、文件名或 external_id) —— 于是「把一份已经录进库
75
79
  * 的文档重新转换一次(比如这次补上了它的图)」会**多出一条来源**,而旧那条仍挂着断引用、
76
80
  * 它的卡片与知识也还指着旧 id。给出 `sourceId` 时走的是同一实体的新版本:
77
81
  * 历史保留、卡片与知识的出处不变、正文与图片引用一起更新。
78
82
  */
79
83
  sourceId?: string
84
+ sourceUrl?: string
85
+ sourceIdentity?: SourceIdentityInput
86
+ allowDuplicateTitle?: boolean
80
87
  metadata?: Record<string, unknown>
81
88
  lineage?: Record<string, unknown>
82
89
  /** Dry run: build the plan without copying assets or writing the source. */
@@ -99,6 +106,9 @@ export interface PlacedOriginal {
99
106
  }
100
107
 
101
108
  export interface IngestOutcome {
109
+ reusedSource?: boolean
110
+ sourceIdentity?: SourceIdentity[]
111
+ candidateSourceIds?: string[]
102
112
  taskId: string
103
113
  sourceId?: string
104
114
  sourcePath?: string
@@ -433,12 +443,12 @@ export async function ingestDelivery(options: IngestOptions): Promise<Envelope<I
433
443
  */
434
444
  const provenance: SourceProvenance = {
435
445
  ...(payload.document?.description ? { description: payload.document.description } : {}),
436
- ...(payload.sourceUrl ? { resource: payload.sourceUrl } : {}),
446
+ ...((options.sourceUrl ?? options.sourceIdentity?.url ?? payload.sourceUrl) ? { resource: options.sourceUrl ?? options.sourceIdentity?.url ?? payload.sourceUrl } : {}),
437
447
  generatedBy: `convert/${payload.parser}`,
438
448
  generatedAt: new Date().toISOString(),
439
449
  sources: [{
440
450
  id: 'origin',
441
- resource: payload.sourceUrl ?? payload.original?.rel ?? payload.sourcePath ?? title,
451
+ resource: options.sourceUrl ?? options.sourceIdentity?.url ?? payload.sourceUrl ?? payload.original?.rel ?? payload.sourcePath ?? title,
442
452
  title,
443
453
  ...(payload.document?.siteName ? { author: payload.document.siteName } : {}),
444
454
  ...(!payload.document?.siteName && payload.document?.author ? { author: payload.document.author } : {}),
@@ -454,7 +464,10 @@ export async function ingestDelivery(options: IngestOptions): Promise<Envelope<I
454
464
  externalId,
455
465
  metadata,
456
466
  provenance,
457
- ...(options.sourceId?.trim() ? { sourceId: options.sourceId.trim() } : {}),
467
+ ...(options.sourceUrl === undefined ? {} : { sourceUrl: options.sourceUrl }),
468
+ ...(options.sourceIdentity === undefined ? {} : { sourceIdentity: options.sourceIdentity }),
469
+ sourceId: options.sourceId?.trim() || `src_convert_${createHash('sha256').update(`convert-task:${taskId}`).digest('hex').slice(0, 32)}`,
470
+ ...(options.allowDuplicateTitle === true ? { allowDuplicateTitle: true } : {}),
458
471
  ...(options.lineage ? { lineage: options.lineage as LineageContext } : {}),
459
472
  })
460
473
 
@@ -472,7 +485,7 @@ export async function ingestDelivery(options: IngestOptions): Promise<Envelope<I
472
485
  */
473
486
  await removeCreated(created)
474
487
  return failure(
475
- emptyOutcome(taskId, options.knowledgeRoot),
488
+ { ...emptyOutcome(taskId, options.knowledgeRoot), candidateSourceIds: result.data.candidates },
476
489
  code,
477
490
  result.error?.message ?? '知识库拒绝了该来源',
478
491
  false,
@@ -491,7 +504,7 @@ export async function ingestDelivery(options: IngestOptions): Promise<Envelope<I
491
504
  return success<IngestOutcome>(
492
505
  {
493
506
  taskId,
494
- sourceId,
507
+ sourceId, reusedSource: result.data.reusedSource, sourceIdentity: result.data.sourceIdentity,
495
508
  ...(sourcePath ? { sourcePath } : {}),
496
509
  knowledgeRoot: options.knowledgeRoot,
497
510
  externalId,
@@ -373,7 +373,7 @@ export class DiagnosisStore {
373
373
  /**
374
374
  * 一次排查结束只写一条 SourceRaw。案例视图从这条记录派生;Knowledge / Event 的抽取与归一
375
375
  * 仍由知识处理阶段执行,避免把同一份排查过程再写成另一张 WikiCard。
376
- * 幂等:`externalId` = runId,同一次排查重跑不会写出第二条来源。
376
+ * 幂等:显式 sourceId = diagnosis:<runId>,同一次排查重跑不会写出第二条来源。
377
377
  */
378
378
  async recordRun(record: DiagnosisRunRecord): Promise<{ sourceId?: string; problems: string[] }> {
379
379
  const problems: string[] = []
@@ -427,13 +427,18 @@ export class DiagnosisStore {
427
427
  * 落原始材料。
428
428
  *
429
429
  * 一个细节:种子手册与三篇复盘在**非首次**运行时可能已经存在(用户的知识根里),
430
- * `addSource` 用 `externalId` 幂等,所以重复调用不会写出第二份。
430
+ * 种子来源复用已有 sourceId;新种子使用稳定的内部 ID,不依赖 addSource 隐式合并。
431
431
  */
432
432
  private async writeSources(sources: SeedSource[]): Promise<{ problems: string[]; refs: Map<string, string> }> {
433
433
  const problems: string[] = []
434
434
  const refs = new Map<string, string>()
435
+ const known = await this.host.loadSources()
435
436
  for (const source of sources) {
437
+ // Compatibility resolution is limited to this internal seed namespace. Keep existing source IDs.
438
+ const matches = known.filter(item => item.externalId === `diagnosis-seed:${source.id}`)
439
+ if (matches.length > 1) { problems.push(`种子来源 ${source.id} 存在多个历史 ID,未自动覆盖。`); continue }
436
440
  const added = await this.host.addSource({
441
+ sourceId: matches[0]?.sourceId ?? `diagnosis-seed:${source.id}`,
437
442
  title: source.title,
438
443
  content: source.content,
439
444
  externalId: `diagnosis-seed:${source.id}`,
@@ -5,7 +5,7 @@
5
5
  * `/diagnosis/graph`)。视图是消费者,不是所有者 —— 模型放在领域层,客户端和宿主
6
6
  * 各自 import,谁都不能私自改形状。
7
7
  *
8
- * 目录约定:`src/diagnosis/` 与 `src/intent/`、`src/convert/` 平级,都是领域子模块;
8
+ * 目录约定:`src/diagnosis/` 与 `src/convert/` 平级,都是领域子模块;
9
9
  * `src/client/` 只放渲染。判据与 0020 第 3 条一致:**目录镜像领域流水线,不镜像技术层次**。
10
10
  */
11
11
 
@@ -62,6 +62,9 @@ import type { KnowledgeCard, SchemaType, SourceRaw, WikiTypeDef } from './types.
62
62
  export type ModelCall = (request: {
63
63
  system: string
64
64
  prompt: string
65
+ signal?: AbortSignal
66
+ retryAttempts?: number
67
+ maxTokens?: number
65
68
  sessionId?: string
66
69
  /** 可选观察本次请求的 provider 用量;普通调用方可以忽略。 */
67
70
  onUsage?: (usage: TrialTokenUsage) => void
package/src/images.ts CHANGED
@@ -185,7 +185,7 @@ export async function publishKnowledgeImage(
185
185
  throw new ImagePublishError('ASSET_NAME_COLLISION', '目标文件名已被不同内容占用,未覆盖现有图片。')
186
186
  }
187
187
  if (!existing?.equals(bytes)) await writeBufferAtomic(join(assetDir, storedFilename), bytes)
188
- // Standalone hosted images have no SourceRaw or card by design. Keep that intent beside the
188
+ // Standalone hosted images have no SourceRaw or card by design. Keep that reason beside the
189
189
  // asset so the orphan sweep does not mistake it for a disposable extraction artifact.
190
190
  await writeTextAtomic(join(assetDir, PUBLISHED_IMAGE_MARKER), 'image_publish\n')
191
191
  const relativePath = `${ASSETS_DIR}/${bucket}/${storedFilename}`