@thinkingai/ae-cli 6.1.25 → 6.1.27

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 (121) hide show
  1. package/README.md +28 -9
  2. package/README.zh.md +25 -9
  3. package/bin/ae-cli.js +6 -1
  4. package/cli-installation-guide.md +91 -0
  5. package/dist/{auth-LHM7NQPR.js → auth-WA7YGGRT.js} +6 -8
  6. package/dist/automation-VAAQBM7Y.js +459 -0
  7. package/dist/{capability-SJI5KOCK.js → capability-GAAYJ3IQ.js} +6 -6
  8. package/dist/{chunk-KJDOTPYU.js → chunk-3YVEAZUN.js} +59 -26
  9. package/dist/{chunk-KS4DE3MX.js → chunk-5XJYLEVC.js} +4 -4
  10. package/dist/{chunk-BW7JUDEI.js → chunk-6VNIR4AP.js} +514 -213
  11. package/dist/{chunk-5XRQ3CZR.js → chunk-7KU2WNQE.js} +43 -8
  12. package/dist/{chunk-7WZACOAI.js → chunk-AP7YP2NX.js} +2 -2
  13. package/dist/{chunk-XKIW3QMF.js → chunk-AQJPNWZF.js} +3 -3
  14. package/dist/chunk-CI24BDR2.js +43 -0
  15. package/dist/{chunk-SO5LKIWP.js → chunk-CT7QDJS6.js} +2 -2
  16. package/dist/{chunk-DU72X2IO.js → chunk-GHXZK54S.js} +22 -10
  17. package/dist/{chunk-2FJM4HRV.js → chunk-MWIA43UJ.js} +3 -3
  18. package/dist/{chunk-N4BLPJC7.js → chunk-OP44TT4H.js} +1 -1
  19. package/dist/chunk-Q5US7JC5.js +133 -0
  20. package/dist/chunk-RK6GLDGI.js +27 -0
  21. package/dist/chunk-RPBOAQQQ.js +56 -0
  22. package/dist/{chunk-GK7WDY7E.js → chunk-S6M42KND.js} +1 -1
  23. package/dist/{chunk-QYXDJOLA.js → chunk-VZZNLV3I.js} +19 -11
  24. package/dist/{chunk-YVGXGTD6.js → chunk-WNGFSIOB.js} +7 -1
  25. package/dist/{chunk-IG3DYEIR.js → chunk-ZL7SGNIA.js} +5 -3
  26. package/dist/{community-report-client-6VJDDMJJ.js → community-report-client-PEERQCX2.js} +1 -1
  27. package/dist/{config-XQWOPDHN.js → config-JOD5DOPE.js} +4 -4
  28. package/dist/context-NGVFPCBK.js +136 -0
  29. package/dist/{data-integration-XBTCGXQT.js → data-integration-BCLQFTGU.js} +6 -6
  30. package/dist/index.js +123 -46
  31. package/dist/{local-data-upload-client-3P7NIVQ4.js → local-data-upload-client-TWMHFLUA.js} +1 -1
  32. package/dist/memory-CQKH2JAV.js +580 -0
  33. package/dist/{metadata-JRYKNUS5.js → metadata-NR3PHR23.js} +7 -7
  34. package/dist/{model-ZS7HVGKN.js → model-VX5M2CJ6.js} +9 -5
  35. package/dist/{personal-semantic-preference-IDE2GE5T.js → personal-semantic-preference-2RAI36P3.js} +117 -11
  36. package/dist/{project-semantic-36KOCYN7.js → project-semantic-3BUGP3ZM.js} +6 -6
  37. package/dist/{sync-EQLSTUS7.js → sync-RZIAV37C.js} +12 -8
  38. package/dist/{te-agent-Z3HCTKR7.js → te-agent-AORRE2FM.js} +90 -454
  39. package/dist/{te-analysis-5ZZ6YHVH.js → te-analysis-GP7DSQ7Q.js} +297 -20
  40. package/dist/{te-community-Z7JACGF3.js → te-community-42M7ICDO.js} +3 -3
  41. package/dist/{te-dataops-F7GQOIS6.js → te-dataops-BNKNZ4F2.js} +5 -5
  42. package/dist/{te-engage-RNRMYDO3.js → te-engage-2CO6LRCX.js} +6 -6
  43. package/dist/{te-experiment-BTAC2UNK.js → te-experiment-652CLXMU.js} +6 -6
  44. package/dist/{te-kb-QJLJEX5K.js → te-kb-ULATOTQ3.js} +494 -214
  45. package/dist/{te-system-IXC43QHZ.js → te-system-UGHVKBVV.js} +10 -9
  46. package/dist/{te-team-BIZIKIHZ.js → te-team-YA6SNOJ3.js} +6 -6
  47. package/dist/things-DTCOQIQB.js +1091 -0
  48. package/dist/{update-W2SGQNKT.js → update-ESNYKUVR.js} +59 -51
  49. package/dist/workspace-JEOHKFPX.js +554 -0
  50. package/package.json +21 -9
  51. package/skills/ae-agent/SKILL.md +91 -139
  52. package/skills/ae-agent/references/add-model.md +1 -1
  53. package/skills/ae-agent/references/create-agent.md +1 -1
  54. package/skills/ae-agent/references/find-archived-conversations.md +12 -5
  55. package/skills/ae-agent/references/get-agent.md +1 -1
  56. package/skills/ae-agent/references/list-agents.md +2 -2
  57. package/skills/ae-agent/references/list-models.md +2 -2
  58. package/skills/ae-agent/references/restore-conversation.md +3 -1
  59. package/skills/ae-analysis/SKILL.md +15 -6
  60. package/skills/ae-analysis/references/ai_models/funnel.md +5 -3
  61. package/skills/ae-analysis/references/ai_models.md +27 -0
  62. package/skills/ae-analysis/references/command_index.md +12 -4
  63. package/skills/ae-analysis/references/dashboard_move.md +21 -0
  64. package/skills/ae-analysis/references/dashboard_report_add.md +2 -0
  65. package/skills/ae-analysis/references/dashboard_report_get.md +19 -0
  66. package/skills/ae-analysis/references/dashboard_report_update.md +27 -0
  67. package/skills/ae-analysis/references/dashboard_update.md +1 -1
  68. package/skills/ae-analysis/references/event_report_presentation.md +76 -0
  69. package/skills/ae-analysis/references/feedback_submit.md +37 -0
  70. package/skills/ae-analysis/references/metadata_resolution.md +15 -0
  71. package/skills/ae-analysis/references/personal_semantic_preference_review.md +9 -0
  72. package/skills/ae-analysis/references/personal_semantic_preference_review_list.md +7 -0
  73. package/skills/ae-analysis/references/personal_semantic_preference_submit.md +7 -0
  74. package/skills/ae-analysis/references/project_semantic_knowledge_wiki.md +68 -27
  75. package/skills/ae-analysis/references/project_semantic_knowledge_wiki_plan_schema.md +40 -1
  76. package/skills/ae-analysis/references/report_create.md +13 -3
  77. package/skills/ae-analysis/references/report_get.md +5 -1
  78. package/skills/ae-analysis/references/report_update.md +15 -4
  79. package/skills/ae-analysis/references/system_query_monitor_overview.md +2 -0
  80. package/skills/ae-analysis/references/system_query_task_cancel.md +2 -0
  81. package/skills/ae-analysis/references/system_query_task_export.md +6 -2
  82. package/skills/ae-analysis/references/system_query_task_get.md +2 -0
  83. package/skills/ae-analysis/references/system_query_task_list.md +7 -3
  84. package/skills/ae-analysis/references/system_query_task_options.md +2 -2
  85. package/skills/ae-analysis/references/system_query_task_realtime_list.md +15 -0
  86. package/skills/ae-analysis/references/user_tag_models.md +2 -0
  87. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/build-empty-skeleton.mjs +105 -0
  88. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/build-project-semantic-wiki.mjs +337 -22
  89. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/company-kb.mjs +21 -8
  90. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/default-compile-rules.md +24 -11
  91. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/default-compile-schema.md +66 -0
  92. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/generate-build-ir.mjs +1 -0
  93. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/package-wiki-source-zip.mjs +109 -40
  94. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/plan-kb-source-sync.mjs +1 -1
  95. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/prepare-compile-schema.mjs +64 -0
  96. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/refresh-state-parts.mjs +91 -0
  97. package/skills/ae-automation/SKILL.md +16 -0
  98. package/skills/ae-automation/references/cli.md +53 -0
  99. package/skills/ae-automation/references/command_index.md +17 -0
  100. package/skills/ae-capability/SKILL.md +15 -6
  101. package/skills/ae-current-context/SKILL.md +3 -2
  102. package/skills/ae-kb/SKILL.md +62 -12
  103. package/skills/ae-kb/references/analysis-workflow.md +73 -36
  104. package/skills/ae-kb/references/query-workflow.md +67 -33
  105. package/skills/ae-kb/references/schema-import.md +5 -3
  106. package/skills/ae-kb/references/source-mutations.md +6 -4
  107. package/skills/ae-kb/references/text-sources.md +53 -0
  108. package/skills/ae-kb-discovery/SKILL.md +1 -1
  109. package/skills/ae-team/SKILL.md +23 -17
  110. package/skills/ae-things/SKILL.md +85 -0
  111. package/skills/ae-things/references/cli.md +103 -0
  112. package/skills/ae-things-progress/SKILL.md +43 -0
  113. package/skills/ae-things-review/SKILL.md +42 -0
  114. package/skills/ae-workspace/SKILL.md +43 -0
  115. package/skills/ae-workspace/references/commands.md +51 -0
  116. package/dist/chunk-E2JKXMVO.js +0 -29
  117. package/dist/context-M3HDGEIR.js +0 -46
  118. package/dist/memory-6APEB2JB.js +0 -892
  119. package/skills/ae-agent/references/create-automation.md +0 -125
  120. package/skills/ae-agent/references/list-automations.md +0 -56
  121. package/skills/ae-agent/references/update-automation.md +0 -107
@@ -71,9 +71,12 @@ const dashboards = readIndex('dashboard-catalog');
71
71
  const reports = readIndex('report-catalog');
72
72
  const metrics = readIndex('metric-catalog');
73
73
  const containers = readIndex('asset-containers');
74
+ const adoptedPersonalSemantics = readPackageJsonl('sources/adopted-personal-semantics.jsonl');
75
+ const eventGroups = readOptionalIndex('event-groups');
74
76
  const dashboardById = indexBy(dashboards, 'resource_key');
75
77
  const reportById = indexBy(reports, 'resource_key');
76
78
  const metricById = indexBy(metrics, 'resource_key');
79
+ const adoptedPersonalBySourceId = indexBy(adoptedPersonalSemantics, 'source_id');
77
80
  const dashboardFileNames = titleFirstAssetFileMap(dashboards, 'dashboard');
78
81
  const reportFileNames = titleFirstAssetFileMap(reports, 'report');
79
82
  const metricFileNames = titleFirstAssetFileMap(metrics, 'metric');
@@ -81,6 +84,7 @@ const titleFirstAssetPathMap = titleFirstAssetPathRewrites();
81
84
  const containerById = indexBy(containers, 'container_key');
82
85
  const domains = asArray(semanticPlan.domains);
83
86
  const appendices = asArray(semanticPlan.appendices);
87
+ const personalSemanticIntegrations = asArray(semanticPlan.personal_semantic_integrations);
84
88
  const domainByDashboardId = new Map();
85
89
  const appendixByDashboardId = new Map();
86
90
  for (const domain of domains) for (const id of asArray(domain.dashboard_ids).map(clean)) domainByDashboardId.set(id, domain);
@@ -110,8 +114,11 @@ try {
110
114
  const wikiRoot = path.join(staging, 'wiki');
111
115
  for (const directory of [
112
116
  'domains', 'technical-appendices', 'source-containers', 'dashboards', 'reports', 'metrics',
113
- 'metadata', 'recall-cards', 'governance',
117
+ 'metadata', 'recall-cards', 'personal-semantics', 'governance', 'common', 'event-map', 'asset-index',
114
118
  ]) fs.mkdirSync(path.join(wikiRoot, directory), { recursive: true });
119
+ for (const type of ['background', 'preference', 'experience', 'asset-context']) {
120
+ fs.mkdirSync(path.join(wikiRoot, 'personal-semantics', type), { recursive: true });
121
+ }
115
122
 
116
123
  for (const metadataType of fs.readdirSync(path.join(buildIr, 'metadata'), { withFileTypes: true })
117
124
  .filter((entry) => entry.isDirectory()).map((entry) => entry.name)) {
@@ -125,7 +132,8 @@ try {
125
132
  const cardLinks = cards.map((card) => `- [${escapeLink(card.intent || card.card_id)}](../recall-cards/${fileName('recall-card', card.card_id)})`).join('\n');
126
133
  writeText(path.join(wikiRoot, 'domains', fileName('domain', id)),
127
134
  replaceFrontmatter(source, domainFrontmatter(domain))
128
- + `\n## 场景召回卡\n\n${cardLinks || '- 无'}\n`);
135
+ + `\n## 场景召回卡\n\n${cardLinks || '- 无'}\n`
136
+ + renderTargetedKnowledge('domain', id, '../personal-semantics/'));
129
137
  }
130
138
 
131
139
  for (const appendix of appendices) {
@@ -185,7 +193,7 @@ try {
185
193
  }
186
194
 
187
195
  copyIndex(path.join(buildIr, 'dashboards', 'index.md'), path.join(wikiRoot, 'dashboards', 'index.md'));
188
- copyIndex(path.join(buildIr, 'reports', 'index.md'), path.join(wikiRoot, 'reports', 'index.md'));
196
+ writeText(path.join(wikiRoot, 'reports', 'index.md'), renderReportIndex());
189
197
  copyIndex(path.join(buildIr, 'metrics', 'index.md'), path.join(wikiRoot, 'metrics', 'index.md'));
190
198
  copyIndex(path.join(buildIr, 'metadata', 'index.md'), path.join(wikiRoot, 'metadata', 'index.md'));
191
199
 
@@ -194,12 +202,29 @@ try {
194
202
  }
195
203
  writeText(path.join(wikiRoot, 'recall-cards', 'index.md'), renderRecallCardIndex(planModel.cards));
196
204
 
205
+ writeText(path.join(wikiRoot, 'common', 'index.md'), renderCommonIndex());
206
+ writeText(path.join(wikiRoot, 'asset-index', 'index.md'), renderAssetIndex());
207
+ writeEventMap(wikiRoot, buildIr);
208
+
209
+ for (const integration of planModel.personalSemanticIntegrations) {
210
+ const contextPath = personalSemanticContextPath(integration.context_type);
211
+ writeText(path.join(wikiRoot, 'personal-semantics', contextPath,
212
+ fileName('personal-semantic', integration.integration_id)), renderPersonalSemanticIntegration(integration));
213
+ }
214
+ writeText(path.join(wikiRoot, 'personal-semantics', 'index.md'),
215
+ renderPersonalSemanticIndex(planModel.personalSemanticIntegrations));
216
+ for (const type of ['background', 'preference', 'experience', 'asset_context']) {
217
+ writeText(path.join(wikiRoot, 'personal-semantics', personalSemanticContextPath(type), 'index.md'),
218
+ renderPersonalSemanticTypeIndex(type, planModel.personalSemanticIntegrations));
219
+ }
220
+
197
221
  const coverage = readText(path.join(buildIr, 'indexes', 'coverage.md'))
198
222
  .replaceAll('../appendices/', '../technical-appendices/')
199
223
  .replaceAll('../briefs/reports/', '../reports/')
200
224
  .replaceAll('../briefs/metadata/', '../metadata/');
201
225
  writeText(path.join(wikiRoot, 'governance', 'coverage.md'), coverage);
202
226
  writeText(path.join(wikiRoot, 'governance', 'authority-index.md'), renderAuthorityIndex(authorityRows));
227
+ writeText(path.join(wikiRoot, 'governance', 'knowledge-adoption.md'), renderKnowledgeAdoptionIndex());
203
228
  writeText(path.join(wikiRoot, 'governance', 'index.md'), renderGovernanceIndex(conflictReportIds.size));
204
229
  writeText(path.join(wikiRoot, 'governance', 'source-project-boundary.md'), renderSourceProjectBoundary(sourceProjectReferencesFromPackage()));
205
230
  writeText(path.join(wikiRoot, 'project-overview.md'), renderProjectOverview(corpus));
@@ -226,9 +251,13 @@ try {
226
251
  semantic_plan_fragment_count: semanticPlanState.fragments.length,
227
252
  asset_package_schema_version: '3.0',
228
253
  asset_scope: packageAssetScope,
229
- wiki_schema_version: '1.0',
254
+ wiki_schema_version: '2.0',
255
+ navigation_taxonomy_version: '2.1',
230
256
  generated_at: descriptor.generated_at ?? packageManifest.generated_at ?? null,
231
- counts: { ...corpus.counts, recall_cards: planModel.cards.length, blocked_assets: blockedCount, unknown_sql_reports: unknownSqlCount },
257
+ counts: { ...corpus.counts, recall_cards: planModel.cards.length,
258
+ adopted_personal_semantics: adoptedPersonalSemantics.length,
259
+ personal_semantic_integrations: planModel.personalSemanticIntegrations.length,
260
+ blocked_assets: blockedCount, unknown_sql_reports: unknownSqlCount },
232
261
  quality_warnings: [...asArray(corpus.warnings),
233
262
  ...(sourceProjectRefs.filter((row) => Number(row.source_project_id) !== semanticProjectId).length
234
263
  ? [`${sourceProjectRefs.filter((row) => Number(row.source_project_id) !== semanticProjectId).length} packaged asset reference(s) mention another project: ${sourceProjectRefs.filter((row) => Number(row.source_project_id) !== semanticProjectId).slice(0, 5).map((row) => `${row.resource_type}:${row.resource_key}->${row.source_project_id}`).join(', ')}`] : []),
@@ -304,6 +333,25 @@ function validateSemanticPlan() {
304
333
  if (!inDomain) fail(`Recall card ${cardId} references ${type}:${key} outside domain ${domainId}.`);
305
334
  if (preferred.includes(ref)) addMulti(preferredByAsset, `${type}:${key}`, cardId);
306
335
  }
336
+ const routing = asObject(card.routing);
337
+ if (card.routing != null) {
338
+ for (const field of ['applicable_when', 'answer_grain', 'stop_when', 'fallback_when_unavailable', 'evidence_boundary']) {
339
+ if (!clean(routing[field])) fail(`Recall card ${cardId} routing requires ${field}.`);
340
+ }
341
+ if (!asArray(routing.branches).length) fail(`Recall card ${cardId} routing requires branches.`);
342
+ for (const branch of asArray(routing.branches)) {
343
+ if (!clean(branch.when) || !asArray(branch.asset_refs).length) {
344
+ fail(`Recall card ${cardId} routing branch requires when and asset_refs.`);
345
+ }
346
+ for (const ref of asArray(branch.asset_refs)) {
347
+ validateAssetRef(ref, cardId);
348
+ if (![...preferred, ...asArray(card.fallback_asset_refs)].some((allowed) =>
349
+ clean(allowed.resource_type) === clean(ref.resource_type) && clean(allowed.resource_key) === clean(ref.resource_key))) {
350
+ fail(`Recall card ${cardId} routing branch references an asset outside preferred/fallback refs.`);
351
+ }
352
+ }
353
+ }
354
+ }
307
355
  for (const ref of asArray(card.excluded_asset_refs)) {
308
356
  validateAssetRef(ref, cardId);
309
357
  if (!clean(ref.reason)) fail(`Recall card ${cardId} excluded asset ${clean(ref.resource_key)} is missing a reason.`);
@@ -344,9 +392,11 @@ function validateSemanticPlan() {
344
392
  if (missingSql.length && !allowAllVisible) {
345
393
  fail(`Semantic plan is missing SQL semantics for ${missingSql.length} report(s): ${missingSql.join(', ')}`);
346
394
  }
395
+ const personalIntegrations = validatePersonalSemanticIntegrations(domainIds, cardIds);
347
396
 
348
397
  return {
349
398
  cards,
399
+ personalSemanticIntegrations: personalIntegrations,
350
400
  excludedByAsset,
351
401
  preferredByAsset,
352
402
  sqlByReportId,
@@ -355,6 +405,73 @@ function validateSemanticPlan() {
355
405
  };
356
406
  }
357
407
 
408
+ function validatePersonalSemanticIntegrations(domainIds, cardIds) {
409
+ const integrationIds = new Set();
410
+ const referencedSourceIds = new Set();
411
+ const integrations = [];
412
+ for (const [index, value] of personalSemanticIntegrations.entries()) {
413
+ const row = asObject(value);
414
+ const integrationId = clean(row.integration_id);
415
+ if (!/^[a-z0-9][a-z0-9_-]{1,79}$/.test(integrationId) || integrationIds.has(integrationId)) {
416
+ fail(`Invalid or duplicate personal semantic integration ID: ${integrationId || `<index ${index}>`}.`);
417
+ }
418
+ integrationIds.add(integrationId);
419
+ const contextType = normalizePersonalSemanticContextType(row.context_type);
420
+ const sourceIds = asArray(row.source_ids).map(clean).filter(Boolean);
421
+ if (!sourceIds.length) fail(`Personal semantic integration ${integrationId} requires source_ids.`);
422
+ for (const sourceId of sourceIds) {
423
+ const source = adoptedPersonalBySourceId.get(sourceId);
424
+ if (!source) fail(`Personal semantic integration ${integrationId} references unknown source ${sourceId}.`);
425
+ if (normalizePersonalSemanticContextType(source.context_type) !== contextType) {
426
+ fail(`Personal semantic integration ${integrationId} mixes context_type ${contextType} with source ${sourceId}:${source.context_type}.`);
427
+ }
428
+ referencedSourceIds.add(sourceId);
429
+ }
430
+ const title = clean(row.title);
431
+ const projectUsage = clean(row.project_usage);
432
+ const boundaries = asArray(row.boundaries).map(clean).filter(Boolean);
433
+ const conflictPolicy = clean(row.conflict_policy);
434
+ if (!title || !projectUsage) fail(`Personal semantic integration ${integrationId} requires title and project_usage.`);
435
+ if (!['compatible', 'scoped', 'blocked'].includes(conflictPolicy)) {
436
+ fail(`Personal semantic integration ${integrationId} has invalid conflict_policy.`);
437
+ }
438
+ if (!boundaries.length) fail(`Personal semantic integration ${integrationId} requires boundaries.`);
439
+ const target = validatePersonalSemanticIntegrationTarget(row.integration_target, integrationId, domainIds, cardIds);
440
+ integrations.push({ ...row, integration_id: integrationId, context_type: contextType,
441
+ source_ids: sourceIds, title, project_usage: projectUsage, boundaries, conflict_policy: conflictPolicy,
442
+ integration_target: target });
443
+ }
444
+ const missing = adoptedPersonalSemantics
445
+ .map((source) => clean(source.source_id))
446
+ .filter((sourceId) => sourceId && !referencedSourceIds.has(sourceId));
447
+ if (missing.length) {
448
+ fail(`Semantic plan is missing adopted personal semantic source(s): ${missing.join(', ')}`);
449
+ }
450
+ return integrations;
451
+ }
452
+
453
+ function validatePersonalSemanticIntegrationTarget(value, integrationId, domainIds, cardIds) {
454
+ const target = asObject(value);
455
+ const type = clean(target.target_type);
456
+ const id = clean(target.target_id);
457
+ if (!['project', 'domain', 'asset', 'recall_card'].includes(type)) {
458
+ fail(`Personal semantic integration ${integrationId} has invalid integration_target.target_type.`);
459
+ }
460
+ if (type === 'project') return { target_type: type, target_id: id || 'project' };
461
+ if (!id) fail(`Personal semantic integration ${integrationId} target ${type} requires target_id.`);
462
+ if (type === 'domain' && !domainIds.has(id)) fail(`Personal semantic integration ${integrationId} references unknown domain ${id}.`);
463
+ if (type === 'recall_card' && !cardIds.has(id)) fail(`Personal semantic integration ${integrationId} references unknown recall card ${id}.`);
464
+ if (type === 'asset') {
465
+ const [assetType, assetKey] = id.includes(':') ? id.split(/:(.*)/s).filter(Boolean) : ['', id];
466
+ const exists = assetType === 'dashboard' ? dashboardById.has(assetKey)
467
+ : assetType === 'report' ? reportById.has(assetKey)
468
+ : assetType === 'metric' ? metricById.has(assetKey)
469
+ : dashboardById.has(assetKey) || reportById.has(assetKey) || metricById.has(assetKey);
470
+ if (!exists) fail(`Personal semantic integration ${integrationId} references unknown asset target ${id}.`);
471
+ }
472
+ return { target_type: type, target_id: id };
473
+ }
474
+
358
475
  function validateProjectBusinessModel(value, domainIds) {
359
476
  const model = asObject(value);
360
477
  const businessPositioning = clean(model.business_positioning);
@@ -579,7 +696,7 @@ function writeMergedAsset(wikiRoot, directory, asset, authority, extra = '') {
579
696
  const type = directory === 'dashboards' ? 'project-dashboard' : directory === 'reports' ? 'project-report' : 'project-reusable-metric';
580
697
  const title = assetReadableTitle(asset, directory.slice(0, -1));
581
698
  const authorityNotice = renderAssetAuthorityNotice(authority);
582
- const mergedExtra = [authorityNotice, extra.trim()].filter(Boolean).join('\n\n');
699
+ const mergedExtra = [authorityNotice, extra.trim(), renderTargetedKnowledge('asset', `${directory.slice(0, -1)}:${id}`, '../personal-semantics/')].filter(Boolean).join('\n\n');
583
700
  writeText(path.join(wikiRoot, directory, assetOutputFileName(directory, id)),
584
701
  ensureReadableHeading(mergePage(source, brief, authorityFrontmatter(type, id, title, authority), mergedExtra), title));
585
702
  }
@@ -635,6 +752,12 @@ ${asArray(values).map((ref) => {
635
752
  const linkPath = assetPath(clean(ref.resource_type), clean(ref.resource_key));
636
753
  return `- [${escapeLink(recallAssetLabel(ref))}](${linkPath}):${clean(ref.reason)}`;
637
754
  }).join('\n') || '- 无'}`;
755
+ const routing = asObject(card.routing);
756
+ const branches = asArray(routing.branches).map((branch) => {
757
+ const item = asObject(branch);
758
+ const targets = asArray(item.asset_refs).map(recallAssetLabel).join('、') || '回到业务域核对资产';
759
+ return `- 当${clean(item.when)}:使用 ${targets}。${clean(item.reason)}`;
760
+ });
638
761
  return `---
639
762
  type: project-semantic-recall-card
640
763
  card_id: ${yaml(card.card_id)}
@@ -662,12 +785,15 @@ ${alternateQuestions.map((value) => `- ${value}`).join('\n') || '- 同页面标
662
785
 
663
786
  ${clean(card.intent)}
664
787
 
665
- ## Agent 执行顺序
788
+ ## 条件路由与停止
666
789
 
667
- 1. 先进入首选资产确认业务范围、指标和维度。
668
- 2. 首选资产不足以回答时,再使用备选资产补充证据。
669
- 3. 排除资产只用于解释为什么不采用,不能作为当前问题的主要答案来源。
670
- 4. 需要当前数值、客户名单、趋势或明细时,必须实时执行报表/查询。
790
+ - 适用条件:${clean(routing.applicable_when) || clean(card.intent)}
791
+ - 答案对象与粒度:${clean(routing.answer_grain) || '先按用户问法确认对象、统计粒度与时间范围。'}
792
+ - 必要参数:${asArray(routing.required_parameters).map(clean).filter(Boolean).join('、') || '按首选资产的输入定义核对。'}
793
+ ${branches.join('\n') || `- 问法和资产定义匹配时使用首选资产;不匹配时先回到业务域确认对象与口径,再考虑备选资产。`}
794
+ - 停止条件:${clean(routing.stop_when) || '取得对象、粒度、范围、时间和权威状态均匹配的最小证据后停止扩搜。'}
795
+ - 无匹配或不可用时:${clean(routing.fallback_when_unavailable) || '回到业务域按对象、粒度、过滤和时间筛选域内资产;仍不匹配则说明缺口。'}
796
+ - 执行边界:${clean(routing.evidence_boundary) || '排除资产不能作为当前答案;当前数值、客户名单、趋势或明细必须实时执行。'}
671
797
 
672
798
  ${refs(card.preferred_asset_refs, '首选资产')}
673
799
 
@@ -679,6 +805,7 @@ ${refs(card.excluded_asset_refs, '排除资产')}
679
805
 
680
806
  - 是否需要执行实时数据:${Boolean(card.requires_live_execution)}
681
807
  - 原因:${clean(card.live_execution_reason) || '未说明'}
808
+ ${renderTargetedKnowledge('recall_card', clean(card.card_id), '../personal-semantics/')}
682
809
  `;
683
810
  }
684
811
 
@@ -695,11 +822,140 @@ function recallAssetLabel(ref) {
695
822
  }
696
823
 
697
824
  function renderRecallCardIndex(cards) {
698
- return `# 场景召回卡\n\n首轮召回先使用这里的业务问题与权威资产路由,不直接宽搜全部资产页。\n\n${cards.map((card) => `- [${escapeLink(card.intent || card.card_id)}](${fileName('recall-card', card.card_id)})(${clean(card.domain_title)})`).join('\n')}\n`;
825
+ return `# 场景召回卡\n\n先按问法、对象、粒度和参数选择匹配的卡;卡内按条件分支找到最小证据并在满足停止条件后停止。无匹配卡时,回到[业务领域](../domains/index.md),按对象、粒度、过滤和时间筛选域内资产;仍无可用资产时说明具体缺口。\n\n${cards.map((card) => `- [${escapeLink(card.intent || card.card_id)}](${fileName('recall-card', card.card_id)})(${clean(card.domain_title)})`).join('\n')}\n`;
826
+ }
827
+
828
+ function renderReportIndex() {
829
+ const assigned = new Set();
830
+ const sections = domains.map((domain) => {
831
+ const dashboardIds = new Set(asArray(domain.dashboard_ids).map(clean));
832
+ const matching = reports.filter((report) => asArray(report.dashboard_refs)
833
+ .some((ref) => dashboardIds.has(clean(ref.dashboard_id))))
834
+ .sort((a, b) => clean(a.resource_key).localeCompare(clean(b.resource_key), 'en'));
835
+ for (const report of matching) assigned.add(clean(report.resource_key));
836
+ const questions = asArray(domain.primary_questions).map(clean).filter(Boolean);
837
+ const cards = asArray(domain.recall_cards)
838
+ .map((card) => `- 问题入口:[${escapeLink(clean(card.questions?.[0]) || clean(card.intent))}](../recall-cards/${fileName('recall-card', card.card_id)})`);
839
+ return `## ${clean(domain.title)}\n\n- 业务问题:${questions.join(';') || clean(domain.summary)}\n- [领域判断](../domains/${fileName('domain', domain.domain_id)})\n${cards.join('\n')}\n${matching.map((report) => `- [${escapeLink(assetReadableTitle(report, 'report'))}](${reportFileNames.get(clean(report.resource_key))})(report:${clean(report.resource_key)})`).join('\n') || '- 本快照无该域报表,按领域页核对其他资产。'}\n`;
840
+ });
841
+ const unassigned = reports.filter((report) => !assigned.has(clean(report.resource_key)))
842
+ .sort((a, b) => clean(a.resource_key).localeCompare(clean(b.resource_key), 'en'));
843
+ return `# 报表\n\n先按业务问题选择领域或场景,再核对报表的对象、粒度、过滤和时间。点名报表时可直接进入报表页;未归属报表不自动视为业务入口。\n\n${sections.join('\n')}\n## 其他与未归属报表\n\n${unassigned.map((report) => `- [${escapeLink(assetReadableTitle(report, 'report'))}](${reportFileNames.get(clean(report.resource_key))})(report:${clean(report.resource_key)})`).join('\n') || '- 无'}\n`;
844
+ }
845
+
846
+ function renderPersonalSemanticIntegration(integration) {
847
+ const sourceRows = integration.source_ids.map((sourceId) => adoptedPersonalBySourceId.get(sourceId)).filter(Boolean);
848
+ const provenanceRows = sourceRows.map((source) => {
849
+ const payload = asObject(source.payload);
850
+ return `| \`${clean(source.source_id)}\` | ${escapeTable(personalSemanticOwnerName(payload))} | ${escapeTable(formatPersonalSemanticTime(payload.updated_at ?? source.updated_at))} | ${escapeTable(payload.heat_count)} |`;
851
+ }).join('\n');
852
+ const sourceSummaries = sourceRows.map((source) => {
853
+ const payload = asObject(source.payload);
854
+ return `### ${clean(payload.title) || clean(source.source_id)}\n\n- Source ID: \`${clean(source.source_id)}\`\n- 来源人:${personalSemanticOwnerName(payload)}\n- 更新时间:${formatPersonalSemanticTime(payload.updated_at ?? source.updated_at)}\n- 热度:${clean(payload.heat_count) || '0'}\n- 摘要:${clean(payload.summary) || 'unknown'}\n- 内容:${clean(payload.content) || 'unknown'}\n- 关键词:${asArray(payload.keywords).map(clean).filter(Boolean).join('、') || '无'}\n- 资源引用:${renderPersonalSemanticResourceRefs(payload.resource_refs)}\n`;
855
+ }).join('\n');
856
+ return `---\ntype: project-semantic-personal-integration\nsemantic_project_id: ${semanticProjectId}\nintegration_id: ${yaml(integration.integration_id)}\ncontext_type: ${yaml(integration.context_type)}\nsource_snapshot_hash: ${yaml(snapshotHash)}\ntarget_type: ${yaml(integration.integration_target.target_type)}\ntarget_id: ${yaml(integration.integration_target.target_id)}\nconflict_policy: ${yaml(integration.conflict_policy)}\n---\n\n# ${clean(integration.title)}\n\n## Agent 使用摘要\n\n- 类型:${personalSemanticContextLabel(integration.context_type)}\n- 项目用法:${clean(integration.project_usage)}\n- 作用目标:${renderPersonalSemanticTarget(integration.integration_target)}\n- 冲突策略:${clean(integration.conflict_policy)}\n- 边界:${joinChineseClauses(integration.boundaries)}\n\n## 来源\n\n| Source ID | 来源人 | 更新时间 | 热度 |\n| --- | --- | --- | ---: |\n${provenanceRows || '| - | - | - | - |'}\n\n## 原始个人语义摘要\n\n${sourceSummaries || '无'}\n`;
857
+ }
858
+
859
+ function personalSemanticOwnerName(payload) {
860
+ return clean(payload.owner_name) || clean(payload.owner_display_name) || '未知用户';
861
+ }
862
+
863
+ function renderPersonalSemanticIndex(integrations) {
864
+ const rows = integrations
865
+ .slice()
866
+ .sort((left, right) => `${left.context_type}:${left.integration_id}`.localeCompare(`${right.context_type}:${right.integration_id}`, 'en'));
867
+ return `# 知识采纳记录\n\n已采纳的补充知识按目标写入项目、业务领域、场景或资产页;这里保留来源和适用边界以便追溯。\n\n- [背景](background/index.md)\n- [偏好](preference/index.md)\n- [经验](experience/index.md)\n- [资产上下文](asset-context/index.md)\n\n## 采纳项\n\n${rows.map((row) => `- [${escapeLink(row.title)}](${personalSemanticContextPath(row.context_type)}/${fileName('personal-semantic', row.integration_id)}):${personalSemanticContextLabel(row.context_type)},${clean(row.project_usage)}`).join('\n') || '- 无已采纳补充知识。'}\n`;
868
+ }
869
+
870
+ function renderTargetedKnowledge(type, id, relativeRoot) {
871
+ const rows = planModel.personalSemanticIntegrations.filter((integration) => {
872
+ const target = integration.integration_target;
873
+ if (target.target_type !== type) return false;
874
+ if (type !== 'asset') return target.target_id === id;
875
+ return target.target_id === id || target.target_id === id.split(':')[1];
876
+ });
877
+ if (!rows.length) return '';
878
+ return `\n## 项目补充知识\n\n${rows.map((row) => `- [${escapeLink(row.title)}](${relativeRoot}${personalSemanticContextPath(row.context_type)}/${fileName('personal-semantic', row.integration_id)}):${clean(row.project_usage)};适用边界:${joinChineseClauses(row.boundaries)}`).join('\n')}\n`;
879
+ }
880
+
881
+ function renderKnowledgeAdoptionIndex() {
882
+ return `# 知识采纳记录\n\n[查看已采纳补充知识及来源](../personal-semantics/index.md)。内容按适用目标进入项目、领域、场景和资产页;个人偏好只在声明的范围内生效。\n`;
883
+ }
884
+
885
+ function renderCommonIndex() {
886
+ const model = planModel.projectBusinessModel;
887
+ const objects = asArray(model.business_objects).map((row) => `- **${clean(row.name)}**:${clean(row.meaning)}`).join('\n');
888
+ const relationships = asArray(model.object_relationships).map((row) => `- ${clean(row.from_object)} → ${clean(row.to_object)}:${clean(row.relationship)}`).join('\n');
889
+ const supplements = planModel.personalSemanticIntegrations.filter((row) => row.integration_target.target_type === 'project')
890
+ .map((row) => `- [${escapeLink(row.title)}](../personal-semantics/${personalSemanticContextPath(row.context_type)}/${fileName('personal-semantic', row.integration_id)}):${clean(row.project_usage)};适用边界:${joinChineseClauses(row.boundaries)}`).join('\n');
891
+ return `# 项目共识\n\n这里收录跨业务领域适用的项目对象、关系与已采纳补充知识。领域特有的判断方法保留在对应领域页。\n\n## 共同业务对象\n\n${objects || '- 无。'}\n\n## 对象关系\n\n${relationships || '- 无。'}\n\n## 跨领域补充知识\n\n${supplements || '- 暂无。'}\n\n[查看完整项目判断链路](../project-overview.md#业务判断链路)。\n`;
892
+ }
893
+
894
+ function renderAssetIndex() {
895
+ return `# 事件与资产目录\n\n事件公共分组帮助按产品模块找到事件;分组名称本身不定义业务领域或认证状态。业务含义以领域判断和事件定义为准。\n\n- [事件地图](../event-map/index.md)\n- [看板](../dashboards/index.md)\n- [报表](../reports/index.md)\n- [可复用指标](../metrics/index.md)\n- [分析元数据](../metadata/index.md)\n`;
896
+ }
897
+
898
+ function writeEventMap(wikiRoot, buildIrRoot) {
899
+ const eventPages = new Map();
900
+ const eventsDir = path.join(buildIrRoot, 'metadata', 'events');
901
+ if (fs.existsSync(eventsDir)) for (const name of fs.readdirSync(eventsDir).filter((item) => item.endsWith('.md'))) {
902
+ const content = readText(path.join(eventsDir, name));
903
+ const key = frontmatterValue(content, 'resource_key');
904
+ if (key) eventPages.set(key, name);
905
+ }
906
+ const groups = eventGroups.filter((group) => clean(group.catalog_scope) === 'project_public');
907
+ const seen = new Set();
908
+ const links = [];
909
+ for (const group of groups) {
910
+ const name = clean(group.group_name);
911
+ if (!name || seen.has(name)) fail(`Duplicate or empty public event group: ${name || '<empty>'}.`);
912
+ seen.add(name);
913
+ const members = asArray(group.events).filter((event) => eventPages.has(clean(event.event_name)));
914
+ if (!members.length) continue;
915
+ const file = fileName('event-group', `group-${name}-${sha256(name).slice(0, 8)}`);
916
+ links.push(`- [${escapeLink(name)}](${file})(${members.length} 个已导出事件)`);
917
+ writeText(path.join(wikiRoot, 'event-map', file), `# ${name}\n\n来源:项目公共事件目录。此分组仅用于查找事件,不自动定义业务领域或事件认证状态。\n\n${members.map((event) => `- [${escapeLink(clean(event.event_title) || clean(event.event_name))}(\`${clean(event.event_name)}\`)](../metadata/events/${eventPages.get(clean(event.event_name))})`).join('\n')}\n`);
918
+ }
919
+ writeText(path.join(wikiRoot, 'event-map', 'index.md'), `# 事件地图\n\n按项目公共事件分组查找已纳入资产包的事件。未纳入当前资产范围的事件不会在这里出现;事件分组不等于业务领域。\n\n${links.join('\n') || '- 当前资产范围内没有可展示的公共事件分组。'}\n\n[按元数据类型查找事件](../metadata/index.md)。\n`);
920
+ }
921
+
922
+ function renderPersonalSemanticTypeIndex(type, integrations) {
923
+ const normalized = normalizePersonalSemanticContextType(type);
924
+ const rows = integrations.filter((row) => normalizePersonalSemanticContextType(row.context_type) === normalized)
925
+ .sort((left, right) => left.integration_id.localeCompare(right.integration_id, 'en'));
926
+ return `# ${personalSemanticContextLabel(normalized)}\n\n${personalSemanticContextUsage(normalized)}\n\n${rows.map((row) => `- [${escapeLink(row.title)}](${fileName('personal-semantic', row.integration_id)}):${clean(row.project_usage)}`).join('\n') || '- 暂无。'}\n`;
927
+ }
928
+
929
+ function renderPersonalSemanticTarget(target) {
930
+ const type = clean(target.target_type);
931
+ const id = clean(target.target_id);
932
+ if (type === 'domain') return `业务域 \`${id}\``;
933
+ if (type === 'recall_card') return `召回卡 \`${id}\``;
934
+ if (type === 'asset') return `资产 \`${id}\``;
935
+ return '项目整体';
936
+ }
937
+
938
+ function renderPersonalSemanticResourceRefs(value) {
939
+ const refs = asArray(value).map(asObject).map((ref) => {
940
+ const type = clean(ref.resource_type);
941
+ const key = clean(ref.resource_key);
942
+ return type && key ? `${type}:${key}` : clean(ref);
943
+ }).filter(Boolean);
944
+ return refs.join('、') || '无';
945
+ }
946
+
947
+ function formatPersonalSemanticTime(value) {
948
+ const text = clean(value);
949
+ if (!text) return 'unknown';
950
+ if (/^\d{12,}$/.test(text)) {
951
+ const date = new Date(Number(text));
952
+ if (!Number.isNaN(date.getTime())) return date.toISOString().replace('T', ' ').replace('.000Z', ' UTC');
953
+ }
954
+ return text;
699
955
  }
700
956
 
701
957
  function renderAuthorityIndex(rows) {
702
- return `# 资产权威状态\n\n## Agent 判定规则\n\n- \`runtime_policy=allow\`:可以作为主要答案依据,但当前数值仍需实时执行。\n- \`runtime_policy=warn\`:只能作为依赖或辅助线索,回答时需要说明它不是独立召回入口。\n- \`runtime_policy=block\`:不能直接回答,必须回到 canonical 资产、业务域或等待资产作者确认。\n- \`authority_role=dependency_only\`:只在报表/指标引用时使用,不能单独作为业务入口。\n- \`definition_state=conflict\` 或 \`unknown\`:不要断言精确口径,只能描述已知证据和待确认点。\n\n| 类型 | 资产 | Authority role | Certification | Definition | Runtime | Standalone recall | Canonical |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n${rows.sort((a, b) => `${a.resource_type}:${a.resource_key}`.localeCompare(`${b.resource_type}:${b.resource_key}`, 'en')).map((row) => `| ${row.resource_type} | \`${row.resource_key}\` ${escapeTable(row.title)} | ${row.authority_role} | ${row.certification_state} | ${row.definition_state} | ${row.runtime_policy} | ${row.standalone_recall} | ${row.canonical_asset_id ?? '-'} |`).join('\n')}\n`;
958
+ return `# 资产权威状态\n\n## Agent 判定规则\n\n- \`runtime_policy=allow\`:可以作为主要答案依据,但当前数值仍需实时执行。\n- \`runtime_policy=warn\`:只能作为依赖或辅助线索,回答时需要说明它不是独立召回入口。\n- \`runtime_policy=block\`:不能直接回答,必须回到 canonical 资产、业务域或等待资产作者确认。\n- \`authority_role=dependency_only\`:定义、依赖关系或冲突解释可以引用已知元数据;不能单独据此断言业务状态或当前数值。平台禁止直接检索时回到授权引用资产。\n- \`definition_state=conflict\` 或 \`unknown\`:不要断言精确口径,只能描述已知证据和待确认点。\n\n| 类型 | 资产 | Authority role | Certification | Definition | Runtime | Standalone recall | Canonical |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n${rows.sort((a, b) => `${a.resource_type}:${a.resource_key}`.localeCompare(`${b.resource_type}:${b.resource_key}`, 'en')).map((row) => `| ${row.resource_type} | \`${row.resource_key}\` ${escapeTable(row.title)} | ${row.authority_role} | ${row.certification_state} | ${row.definition_state} | ${row.runtime_policy} | ${row.standalone_recall} | ${row.canonical_asset_id ?? '-'} |`).join('\n')}\n`;
703
959
  }
704
960
 
705
961
  function renderSourceProjectBoundary(rows) {
@@ -721,20 +977,20 @@ ${external.map((row) => `| ${row.resource_type}:${row.resource_key} ${escapeTabl
721
977
  }
722
978
 
723
979
  function renderGovernanceIndex(conflicts) {
724
- return `# 治理与质量\n\n- [资产权威状态](authority-index.md)\n- [覆盖、物理来源与未归属资产](coverage.md)\n- [项目来源边界](source-project-boundary.md)\n\n## Agent 使用方式\n\n- 回答前先看资产页 frontmatter 的 \`runtime_policy\`、\`definition_state\` 和 \`authority_role\`。\n- \`runtime_policy=block\` 的资产不能直接回答;如果存在 canonical 资产,优先跳转到 canonical。\n- \`dependency_only\` 元数据只解释报表/指标依赖,不单独发起召回。\n- 语义冲突表示多个资产表达同一业务含义但口径不同,不能只凭名称选择资产;优先使用业务域、召回卡或用户确认。\n- 项目来源边界用于识别资产包里引用了其他项目 ID 的字段;跨项目引用只能作为来源线索,不代表当前项目事实。\n\n当前检测到 ${conflicts} 张语义冲突报表。\n`;
980
+ return `# 治理与来源\n\n- [知识采纳记录](knowledge-adoption.md)\n- [资产权威状态](authority-index.md)\n- [覆盖、物理来源与未归属资产](coverage.md)\n- [项目来源边界](source-project-boundary.md)\n\n## Agent 使用方式\n\n- 回答前先看资产页 frontmatter 的 \`runtime_policy\`、\`definition_state\` 和 \`authority_role\`。\n- \`runtime_policy=block\` 的资产不能直接回答;如果存在 canonical 资产,优先跳转到 canonical。\n- \`dependency_only\` 元数据可用于有证据的定义、依赖和冲突解释;不能据此单独回答业务状态或当前数值。\n- 语义冲突表示多个资产表达同一业务含义但口径不同,不能只凭名称选择资产;优先使用业务域、召回卡或用户确认。\n- 项目来源边界用于识别资产包里引用了其他项目 ID 的字段;跨项目引用只能作为来源线索,不代表当前项目事实。\n\n当前检测到 ${conflicts} 张语义冲突报表。\n`;
725
981
  }
726
982
 
727
983
  function renderWikiIndex(corpus) {
728
- return `---\ntype: project-semantic-knowledge-wiki-index\nsemantic_project_id: ${semanticProjectId}\nproject_name: ${yaml(projectName)}\nsource_snapshot_hash: ${yaml(snapshotHash)}\n---\n\n# ${projectName} 项目语义知识库 Wiki\n\n## 查询顺序\n\n1. [项目业务模型](project-overview.md)\n2. [场景召回卡](recall-cards/index.md)\n3. [业务域](domains/index.md)\n4. [看板](dashboards/index.md)、[报表](reports/index.md)、[可复用指标](metrics/index.md)\n5. [分析元数据](metadata/index.md)\n6. [治理与质量](governance/index.md)\n\n技术、演示、副本和定向诊断资产只在[技术附录](technical-appendices/index.md)中追溯。物理空间在[source containers](source-containers/index.md)中保留,不作为业务域。\n\n${countSummary(corpus)}\n`;
984
+ return `---\ntype: project-semantic-knowledge-wiki-index\nsemantic_project_id: ${semanticProjectId}\nproject_name: ${yaml(projectName)}\nsource_snapshot_hash: ${yaml(snapshotHash)}\nnavigation_taxonomy_version: "2.1"\n---\n\n# ${projectName} 项目语义知识库 Wiki\n\n## 问题意图路由\n\n- 业务判断或当前数值:先选[业务领域](domains/index.md)及其[场景召回卡](recall-cards/index.md),按对象、粒度、参数选择分支并实时执行。\n- 点名资产、字段定义或依赖关系:从[事件与资产目录](asset-index/index.md)定位资产,再核对[治理与来源](governance/index.md);依赖元数据可解释定义与关系,但不能单独证明业务状态。\n- 产品机制或共同背景:先读[项目共识](common/index.md),再进入相关业务域。\n- 无匹配场景卡:进入相关业务域,按对象、粒度、过滤和时间筛选域内资产;仍无合适资产就说明缺口,不编造答案。\n- 已取得对象、粒度、时间、范围和权威性匹配的最小证据后停止扩搜;需要当前值时按权限执行。\n\n## 知识导航\n\n1. [项目总览](project-overview.md):项目定位、对象关系和判断链路。\n2. [项目共识](common/index.md):跨领域适用的知识。\n3. [业务领域](domains/index.md):按业务问题进入判断路径和证据。\n4. [事件与资产目录](asset-index/index.md):按事件公共分组或资产类型查找。\n5. [治理与来源](governance/index.md):采纳、权威状态和边界。\n\n技术、演示、副本和定向诊断资产只在[技术附录](technical-appendices/index.md)中追溯。物理空间在[source containers](source-containers/index.md)中保留,不作为业务域。\n\n${countSummary(corpus)}\n`;
729
985
  }
730
986
 
731
987
  function renderSnapshotIndex(corpus) {
732
988
  const title = String(projectName).replace(/\r?\n/g, ' ').replace(/^#+\s*/, '');
733
- return `# ${title} 项目语义知识库 Wiki\n\n这是可导入知识库源文件体系的项目语义 Wiki,共包含 ${corpus.counts.dashboards} 张看板、${corpus.counts.reports} 张报表和 ${corpus.counts.analysis_metadata} 条分析元数据。\n\n## 查询顺序\n\n1. [项目业务模型](wiki/project-overview.md)\n2. [场景召回卡](wiki/recall-cards/index.md)\n3. [业务域](wiki/domains/index.md)\n4. [看板](wiki/dashboards/index.md)、[报表](wiki/reports/index.md)、[可复用指标](wiki/metrics/index.md)\n5. [分析元数据](wiki/metadata/index.md)\n6. [治理与质量](wiki/governance/index.md)\n\n技术、演示、副本和定向诊断资产只在[技术附录](wiki/technical-appendices/index.md)中追溯。物理空间在[source containers](wiki/source-containers/index.md)中保留,不作为业务域。\n\n${countSummary(corpus)}\n`;
989
+ return `# ${title} 项目语义知识库 Wiki\n\n## 问题意图路由\n\n- 业务判断或当前数值:先选[业务领域](wiki/domains/index.md)和匹配的[场景召回卡](wiki/recall-cards/index.md),按对象、粒度和参数选择分支。\n- 点名资产、字段定义或依赖关系:从[事件与资产目录](wiki/asset-index/index.md)定位,再核对[治理与来源](wiki/governance/index.md)。\n- 产品机制或共同背景:先读[项目共识](wiki/common/index.md)。\n- 无匹配场景卡:回到相关业务域,按对象、粒度、过滤和时间筛选;仍无合适资产则说明缺口。\n- 证据已经匹配对象、粒度、范围、时间和权威状态时停止扩搜;当前数值需要授权实时执行。\n\n## 知识导航\n\n1. [项目总览](wiki/project-overview.md)\n2. [项目共识](wiki/common/index.md)\n3. [业务领域](wiki/domains/index.md)\n4. [事件与资产目录](wiki/asset-index/index.md)\n5. [治理与来源](wiki/governance/index.md)\n\n${countSummary(corpus)}\n`;
734
990
  }
735
991
 
736
992
  function renderProjectOverview(corpus) {
737
- return `# ${projectName} 项目业务模型\n\n${renderProjectBusinessModel(planModel.projectBusinessModel)}\n\n## 资产与治理范围\n\n- Semantic project ID: ${semanticProjectId}\n- Snapshot hash: \`${snapshotHash}\`\n- Asset scope: ${packageAssetScope}\n- Business domains: ${corpus.counts.business_domains}\n- Dashboards: ${corpus.counts.dashboards}\n- Reports: ${corpus.counts.reports}\n- Reusable metrics: ${corpus.counts.reusable_metrics}\n- Metadata: ${corpus.counts.analysis_metadata}\n\nCommon 资产包是权威来源。本 Wiki 将业务判断模型、用途总结和精确证据合并为单一页面,不再发布第二套 raw/briefs 语料。\n`;
993
+ return `# ${projectName} 项目总览\n\n${renderProjectBusinessModel(planModel.projectBusinessModel)}${renderTargetedKnowledge('project', 'project', 'personal-semantics/')}\n\n## 资产与治理范围\n\n- Semantic project ID: ${semanticProjectId}\n- Snapshot hash: \`${snapshotHash}\`\n- Asset scope: ${packageAssetScope}\n- Business domains: ${corpus.counts.business_domains}\n- Dashboards: ${corpus.counts.dashboards}\n- Reports: ${corpus.counts.reports}\n- Reusable metrics: ${corpus.counts.reusable_metrics}\n- Metadata: ${corpus.counts.analysis_metadata}\n\nCommon 资产包是权威来源。本 Wiki 将业务判断模型、用途总结和精确证据合并为单一页面,不再发布第二套 raw/briefs 语料。\n`;
738
994
  }
739
995
 
740
996
  function renderProjectBusinessModel(model) {
@@ -774,7 +1030,7 @@ function renderLog(corpus) {
774
1030
  }
775
1031
 
776
1032
  function countSummary(corpus) {
777
- return `## 数量\n\n| 资产 | 数量 |\n| --- | ---: |\n| 业务域 | ${corpus.counts.business_domains} |\n| 技术附录 | ${corpus.counts.technical_appendices} |\n| 看板 | ${corpus.counts.dashboards} |\n| 报表 | ${corpus.counts.reports} |\n| 可复用指标 | ${corpus.counts.reusable_metrics} |\n| 分析元数据 | ${corpus.counts.analysis_metadata} |\n| 场景召回卡 | ${planModel.cards.length} |`;
1033
+ return `## 数量\n\n| 内容 | 数量 |\n| --- | ---: |\n| 业务领域 | ${corpus.counts.business_domains} |\n| 公共事件分组 | ${eventGroups.length} |\n| 看板 | ${corpus.counts.dashboards} |\n| 报表 | ${corpus.counts.reports} |\n| 可复用指标 | ${corpus.counts.reusable_metrics} |\n| 分析元数据 | ${corpus.counts.analysis_metadata} |\n| 场景召回卡 | ${planModel.cards.length} |\n| 已采纳补充知识 | ${planModel.personalSemanticIntegrations.length} |`;
778
1034
  }
779
1035
 
780
1036
  function authorityFrontmatter(type, resourceKey, title, authority) {
@@ -801,6 +1057,35 @@ function assetPath(type, key) {
801
1057
  fail(`Unsupported recall-card asset type ${type}.`);
802
1058
  }
803
1059
 
1060
+ function normalizePersonalSemanticContextType(value) {
1061
+ const type = clean(value).toLowerCase().replace('-', '_');
1062
+ if (!['background', 'preference', 'experience', 'asset_context'].includes(type)) {
1063
+ fail(`Invalid adopted personal semantic context_type: ${clean(value) || '<empty>'}.`);
1064
+ }
1065
+ return type;
1066
+ }
1067
+
1068
+ function personalSemanticContextPath(value) {
1069
+ const type = normalizePersonalSemanticContextType(value);
1070
+ return type === 'asset_context' ? 'asset-context' : type;
1071
+ }
1072
+
1073
+ function personalSemanticContextLabel(value) {
1074
+ const type = normalizePersonalSemanticContextType(value);
1075
+ if (type === 'background') return '项目背景';
1076
+ if (type === 'preference') return '偏好';
1077
+ if (type === 'experience') return '经验';
1078
+ return '资产上下文';
1079
+ }
1080
+
1081
+ function personalSemanticContextUsage(value) {
1082
+ const type = normalizePersonalSemanticContextType(value);
1083
+ if (type === 'background') return '用于补充项目、业务域和对象关系的背景信息。';
1084
+ if (type === 'preference') return '用于补充项目内的回答偏好、入口选择和路由方式。';
1085
+ if (type === 'experience') return '用于补充已经验证过的分析路径、风险提示和经验规则。';
1086
+ return '用于补充具体资产的使用语境;引用资产不存在时不能作为当前资产事实。';
1087
+ }
1088
+
804
1089
  function assetOutputFileName(directory, key) {
805
1090
  if (directory === 'dashboards') return dashboardFileNames.get(key) ?? fileName('dashboard', key);
806
1091
  if (directory === 'reports') return reportFileNames.get(key) ?? fileName('report', key);
@@ -947,10 +1232,8 @@ function assertSnapshotEntryPoint(root) {
947
1232
  const content = readText(path.join(root, 'index.md'));
948
1233
  const requiredLinks = [
949
1234
  'wiki/project-overview.md',
950
- 'wiki/recall-cards/index.md', 'wiki/domains/index.md', 'wiki/dashboards/index.md',
951
- 'wiki/reports/index.md', 'wiki/metrics/index.md', 'wiki/metadata/index.md',
952
- 'wiki/governance/index.md', 'wiki/technical-appendices/index.md',
953
- 'wiki/source-containers/index.md',
1235
+ 'wiki/common/index.md', 'wiki/domains/index.md', 'wiki/asset-index/index.md',
1236
+ 'wiki/governance/index.md',
954
1237
  ];
955
1238
  const missing = requiredLinks.filter((target) => !content.includes(`(${target})`));
956
1239
  if (missing.length) fail(`Snapshot root index is missing required navigation: ${missing.join(', ')}.`);
@@ -1118,6 +1401,7 @@ function writeFlatUploadSources(sourceRoot, targetRoot, replace) {
1118
1401
  writeJson(path.join(staging, 'kb-upload-manifest.json'), {
1119
1402
  schema_version: '1.0',
1120
1403
  manifest_kind: 'ae_project_semantic_kb_upload_sources',
1404
+ navigation_taxonomy_version: '2.1',
1121
1405
  namespace: uploadSourceNamespace,
1122
1406
  semantic_project_id: semanticProjectId,
1123
1407
  project_name: projectName,
@@ -1162,6 +1446,17 @@ function uploadSourceIdentity(rel, content = '') {
1162
1446
  if (section === 'metrics') return namedUploadSource('metric', stem, frontmatterValue(content, 'resource_key'));
1163
1447
  if (section === 'domains') return namedUploadSource('domain', stem);
1164
1448
  if (section === 'recall-cards') return namedUploadSource('recall-card', stem);
1449
+ if (section === 'personal-semantics') {
1450
+ if (rest.endsWith('/index') || rest === 'index') {
1451
+ return {
1452
+ source_kind: 'personal-semantic',
1453
+ asset_type: null,
1454
+ asset_key: null,
1455
+ file_stem: `personal-semantic-${stem}`,
1456
+ };
1457
+ }
1458
+ return namedUploadSource('personal-semantic', stem, frontmatterValue(content, 'integration_id'));
1459
+ }
1165
1460
  if (section === 'technical-appendices') return namedUploadSource('appendix', stem);
1166
1461
  if (section === 'source-containers') return namedUploadSource('source-container', stem);
1167
1462
  if (section === 'governance') return namedUploadSource('governance', stem);
@@ -1187,6 +1482,9 @@ function semanticPlanFragmentRefsForSource(identity, rel) {
1187
1482
  if (sourceKind === 'domain' && assetKey) refs.add(`domain:${assetKey}`);
1188
1483
  if (sourceKind === 'appendix' && assetKey) refs.add(`appendix:${assetKey}`);
1189
1484
  if (sourceKind === 'recall-card' && assetKey) refs.add(`recall-card:${assetKey}`);
1485
+ if (sourceKind === 'personal-semantic' && assetKey) {
1486
+ refs.add(`personal-semantic-integration:${assetKey}`);
1487
+ }
1190
1488
  if (assetType === 'dashboard' && assetKey) {
1191
1489
  const domain = domainByDashboardId.get(assetKey);
1192
1490
  const appendix = appendixByDashboardId.get(assetKey);
@@ -1210,6 +1508,9 @@ function semanticPlanFragmentRefsForSource(identity, rel) {
1210
1508
  if (sourceKind === 'index' && rel === 'wiki/domains/index.md') {
1211
1509
  for (const domain of domains) refs.add(`domain:${clean(domain.domain_id)}`);
1212
1510
  }
1511
+ if (sourceKind === 'personal-semantic' && /\/index\.md$/.test(rel)) {
1512
+ for (const integration of planModel.personalSemanticIntegrations) refs.add(`personal-semantic-integration:${clean(integration.integration_id)}`);
1513
+ }
1213
1514
  return [...refs].filter(Boolean).sort();
1214
1515
  }
1215
1516
 
@@ -1242,6 +1543,14 @@ function buildSemanticPlanState() {
1242
1543
  for (const [reportId, summary] of [...planModel.sqlByReportId.entries()].sort(([left], [right]) => left.localeCompare(right, 'en'))) {
1243
1544
  add(`report:${reportId}:sql_semantics`, 'sql-report', summary, { report_id: reportId });
1244
1545
  }
1546
+ for (const integration of planModel.personalSemanticIntegrations) {
1547
+ const id = clean(integration.integration_id);
1548
+ add(`personal-semantic-integration:${id}`, 'personal-semantic-integration', integration, {
1549
+ integration_id: id,
1550
+ context_type: clean(integration.context_type),
1551
+ source_ids: integration.source_ids,
1552
+ });
1553
+ }
1245
1554
  fragments.sort((left, right) => left.id.localeCompare(right.id, 'en'));
1246
1555
  return {
1247
1556
  schema_version: '1.0',
@@ -1355,6 +1664,12 @@ function renderNumberedSteps(value, indent = '') {
1355
1664
  function writeText(file, value) { fs.mkdirSync(path.dirname(file), { recursive: true }); fs.writeFileSync(file, value.endsWith('\n') ? value : `${value}\n`, 'utf8'); }
1356
1665
  function writeJson(file, value) { writeText(file, JSON.stringify(value, null, 2)); }
1357
1666
  function readIndex(name) { return readText(path.join(packageRoot, 'indexes', `${name}.jsonl`)).split(/\r?\n/).filter(Boolean).map(JSON.parse).filter((row) => row.record_type !== 'header'); }
1667
+ function readOptionalIndex(name) { return fs.existsSync(path.join(packageRoot, 'indexes', `${name}.jsonl`)) ? readIndex(name) : []; }
1668
+ function readPackageJsonl(rel) {
1669
+ const file = path.join(packageRoot, rel);
1670
+ if (!fs.existsSync(file)) return [];
1671
+ return readText(file).split(/\r?\n/).filter(Boolean).map(JSON.parse).filter((row) => row.record_type !== 'header');
1672
+ }
1358
1673
  function listFiles(root) { return fs.readdirSync(root, { withFileTypes: true }).flatMap((entry) => entry.isDirectory() ? listFiles(path.join(root, entry.name)) : [path.join(root, entry.name)]); }
1359
1674
  function parseArgs(values) { const out = {}; for (let i = 0; i < values.length; i += 1) { const value = values[i]; if (!value.startsWith('--')) fail(`Unexpected argument: ${value}`); const key = value.slice(2); if (['force', 'keep-build-ir', 'allow-all-visible', 'allow-semantic-plan-snapshot-drift'].includes(key)) out[key] = true; else out[key] = values[++i]; } return out; }
1360
1675
  function required(values, key) { const value = clean(values[key]); if (!value) fail(`--${key} is required.`); return value; }
@@ -1,13 +1,29 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from 'node:fs';
3
+ import path from 'node:path';
3
4
  import { spawnSync } from 'node:child_process';
4
5
  import { pathToFileURL } from 'node:url';
5
6
 
6
- const scoped = new Set(['+new', '+add', '+list-sources', '+schema', '+compile', '+status', '+import']);
7
+ const scoped = new Set(['+new', '+add', '+list-sources', '+schema', '+compile', '+status', '+import', '+schema-download', '+schema-import', '+schema-import-list']);
7
8
  const requestScoped = new Set(['+import-status']);
8
9
  const directory = new Set(['+source-ls', '+source-read', '+source-put', '+source-rm']);
9
10
  const value = (args, key) => args[args.indexOf(key) + 1];
10
11
 
12
+ export function invokeCompanyKbCli(flags, { spawn = spawnSync, localCli = path.resolve(import.meta.dirname, '../../../../bin/ae-cli.js') } = {}) {
13
+ // Packaged/project scripts use their matching CLI; standalone installed Skills retain PATH lookup.
14
+ const bundled = fs.existsSync(localCli);
15
+ const child = spawn(bundled ? process.execPath : 'ae-cli', bundled ? [localCli, ...flags] : flags,
16
+ { encoding: 'utf8', stdio: ['inherit', 'pipe', 'pipe'] });
17
+ if (child.stderr) process.stderr.write(child.stderr);
18
+ if (child.error) throw child.error;
19
+ if (child.status === 0) return JSON.parse(child.stdout);
20
+ // CLI failures are JSON on stderr, possibly surrounded by compatibility/progress messages.
21
+ const start = child.stderr.indexOf('{\n "ok": false,');
22
+ const end = start < 0 ? -1 : child.stderr.indexOf('\n}', start);
23
+ if (end >= 0) return JSON.parse(child.stderr.slice(start, end + 2));
24
+ throw new Error(child.stderr || child.stdout || 'CLI process failed.');
25
+ }
26
+
11
27
  // Project semantic KB operations must never fall back to a personal KB.
12
28
  export async function runCompanyKb(command, args, invoke) {
13
29
  if (args.some((arg) => arg === '--custom-instructions' || arg.startsWith('--custom-instructions='))) {
@@ -24,7 +40,9 @@ export async function runCompanyKb(command, args, invoke) {
24
40
  const call = async (cmd, flags) => {
25
41
  const result = await invoke(['kb', cmd, ...flags]);
26
42
  if (!result || result.ok !== true) {
27
- throw new Error(`Company KB operation failed; stop without personal fallback: ${JSON.stringify(result?.error ?? result)}`);
43
+ const error = new Error(`Company KB operation failed; stop without personal fallback: ${JSON.stringify(result?.error ?? result)}`);
44
+ error.code = result?.error?.code;
45
+ throw error;
28
46
  }
29
47
  return result;
30
48
  };
@@ -98,12 +116,7 @@ function expandCustomInstructionsFile(args) {
98
116
  if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
99
117
  try {
100
118
  const [command, ...args] = process.argv.slice(2);
101
- const result = await runCompanyKb(command, args, (flags) => {
102
- const child = spawnSync('ae-cli', flags, { encoding: 'utf8', stdio: ['inherit', 'pipe', 'pipe'] });
103
- if (child.stderr) process.stderr.write(child.stderr);
104
- if (child.error || child.status !== 0) throw new Error(child.error?.message || child.stderr || child.stdout || 'CLI failed.');
105
- return JSON.parse(child.stdout);
106
- });
119
+ const result = await runCompanyKb(command, args, invokeCompanyKbCli);
107
120
  console.log(JSON.stringify(result, null, 2));
108
121
  } catch (error) {
109
122
  console.error(error.message);