dsh-data-domain-knowledge 0.1.0-alpha.1

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 (231) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +360 -0
  3. package/cordis.patch.yml +410 -0
  4. package/eval/README.md +76 -0
  5. package/eval/evaluate-retrieval.mjs +121 -0
  6. package/eval/queries.example.jsonl +2 -0
  7. package/lib/client.js +111415 -0
  8. package/lib/types/config-schema.d.ts +35 -0
  9. package/lib/types/config-schema.d.ts.map +1 -0
  10. package/lib/types/config-schema.js +34 -0
  11. package/lib/types/config-schema.js.map +1 -0
  12. package/lib/types/convert/core-loader.d.ts +32 -0
  13. package/lib/types/convert/core-loader.d.ts.map +1 -0
  14. package/lib/types/convert/core-loader.js +60 -0
  15. package/lib/types/convert/core-loader.js.map +1 -0
  16. package/lib/types/convert/host.d.ts +88 -0
  17. package/lib/types/convert/host.d.ts.map +1 -0
  18. package/lib/types/convert/host.js +479 -0
  19. package/lib/types/convert/host.js.map +1 -0
  20. package/lib/types/convert/ingest.d.ts +125 -0
  21. package/lib/types/convert/ingest.d.ts.map +1 -0
  22. package/lib/types/convert/ingest.js +228 -0
  23. package/lib/types/convert/ingest.js.map +1 -0
  24. package/lib/types/convert/settings-schema.d.ts +18 -0
  25. package/lib/types/convert/settings-schema.d.ts.map +1 -0
  26. package/lib/types/convert/settings-schema.js +26 -0
  27. package/lib/types/convert/settings-schema.js.map +1 -0
  28. package/lib/types/convert/types.d.ts +46 -0
  29. package/lib/types/convert/types.d.ts.map +1 -0
  30. package/lib/types/convert/types.js +41 -0
  31. package/lib/types/convert/types.js.map +1 -0
  32. package/lib/types/diagnosis/cards.d.ts +71 -0
  33. package/lib/types/diagnosis/cards.d.ts.map +1 -0
  34. package/lib/types/diagnosis/cards.js +482 -0
  35. package/lib/types/diagnosis/cards.js.map +1 -0
  36. package/lib/types/diagnosis/problem-classes.d.ts +51 -0
  37. package/lib/types/diagnosis/problem-classes.d.ts.map +1 -0
  38. package/lib/types/diagnosis/problem-classes.js +66 -0
  39. package/lib/types/diagnosis/problem-classes.js.map +1 -0
  40. package/lib/types/diagnosis/schema.d.ts +65 -0
  41. package/lib/types/diagnosis/schema.d.ts.map +1 -0
  42. package/lib/types/diagnosis/schema.js +208 -0
  43. package/lib/types/diagnosis/schema.js.map +1 -0
  44. package/lib/types/diagnosis/seed-source.d.ts +20 -0
  45. package/lib/types/diagnosis/seed-source.d.ts.map +1 -0
  46. package/lib/types/diagnosis/seed-source.js +96 -0
  47. package/lib/types/diagnosis/seed-source.js.map +1 -0
  48. package/lib/types/diagnosis/seed.d.ts +50 -0
  49. package/lib/types/diagnosis/seed.d.ts.map +1 -0
  50. package/lib/types/diagnosis/seed.js +242 -0
  51. package/lib/types/diagnosis/seed.js.map +1 -0
  52. package/lib/types/diagnosis/store.d.ts +81 -0
  53. package/lib/types/diagnosis/store.d.ts.map +1 -0
  54. package/lib/types/diagnosis/store.js +409 -0
  55. package/lib/types/diagnosis/store.js.map +1 -0
  56. package/lib/types/diagnosis/types.d.ts +124 -0
  57. package/lib/types/diagnosis/types.d.ts.map +1 -0
  58. package/lib/types/diagnosis/types.js +13 -0
  59. package/lib/types/diagnosis/types.js.map +1 -0
  60. package/lib/types/directory-picker.d.ts +94 -0
  61. package/lib/types/directory-picker.d.ts.map +1 -0
  62. package/lib/types/directory-picker.js +110 -0
  63. package/lib/types/directory-picker.js.map +1 -0
  64. package/lib/types/extract-model.d.ts +76 -0
  65. package/lib/types/extract-model.d.ts.map +1 -0
  66. package/lib/types/extract-model.js +193 -0
  67. package/lib/types/extract-model.js.map +1 -0
  68. package/lib/types/extract.d.ts +121 -0
  69. package/lib/types/extract.d.ts.map +1 -0
  70. package/lib/types/extract.js +549 -0
  71. package/lib/types/extract.js.map +1 -0
  72. package/lib/types/fts.d.ts +96 -0
  73. package/lib/types/fts.d.ts.map +1 -0
  74. package/lib/types/fts.js +184 -0
  75. package/lib/types/fts.js.map +1 -0
  76. package/lib/types/index.d.ts +39 -0
  77. package/lib/types/index.d.ts.map +1 -0
  78. package/lib/types/index.js +1112 -0
  79. package/lib/types/index.js.map +1 -0
  80. package/lib/types/intent/engine.d.ts +108 -0
  81. package/lib/types/intent/engine.d.ts.map +1 -0
  82. package/lib/types/intent/engine.js +501 -0
  83. package/lib/types/intent/engine.js.map +1 -0
  84. package/lib/types/intent/host.d.ts +88 -0
  85. package/lib/types/intent/host.d.ts.map +1 -0
  86. package/lib/types/intent/host.js +389 -0
  87. package/lib/types/intent/host.js.map +1 -0
  88. package/lib/types/intent/preset-tools.d.ts +37 -0
  89. package/lib/types/intent/preset-tools.d.ts.map +1 -0
  90. package/lib/types/intent/preset-tools.js +45 -0
  91. package/lib/types/intent/preset-tools.js.map +1 -0
  92. package/lib/types/intent/settings-schema.d.ts +59 -0
  93. package/lib/types/intent/settings-schema.d.ts.map +1 -0
  94. package/lib/types/intent/settings-schema.js +57 -0
  95. package/lib/types/intent/settings-schema.js.map +1 -0
  96. package/lib/types/intent/store.d.ts +75 -0
  97. package/lib/types/intent/store.d.ts.map +1 -0
  98. package/lib/types/intent/store.js +139 -0
  99. package/lib/types/intent/store.js.map +1 -0
  100. package/lib/types/intent/tools.d.ts +63 -0
  101. package/lib/types/intent/tools.d.ts.map +1 -0
  102. package/lib/types/intent/tools.js +108 -0
  103. package/lib/types/intent/tools.js.map +1 -0
  104. package/lib/types/intent/types.d.ts +189 -0
  105. package/lib/types/intent/types.d.ts.map +1 -0
  106. package/lib/types/intent/types.js +62 -0
  107. package/lib/types/intent/types.js.map +1 -0
  108. package/lib/types/knowledge-bases.d.ts +266 -0
  109. package/lib/types/knowledge-bases.d.ts.map +1 -0
  110. package/lib/types/knowledge-bases.js +1107 -0
  111. package/lib/types/knowledge-bases.js.map +1 -0
  112. package/lib/types/layout.d.ts +262 -0
  113. package/lib/types/layout.d.ts.map +1 -0
  114. package/lib/types/layout.js +312 -0
  115. package/lib/types/layout.js.map +1 -0
  116. package/lib/types/migrate.d.ts +110 -0
  117. package/lib/types/migrate.d.ts.map +1 -0
  118. package/lib/types/migrate.js +280 -0
  119. package/lib/types/migrate.js.map +1 -0
  120. package/lib/types/recall-rank.d.ts +158 -0
  121. package/lib/types/recall-rank.d.ts.map +1 -0
  122. package/lib/types/recall-rank.js +261 -0
  123. package/lib/types/recall-rank.js.map +1 -0
  124. package/lib/types/runtime.d.ts +568 -0
  125. package/lib/types/runtime.d.ts.map +1 -0
  126. package/lib/types/runtime.js +3045 -0
  127. package/lib/types/runtime.js.map +1 -0
  128. package/lib/types/settings-schema.d.ts +117 -0
  129. package/lib/types/settings-schema.d.ts.map +1 -0
  130. package/lib/types/settings-schema.js +92 -0
  131. package/lib/types/settings-schema.js.map +1 -0
  132. package/lib/types/settings-volatile.d.ts +102 -0
  133. package/lib/types/settings-volatile.d.ts.map +1 -0
  134. package/lib/types/settings-volatile.js +114 -0
  135. package/lib/types/settings-volatile.js.map +1 -0
  136. package/lib/types/types.d.ts +799 -0
  137. package/lib/types/types.d.ts.map +1 -0
  138. package/lib/types/types.js +387 -0
  139. package/lib/types/types.js.map +1 -0
  140. package/lib/types/wiki-types.d.ts +68 -0
  141. package/lib/types/wiki-types.d.ts.map +1 -0
  142. package/lib/types/wiki-types.js +322 -0
  143. package/lib/types/wiki-types.js.map +1 -0
  144. package/package.json +101 -0
  145. package/preset/tita-data-agent/agent.cordis.yml +376 -0
  146. package/preset/tita-data-agent/preset.yml +3 -0
  147. package/python/requirements.txt +8 -0
  148. package/scripts/doctor.mjs +70 -0
  149. package/scripts/e2e-pipeline.mjs +355 -0
  150. package/scripts/install-agent-preset.mjs +92 -0
  151. package/scripts/setup-embedding.sh +34 -0
  152. package/scripts/sync-agent-preset.mjs +188 -0
  153. package/src/client/AgentPresetCard.tsx +247 -0
  154. package/src/client/DirectoryPicker.tsx +357 -0
  155. package/src/client/InfoHint.tsx +65 -0
  156. package/src/client/IntentLogicTab.tsx +185 -0
  157. package/src/client/IntentTab.tsx +715 -0
  158. package/src/client/KnowledgeBaseTab.tsx +1454 -0
  159. package/src/client/KnowledgeGraphView.tsx +306 -0
  160. package/src/client/KnowledgePanel.tsx +381 -0
  161. package/src/client/KnowledgeStepRail.tsx +60 -0
  162. package/src/client/NodeSettings.tsx +392 -0
  163. package/src/client/PreviewSections.tsx +116 -0
  164. package/src/client/RecallPanel.tsx +399 -0
  165. package/src/client/TypeDefinitionDrawer.tsx +215 -0
  166. package/src/client/WikiFlowCard.tsx +226 -0
  167. package/src/client/WikiTypeEditor.tsx +332 -0
  168. package/src/client/agent-preset.ts +121 -0
  169. package/src/client/arch/AgentArchitectureGraph.tsx +219 -0
  170. package/src/client/arch/NodeLogicPanel.tsx +133 -0
  171. package/src/client/arch/ProblemDetailPanel.tsx +88 -0
  172. package/src/client/arch/model.ts +691 -0
  173. package/src/client/architecture.css +191 -0
  174. package/src/client/architecture.tsx +530 -0
  175. package/src/client/attribution/AttributionGraph.tsx +104 -0
  176. package/src/client/attribution/AttributionTab.tsx +332 -0
  177. package/src/client/attribution/CaseView.tsx +102 -0
  178. package/src/client/attribution/GraphStage.tsx +87 -0
  179. package/src/client/attribution/RunView.tsx +184 -0
  180. package/src/client/attribution/StageChrome.tsx +403 -0
  181. package/src/client/attribution/ViewSwitch.tsx +107 -0
  182. package/src/client/attribution/graph-options.ts +381 -0
  183. package/src/client/attribution/model.ts +462 -0
  184. package/src/client/config-scope.ts +119 -0
  185. package/src/client/convert/ConvertPanel.tsx +594 -0
  186. package/src/client/convert/styles.css +138 -0
  187. package/src/client/convert/styles.ts +49 -0
  188. package/src/client/format.ts +39 -0
  189. package/src/client/graph/layout.ts +181 -0
  190. package/src/client/graph.css +113 -0
  191. package/src/client/index.ts +98 -0
  192. package/src/client/problem-domains.ts +114 -0
  193. package/src/client/styles.css +940 -0
  194. package/src/client/styles.ts +11 -0
  195. package/src/client/useFitScale.ts +68 -0
  196. package/src/config-schema.ts +45 -0
  197. package/src/convert/core-loader.ts +68 -0
  198. package/src/convert/host.ts +579 -0
  199. package/src/convert/ingest.ts +367 -0
  200. package/src/convert/settings-schema.ts +27 -0
  201. package/src/convert/types.ts +65 -0
  202. package/src/diagnosis/cards.ts +498 -0
  203. package/src/diagnosis/problem-classes.ts +117 -0
  204. package/src/diagnosis/schema.ts +260 -0
  205. package/src/diagnosis/seed-source.ts +107 -0
  206. package/src/diagnosis/seed.ts +291 -0
  207. package/src/diagnosis/store.ts +432 -0
  208. package/src/diagnosis/types.ts +152 -0
  209. package/src/directory-picker.ts +164 -0
  210. package/src/extract-model.ts +227 -0
  211. package/src/extract.ts +632 -0
  212. package/src/fts.ts +203 -0
  213. package/src/index.ts +1091 -0
  214. package/src/intent/README.md +63 -0
  215. package/src/intent/engine.ts +527 -0
  216. package/src/intent/host.ts +462 -0
  217. package/src/intent/preset-tools.ts +48 -0
  218. package/src/intent/settings-schema.ts +63 -0
  219. package/src/intent/store.ts +143 -0
  220. package/src/intent/tools.ts +139 -0
  221. package/src/intent/types.ts +245 -0
  222. package/src/knowledge-bases.ts +1160 -0
  223. package/src/layout.ts +350 -0
  224. package/src/migrate.ts +262 -0
  225. package/src/python/embedding_worker.py +471 -0
  226. package/src/recall-rank.ts +299 -0
  227. package/src/runtime.ts +3069 -0
  228. package/src/settings-schema.ts +94 -0
  229. package/src/settings-volatile.ts +122 -0
  230. package/src/types.ts +1140 -0
  231. package/src/wiki-types.ts +357 -0
@@ -0,0 +1,138 @@
1
+ /* Convert panel — semantic classes only; the client bundle inlines this file. */
2
+
3
+ .tdc-panel {
4
+ display: flex;
5
+ flex-direction: column;
6
+ height: 100%;
7
+ min-height: 0;
8
+ font-size: 13px;
9
+ }
10
+
11
+ .tdc-header {
12
+ display: flex;
13
+ align-items: flex-start;
14
+ gap: 12px;
15
+ padding: 12px 14px 10px;
16
+ border-bottom: 1px solid var(--tita-line);
17
+ }
18
+
19
+ .tdc-header-copy { min-width: 0; flex: 1; }
20
+ .tdc-header-title { margin: 0; font-size: 14px; font-weight: 600; letter-spacing: -0.01em; }
21
+ .tdc-header-subtitle { margin: 2px 0 0; font-size: 11.5px; opacity: 0.65; }
22
+ .tdc-header-actions { display: flex; align-items: center; gap: 6px; }
23
+
24
+ .tdc-body {
25
+ flex: 1;
26
+ min-height: 0;
27
+ overflow: auto;
28
+ padding: 12px 14px 20px;
29
+ }
30
+
31
+ .tdc-section { margin-bottom: 16px; }
32
+ .tdc-section-title {
33
+ font-size: 11px;
34
+ text-transform: uppercase;
35
+ letter-spacing: 0.08em;
36
+ opacity: 0.6;
37
+ margin: 0 0 8px;
38
+ }
39
+
40
+ .tdc-row { display: flex; align-items: center; gap: 8px; }
41
+ .tdc-row-between { justify-content: space-between; }
42
+ .tdc-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
43
+ .tdc-field { display: block; margin-bottom: 10px; }
44
+ .tdc-field-label { display: block; font-size: 11.5px; opacity: 0.65; margin-bottom: 4px; }
45
+ .tdc-field-hint { display: block; font-size: 11px; opacity: 0.55; margin-top: 4px; line-height: 1.45; }
46
+
47
+ .tdc-muted { opacity: 0.65; }
48
+ .tdc-small { font-size: 11.5px; }
49
+ .tdc-error { color: var(--tita-danger); }
50
+ .tdc-notice { color: var(--tita-success); }
51
+
52
+ .tdc-task-list { display: flex; flex-direction: column; gap: 6px; }
53
+ .tdc-task {
54
+ display: flex;
55
+ gap: 10px;
56
+ padding: 8px 10px;
57
+ border: 1px solid var(--tita-line);
58
+ border-radius: 8px;
59
+ }
60
+ .tdc-task-active { border-color: var(--tita-primary-line); }
61
+ .tdc-task-main { flex: 1; min-width: 0; }
62
+ .tdc-task-title { font-weight: 550; font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
63
+ .tdc-task-meta { font-size: 11px; opacity: 0.6; margin-top: 2px; overflow-wrap: anywhere; }
64
+ .tdc-task-side { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
65
+ .tdc-task-actions { display: flex; gap: 4px; }
66
+
67
+ .tdc-progress { height: 4px; border-radius: 999px; background: var(--tita-surface-muted); overflow: hidden; width: 100%; }
68
+ .tdc-progress-bar { height: 100%; background: var(--tita-primary); transition: width 240ms ease; }
69
+
70
+ .tdc-tag {
71
+ display: inline-block;
72
+ padding: 1px 7px;
73
+ border-radius: 999px;
74
+ font-size: 11px;
75
+ border: 1px solid var(--tita-line-strong);
76
+ white-space: nowrap;
77
+ }
78
+ .tdc-tag-ok { color: var(--tita-success); border-color: var(--tita-tone-green-line); }
79
+ .tdc-tag-run { color: var(--tita-primary); border-color: var(--tita-primary-line); }
80
+ .tdc-tag-fail { color: var(--tita-danger); border-color: var(--ddk-danger-line); }
81
+ .tdc-tag-idle { opacity: 0.6; }
82
+
83
+ .tdc-cap-list { list-style: none; margin: 0; padding: 0; }
84
+ .tdc-cap-item {
85
+ display: flex;
86
+ gap: 8px;
87
+ padding: 7px 0;
88
+ border-bottom: 1px dashed var(--tita-line);
89
+ }
90
+ .tdc-cap-item:last-child { border-bottom: 0; }
91
+ .tdc-dot { width: 8px; height: 8px; border-radius: 50%; margin-top: 5px; flex: none; }
92
+ .tdc-dot-on { background: var(--tita-success); }
93
+ .tdc-dot-off { background: var(--tita-ink-faint); }
94
+
95
+ .tdc-pre {
96
+ margin: 0;
97
+ padding: 9px 10px;
98
+ border: 1px solid var(--tita-line);
99
+ border-radius: 7px;
100
+ background: var(--tita-surface-muted);
101
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
102
+ font-size: 11.5px;
103
+ line-height: 1.5;
104
+ max-height: 260px;
105
+ overflow: auto;
106
+ white-space: pre-wrap;
107
+ overflow-wrap: anywhere;
108
+ }
109
+
110
+ .tdc-kv { display: grid; gap: 4px; margin: 0 0 10px; }
111
+ .tdc-kv > div { display: grid; grid-template-columns: 120px minmax(0, 1fr); gap: 8px; }
112
+ .tdc-kv dt { font-size: 11.5px; opacity: 0.6; }
113
+ .tdc-kv dd { margin: 0; font-size: 12px; overflow-wrap: anywhere; }
114
+
115
+ .tdc-asset-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; }
116
+ .tdc-asset-card { border: 1px solid var(--tita-line); border-radius: 7px; padding: 5px; }
117
+ .tdc-asset-card img { width: 100%; height: 68px; object-fit: contain; display: block; }
118
+ .tdc-asset-card figcaption { font-size: 10px; opacity: 0.6; margin-top: 4px; overflow-wrap: anywhere; }
119
+
120
+ .tdc-empty { padding: 18px 0; text-align: center; opacity: 0.6; font-size: 12px; }
121
+ .tdc-code {
122
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
123
+ font-size: 11px;
124
+ background: var(--tita-surface-muted);
125
+ border-radius: 4px;
126
+ padding: 1px 4px;
127
+ }
128
+
129
+ .tdc-file-input {
130
+ width: 100%;
131
+ font-size: 12px;
132
+ padding: 7px 8px;
133
+ border: 1px dashed var(--tita-line-strong);
134
+ border-radius: 7px;
135
+ background: var(--tita-surface-muted);
136
+ color: inherit;
137
+ cursor: pointer;
138
+ }
@@ -0,0 +1,49 @@
1
+ /** Class-name registry for the convert panel; definitions live in styles.css. */
2
+ export const styles = {
3
+ panel: 'tdc-panel',
4
+ header: 'tdc-header',
5
+ headerCopy: 'tdc-header-copy',
6
+ headerTitle: 'tdc-header-title',
7
+ headerSubtitle: 'tdc-header-subtitle',
8
+ headerActions: 'tdc-header-actions',
9
+ body: 'tdc-body',
10
+ section: 'tdc-section',
11
+ sectionTitle: 'tdc-section-title',
12
+ row: 'tdc-row',
13
+ rowBetween: 'tdc-row tdc-row-between',
14
+ grid2: 'tdc-grid-2',
15
+ field: 'tdc-field',
16
+ fieldLabel: 'tdc-field-label',
17
+ fieldHint: 'tdc-field-hint',
18
+ muted: 'tdc-muted',
19
+ small: 'tdc-small',
20
+ error: 'tdc-error',
21
+ notice: 'tdc-notice',
22
+ taskList: 'tdc-task-list',
23
+ task: 'tdc-task',
24
+ taskActive: 'tdc-task-active',
25
+ taskMain: 'tdc-task-main',
26
+ taskTitle: 'tdc-task-title',
27
+ taskMeta: 'tdc-task-meta',
28
+ taskSide: 'tdc-task-side',
29
+ taskActions: 'tdc-task-actions',
30
+ progress: 'tdc-progress',
31
+ progressBar: 'tdc-progress-bar',
32
+ tag: 'tdc-tag',
33
+ tagOk: 'tdc-tag tdc-tag-ok',
34
+ tagRun: 'tdc-tag tdc-tag-run',
35
+ tagFail: 'tdc-tag tdc-tag-fail',
36
+ tagIdle: 'tdc-tag tdc-tag-idle',
37
+ capList: 'tdc-cap-list',
38
+ capItem: 'tdc-cap-item',
39
+ dot: 'tdc-dot',
40
+ dotOn: 'tdc-dot tdc-dot-on',
41
+ dotOff: 'tdc-dot tdc-dot-off',
42
+ pre: 'tdc-pre',
43
+ kv: 'tdc-kv',
44
+ assetGrid: 'tdc-asset-grid',
45
+ assetCard: 'tdc-asset-card',
46
+ empty: 'tdc-empty',
47
+ code: 'tdc-code',
48
+ fileInput: 'tdc-file-input',
49
+ } as const
@@ -0,0 +1,39 @@
1
+ /**
2
+ * 展示层的三个格式化函数:字节、紧凑时间、标准时间。
3
+ *
4
+ * 为什么单独一个模块:预览抽屉(`PreviewSections.tsx`)与列表行都要用它们。放在
5
+ * 某个组件文件里会让另一个组件为了一个格式化函数去 import 组件模块 —— 反向依赖。
6
+ *
7
+ * **两种时间是两种读法,不是一个格式没统一**:
8
+ * - `formatTime` 给**列表行**用(`9/22 09:11`):窄栏里一行还要放标题、类型、大小。
9
+ * - `formatDateTime` 给**元信息**用(`2026-09-22 09:11`):那里是逐字核对事实的地方,
10
+ * 年份不能省,而且这个字符串要能被复制出去直接用。
11
+ * 两者都只到分钟 —— 秒是版本号那种需要唯一性的地方的读法,不是这里的。
12
+ */
13
+
14
+ export function formatBytes(bytes: number | undefined): string {
15
+ if (!bytes) return '0 B'
16
+ const units = ['B', 'KB', 'MB', 'GB']
17
+ let value = bytes
18
+ let unit = 0
19
+ while (value >= 1024 && unit < units.length - 1) { value /= 1024; unit += 1 }
20
+ return `${value.toFixed(value >= 10 || unit === 0 ? 0 : 1)} ${units[unit]}`
21
+ }
22
+
23
+ const pad2 = (value: number): string => String(value).padStart(2, '0')
24
+
25
+ /** 列表行的紧凑时间:`9/22 09:11`。 */
26
+ export function formatTime(value: string | undefined): string {
27
+ if (!value) return '—'
28
+ const date = new Date(value)
29
+ if (Number.isNaN(date.getTime())) return '—'
30
+ return date.toLocaleString('zh-CN', { month: 'numeric', day: 'numeric', hour: '2-digit', minute: '2-digit' })
31
+ }
32
+
33
+ /** 元信息里的标准时间:`2026-09-22 09:11`。 */
34
+ export function formatDateTime(value: string | undefined): string {
35
+ if (!value) return '—'
36
+ const date = new Date(value)
37
+ if (Number.isNaN(date.getTime())) return '—'
38
+ return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())} ${pad2(date.getHours())}:${pad2(date.getMinutes())}`
39
+ }
@@ -0,0 +1,181 @@
1
+ /**
2
+ * 知识图谱的**几何与筛选**。
3
+ *
4
+ * 与「Agent 概览」那张架构图恰好相反:架构图的坐标是**设计结论**(谁必须在谁下面),
5
+ * 所以手算;知识图谱的坐标是**数据结论**(这张卡片连到哪份来源),节点数随知识库增长,
6
+ * 所以交给 dagre 分层布局。但它仍然是**确定性**的:同一份图数据每次得到同一个形状 ——
7
+ * 否则每次刷新都换一个样子,「那张卡片在右边」这种空间记忆就失效了。
8
+ *
9
+ * 这里没有 G6 实例,只有数据进、坐标出,所以可以在 node:test 里断言。
10
+ */
11
+
12
+ import { DagreLayout } from '@antv/g6'
13
+
14
+ export interface GraphNodeView { id: string; type: string; title: string; status?: string }
15
+ export interface GraphEdgeView { from: string; to: string; type: string }
16
+
17
+ /**
18
+ * 图谱节点的唯一键。
19
+ *
20
+ * **接口两边的键不一样,这是实测踩到的坑**:`nodes[].id` 是裸 id(`e062c1bf…`),
21
+ * 而 `edges[].from / to` 是 `type:id` 的复合键(`diagnosis/case:e062c1bf…`)。
22
+ * 拿裸 id 去比,一条边都连不上 —— 图上于是只剩一排孤立的卡片,边数永远是 0/26。
23
+ * 对齐只做一次,就在这里。
24
+ */
25
+ export function keyOf(node: { id: string; type: string }): string {
26
+ return `${node.type}:${node.id}`
27
+ }
28
+
29
+ /** 节点卡片的尺寸。宽度要放得下一张卡片的标题,高度只放得下一行。 */
30
+ export const GRAPH_NODE_W = 176
31
+ export const GRAPH_NODE_H = 34
32
+ /** 画布四周留白。 */
33
+ export const GRAPH_PAD = 28
34
+ /** 卡片列与来源列之间的水平间距。 */
35
+ export const GRAPH_RANK_SEP = 132
36
+
37
+ export interface PlacedNode extends GraphNodeView {
38
+ /** 复合键 `type:id`。图上元素的 id、边的两端、选中态用的都是它。 */
39
+ key: string
40
+ /** 中心坐标。 */
41
+ x: number
42
+ y: number
43
+ /** 与该节点相连的边数:面板上用它回答「这张卡片被几条边接着」。 */
44
+ degree: number
45
+ }
46
+
47
+ export interface PlacedGraph {
48
+ nodes: PlacedNode[]
49
+ edges: GraphEdgeView[]
50
+ width: number
51
+ height: number
52
+ }
53
+
54
+ export interface GraphFilter {
55
+ /** 空数组 = 不筛。 */
56
+ nodeTypes: string[]
57
+ edgeTypes: string[]
58
+ query: string
59
+ }
60
+
61
+ export interface FilterResult {
62
+ /** 画布上要画的节点:命中的 + 为了不剪断边而带上的邻居。 */
63
+ nodes: GraphNodeView[]
64
+ edges: GraphEdgeView[]
65
+ /** 直接命中筛选条件的节点数。`nodes.length` 可能更大。 */
66
+ matched: number
67
+ }
68
+
69
+ /**
70
+ * 按类型与关键词筛。
71
+ *
72
+ * 两条规则,都是实测逼出来的:
73
+ *
74
+ * 1. **不留悬空边**:只留下两端都还在图上的边。一条指向画布外的线比没有线更让人困惑。
75
+ * 2. **带上邻居**:这张图今天只有 `supported_by`(卡片 → 来源),是**二部图** ——
76
+ * 按节点类型筛任何一侧,边都会被规则 1 全部剪掉,于是筛完只剩一排孤立卡片,
77
+ * 看起来像坏了。所以命中节点的**直接邻居一起带上**:边仍然连得起来,
78
+ * 而「筛的是哪一类」由筛选条和图上的节点自己说清楚。带进来的数量会在计数里标出来。
79
+ */
80
+ export function filterGraph(
81
+ nodes: GraphNodeView[],
82
+ edges: GraphEdgeView[],
83
+ filter: GraphFilter,
84
+ options: { includeNeighbors?: boolean } = {},
85
+ ): FilterResult {
86
+ const needle = filter.query.trim().toLocaleLowerCase()
87
+ const matched = nodes.filter(node =>
88
+ (filter.nodeTypes.length === 0 || filter.nodeTypes.includes(node.type))
89
+ && (needle === '' || `${node.title}\n${node.id}`.toLocaleLowerCase().includes(needle)))
90
+ const byEdgeType = edges.filter(edge => filter.edgeTypes.length === 0 || filter.edgeTypes.includes(edge.type))
91
+
92
+ const active = filter.nodeTypes.length > 0 || filter.edgeTypes.length > 0 || needle !== ''
93
+ const keep = new Set(matched.map(keyOf))
94
+ if (active && options.includeNeighbors !== false) {
95
+ const direct = new Set(keep)
96
+ for (const edge of byEdgeType) {
97
+ if (direct.has(edge.from)) keep.add(edge.to)
98
+ if (direct.has(edge.to)) keep.add(edge.from)
99
+ }
100
+ }
101
+ const byKey = new Map(nodes.map(node => [keyOf(node), node]))
102
+ const kept = [...keep].map(key => byKey.get(key)).filter((node): node is GraphNodeView => Boolean(node))
103
+ return {
104
+ nodes: kept,
105
+ edges: byEdgeType.filter(edge => keep.has(edge.from) && keep.has(edge.to)),
106
+ matched: matched.length,
107
+ }
108
+ }
109
+
110
+ /** 每种节点类型各有多少个。筛选条上的计数读它,不从筛完的结果里再数一次。 */
111
+ export function nodeTypeCounts(nodes: GraphNodeView[]): Array<{ type: string; count: number }> {
112
+ const counts = new Map<string, number>()
113
+ for (const node of nodes) counts.set(node.type, (counts.get(node.type) ?? 0) + 1)
114
+ return [...counts.entries()].map(([type, count]) => ({ type, count })).sort((a, b) => b.count - a.count || a.type.localeCompare(b.type))
115
+ }
116
+
117
+ /** 每种边类型各有多少条。 */
118
+ export function edgeTypeCounts(edges: GraphEdgeView[]): Array<{ type: string; count: number }> {
119
+ const counts = new Map<string, number>()
120
+ for (const edge of edges) counts.set(edge.type, (counts.get(edge.type) ?? 0) + 1)
121
+ return [...counts.entries()].map(([type, count]) => ({ type, count })).sort((a, b) => b.count - a.count || a.type.localeCompare(b.type))
122
+ }
123
+
124
+ /** 某个节点直接相连的节点(不分方向)。选中一个节点时,面板列的就是它。 */
125
+ export function neighborsOf(id: string, nodes: GraphNodeView[], edges: GraphEdgeView[]): GraphNodeView[] {
126
+ const byId = new Map(nodes.map(node => [keyOf(node), node]))
127
+ const ids = new Set<string>()
128
+ for (const edge of edges) {
129
+ if (edge.from === id) ids.add(edge.to)
130
+ else if (edge.to === id) ids.add(edge.from)
131
+ }
132
+ return [...ids].map(other => byId.get(other)).filter((node): node is GraphNodeView => Boolean(node))
133
+ }
134
+
135
+ /**
136
+ * 排一次版。
137
+ *
138
+ * `rankdir: 'LR'`:卡片在左、来源在右。这张图今天只有 `supported_by` 一种边
139
+ * (卡片 → 来源),所以它就是一张两层二部图,横向分层正好读出「这张卡片由哪几份
140
+ * 原始文档支撑」。将来 wiki↔wiki 的关系边抽出来之后,分层仍然成立,不用改这里。
141
+ */
142
+ export async function layoutGraph(nodes: GraphNodeView[], edges: GraphEdgeView[]): Promise<PlacedGraph> {
143
+ if (nodes.length === 0) return { nodes: [], edges: [], width: GRAPH_NODE_W + GRAPH_PAD * 2, height: GRAPH_NODE_H + GRAPH_PAD * 2 }
144
+
145
+ const layout = new DagreLayout({
146
+ rankdir: 'LR',
147
+ nodesep: 12,
148
+ ranksep: GRAPH_RANK_SEP,
149
+ nodeSize: [GRAPH_NODE_W, GRAPH_NODE_H],
150
+ })
151
+ await layout.execute({
152
+ // dagre 拿到的键必须是复合键:边的两端用的就是它。
153
+ nodes: nodes.map(node => ({ id: keyOf(node) })),
154
+ edges: edges.map((edge, index) => ({ id: `e${index}`, source: edge.from, target: edge.to })),
155
+ })
156
+ const positions = new Map<string, { x: number; y: number }>()
157
+ layout.forEachNode(node => { positions.set(String(node.id), { x: Number(node.x ?? 0), y: Number(node.y ?? 0) }) })
158
+ layout.destroy()
159
+
160
+ const degree = new Map<string, number>()
161
+ for (const edge of edges) {
162
+ degree.set(edge.from, (degree.get(edge.from) ?? 0) + 1)
163
+ degree.set(edge.to, (degree.get(edge.to) ?? 0) + 1)
164
+ }
165
+
166
+ const raw = nodes.map(node => {
167
+ const key = keyOf(node)
168
+ const at = positions.get(key) ?? { x: 0, y: 0 }
169
+ return { ...node, key, x: at.x, y: at.y, degree: degree.get(key) ?? 0 }
170
+ })
171
+ // 归一化:把整块内容推到留白之内,坐标因此永远非负,画布尺寸就是它的包围盒。
172
+ const minX = Math.min(...raw.map(node => node.x)) - GRAPH_NODE_W / 2
173
+ const minY = Math.min(...raw.map(node => node.y)) - GRAPH_NODE_H / 2
174
+ const placed = raw.map(node => ({ ...node, x: node.x - minX + GRAPH_PAD, y: node.y - minY + GRAPH_PAD }))
175
+ return {
176
+ nodes: placed,
177
+ edges,
178
+ width: Math.max(...placed.map(node => node.x)) + GRAPH_NODE_W / 2 + GRAPH_PAD,
179
+ height: Math.max(...placed.map(node => node.y)) + GRAPH_NODE_H / 2 + GRAPH_PAD,
180
+ }
181
+ }
@@ -0,0 +1,113 @@
1
+ /* =====================================================================
2
+ 知识库 → 知识图谱:一张 AntV 画布 + 一条筛选栏
3
+ ---------------------------------------------------------------------
4
+ 这个文件跟着 `KnowledgeGraphView` 走,不并进 architecture.css:那一份是
5
+ 「架构图 + 节点面板」的版式,这里是「图谱 + 筛选 + 详情」,共同词汇只有画布本身。
6
+ ===================================================================== */
7
+
8
+ .tita-kgStage {
9
+ position: relative; /* 全屏时的定位上下文 */
10
+ display: flex;
11
+ min-width: 0;
12
+ flex-direction: column;
13
+ border: 1px solid var(--tita-line);
14
+ border-radius: 10px;
15
+ background: var(--tita-surface);
16
+ overflow: hidden;
17
+ }
18
+
19
+ .tita-kgStage:fullscreen { border: 0; border-radius: 0; }
20
+
21
+ /* ---------------------------------------------------------------- 筛选栏 */
22
+
23
+ .tita-kgBar {
24
+ display: flex;
25
+ flex: 0 0 auto;
26
+ align-items: flex-start;
27
+ justify-content: space-between;
28
+ gap: 10px;
29
+ flex-wrap: wrap;
30
+ border-bottom: 1px solid var(--tita-line);
31
+ padding: 8px 10px;
32
+ }
33
+
34
+ .tita-kgFilters { display: flex; flex: 1 1 320px; min-width: 0; flex-direction: column; gap: 6px; }
35
+ .tita-kgSearch { display: flex; align-items: center; gap: 6px; min-width: 0; }
36
+ .tita-kgSearch > svg { flex: 0 0 auto; color: var(--tita-ink-faint); }
37
+ .tita-kgSearch input { min-width: 0; }
38
+
39
+ .tita-kgChips { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
40
+ /* 类型色点 + schema id + 计数。三样都要在:色点连着图上的节点,id 是真实类型名,
41
+ 计数回答「筛掉它会少几个」。 */
42
+ .tita-kgChip {
43
+ display: inline-flex;
44
+ align-items: center;
45
+ gap: 5px;
46
+ border: 1px solid var(--tita-line);
47
+ border-radius: 6px;
48
+ background: var(--tita-surface);
49
+ padding: 2px 7px;
50
+ color: var(--tita-ink-muted);
51
+ font: inherit;
52
+ font-size: 10.5px;
53
+ cursor: pointer;
54
+ }
55
+ .tita-kgChip:hover { background: var(--tita-surface-muted); color: var(--tita-ink); }
56
+ .tita-kgChip > i { width: 6px; height: 6px; border-radius: 999px; }
57
+ .tita-kgChip code { font-family: var(--font-tita-mono); font-size: 10px; }
58
+ .tita-kgChip b { color: var(--tita-ink); font-weight: 600; font-variant-numeric: tabular-nums; }
59
+ .tita-kgChipOn { border-color: var(--tita-ink); background: var(--tita-surface-muted); color: var(--tita-ink); font-weight: 600; }
60
+
61
+ .tita-kgActions { display: flex; flex: 0 0 auto; align-items: center; gap: 8px; padding-top: 2px; }
62
+ .tita-kgCounts { color: var(--tita-ink-muted); font-size: 10.5px; }
63
+ .tita-kgCounts b { color: var(--tita-ink); font-variant-numeric: tabular-nums; }
64
+ .tita-kgCounts b:not(:first-child) { margin-left: 6px; }
65
+ .tita-kgCounts em { margin-left: 4px; color: var(--tita-ink-faint); font-style: normal; }
66
+
67
+ .tita-kgZoom { display: inline-flex; overflow: hidden; border: 1px solid var(--tita-line); border-radius: 7px; background: var(--tita-surface); }
68
+ .tita-kgZoomOption { border: 0; background: transparent; padding: 4px 9px; color: var(--tita-ink-muted); font: inherit; font-size: 11px; cursor: pointer; }
69
+ .tita-kgZoomOption + .tita-kgZoomOption { border-left: 1px solid var(--tita-line); }
70
+ .tita-kgZoomOption:hover { background: var(--tita-surface-muted); color: var(--tita-ink); }
71
+ .tita-kgZoomOn { background: var(--tita-surface-muted); color: var(--tita-ink); font-weight: 600; }
72
+
73
+ /* ---------------------------------------------------------------- 画布 */
74
+
75
+ /* 画布永远按设计尺寸渲染,缩放由板上的 transform 承担(见 useFitScale)。
76
+ 外层 `overflow: auto` 承接 1:1 档的滚动;全屏下两个方向都可能有余量,居中。 */
77
+ .tita-kgScroll { position: relative; width: 100%; min-width: 0; overflow: auto; background: var(--tita-surface); }
78
+ .tita-kgStage:fullscreen .tita-kgScroll {
79
+ flex: 1 1 auto;
80
+ min-height: 0;
81
+ display: flex;
82
+ align-items: safe center;
83
+ justify-content: safe center;
84
+ }
85
+ .tita-kgBoard { flex: 0 0 auto; }
86
+ .tita-kgCanvas { width: 100%; height: 100%; min-width: 0; }
87
+
88
+ .tita-kgNode { cursor: pointer; }
89
+ .tita-kgStage .tita-kgNode:hover { border-color: var(--tita-primary) !important; }
90
+ .tita-kgNodeOn { cursor: default; }
91
+
92
+ .tita-kgEmpty { position: absolute; inset: 0; display: grid; place-content: center; margin: 0; padding: 24px; color: var(--tita-ink-muted); font-size: 11.5px; text-align: center; }
93
+
94
+ /* ---------------------------------------------------------------- 详情条 */
95
+
96
+ .tita-kgDetail { display: grid; gap: 6px; flex: 0 0 auto; border-top: 1px solid var(--tita-line); padding: 9px 10px; }
97
+ .tita-kgDetailHead { display: flex; flex-wrap: wrap; align-items: baseline; gap: 7px; min-width: 0; }
98
+ .tita-kgDetailHead > i { align-self: center; width: 6px; height: 6px; border-radius: 999px; }
99
+ .tita-kgDetailHead code { color: var(--tita-ink-muted); font-family: var(--font-tita-mono); font-size: 10px; }
100
+ .tita-kgDetailHead strong { min-width: 0; color: var(--tita-ink); font-size: 12px; font-weight: 650; }
101
+ .tita-kgDetailHead em { margin-left: auto; color: var(--tita-ink-faint); font-size: 10px; font-style: normal; font-variant-numeric: tabular-nums; }
102
+ .tita-kgNeighbors { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; color: var(--tita-ink-muted); font-size: 10.5px; }
103
+ .tita-kgNeighbors button {
104
+ border: 1px solid var(--tita-line);
105
+ border-radius: 999px;
106
+ background: var(--tita-surface);
107
+ padding: 2px 8px;
108
+ color: var(--tita-ink);
109
+ font: inherit;
110
+ font-size: 10.5px;
111
+ cursor: pointer;
112
+ }
113
+ .tita-kgNeighbors button:hover { background: var(--tita-surface-muted); }
@@ -0,0 +1,98 @@
1
+ import type { Context as ClientContext } from '@deepseek-ai/cordis'
2
+ // 只为一个类型副作用而导入:`configForms` 是 `dsh-client-ui-settings/client` 通过
3
+ // `declare module '@deepseek-ai/cordis'` 挂到 Context 上的,不导入这个模块就看不见它。
4
+ import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
5
+ import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
6
+ import type { SidebarRightTabDefinition } from '@deepseek-ai/dsh-client-ui-sidebar-right/client'
7
+ import type {} from '@deepseek-ai/dsh-client-ui-sidebar-right/client'
8
+ import type {} from '@deepseek-ai/dsh-client-ui-renderer/client'
9
+ import { createElement, useEffect, useState, type ReactNode } from 'react'
10
+ import { KnowledgePanel } from './KnowledgePanel.tsx'
11
+ import type { SettingsScopeLike } from './NodeSettings.tsx'
12
+ import { groupScope } from './config-scope.ts'
13
+ import { type AgentPresetRemote } from './AgentPresetCard.tsx'
14
+
15
+ const ID = 'dsh-data-domain-knowledge'
16
+ const KIND = 'data-domain-knowledge'
17
+ /**
18
+ * 本插件**Host entry** 的 id —— 注意它不是包名。
19
+ *
20
+ * 0.1.7 之后客户端的设置入口是 `configForms.get(entryId)`:设置已经不是一个独立的
21
+ * 命名空间文档,而是某条 profile entry 的 config。所以这里要写 `cordis.patch.yml`
22
+ * 里 `insert` 行的 `id`(`tita-data-domain-knowledge`),而不是包名
23
+ * `dsh-data-domain-knowledge`(那是模块说明符,也是下面 `ID` 的值)。
24
+ */
25
+ const HOST_ENTRY_ID = 'tita-data-domain-knowledge'
26
+ function Icon({ size = 16 }: { size?: number }): ReactNode { return createElement('svg', { width: size, height: size, viewBox: '0 0 16 16', fill: 'none', 'aria-hidden': true }, createElement('path', { d: 'M2 3.5 8 1l6 2.5v8L8 14l-6-2.5v-8Z M2 3.5 8 6l6-2.5 M8 6v8', stroke: 'currentColor', strokeWidth: 1.2, strokeLinejoin: 'round' })) }
27
+ function definition(): SidebarRightTabDefinition { return { id: ID, kind: KIND, priority: 'extension', title: () => 'TitaAgent', guide: [{ id: `${ID}-guide`, order: 47, title: () => 'TitaAgent', icon: Icon }] } }
28
+ interface TabInjected {
29
+ intent: SettingsScopeLike
30
+ knowledge: SettingsScopeLike
31
+ convert: SettingsScopeLike
32
+ /**
33
+ * agent preset 的 Remote 命名空间。取不到成员时「Agent 提示词」卡片会如实说不可用。
34
+ *
35
+ * 注意它现在只承载**只读**的两件事:名单(`agentPresets`)与打开真源目录
36
+ * (`session`)。「默认 preset」那一项已经撤了 —— 它是 DSH 设置里的一项,在这里
37
+ * 再摆一个只是同一字段的第二个入口。
38
+ */
39
+ remote: AgentPresetRemote
40
+ }
41
+
42
+ function Tab({ useTabInfo }: PropsRuntime<'sidebar.right.pane.tab'>, injected: TabInjected): ReactNode {
43
+ const { tab } = useTabInfo()
44
+ return createElement(KnowledgePanel, {
45
+ visible: tab.visible,
46
+ onOpenTab: kind => { tab.actions.openTab(kind) },
47
+ settings: injected,
48
+ remote: injected.remote,
49
+ })
50
+ }
51
+
52
+ /**
53
+ * `remote.agentPresets` 是「Agent 提示词」卡片读 preset 名单用的。
54
+ *
55
+ * **这个命名空间必须写进 `inject`,不能只 inject 顶层的 `remote` 再懒读。**
56
+ * 一开始就是这么写的(想的是「一张只读卡片不该有能力让整个 tab 消失」),结果
57
+ * cordis 的 remote 代理对未声明 inject 的属性**直接抛**:
58
+ *
59
+ * Error: cannot get property "remote.agentPresets" without inject
60
+ *
61
+ * 而它抛在 `useEffect` 里,于是 React 把整个面板子树卸掉了 —— 打开设置抽屉 =
62
+ * 面板变空白。浏览器核验才看出来(测试断言不出来),所以这里如实记下来。
63
+ *
64
+ * 现在按 DSH 自己的写法声明(`dsh-client-ui-agent-preset` 就是这么 inject 的),
65
+ * 并且卡片内部再兜一层 try/catch:命名空间万一不在,卡片显示「不可用」,而不是
66
+ * 把面板带走。
67
+ *
68
+ * 两个命名空间,两件事:
69
+ *
70
+ * - `remote.agentPresets` —— 读 preset 名单与组合正文(只读)。
71
+ * - `remote.session` —— 打开提示词的**真源目录**(`openWorkspacePath`)。0.1.7 去掉的
72
+ * 是 `remote.settings`(它指向 DSH 那边装好的 preset,而那边不再是目录),不是
73
+ * 「打开一个目录」这件事本身:真源目录是插件自己的 `preset/tita-data-agent/`,
74
+ * 提示词就住在里面。`dsh-client-ui-open-in-app` 用的也是 `remote.session`。
75
+ *
76
+ * `settingsScope`(0.1.5 的客户端设置入口)在 0.1.7 被 `configForms` 取代,所以
77
+ * 这里换的就是它 —— 名字变了,理由没变:**用哪个服务就必须声明哪个**。
78
+ */
79
+ export const inject = [
80
+ 'slots', 'sidebarRightTabs', 'configForms',
81
+ 'remote', 'remote.agentPresets', 'remote.session',
82
+ ] as const
83
+ export function apply(ctx: ClientContext): void {
84
+ /**
85
+ * 三块设置现在住在**同一个 entry** 的 config 里(`knowledge` / `intent` / `convert`
86
+ * 三个组),所以取一次表单、按组切三次,而不是像 0.1.5 那样 bind 三个命名空间。
87
+ * `groupScope` 负责把「组」这件事挡住:值切到组内,写操作补上路径前缀。
88
+ */
89
+ const form = ctx.configForms.get<Record<string, unknown>>(HOST_ENTRY_ID)
90
+ const settings = {
91
+ intent: groupScope(form, 'intent'),
92
+ knowledge: groupScope(form, 'knowledge'),
93
+ convert: groupScope(form, 'convert'),
94
+ }
95
+ const injected: TabInjected = { ...settings, remote: (ctx as unknown as { remote?: AgentPresetRemote }).remote ?? {} }
96
+ ctx.effect(() => ctx.sidebarRightTabs.register(definition()), 'tita knowledge: tab type')
97
+ ctx.effect(() => ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register({ name: 'sidebar.right.pane.tab', key: ID }, props => Tab(props, injected))), 'tita knowledge: tab body')
98
+ }