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

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 (189) hide show
  1. package/README.md +23 -16
  2. package/lib/client.js +596 -841
  3. package/lib/contract-refs.d.ts +0 -1
  4. package/lib/doc-page.js +7 -7
  5. package/lib/pages.js +928 -1145
  6. package/lib/types/agent-behavior-rules.d.ts.map +1 -1
  7. package/lib/types/agent-behavior-rules.js +1 -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 +5 -2
  20. package/lib/types/convert/host.js.map +1 -1
  21. package/lib/types/convert/ingest.d.ts +2 -0
  22. package/lib/types/convert/ingest.d.ts.map +1 -1
  23. package/lib/types/convert/ingest.js +1 -0
  24. package/lib/types/convert/ingest.js.map +1 -1
  25. package/lib/types/diagnosis/types.d.ts +1 -1
  26. package/lib/types/diagnosis/types.js +1 -1
  27. package/lib/types/images.js +1 -1
  28. package/lib/types/index.d.ts +0 -4
  29. package/lib/types/index.d.ts.map +1 -1
  30. package/lib/types/index.js +30 -41
  31. package/lib/types/index.js.map +1 -1
  32. package/lib/types/knowledge-contract.d.ts +6 -0
  33. package/lib/types/knowledge-contract.d.ts.map +1 -1
  34. package/lib/types/knowledge-contract.js +6 -2
  35. package/lib/types/knowledge-contract.js.map +1 -1
  36. package/lib/types/layout.d.ts +9 -26
  37. package/lib/types/layout.d.ts.map +1 -1
  38. package/lib/types/layout.js +10 -28
  39. package/lib/types/layout.js.map +1 -1
  40. package/lib/types/materialize-snapshot.d.ts +5 -0
  41. package/lib/types/materialize-snapshot.d.ts.map +1 -1
  42. package/lib/types/migrate.d.ts +3 -1
  43. package/lib/types/migrate.d.ts.map +1 -1
  44. package/lib/types/migrate.js +3 -1
  45. package/lib/types/migrate.js.map +1 -1
  46. package/lib/types/organize-contract.d.ts.map +1 -1
  47. package/lib/types/organize-contract.js +2 -1
  48. package/lib/types/organize-contract.js.map +1 -1
  49. package/lib/types/pipeline/engine.d.ts +1 -0
  50. package/lib/types/pipeline/engine.d.ts.map +1 -1
  51. package/lib/types/pipeline/engine.js +9 -3
  52. package/lib/types/pipeline/engine.js.map +1 -1
  53. package/lib/types/pipeline/types.d.ts +6 -0
  54. package/lib/types/pipeline/types.d.ts.map +1 -1
  55. package/lib/types/pipeline/types.js.map +1 -1
  56. package/lib/types/present.d.ts +1 -1
  57. package/lib/types/present.js +1 -1
  58. package/lib/types/preset-ids.d.ts +0 -1
  59. package/lib/types/preset-ids.d.ts.map +1 -1
  60. package/lib/types/preset-ids.js +0 -3
  61. package/lib/types/preset-ids.js.map +1 -1
  62. package/lib/types/query-analysis.d.ts +0 -5
  63. package/lib/types/query-analysis.d.ts.map +1 -1
  64. package/lib/types/query-analysis.js +0 -16
  65. package/lib/types/query-analysis.js.map +1 -1
  66. package/lib/types/recall-delivery.d.ts +36 -0
  67. package/lib/types/recall-delivery.d.ts.map +1 -0
  68. package/lib/types/recall-delivery.js +16 -0
  69. package/lib/types/recall-delivery.js.map +1 -0
  70. package/lib/types/runtime.d.ts +26 -12
  71. package/lib/types/runtime.d.ts.map +1 -1
  72. package/lib/types/runtime.js +231 -106
  73. package/lib/types/runtime.js.map +1 -1
  74. package/lib/types/settings-volatile.d.ts +0 -7
  75. package/lib/types/settings-volatile.d.ts.map +1 -1
  76. package/lib/types/settings-volatile.js +0 -7
  77. package/lib/types/settings-volatile.js.map +1 -1
  78. package/lib/types/source-identity.d.ts +3 -0
  79. package/lib/types/source-identity.d.ts.map +1 -0
  80. package/lib/types/source-identity.js +5 -0
  81. package/lib/types/source-identity.js.map +1 -0
  82. package/lib/types/store/axis-references.d.ts.map +1 -1
  83. package/lib/types/store/axis-references.js +5 -3
  84. package/lib/types/store/axis-references.js.map +1 -1
  85. package/lib/types/store/knowledge-db.d.ts +1 -1
  86. package/lib/types/store/knowledge-db.d.ts.map +1 -1
  87. package/lib/types/store/knowledge-db.js +16 -2
  88. package/lib/types/store/knowledge-db.js.map +1 -1
  89. package/lib/types/types.d.ts +5 -4
  90. package/lib/types/types.d.ts.map +1 -1
  91. package/lib/types/types.js +6 -3
  92. package/lib/types/types.js.map +1 -1
  93. package/lib/types/wiki-availability.d.ts +11 -0
  94. package/lib/types/wiki-availability.d.ts.map +1 -0
  95. package/lib/types/wiki-availability.js +39 -0
  96. package/lib/types/wiki-availability.js.map +1 -0
  97. package/lib/types/wiki-finalize.d.ts +4 -2
  98. package/lib/types/wiki-finalize.d.ts.map +1 -1
  99. package/lib/types/wiki-finalize.js +9 -27
  100. package/lib/types/wiki-finalize.js.map +1 -1
  101. package/lib/types/wiki-links.d.ts +35 -0
  102. package/lib/types/wiki-links.d.ts.map +1 -0
  103. package/lib/types/wiki-links.js +129 -0
  104. package/lib/types/wiki-links.js.map +1 -0
  105. package/package.json +16 -16
  106. package/scripts/doctor.mjs +0 -7
  107. package/scripts/setup-embedding.sh +0 -0
  108. package/src/agent-behavior-rules.ts +1 -2
  109. package/src/axis-contract.ts +1 -1
  110. package/src/card-organize.ts +5 -3
  111. package/src/client/AttributionEventGuide.tsx +3 -3
  112. package/src/client/AxisMatrix.tsx +86 -19
  113. package/src/client/DirectoryPicker.tsx +0 -2
  114. package/src/client/GuideArchitectureCanvas.tsx +5 -12
  115. package/src/client/ImportFilesDialog.tsx +1 -0
  116. package/src/client/InfoHint.tsx +1 -1
  117. package/src/client/KnowledgeBaseTab.tsx +113 -39
  118. package/src/client/KnowledgePanel.tsx +16 -9
  119. package/src/client/OperationLogTab.tsx +1 -1
  120. package/src/client/RecallPanel.tsx +5 -16
  121. package/src/client/TabGuide.tsx +3 -7
  122. package/src/client/WikiFlowPanel.tsx +10 -11
  123. package/src/client/arch/NodeLogicPanel.tsx +3 -3
  124. package/src/client/arch/model.ts +5 -113
  125. package/src/client/architecture.css +1 -1
  126. package/src/client/architecture.tsx +13 -27
  127. package/src/client/attribution/AttributionTab.tsx +141 -10
  128. package/src/client/pages.css +2 -2
  129. package/src/client/pages.tsx +4 -11
  130. package/src/client/styles.css +15 -96
  131. package/src/config-schema.ts +1 -1
  132. package/src/convert/host.ts +7 -5
  133. package/src/convert/ingest.ts +3 -0
  134. package/src/diagnosis/types.ts +1 -1
  135. package/src/images.ts +1 -1
  136. package/src/index.ts +27 -49
  137. package/src/knowledge-contract.ts +7 -2
  138. package/src/layout.ts +10 -32
  139. package/src/materialize-snapshot.ts +1 -0
  140. package/src/migrate.ts +3 -1
  141. package/src/organize-contract.ts +2 -1
  142. package/src/pipeline/engine.ts +9 -3
  143. package/src/pipeline/types.ts +2 -0
  144. package/src/present.ts +1 -1
  145. package/src/preset-ids.ts +0 -4
  146. package/src/query-analysis.ts +0 -20
  147. package/src/recall-delivery.ts +19 -0
  148. package/src/runtime.ts +220 -102
  149. package/src/settings-volatile.ts +0 -7
  150. package/src/source-identity.ts +4 -0
  151. package/src/store/axis-references.ts +5 -3
  152. package/src/store/knowledge-db.ts +14 -2
  153. package/src/types.ts +6 -4
  154. package/src/wiki-availability.ts +37 -0
  155. package/src/wiki-finalize.ts +9 -25
  156. package/src/wiki-links.ts +120 -0
  157. package/lib/types/intent/engine.d.ts +0 -122
  158. package/lib/types/intent/engine.d.ts.map +0 -1
  159. package/lib/types/intent/engine.js +0 -488
  160. package/lib/types/intent/engine.js.map +0 -1
  161. package/lib/types/intent/host.d.ts +0 -68
  162. package/lib/types/intent/host.d.ts.map +0 -1
  163. package/lib/types/intent/host.js +0 -249
  164. package/lib/types/intent/host.js.map +0 -1
  165. package/lib/types/intent/scope.d.ts +0 -25
  166. package/lib/types/intent/scope.d.ts.map +0 -1
  167. package/lib/types/intent/scope.js +0 -116
  168. package/lib/types/intent/scope.js.map +0 -1
  169. package/lib/types/intent/store.d.ts +0 -33
  170. package/lib/types/intent/store.d.ts.map +0 -1
  171. package/lib/types/intent/store.js +0 -239
  172. package/lib/types/intent/store.js.map +0 -1
  173. package/lib/types/intent/tools.d.ts +0 -62
  174. package/lib/types/intent/tools.d.ts.map +0 -1
  175. package/lib/types/intent/tools.js +0 -130
  176. package/lib/types/intent/tools.js.map +0 -1
  177. package/lib/types/intent/types.d.ts +0 -170
  178. package/lib/types/intent/types.d.ts.map +0 -1
  179. package/lib/types/intent/types.js +0 -56
  180. package/lib/types/intent/types.js.map +0 -1
  181. package/src/client/IntentLogic.tsx +0 -192
  182. package/src/client/IntentTab.tsx +0 -343
  183. package/src/intent/README.md +0 -42
  184. package/src/intent/engine.ts +0 -515
  185. package/src/intent/host.ts +0 -315
  186. package/src/intent/scope.ts +0 -125
  187. package/src/intent/store.ts +0 -218
  188. package/src/intent/tools.ts +0 -154
  189. package/src/intent/types.ts +0 -229
@@ -93,13 +93,6 @@ export function unwrapVolatile<T>(value: unknown): T | undefined {
93
93
  * `knowledge` / `convert` / `behaviorRules` 三组),而只解一层的结果是「组本身还是原始对象、
94
94
  * 组里的字段还是引用」。
95
95
  *
96
- * 0029 实测到的故障就是这样来的:`intent` 组被原样交给 `applyIntent`,于是
97
- * `engine.settings.predicates` 是一个 `{get, write}` 引用而不是字符串数组,
98
- * `routingPrompt()` 里那句 `predicates.join('、')` 抛
99
- * `this.settings.predicates.join is not a function`。抛出点在 **preset 的挂载行**上,
100
- * 所以整个 preset 组合不出会话 —— 宿主平面没事(它在 apply 时不读谓词),一个字段
101
- * 的类型错误把 preset 打挂。
102
- *
103
96
  * ## 为什么是两层而不是把 `unwrapVolatile` 写成递归
104
97
  *
105
98
  * 多出来的那一层是**已知的、唯一的一层**:组 → 字段。递归会把「数组里的对象恰好
@@ -0,0 +1,4 @@
1
+ /** Allocate once when an input joins the import queue; retries and conversions keep this ID. */
2
+ export function newSourceId(): string {
3
+ return `src_${globalThis.crypto.randomUUID()}`
4
+ }
@@ -15,13 +15,14 @@ export function redirectAxisReferences(db: KnowledgeDb, kind: AxisKind, fromId:
15
15
  if (hasKnowledge) db.raw(`UPDATE knowledge SET ${column} = ?, updated_at = ? WHERE ${column} = ?`).run(intoId, new Date().toISOString(), fromId)
16
16
  const placements = db.placements()
17
17
  const cards = db.cards()
18
- const byCoordinate = new Map(cards.filter(row => row.card.coordinate).map(row => [cardCoordinateKey(row.card.coordinate!), row]))
18
+ const wikiKind = (card: typeof cards[number]['card']) => card.metadata.wiki_kind === 'fact' || card.metadata.wiki_kind === 'way' ? card.metadata.wiki_kind : undefined
19
+ const byCoordinate = new Map(cards.filter(row => row.card.coordinate).map(row => [cardCoordinateKey(row.card.coordinate!, wikiKind(row.card)), row]))
19
20
  db.transact({ opId: `axis-merge:${fromId}:${intoId}:${Date.now()}`, actor: 'user', summary: '合并轴实体并迁移知识、卡片与事件引用' }, tx => {
20
21
  for (const row of cards) {
21
22
  const coordinate = row.card.coordinate
22
23
  if (!coordinate || coordinate[field] !== fromId) continue
23
24
  const next = redirect(coordinate)
24
- const key = cardCoordinateKey(next)
25
+ const key = cardCoordinateKey(next, wikiKind(row.card))
25
26
  const target = byCoordinate.get(key)
26
27
  const base = target && target.card.entityId !== row.card.entityId ? target : row
27
28
  const merged = base !== row
@@ -32,6 +33,7 @@ export function redirectAxisReferences(db: KnowledgeDb, kind: AxisKind, fromId:
32
33
  sourceRefs: [...new Set([...base.card.sourceRefs, ...row.card.sourceRefs])],
33
34
  annotations: [...new Map([...(base.card.annotations ?? []), ...(row.card.annotations ?? [])].map(annotation => [annotation.id, annotation])).values()],
34
35
  metadata: { ...base.card.metadata, layout: undefined, citations: undefined,
36
+ knowledge_versions: { ...(row.card.metadata.knowledge_versions as Record<string, number> ?? {}), ...(base.card.metadata.knowledge_versions as Record<string, number> ?? {}) },
35
37
  aliases: [...new Set([...(Array.isArray(base.card.metadata.aliases) ? base.card.metadata.aliases : []), ...(Array.isArray(row.card.metadata.aliases) ? row.card.metadata.aliases : []), base.card.title, row.card.title])],
36
38
  ...(eventRefs.length ? { event_refs: JSON.stringify(eventRefs) } : {}) },
37
39
  }
@@ -41,7 +43,7 @@ export function redirectAxisReferences(db: KnowledgeDb, kind: AxisKind, fromId:
41
43
  const frontmatter = { ...base.frontmatter, coordinate: { object: next.objectId, action: next.actionId, scene: next.sceneId }, source_refs: card.sourceRefs, metadata: card.metadata, annotations: card.annotations }
42
44
  tx.putCard(card, frontmatter, { actor: 'user', kind: 'named', summary: '轴合并后的卡片引用' }, combined, true)
43
45
  db.setCardEvents(card.entityId, eventRefs)
44
- byCoordinate.delete(cardCoordinateKey(coordinate))
46
+ byCoordinate.delete(cardCoordinateKey(coordinate, wikiKind(row.card)))
45
47
  byCoordinate.set(key, { ...base, card, frontmatter })
46
48
  placements.set(card.entityId, combined)
47
49
  }
@@ -54,7 +54,7 @@ export const KNOWLEDGE_DB_APPLICATION_ID = 0x544B4244
54
54
  * 旧库会在打开时做一次**事务化就地迁移**。知识库是用户数据,不能因为存储层增加了
55
55
  * 一列或调整了主键就要求用户重新导入;只有比当前版本更新的库才拒绝打开。
56
56
  */
57
- export const KNOWLEDGE_DB_SCHEMA = 7
57
+ export const KNOWLEDGE_DB_SCHEMA = 8
58
58
 
59
59
  /** 实体种类。`knowledge` 与轴注册表不走这张表(它们各有自己的表,见 §3.2)。 */
60
60
  export type EntityKind = 'source' | 'card'
@@ -581,6 +581,18 @@ function migrateSchema(db: SqliteDatabase, fromVersion: number): void {
581
581
 
582
582
  // 其他模块可能已经先建了自己的表;schemaSql 使用 IF NOT EXISTS,可安全补齐剩余结构和索引。
583
583
  db.exec(schemaSql())
584
+ // Rekey typed Wiki rows without changing entity IDs, content, reviews or revisions.
585
+ const wikiRows = db.prepare('SELECT entity_id,coordinate_key,object_id,action_id,scene_id,frontmatter FROM card').all() as Array<Record<string, unknown>>
586
+ for (const row of wikiRows) {
587
+ const metadata = parseFrontmatter(String(row.frontmatter)).metadata as Record<string, unknown> | undefined
588
+ const kind = metadata?.wiki_kind
589
+ const coordinate = coordinateOfRow(row)
590
+ if (!coordinate || (kind !== 'fact' && kind !== 'way')) continue
591
+ const key = cardCoordinateKey(coordinate, kind)
592
+ const holder = db.prepare('SELECT entity_id FROM card WHERE coordinate_key=?').get(key) as { entity_id: string } | undefined
593
+ if (holder && holder.entity_id !== row.entity_id) continue
594
+ db.prepare('UPDATE card SET coordinate_key=? WHERE entity_id=?').run(key, String(row.entity_id))
595
+ }
584
596
  db.exec(`PRAGMA application_id = ${KNOWLEDGE_DB_APPLICATION_ID}`)
585
597
  db.exec(`PRAGMA user_version = ${KNOWLEDGE_DB_SCHEMA}`)
586
598
  db.exec('COMMIT')
@@ -736,7 +748,7 @@ export class KnowledgeTx {
736
748
  const storedCoordinate = card.coordinate ?? (existing === undefined ? undefined : coordinateOfRow(existing))
737
749
  const coordinateKey = storedCoordinate === undefined
738
750
  ? legacyCardStorageKey(card.entityId)
739
- : cardCoordinateKey(storedCoordinate)
751
+ : cardCoordinateKey(storedCoordinate, card.metadata.wiki_kind === 'fact' || card.metadata.wiki_kind === 'way' ? card.metadata.wiki_kind : undefined)
740
752
  const oldKey = typeof existing?.coordinate_key === 'string' ? existing.coordinate_key : undefined
741
753
  if (existing !== undefined) {
742
754
  if (!allowCoordinateMove && oldKey !== undefined && oldKey !== coordinateKey && !oldKey.startsWith('legacy:')) {
package/src/types.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { wikiGroupKey, type WikiKind } from './knowledge-contract.js'
1
2
  import { DIAGNOSIS_ENTITY_TYPES } from './diagnosis/schema.js'
2
3
 
3
4
  /** Wiki Card 自己的生命周期。它描述卡片是否可展示、是否被审核或是否有编译问题。 */
@@ -382,10 +383,11 @@ export interface CardCoordinate {
382
383
  /**
383
384
  * 卡片的存储主键。
384
385
  *
385
- * `entityId` 仍是旧接口与版本表使用的稳定别名;真正决定一张有坐标卡片身份的,
386
- * 是这三个轴实体 id。加前缀是为了把「坐标主键」和「未绑定坐标的存量卡片」分开。
386
+ * `entityId` 是读取与版本表使用的稳定 ID。类型化 Wiki 由 Wiki 类型和完整三轴共同
387
+ * 确定身份;未指定类型时保留旧坐标键,供存量卡片兼容使用。知识库由所在数据库隔离。
387
388
  */
388
- export function cardCoordinateKey(coordinate: CardCoordinate): string {
389
+ export function cardCoordinateKey(coordinate: CardCoordinate, kind?: WikiKind): string {
390
+ if (kind) return wikiGroupKey(kind, coordinate)
389
391
  return `coord:${coordinate.objectId}\u0001${coordinate.actionId}\u0001${coordinate.sceneId}`
390
392
  }
391
393
 
@@ -839,7 +841,7 @@ export interface RecallResult {
839
841
  /** 各阶段耗时(毫秒)。它很小,所以不藏在 `detail` 后面 —— Agent 也会问「为什么慢」。 */
840
842
  timings?: RecallTimings
841
843
  recallId: string
842
- /** 查询侧的意图与 OAS 解析结果,供诊断和召回解释使用。 */
844
+ /** 查询侧的 OAS 坐标解析结果,供诊断和召回解释使用。 */
843
845
  queryAnalysis?: import('./query-analysis.js').QueryAnalysis
844
846
  indexVersion?: number
845
847
  compileRunId?: string
@@ -0,0 +1,37 @@
1
+ import type { Knowledge } from './knowledge.js'
2
+ import type { CardKnowledgeRow } from './store/knowledge-db.js'
3
+ import type { KnowledgeCard } from './types.js'
4
+
5
+ export interface WikiTruth {
6
+ knowledge: ReadonlyMap<string, Knowledge>
7
+ sources: ReadonlyMap<string, number>
8
+ caseVersion(id: string): number | undefined
9
+ }
10
+
11
+ /** The saved Wiki generation, rather than the index build time, binds its supporting versions. */
12
+ export function wikiUnavailableReason(card: KnowledgeCard, placements: readonly CardKnowledgeRow[], truth: WikiTruth): string | undefined {
13
+ if (card.status === 'deprecated') return '该 Wiki 已停用。'
14
+ if (card.sourceRefs.some(id => !truth.sources.has(id))) return '支撑该 Wiki 的 Source Raw 已缺失。'
15
+ const kind = card.metadata.wiki_kind
16
+ if (kind !== 'fact' && kind !== 'way') return undefined
17
+ if (card.metadata.wiki_coordinate_version !== 2) return '该 Wiki 仍使用旧聚合规则,请先重新组织 Wiki。'
18
+ const versions = card.metadata.knowledge_versions as Record<string, number> | undefined
19
+ const ids = new Set([...placements.map(item => item.knowledgeId), ...Object.keys(versions ?? {})])
20
+ if (kind === 'way' && ids.size === 0) return '方法缺少知识落位与版本依据,请重新组织 Wiki。'
21
+ for (const id of ids) {
22
+ const row = truth.knowledge.get(id)
23
+ if (!row || row.mergedInto || row.status === 'deprecated') return '支撑知识已撤回、合并或停用,请重新组织 Wiki。'
24
+ if ((row.kind ?? 'fact') !== (kind === 'way' ? 'procedure' : 'fact')) return 'Wiki 类型与支撑知识不一致,请重新组织 Wiki。'
25
+ if ((versions?.[id] !== undefined && versions[id] !== row.version) || (kind === 'way' && versions?.[id] === undefined)) return '支撑知识内容版本已变化,请重新组织 Wiki。'
26
+ if (card.coordinate && (row.objectId !== card.coordinate.objectId || row.actionId !== card.coordinate.actionId || row.sceneId !== card.coordinate.sceneId)) return '支撑知识坐标已变化,请重新组织 Wiki。'
27
+ const active = row.sources.filter(source => !source.withdrawn && (!source.documentId || truth.sources.has(source.documentId)))
28
+ if (!active.length) return '支撑知识的来源贡献已撤回,请重新组织 Wiki。'
29
+ if (kind === 'way') {
30
+ const method = row.data?.procedure
31
+ if (row.status === 'disputed' || !method || method.completeness !== 'complete' || !method.method.length
32
+ || method.caseRefs.some(ref => truth.caseVersion(ref.id) !== ref.version)
33
+ || active.some(source => source.documentId && source.toVersion !== undefined && truth.sources.get(source.documentId) !== source.toVersion)) return '支撑方法或证据版本已变化,请重新组织 Wiki。'
34
+ }
35
+ }
36
+ return undefined
37
+ }
@@ -9,7 +9,8 @@
9
9
  *
10
10
  * 1. **相关卡片**(`planRelated`)—— 卡片之间原来只有图上的边,卡片正文里什么都没有,
11
11
  * 人打开一张卡片看不到它跟谁有关系。现在把关系存进卡片的 `metadata.related_cards`,
12
- * 正文保持纯正文;面板和索引页按这份结构化数据展示。
12
+ * 面板和索引页按这份结构化数据展示。类型化 Wiki 的正文双链和导航另由
13
+ * `wiki-links.ts` 生成,索引前剥离导航。
13
14
  * 2. **断链统计**(`deadLinksOf`)—— 为编译报告与索引页提供关系图的可见性,不修改卡片
14
15
  * 内容,也不写入卡片 metadata。
15
16
  * 3. **索引页**(`buildWikiIndex`)—— 知识库原来没有「这里有什么」的落盘入口:
@@ -24,12 +25,13 @@
24
25
  * ## 为什么只认 wiki 链接语法
25
26
  *
26
27
  * 旧相关块里写的是 `[[类型/标题|标题]]`,不是 markdown 相对链接。这个语法仍由迁移
27
- * 与纯函数测试保留,但新编译不再把它写进正文;正文里的 wiki 链接仍由断链检查识别。
28
+ * 与纯函数测试保留。新编译使用稳定 `[[类型/实体ID|显示名称]]`,正文里的有效链接
29
+ * 由断链检查识别;代码与 URL 内的示例不作为链接。
28
30
  *
29
31
  * @module
30
32
  */
31
33
 
32
- import { buildAliasIndex, candidateKey, normalizeCandidateName } from './extract.js'
34
+ import { scanWikiLinks, wikiLinkCandidates } from './wiki-links.js'
33
35
  import type { KnowledgeCard } from './types.js'
34
36
 
35
37
  /**
@@ -89,17 +91,9 @@ export function relatedEntriesOf(metadata: Record<string, unknown> | undefined):
89
91
  })
90
92
  }
91
93
 
92
- /** 正文里的 wiki 链接:`[[类型/名称]]`、`[[类型/名称|显示文字]]`、`[[名称]]`。 */
93
- const WIKI_LINK = /\[\[([^\]|]+)(?:\|[^\]]*)?\]\]/g
94
-
95
94
  /** 卡片正文里出现的 wiki 链接目标(原样,未归一)。 */
96
95
  export function wikiLinkTargets(content: string): string[] {
97
- const targets: string[] = []
98
- for (const match of content.matchAll(WIKI_LINK)) {
99
- const target = (match[1] ?? '').trim()
100
- if (target) targets.push(target)
101
- }
102
- return targets
96
+ return scanWikiLinks(content).map(link => link.target)
103
97
  }
104
98
 
105
99
  /**
@@ -278,20 +272,10 @@ export interface DeadLink {
278
272
  * 会被整批报成断链。这里从最长的类型前缀开始试(即从左往右逐个 `/` 切)。
279
273
  */
280
274
  export function deadLinksOf(cards: KnowledgeCard[], index?: Map<string, KnowledgeCard>): DeadLink[] {
281
- const resolved = index ?? buildAliasIndex(cards)
282
- const byName = new Map<string, KnowledgeCard>()
283
- for (const card of cards) {
284
- for (const name of [card.entityId, card.title, ...aliasesOf(card)]) {
285
- const normalized = normalizeCandidateName(name)
286
- if (normalized && !byName.has(normalized)) byName.set(normalized, card)
287
- }
288
- }
275
+ void index // Retained for compatibility; all targets must resolve uniquely.
289
276
  const resolveTarget = (target: string): KnowledgeCard | undefined => {
290
- for (let at = target.indexOf('/'); at > 0; at = target.indexOf('/', at + 1)) {
291
- const hit = resolved.get(candidateKey({ type: target.slice(0, at), name: target.slice(at + 1) }))
292
- if (hit) return hit
293
- }
294
- return byName.get(normalizeCandidateName(target))
277
+ const candidates = wikiLinkCandidates(cards, target)
278
+ return candidates.length === 1 ? candidates[0] : undefined
295
279
  }
296
280
  const dead: DeadLink[] = []
297
281
  for (const card of cards) {
@@ -0,0 +1,120 @@
1
+ import { normalizeCandidateName } from './extract.js'
2
+ import type { KnowledgeCard } from './types.js'
3
+
4
+ export const WIKI_NAV_START = '<!-- compiled:wiki-links -->'
5
+ const WIKI_NAV_END = '<!-- /compiled:wiki-links -->'
6
+ export interface WikiObject { id: string; name: string; aliases: string[]; scopeId?: string }
7
+ export interface WikiReference { entityId: string; entityType: string; title: string; target: string; link: string }
8
+
9
+ export function stripWikiNavigation(content: string): string {
10
+ return content.replace(/\n*<!-- compiled:wiki-links -->\n[\s\S]*?\n<!-- \/compiled:wiki-links -->\s*$/, '').trim()
11
+ }
12
+
13
+ /** Preserve offsets while excluding fenced code, inline code, URLs and ordinary Markdown links. */
14
+ function proseMask(content: string): string {
15
+ let fence: string | undefined
16
+ let result = content.split('\n').map(line => {
17
+ const match = line.match(/^\s*(?:(?:\d+[.)]|[-+*])\s+)?(`{3,}|~{3,})(.*)$/)
18
+ const wasCode = Boolean(fence)
19
+ if (match) {
20
+ if (!fence) fence = match[1]!
21
+ else if (match[1]![0] === fence[0] && match[1]!.length >= fence.length && !match[2]!.trim()) fence = undefined
22
+ }
23
+ return wasCode || match ? ' '.repeat(line.length) : line
24
+ }).join('\n')
25
+ result = result.replace(/(`+)[^\n]*?\1|!?\[[^\]\n]*\]\([^\n)]*\)|https?:\/\/[^\s<>]+/g, match => ' '.repeat(match.length))
26
+ return result
27
+ }
28
+
29
+ export function scanWikiLinks(content: string): Array<{ target: string; label: string; start: number; end: number }> {
30
+ return [...proseMask(content).matchAll(/\[\[([^\]|\n]+)(?:\|([^\]\n]*))?\]\]/g)].map(match => ({
31
+ target: match[1]!.trim(), label: match[2]?.trim() || match[1]!.trim(), start: match.index!, end: match.index! + match[0].length,
32
+ }))
33
+ }
34
+
35
+ export function wikiReference(card: Pick<KnowledgeCard, 'entityId' | 'entityType' | 'title'>): WikiReference {
36
+ const target = `${card.entityType}/${card.entityId}`
37
+ const metadata = (card as KnowledgeCard).metadata
38
+ const labels = metadata?.wiki_coordinate_labels as { action?: string; scene?: string } | undefined
39
+ const details = metadata?.wiki_kind ? [metadata.wiki_kind === 'way' ? 'Wiki Way' : 'Wiki Fact', labels?.action, labels?.scene].filter((value): value is string => Boolean(value) && !card.title.includes(value!)) : []
40
+ const display = `${card.title}${details.length ? ` · ${details.join(' · ')}` : ''}`
41
+ const label = display.replace(/[\[\]|\r\n]/g, ' ').trim() || card.entityId
42
+ return { entityId: card.entityId, entityType: card.entityType, title: card.title, target, link: `[[${target}|${label}]]` }
43
+ }
44
+
45
+ /** Stable IDs take precedence. A title or alias must resolve uniquely; never pick an arbitrary match. */
46
+ export function wikiLinkCandidates(cards: KnowledgeCard[], raw: string): KnowledgeCard[] {
47
+ const target = raw.trim().replace(/^\[\[([^\]|]+)(?:\|[^\]]*)?\]\]$/, '$1').trim()
48
+ const exact = cards.filter(card => target === card.entityId || target === `${card.entityType}/${card.entityId}` || target === `${card.entityType}:${card.entityId}`)
49
+ if (exact.length) return exact
50
+ return cards.filter(card => {
51
+ const names = [card.title, ...(Array.isArray(card.metadata.aliases) ? card.metadata.aliases.filter((a): a is string => typeof a === 'string') : typeof card.metadata.aliases === 'string' ? [card.metadata.aliases] : [])]
52
+ return names.some(name => normalizeCandidateName(target) === normalizeCandidateName(name)
53
+ || normalizeCandidateName(target) === normalizeCandidateName(`${card.entityType}/${name}`))
54
+ })
55
+ }
56
+
57
+ /** Index labels, not storage IDs or navigation text. Code examples remain unchanged. */
58
+ export function wikiIndexContent(content: string): string {
59
+ let text = stripWikiNavigation(content)
60
+ for (const link of scanWikiLinks(text).reverse()) text = text.slice(0, link.start) + link.label + text.slice(link.end)
61
+ return text
62
+ }
63
+
64
+ /** Link only registered Objects and real Wiki files. Sharing a Source alone is not a semantic link. */
65
+ export function compileWikiNavigation(card: KnowledgeCard, cards: KnowledgeCard[], objects: WikiObject[]): {
66
+ content: string; outgoing: WikiReference[]; inlineTargets: string[]; objectIds: string[]
67
+ } {
68
+ let body = stripWikiNavigation(card.content)
69
+ const previous = new Set(Array.isArray(card.metadata.wiki_inline_targets) ? card.metadata.wiki_inline_targets : [])
70
+ for (const link of scanWikiLinks(body).reverse()) if (previous.has(link.target)) body = body.slice(0, link.start) + link.label + body.slice(link.end)
71
+ const mask = proseMask(body)
72
+ const covered = scanWikiLinks(body)
73
+ const byName = new Map<string, Set<string>>()
74
+ for (const object of objects) for (const name of [object.name, ...object.aliases]) {
75
+ if (name.length < 2) continue
76
+ const key = name.toLocaleLowerCase()
77
+ const ids = byName.get(key) ?? new Set<string>(); ids.add(object.id); byName.set(key, ids)
78
+ }
79
+ const mentioned = new Set(card.coordinate?.objectId ? [card.coordinate.objectId] : [])
80
+ const edits: Array<{ start: number; end: number; value: string; target: string }> = []
81
+ const live = cards.filter(candidate => candidate.status !== 'deprecated' && candidate.entityId !== card.entityId)
82
+ for (const [name, ids] of [...byName].sort((a, b) => b[0].length - a[0].length)) {
83
+ if (ids.size !== 1) continue
84
+ const objectId = [...ids][0]!
85
+ const literal = new RegExp(name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'giu')
86
+ for (const match of mask.matchAll(literal)) {
87
+ const at = match.index!
88
+ const end = at + match[0].length
89
+ if (covered.some(link => at < link.end && end > link.start) || edits.some(edit => at < edit.end && end > edit.start)) continue
90
+ if (/^[a-z0-9_]+$/i.test(name) && (/[a-z0-9_]/i.test(mask[at - 1] ?? '') || /[a-z0-9_]/i.test(mask[end] ?? ''))) continue
91
+ mentioned.add(objectId)
92
+ const candidates = live.filter(candidate => candidate.coordinate?.objectId === objectId)
93
+ const preferred = candidates.filter(candidate => candidate.metadata.wiki_kind === 'fact'
94
+ && candidate.coordinate?.actionId === card.coordinate?.actionId && candidate.coordinate?.sceneId === card.coordinate?.sceneId)
95
+ const general = candidates.filter(candidate => candidate.metadata.wiki_kind === 'fact' && !candidate.coordinate?.actionId && !candidate.coordinate?.sceneId)
96
+ const choices = preferred.length ? preferred : general.length ? general : candidates.length === 1 ? candidates : []
97
+ if (choices.length !== 1) continue
98
+ const target = wikiReference(choices[0]!).target
99
+ edits.push({ start: at, end, value: `[[${target}|${body.slice(at, end)}]]`, target })
100
+ }
101
+ }
102
+ // Registry scope is an established conceptual parent, not a guessed relationship.
103
+ const byId = new Map(objects.map(object => [object.id, object]))
104
+ for (const id of mentioned) {
105
+ const parent = byId.get(id)?.scopeId
106
+ if (parent && byId.has(parent)) mentioned.add(parent)
107
+ }
108
+ for (const edit of edits.sort((a, b) => b.start - a.start)) body = body.slice(0, edit.start) + edit.value + body.slice(edit.end)
109
+ const outgoing = new Map<string, WikiReference>()
110
+ for (const candidate of live) if (candidate.coordinate && mentioned.has(candidate.coordinate.objectId)) {
111
+ const reference = wikiReference(candidate); outgoing.set(reference.target, reference)
112
+ }
113
+ for (const link of scanWikiLinks(body)) {
114
+ const matches = wikiLinkCandidates(live, link.target)
115
+ if (matches.length === 1) { const reference = wikiReference(matches[0]!); outgoing.set(reference.target, reference) }
116
+ }
117
+ const references = [...outgoing.values()].sort((a, b) => a.target.localeCompare(b.target))
118
+ const navigation = references.length ? `\n\n${WIKI_NAV_START}\n## Wiki 导航\n\n${references.map(reference => `- ${reference.link}`).join('\n')}\n${WIKI_NAV_END}` : ''
119
+ return { content: body + navigation, outgoing: references, inlineTargets: [...new Set(edits.map(edit => edit.target))].sort(), objectIds: [...mentioned].sort() }
120
+ }
@@ -1,122 +0,0 @@
1
- import { type IntentRecord, type IntentRecordInput, type IntentResult, type IntentSettings, type IntentSource, type IntentTaskSummary, type IntentTurnState } from './types.js';
2
- import type { LineageContext } from '#contracts';
3
- /**
4
- * Bounded audit store: one record per session turn, expiring after the configured TTL.
5
- *
6
- * 0033 起它同时是**工作集**与**留痕的入口**,两者的分工要说清:
7
- *
8
- * - 内存里这份是有 TTL 的**工作集**(默认 600 秒):血缘判定只关心最近的轮次,
9
- * 一个十轮之前就结束的会话不该让新的一轮接到它的 task 上。
10
- * - 磁盘上那份(`intent/intent.db`)是**留痕**,当前版本便于读取,历史版本只追加、不随 TTL 消失。
11
- * `sink` 在每次 `save` 时被调用,`hydrate` 在启动时把留痕读回工作集 ——
12
- * 重启之后同一个会话的血缘因此还在(在 TTL 窗口内)。
13
- *
14
- * 为什么不干脆让 `records()` 直接读文件:那会让 TTL 名存实亡(文件永远有),
15
- * 于是「工作集」这个概念消失,血缘会接到很久以前的 task 上。两件事分开,
16
- * 各自的契约才都成立。
17
- */
18
- export declare class IntentStore {
19
- private ttlSeconds;
20
- private readonly now;
21
- private readonly sessions;
22
- /**
23
- * 每条被保存的记录都会经过它(落盘留痕)。
24
- *
25
- * 同步签名、由调用方自己吞异常:`save` 在记录路径的关键路径上,而留痕失败
26
- * 不该让这一轮的意图记录失败 —— 「记录不拦截请求」这条契约对留痕也成立。
27
- */
28
- private sink?;
29
- constructor(ttlSeconds?: number, now?: () => number);
30
- /** 装上一个留痕出口。多次调用以最后一次为准。 */
31
- setSink(sink: (record: IntentRecord) => void): void;
32
- /**
33
- * 把留痕读回工作集(启动时调用一次)。
34
- *
35
- * `touched` 用记录自己的 `updatedAt`,于是「重启」不会把 TTL 归零:一份十小时
36
- * 前的记录在 TTL 600 秒下本来就该过期,重启不该让它复活。
37
- */
38
- hydrate(records: IntentRecord[]): number;
39
- setTtl(seconds: number): void;
40
- private entry;
41
- /** Upsert by turn key so repeated records within one turn stay a single fact. */
42
- save(record: IntentRecord): IntentRecord;
43
- records(sessionKey: string): IntentRecord[];
44
- last(sessionKey: string): IntentRecord | undefined;
45
- findByTurn(sessionKey: string, turnKey: string): IntentRecord | undefined;
46
- taskExists(sessionKey: string, taskId: string): boolean;
47
- lastSubtaskOf(sessionKey: string, taskId: string): string | undefined;
48
- expiresAt(sessionKey: string): string | undefined;
49
- clear(sessionKey: string): void;
50
- /** When the session was last touched (ms since epoch); 0 when unknown. */
51
- touchedAt(sessionKey: string): number;
52
- /** Live session keys, most recently touched first. Expired sessions are skipped. */
53
- sessionKeys(): string[];
54
- }
55
- /**
56
- * Intent recorder. Every entry point is total: an unrecognized act, an unknown
57
- * task reference, or a missing classification degrade into warnings on a stored
58
- * record. Nothing here blocks, confirms, or rewrites the user's request.
59
- */
60
- export declare class IntentEngine {
61
- readonly store: IntentStore;
62
- private settings;
63
- private readonly turns;
64
- constructor(settings?: Partial<IntentSettings>, store?: IntentStore);
65
- setSettings(settings: Partial<IntentSettings>): void;
66
- getSettings(): IntentSettings;
67
- /**
68
- * Open a turn and allocate its provisional ids before the model records.
69
- * Downstream calls dispatched in parallel with the record can already carry
70
- * an id; the record later commits the authoritative task binding.
71
- */
72
- beginTurn(sessionKey: string, turn: number, userInput: string, eligible?: boolean): IntentTurnState;
73
- /**
74
- * 用户消息可能在 `turn/start` 之后才到达。
75
- *
76
- * 宿主事件顺序不是固定的:有些会话先发 `turn/start`,再发 `user/message`。如果只在
77
- * `beginTurn` 读取一次原文,就会把整轮意图永久记录成空字符串。收到真实用户消息后,
78
- * 只更新尚未提交的当前轮次;已经记录的判断不再被后来事件改写。
79
- */
80
- updateTurnInput(sessionKey: string, userInput: string): boolean;
81
- /** Mark the current turn as a data turn without classifying it. */
82
- markDataIntent(sessionKey: string, turn?: number): boolean;
83
- /** Mark the current turn as a data turn based on actual data-tool use. */
84
- markDataToolUse(sessionKey: string, turn?: number): boolean;
85
- /** Clear a text-only gate when the model explicitly identifies a non-data turn. */
86
- clearDataIntent(sessionKey: string, turn?: number): boolean;
87
- /** Whether the current turn is eligible for data intent recording. */
88
- isDataIntentTurn(sessionKey: string, turn?: number): boolean;
89
- /** Backfill one `unknown` record only when an eligible data turn has no model record. */
90
- fallbackWhenMissing(sessionKey: string, turn: number): IntentResult | undefined;
91
- /** Record the model's intent judgement. The host applies scope before calling this method. */
92
- record(input: IntentRecordInput, source?: IntentSource): IntentResult;
93
- /** Ids a downstream tool call in this session should carry. */
94
- activeLineage(sessionKey: string): {
95
- lineage: Partial<LineageContext>;
96
- committed: boolean;
97
- } | undefined;
98
- tasks(sessionKey: string): IntentTaskSummary[];
99
- view(sessionKey?: string, includePrompt?: boolean): IntentResult;
100
- /**
101
- * Session-level index for the 意图 panel: every live session with its last
102
- * classification and active task ids. Records themselves stay per-session.
103
- */
104
- overview(): {
105
- sessions: Array<Record<string, unknown>>;
106
- };
107
- /**
108
- * Model-facing contract: record first, then continue; the record never gates the request.
109
- *
110
- * 这段文本就是 `intent_record` 的**工具描述**(0023 起不再单独注入 system prompt 段),
111
- * 分类规则固定随工具在场,不再通过设置或注册表变更。
112
- *
113
- * 分类判定是**两段式**的(issue 0023):
114
- * 1. 先拿用户意思去比对预置分类的 `description` / `examples`,命中就**直接用该 id**;
115
- * 2. 全不命中才允许自行判断(新起一个 id 或落 `unknown`)。
116
- *
117
- * 这样预置项就是"先验",而不是一份仅供参考的清单——模型不必每轮从零分类。
118
- */
119
- routingPrompt(): string;
120
- }
121
- export declare function createIntentEngine(settings?: Partial<IntentSettings>): IntentEngine;
122
- //# sourceMappingURL=engine.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/intent/engine.ts"],"names":[],"mappings":"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"}