@swarmvaultai/engine 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  uniqueBy,
22
22
  writeFileIfChanged,
23
23
  writeJsonFile
24
- } from "./chunk-FD3LJQ4T.js";
24
+ } from "./chunk-OK5752AP.js";
25
25
 
26
26
  // src/agents.ts
27
27
  import crypto from "crypto";
@@ -10579,6 +10579,43 @@ import matter5 from "gray-matter";
10579
10579
  function uniqueStrings2(values) {
10580
10580
  return uniqueBy(values.filter(Boolean), (value) => value);
10581
10581
  }
10582
+ var GUIDED_SOURCE_MARKER_PREFIX = "<!-- swarmvault-guided-source:";
10583
+ var GUIDED_SOURCE_START_SUFFIX = ":start -->";
10584
+ var GUIDED_SOURCE_END_SUFFIX = ":end -->";
10585
+ function extractGuidedSourceBlocks(content) {
10586
+ if (!content) {
10587
+ return [];
10588
+ }
10589
+ const blocks = [];
10590
+ let cursor = 0;
10591
+ while (cursor < content.length) {
10592
+ const startIndex = content.indexOf(GUIDED_SOURCE_MARKER_PREFIX, cursor);
10593
+ if (startIndex === -1) {
10594
+ break;
10595
+ }
10596
+ const scopeEndIndex = content.indexOf(GUIDED_SOURCE_START_SUFFIX, startIndex);
10597
+ if (scopeEndIndex === -1) {
10598
+ break;
10599
+ }
10600
+ const scopeId = content.slice(startIndex + GUIDED_SOURCE_MARKER_PREFIX.length, scopeEndIndex);
10601
+ const endMarker = `${GUIDED_SOURCE_MARKER_PREFIX}${scopeId}${GUIDED_SOURCE_END_SUFFIX}`;
10602
+ const endIndex = content.indexOf(endMarker, scopeEndIndex);
10603
+ if (endIndex === -1) {
10604
+ break;
10605
+ }
10606
+ const blockEnd = endIndex + endMarker.length;
10607
+ blocks.push(content.slice(startIndex, blockEnd).trim());
10608
+ cursor = blockEnd;
10609
+ }
10610
+ return uniqueStrings2(blocks);
10611
+ }
10612
+ function appendGuidedSourceBlocks(body, existingContent) {
10613
+ const blocks = extractGuidedSourceBlocks(existingContent);
10614
+ if (!blocks.length) {
10615
+ return body;
10616
+ }
10617
+ return [body.trimEnd(), "", "## Guided Session Notes", "", ...blocks, ""].join("\n");
10618
+ }
10582
10619
  function safeFrontmatter(value) {
10583
10620
  return JSON.parse(JSON.stringify(value));
10584
10621
  }
@@ -10667,7 +10704,7 @@ function detailList(manifest, key) {
10667
10704
  const items = value.split(",").map((item) => item.trim()).filter(Boolean);
10668
10705
  return items.length ? items : void 0;
10669
10706
  }
10670
- function buildSourcePage(manifest, analysis, schemaHash, metadata, relatedOutputs = [], modulePage, decorations) {
10707
+ function buildSourcePage(manifest, analysis, schemaHash, metadata, relatedOutputs = [], modulePage, decorations, existingContent) {
10671
10708
  const relativePath = pagePathFor("source", manifest.sourceId);
10672
10709
  const pageId = `source:${manifest.sourceId}`;
10673
10710
  const { sourceHashes, sourceSemanticHashes } = sourceHashesForManifest(manifest);
@@ -10709,61 +10746,64 @@ function buildSourcePage(manifest, analysis, schemaHash, metadata, relatedOutput
10709
10746
  schema_hash: schemaHash,
10710
10747
  ...sourceHashFrontmatter(sourceHashes, sourceSemanticHashes)
10711
10748
  };
10712
- const body = [
10713
- `# ${analysis.title}`,
10714
- "",
10715
- `Source ID: \`${manifest.sourceId}\``,
10716
- `Source Kind: \`${manifest.sourceKind}\``,
10717
- manifest.url ? `Source URL: ${manifest.url}` : `Source Path: \`${manifest.originalPath ?? manifest.storedPath}\``,
10718
- ...manifest.sourceType ? [`Source Type: \`${manifest.sourceType}\``, ""] : [""],
10719
- ...manifest.sourceClass ? [`Source Class: \`${manifest.sourceClass}\``, ""] : [],
10720
- ...manifest.sourceGroupTitle ? [`Source Group: ${manifest.sourceGroupTitle}`] : [],
10721
- ...manifest.partTitle ? [`Part: ${manifest.partIndex ?? "?"}/${manifest.partCount ?? "?"} - ${manifest.partTitle}`] : [],
10722
- ...manifest.details && Object.keys(manifest.details).length ? [
10749
+ const body = appendGuidedSourceBlocks(
10750
+ [
10751
+ `# ${analysis.title}`,
10723
10752
  "",
10724
- "## Source Details",
10753
+ `Source ID: \`${manifest.sourceId}\``,
10754
+ `Source Kind: \`${manifest.sourceKind}\``,
10755
+ manifest.url ? `Source URL: ${manifest.url}` : `Source Path: \`${manifest.originalPath ?? manifest.storedPath}\``,
10756
+ ...manifest.sourceType ? [`Source Type: \`${manifest.sourceType}\``, ""] : [""],
10757
+ ...manifest.sourceClass ? [`Source Class: \`${manifest.sourceClass}\``, ""] : [],
10758
+ ...manifest.sourceGroupTitle ? [`Source Group: ${manifest.sourceGroupTitle}`] : [],
10759
+ ...manifest.partTitle ? [`Part: ${manifest.partIndex ?? "?"}/${manifest.partCount ?? "?"} - ${manifest.partTitle}`] : [],
10760
+ ...manifest.details && Object.keys(manifest.details).length ? [
10761
+ "",
10762
+ "## Source Details",
10763
+ "",
10764
+ ...Object.entries(manifest.details).map(([key, value]) => `- ${key.replace(/_/g, " ")}: ${value}`),
10765
+ ""
10766
+ ] : [],
10725
10767
  "",
10726
- ...Object.entries(manifest.details).map(([key, value]) => `- ${key.replace(/_/g, " ")}: ${value}`),
10727
- ""
10728
- ] : [],
10729
- "",
10730
- "## Summary",
10731
- "",
10732
- analysis.summary,
10733
- "",
10734
- ...analysis.code ? [
10735
- "## Code Module",
10768
+ "## Summary",
10769
+ "",
10770
+ analysis.summary,
10771
+ "",
10772
+ ...analysis.code ? [
10773
+ "## Code Module",
10774
+ "",
10775
+ `- Language: \`${analysis.code.language}\``,
10776
+ modulePage ? `- Module Page: [[${modulePage.path.replace(/\.md$/, "")}|${modulePage.title}]]` : "- Module Page: Not generated.",
10777
+ `- Exports: ${analysis.code.exports.length ? analysis.code.exports.join(", ") : "None detected."}`,
10778
+ `- Symbols: ${analysis.code.symbols.length ? analysis.code.symbols.map((symbol) => symbol.name).join(", ") : "None detected."}`,
10779
+ analysis.code.diagnostics.length ? `- Diagnostics: ${analysis.code.diagnostics.length}` : "- Diagnostics: None.",
10780
+ ""
10781
+ ] : [],
10782
+ "## Concepts",
10783
+ "",
10784
+ ...analysis.concepts.length ? analysis.concepts.map(
10785
+ (item) => `- [[${pagePathFor("concept", slugify(item.name)).replace(/\.md$/, "")}|${item.name}]]: ${item.description}`
10786
+ ) : ["- None detected."],
10787
+ "",
10788
+ "## Entities",
10789
+ "",
10790
+ ...analysis.entities.length ? analysis.entities.map(
10791
+ (item) => `- [[${pagePathFor("entity", slugify(item.name)).replace(/\.md$/, "")}|${item.name}]]: ${item.description}`
10792
+ ) : ["- None detected."],
10793
+ "",
10794
+ "## Claims",
10736
10795
  "",
10737
- `- Language: \`${analysis.code.language}\``,
10738
- modulePage ? `- Module Page: [[${modulePage.path.replace(/\.md$/, "")}|${modulePage.title}]]` : "- Module Page: Not generated.",
10739
- `- Exports: ${analysis.code.exports.length ? analysis.code.exports.join(", ") : "None detected."}`,
10740
- `- Symbols: ${analysis.code.symbols.length ? analysis.code.symbols.map((symbol) => symbol.name).join(", ") : "None detected."}`,
10741
- analysis.code.diagnostics.length ? `- Diagnostics: ${analysis.code.diagnostics.length}` : "- Diagnostics: None.",
10796
+ ...analysis.claims.length ? analysis.claims.map((claim) => `- ${claim.text} [source:${claim.citation}]`) : ["- No claims extracted."],
10797
+ "",
10798
+ "## Questions",
10799
+ "",
10800
+ ...analysis.questions.length ? analysis.questions.map((question) => `- ${question}`) : ["- No follow-up questions yet."],
10801
+ "",
10802
+ ...relatedOutputsSection(relatedOutputs),
10742
10803
  ""
10743
- ] : [],
10744
- "## Concepts",
10745
- "",
10746
- ...analysis.concepts.length ? analysis.concepts.map(
10747
- (item) => `- [[${pagePathFor("concept", slugify(item.name)).replace(/\.md$/, "")}|${item.name}]]: ${item.description}`
10748
- ) : ["- None detected."],
10749
- "",
10750
- "## Entities",
10751
- "",
10752
- ...analysis.entities.length ? analysis.entities.map(
10753
- (item) => `- [[${pagePathFor("entity", slugify(item.name)).replace(/\.md$/, "")}|${item.name}]]: ${item.description}`
10754
- ) : ["- None detected."],
10755
- "",
10756
- "## Claims",
10757
- "",
10758
- ...analysis.claims.length ? analysis.claims.map((claim) => `- ${claim.text} [source:${claim.citation}]`) : ["- No claims extracted."],
10759
- "",
10760
- "## Questions",
10761
- "",
10762
- ...analysis.questions.length ? analysis.questions.map((question) => `- ${question}`) : ["- No follow-up questions yet."],
10763
- "",
10764
- ...relatedOutputsSection(relatedOutputs),
10765
- ""
10766
- ].join("\n");
10804
+ ].join("\n"),
10805
+ existingContent
10806
+ );
10767
10807
  return {
10768
10808
  page: {
10769
10809
  id: pageId,
@@ -10943,7 +10983,7 @@ function buildModulePage(input) {
10943
10983
  content: matter5.stringify(body, frontmatter)
10944
10984
  };
10945
10985
  }
10946
- function buildAggregatePage(kind, name, descriptions, sourceAnalyses, sourceHashes, sourceSemanticHashes, schemaHash, metadata, relativePath, relatedOutputs = [], decorations) {
10986
+ function buildAggregatePage(kind, name, descriptions, sourceAnalyses, sourceHashes, sourceSemanticHashes, schemaHash, metadata, relativePath, relatedOutputs = [], decorations, existingContent) {
10947
10987
  const slug = slugify(name);
10948
10988
  const pageId = `${kind}:${slug}`;
10949
10989
  const sourceIds = sourceAnalyses.map((item) => item.sourceId);
@@ -10969,26 +11009,29 @@ function buildAggregatePage(kind, name, descriptions, sourceAnalyses, sourceHash
10969
11009
  schema_hash: schemaHash,
10970
11010
  ...sourceHashFrontmatter(sourceHashes, sourceSemanticHashes)
10971
11011
  };
10972
- const body = [
10973
- `# ${name}`,
10974
- "",
10975
- "## Summary",
10976
- "",
10977
- summary,
10978
- "",
10979
- "## Seen In",
10980
- "",
10981
- ...sourceAnalyses.map((item) => `- [[${pagePathFor("source", item.sourceId).replace(/\.md$/, "")}|${item.title}]]`),
10982
- "",
10983
- "## Source Claims",
10984
- "",
10985
- ...sourceAnalyses.flatMap(
10986
- (item) => item.claims.filter((claim) => claim.text.toLowerCase().includes(name.toLowerCase())).map((claim) => `- ${claim.text} [source:${claim.citation}]`)
10987
- ),
10988
- "",
10989
- ...relatedOutputsSection(relatedOutputs),
10990
- ""
10991
- ].join("\n");
11012
+ const body = appendGuidedSourceBlocks(
11013
+ [
11014
+ `# ${name}`,
11015
+ "",
11016
+ "## Summary",
11017
+ "",
11018
+ summary,
11019
+ "",
11020
+ "## Seen In",
11021
+ "",
11022
+ ...sourceAnalyses.map((item) => `- [[${pagePathFor("source", item.sourceId).replace(/\.md$/, "")}|${item.title}]]`),
11023
+ "",
11024
+ "## Source Claims",
11025
+ "",
11026
+ ...sourceAnalyses.flatMap(
11027
+ (item) => item.claims.filter((claim) => claim.text.toLowerCase().includes(name.toLowerCase())).map((claim) => `- ${claim.text} [source:${claim.citation}]`)
11028
+ ),
11029
+ "",
11030
+ ...relatedOutputsSection(relatedOutputs),
11031
+ ""
11032
+ ].join("\n"),
11033
+ existingContent
11034
+ );
10992
11035
  return {
10993
11036
  page: {
10994
11037
  id: pageId,
@@ -11744,7 +11787,8 @@ function buildOutputPage(input) {
11744
11787
  question: input.question,
11745
11788
  output_format: input.outputFormat,
11746
11789
  output_assets: outputAssets,
11747
- ...input.outputFormat === "slides" ? { marp: true } : {}
11790
+ ...input.outputFormat === "slides" ? { marp: true } : {},
11791
+ ...input.frontmatter ?? {}
11748
11792
  };
11749
11793
  return {
11750
11794
  page: {
@@ -11832,7 +11876,7 @@ function buildOutputPage(input) {
11832
11876
  ...input.citations.map((citation) => `- [source:${citation}]`),
11833
11877
  ""
11834
11878
  ]).join("\n"),
11835
- frontmatter
11879
+ safeFrontmatter(frontmatter)
11836
11880
  )
11837
11881
  };
11838
11882
  }
@@ -12937,7 +12981,7 @@ async function resolveImageGenerationProvider(rootDir) {
12937
12981
  if (!providerConfig) {
12938
12982
  throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
12939
12983
  }
12940
- const { createProvider: createProvider2 } = await import("./registry-XOPLQNZY.js");
12984
+ const { createProvider: createProvider2 } = await import("./registry-TYROWPR5.js");
12941
12985
  return createProvider2(preferredProviderId, providerConfig, rootDir);
12942
12986
  }
12943
12987
  async function generateOutputArtifacts(rootDir, input) {
@@ -13342,6 +13386,7 @@ function pageHashes(pages) {
13342
13386
  }
13343
13387
  async function buildManagedGraphPage(absolutePath, defaults, build) {
13344
13388
  const existingContent = await fileExists(absolutePath) ? await fs19.readFile(absolutePath, "utf8") : null;
13389
+ let carriedContent = existingContent;
13345
13390
  let existing = await loadExistingManagedPageState(absolutePath, {
13346
13391
  status: defaults.status ?? "active",
13347
13392
  managedBy: defaults.managedBy
@@ -13356,6 +13401,7 @@ async function buildManagedGraphPage(absolutePath, defaults, build) {
13356
13401
  status: defaults.status ?? "active",
13357
13402
  managedBy: defaults.managedBy
13358
13403
  });
13404
+ carriedContent = await fs19.readFile(candidatePath, "utf8");
13359
13405
  usedFallbackState = true;
13360
13406
  break;
13361
13407
  }
@@ -13368,13 +13414,13 @@ async function buildManagedGraphPage(absolutePath, defaults, build) {
13368
13414
  managedBy: defaults.managedBy,
13369
13415
  confidence: defaults.confidence
13370
13416
  };
13371
- let built = build(metadata);
13372
- if (existingContent && existingContent !== built.content) {
13417
+ let built = build(metadata, carriedContent);
13418
+ if (carriedContent && carriedContent !== built.content) {
13373
13419
  metadata = {
13374
13420
  ...metadata,
13375
13421
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
13376
13422
  };
13377
- built = build(metadata);
13423
+ built = build(metadata, carriedContent);
13378
13424
  }
13379
13425
  return built;
13380
13426
  }
@@ -13425,7 +13471,10 @@ async function loadAnalysesBySourceIds(paths, sourceIds) {
13425
13471
  );
13426
13472
  return analyses.filter((analysis) => Boolean(analysis?.sourceId));
13427
13473
  }
13428
- async function buildDashboardRecords(paths, graph, schemaHash, report) {
13474
+ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
13475
+ const dataviewEnabled = config.profile.dataviewBlocks;
13476
+ const profilePresets = config.profile.presets;
13477
+ const dashboardPack = config.profile.dashboardPack;
13429
13478
  const sourcePages = graph.pages.filter((page) => page.kind === "source");
13430
13479
  const reviewPages = graph.pages.filter((page) => page.kind === "output" && page.path.startsWith("outputs/source-reviews/"));
13431
13480
  const briefPages = graph.pages.filter((page) => page.kind === "output" && page.path.startsWith("outputs/source-briefs/"));
@@ -13445,6 +13494,8 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13445
13494
  const stagedGuideBundles = (await Promise.all(
13446
13495
  (await fs19.readdir(paths.approvalsDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map(async (entry) => await readJsonFile(approvalManifestPath(paths, entry.name)))
13447
13496
  )).filter((manifest) => Boolean(manifest)).filter((manifest) => manifest.bundleType === "guided_source" || manifest.bundleType === "guided_session").sort((left, right) => right.createdAt.localeCompare(left.createdAt)).slice(0, 12);
13497
+ const readerFocusPages = uniqueBy([...guidePages, ...briefPages, ...conceptPages, ...entityPages], (page) => page.id).slice(0, 8);
13498
+ const diligenceSessions = sourceSessions.filter((session) => session.status === "staged" || session.status === "awaiting_input").slice(0, 8);
13448
13499
  const dashboards = [
13449
13500
  {
13450
13501
  relativePath: "dashboards/index.md",
@@ -13462,12 +13513,17 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13462
13513
  "- [[dashboards/contradictions|Contradictions]]",
13463
13514
  "- [[dashboards/open-questions|Open Questions]]",
13464
13515
  "",
13465
- "```dataview",
13466
- "TABLE file.mtime AS updated",
13467
- 'FROM "dashboards"',
13468
- 'WHERE file.name != "index"',
13469
- "SORT file.mtime desc",
13470
- "```",
13516
+ `Profile Presets: ${profilePresets.length ? profilePresets.map((preset) => `\`${preset}\``).join(", ") : "_default_"}`,
13517
+ `Dashboard Pack: \`${dashboardPack}\``,
13518
+ ...dataviewEnabled ? [
13519
+ "",
13520
+ "```dataview",
13521
+ "TABLE file.mtime AS updated",
13522
+ 'FROM "dashboards"',
13523
+ 'WHERE file.name != "index"',
13524
+ "SORT file.mtime desc",
13525
+ "```"
13526
+ ] : [],
13471
13527
  ""
13472
13528
  ].join("\n"),
13473
13529
  {
@@ -13488,7 +13544,8 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13488
13544
  backlinks: [],
13489
13545
  schema_hash: schemaHash,
13490
13546
  source_hashes: {},
13491
- source_semantic_hashes: {}
13547
+ source_semantic_hashes: {},
13548
+ profile_presets: profilePresets
13492
13549
  }
13493
13550
  )
13494
13551
  },
@@ -13500,13 +13557,16 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13500
13557
  "# Recent Sources",
13501
13558
  "",
13502
13559
  ...recentSourcePages.length ? recentSourcePages.map((page) => `- ${page.updatedAt}: [[${page.path.replace(/\.md$/, "")}|${page.title}]]`) : ["- No source pages yet."],
13503
- "",
13504
- "```dataview",
13505
- "TABLE source_type, occurred_at, participants",
13506
- 'FROM "sources"',
13507
- "SORT updated_at desc",
13508
- "LIMIT 25",
13509
- "```",
13560
+ ...dashboardPack === "reader" && readerFocusPages.length ? ["", "## Reader Focus", "", ...readerFocusPages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`)] : [],
13561
+ ...dataviewEnabled ? [
13562
+ "",
13563
+ "```dataview",
13564
+ "TABLE source_type, occurred_at, participants",
13565
+ 'FROM "sources"',
13566
+ "SORT updated_at desc",
13567
+ "LIMIT 25",
13568
+ "```"
13569
+ ] : [],
13510
13570
  ""
13511
13571
  ].join("\n"),
13512
13572
  {
@@ -13527,7 +13587,8 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13527
13587
  backlinks: [],
13528
13588
  schema_hash: schemaHash,
13529
13589
  source_hashes: {},
13530
- source_semantic_hashes: {}
13590
+ source_semantic_hashes: {},
13591
+ profile_presets: profilePresets
13531
13592
  }
13532
13593
  )
13533
13594
  },
@@ -13551,13 +13612,21 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13551
13612
  (session) => `- ${session.updatedAt}: \`${session.status}\` [[outputs/source-sessions/${session.scopeId}|${session.scopeTitle}]]`
13552
13613
  )
13553
13614
  ] : [],
13554
- "",
13555
- "```dataview",
13556
- "TABLE occurred_at, source_type, participants, container_title",
13557
- 'FROM "sources"',
13558
- "SORT occurred_at desc",
13559
- "LIMIT 25",
13560
- "```",
13615
+ ...dashboardPack === "reader" && conceptPages.length ? [
13616
+ "",
13617
+ "## Thesis And Hub Pages",
13618
+ "",
13619
+ ...conceptPages.slice(0, 6).map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`)
13620
+ ] : [],
13621
+ ...dataviewEnabled ? [
13622
+ "",
13623
+ "```dataview",
13624
+ "TABLE occurred_at, source_type, participants, container_title",
13625
+ 'FROM "sources"',
13626
+ "SORT occurred_at desc",
13627
+ "LIMIT 25",
13628
+ "```"
13629
+ ] : [],
13561
13630
  ""
13562
13631
  ].join("\n"),
13563
13632
  {
@@ -13578,7 +13647,8 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13578
13647
  backlinks: [],
13579
13648
  schema_hash: schemaHash,
13580
13649
  source_hashes: {},
13581
- source_semantic_hashes: {}
13650
+ source_semantic_hashes: {},
13651
+ profile_presets: profilePresets
13582
13652
  }
13583
13653
  )
13584
13654
  },
@@ -13594,13 +13664,15 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13594
13664
  const sourcePage = sourcePages.find((page) => page.sourceIds.includes(manifest.sourceId));
13595
13665
  return `- ${occurredAt}: ${sourcePage ? `[[${sourcePage.path.replace(/\.md$/, "")}|${sourcePage.title}]]` : manifest.title}`;
13596
13666
  }) : ["- No timeline-aware sources yet."],
13597
- "",
13598
- "```dataview",
13599
- "TABLE occurred_at, participants, container_title",
13600
- 'FROM "sources"',
13601
- "WHERE occurred_at",
13602
- "SORT occurred_at desc",
13603
- "```",
13667
+ ...dataviewEnabled ? [
13668
+ "",
13669
+ "```dataview",
13670
+ "TABLE occurred_at, participants, container_title",
13671
+ 'FROM "sources"',
13672
+ "WHERE occurred_at",
13673
+ "SORT occurred_at desc",
13674
+ "```"
13675
+ ] : [],
13604
13676
  ""
13605
13677
  ].join("\n"),
13606
13678
  {
@@ -13621,7 +13693,8 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13621
13693
  backlinks: [],
13622
13694
  schema_hash: schemaHash,
13623
13695
  source_hashes: {},
13624
- source_semantic_hashes: {}
13696
+ source_semantic_hashes: {},
13697
+ profile_presets: profilePresets
13625
13698
  }
13626
13699
  )
13627
13700
  },
@@ -13643,11 +13716,14 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13643
13716
  ...stagedGuideBundles.length ? stagedGuideBundles.map(
13644
13717
  (bundle) => `- ${bundle.createdAt}: \`${bundle.approvalId}\`${bundle.title ? ` ${bundle.title}` : ""} (${bundle.entries.length} staged entr${bundle.entries.length === 1 ? "y" : "ies"})`
13645
13718
  ) : ["- No staged guided bundles right now."],
13646
- "",
13647
- "```dataview",
13648
- 'LIST FROM "outputs/source-sessions"',
13649
- "SORT file.mtime desc",
13650
- "```",
13719
+ ...dataviewEnabled ? [
13720
+ "",
13721
+ "```dataview",
13722
+ "TABLE session_status, evidence_state, canonical_targets",
13723
+ 'FROM "outputs/source-sessions"',
13724
+ "SORT file.mtime desc",
13725
+ "```"
13726
+ ] : [],
13651
13727
  ""
13652
13728
  ].join("\n"),
13653
13729
  {
@@ -13674,7 +13750,8 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13674
13750
  backlinks: [],
13675
13751
  schema_hash: schemaHash,
13676
13752
  source_hashes: {},
13677
- source_semantic_hashes: {}
13753
+ source_semantic_hashes: {},
13754
+ profile_presets: profilePresets
13678
13755
  }
13679
13756
  )
13680
13757
  },
@@ -13692,11 +13769,14 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13692
13769
  ...stagedGuideBundles.length ? stagedGuideBundles.map(
13693
13770
  (bundle) => `- ${bundle.createdAt}: \`${bundle.approvalId}\`${bundle.title ? ` ${bundle.title}` : ""} (${bundle.entries.length} staged entr${bundle.entries.length === 1 ? "y" : "ies"})`
13694
13771
  ) : ["- No staged guided bundles right now."],
13695
- "",
13696
- "```dataview",
13697
- 'LIST FROM "outputs/source-guides"',
13698
- "SORT file.mtime desc",
13699
- "```",
13772
+ ...dataviewEnabled ? [
13773
+ "",
13774
+ "```dataview",
13775
+ "TABLE evidence_state, canonical_targets, file.mtime AS updated",
13776
+ 'FROM "outputs/source-guides"',
13777
+ "SORT file.mtime desc",
13778
+ "```"
13779
+ ] : [],
13700
13780
  ""
13701
13781
  ].join("\n"),
13702
13782
  {
@@ -13723,7 +13803,8 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13723
13803
  backlinks: [],
13724
13804
  schema_hash: schemaHash,
13725
13805
  source_hashes: {},
13726
- source_semantic_hashes: {}
13806
+ source_semantic_hashes: {},
13807
+ profile_presets: profilePresets
13727
13808
  }
13728
13809
  )
13729
13810
  },
@@ -13750,12 +13831,14 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13750
13831
  "",
13751
13832
  ...sourceSessions.length ? sourceSessions.slice(0, 8).map((session) => `- \`${session.status}\` [[outputs/source-sessions/${session.scopeId}|${session.scopeTitle}]]`) : ["- No active source sessions yet."],
13752
13833
  ...report?.suggestedQuestions?.length ? ["", "## Suggested Questions", "", ...report.suggestedQuestions.slice(0, 8).map((question) => `- ${question}`)] : [],
13753
- "",
13754
- "```dataview",
13755
- 'TABLE file.folder, file.mtime FROM "concepts" OR "entities"',
13756
- "SORT file.mtime desc",
13757
- "LIMIT 30",
13758
- "```",
13834
+ ...dataviewEnabled ? [
13835
+ "",
13836
+ "```dataview",
13837
+ 'TABLE file.folder, file.mtime FROM "concepts" OR "entities"',
13838
+ "SORT file.mtime desc",
13839
+ "LIMIT 30",
13840
+ "```"
13841
+ ] : [],
13759
13842
  ""
13760
13843
  ].join("\n"),
13761
13844
  {
@@ -13784,7 +13867,8 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13784
13867
  backlinks: [],
13785
13868
  schema_hash: schemaHash,
13786
13869
  source_hashes: {},
13787
- source_semantic_hashes: {}
13870
+ source_semantic_hashes: {},
13871
+ profile_presets: profilePresets
13788
13872
  }
13789
13873
  )
13790
13874
  },
@@ -13807,10 +13891,21 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13807
13891
  ...[...guidePages, ...reviewPages, ...briefPages].slice(0, 12).map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`),
13808
13892
  ""
13809
13893
  ] : [],
13810
- "```dataview",
13811
- 'LIST FROM "outputs/source-reviews" OR "outputs/source-guides"',
13812
- "SORT file.mtime desc",
13813
- "```",
13894
+ ...dashboardPack === "diligence" && diligenceSessions.length ? [
13895
+ "## Active Evidence Review Sessions",
13896
+ "",
13897
+ ...diligenceSessions.map(
13898
+ (session) => `- \`${session.status}\` [[outputs/source-sessions/${session.scopeId}|${session.scopeTitle}]]`
13899
+ ),
13900
+ ""
13901
+ ] : [],
13902
+ ...dataviewEnabled ? [
13903
+ "```dataview",
13904
+ 'TABLE evidence_state, session_status, canonical_targets FROM "outputs/source-reviews" OR "outputs/source-guides" OR "outputs/source-sessions"',
13905
+ 'WHERE evidence_state = "conflicting"',
13906
+ "SORT file.mtime desc",
13907
+ "```"
13908
+ ] : [],
13814
13909
  ""
13815
13910
  ].join("\n"),
13816
13911
  {
@@ -13831,7 +13926,8 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13831
13926
  backlinks: [],
13832
13927
  schema_hash: schemaHash,
13833
13928
  source_hashes: {},
13834
- source_semantic_hashes: {}
13929
+ source_semantic_hashes: {},
13930
+ profile_presets: profilePresets
13835
13931
  }
13836
13932
  )
13837
13933
  },
@@ -13849,11 +13945,13 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13849
13945
  "",
13850
13946
  ...sourceSessions.filter((session) => session.status === "awaiting_input" || session.status === "staged").slice(0, 8).map((session) => `- \`${session.status}\` [[outputs/source-sessions/${session.scopeId}|${session.scopeTitle}]]`)
13851
13947
  ] : [],
13852
- "",
13853
- "```dataview",
13854
- 'LIST FROM "outputs/source-briefs" OR "outputs/source-reviews" OR "outputs/source-guides" OR "outputs/source-sessions"',
13855
- "SORT file.mtime desc",
13856
- "```",
13948
+ ...dataviewEnabled ? [
13949
+ "",
13950
+ "```dataview",
13951
+ 'TABLE question_state, session_status, evidence_state FROM "outputs/source-briefs" OR "outputs/source-reviews" OR "outputs/source-guides" OR "outputs/source-sessions"',
13952
+ "SORT file.mtime desc",
13953
+ "```"
13954
+ ] : [],
13857
13955
  ""
13858
13956
  ].join("\n"),
13859
13957
  {
@@ -13874,7 +13972,8 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13874
13972
  backlinks: [],
13875
13973
  schema_hash: schemaHash,
13876
13974
  source_hashes: {},
13877
- source_semantic_hashes: {}
13975
+ source_semantic_hashes: {},
13976
+ profile_presets: profilePresets
13878
13977
  }
13879
13978
  )
13880
13979
  }
@@ -14766,7 +14865,7 @@ async function syncVaultArtifacts(rootDir, input) {
14766
14865
  confidence: 1,
14767
14866
  compiledFrom: [manifest.sourceId]
14768
14867
  },
14769
- (metadata) => buildSourcePage(
14868
+ (metadata, existingContent) => buildSourcePage(
14770
14869
  manifest,
14771
14870
  analysis,
14772
14871
  sourceSchemaHash,
@@ -14777,7 +14876,8 @@ async function syncVaultArtifacts(rootDir, input) {
14777
14876
  projectIds: sourceProjectIds,
14778
14877
  extraTags: [...sourceCategoryTags, ...analysis.tags ?? []],
14779
14878
  sourceClass: manifest.sourceClass
14780
- }
14879
+ },
14880
+ existingContent
14781
14881
  )
14782
14882
  );
14783
14883
  records.push(sourceRecord);
@@ -14867,7 +14967,7 @@ async function syncVaultArtifacts(rootDir, input) {
14867
14967
  compiledFrom: sourceIds,
14868
14968
  statePathCandidates: fallbackPaths
14869
14969
  },
14870
- (metadata) => buildAggregatePage(
14970
+ (metadata, existingContent) => buildAggregatePage(
14871
14971
  itemKind,
14872
14972
  aggregate.name,
14873
14973
  aggregate.descriptions,
@@ -14886,7 +14986,8 @@ async function syncVaultArtifacts(rootDir, input) {
14886
14986
  ...aggregate.sourceAnalyses.map((item) => item.summary)
14887
14987
  ]),
14888
14988
  sourceClass: aggregateSourceClass2
14889
- }
14989
+ },
14990
+ existingContent
14890
14991
  )
14891
14992
  );
14892
14993
  if (promoted && previousEntry?.status === "candidate") {
@@ -14940,6 +15041,7 @@ async function syncVaultArtifacts(rootDir, input) {
14940
15041
  );
14941
15042
  const preliminaryPages = [...basePages, ...graphOrientation.records.map((record) => record.page)];
14942
15043
  const dashboardRecords = await buildDashboardRecords(
15044
+ config,
14943
15045
  paths,
14944
15046
  {
14945
15047
  ...baseGraph,
@@ -15179,6 +15281,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
15179
15281
  compileState?.generatedAt
15180
15282
  ) : { records: [], report: null };
15181
15283
  const dashboardRecords = currentGraph ? await buildDashboardRecords(
15284
+ config,
15182
15285
  paths,
15183
15286
  {
15184
15287
  ...currentGraph,
@@ -16051,21 +16154,27 @@ async function ensureObsidianWorkspace(rootDir) {
16051
16154
  ]);
16052
16155
  }
16053
16156
  async function initVault(rootDir, options = {}) {
16054
- const profile = options.profile ?? "default";
16055
- const { paths } = await initWorkspace(rootDir, { profile });
16157
+ const requestedProfile = options.profile ?? "default";
16158
+ const { config, paths } = await initWorkspace(rootDir, { profile: requestedProfile });
16159
+ const profile = config.profile;
16160
+ const isResearchProfile = profile.presets.length > 0 || profile.guidedSessionMode === "canonical_review" || profile.dataviewBlocks;
16056
16161
  await installConfiguredAgents(rootDir);
16057
16162
  const insightsIndexPath = path23.join(paths.wikiDir, "insights", "index.md");
16058
16163
  const now = (/* @__PURE__ */ new Date()).toISOString();
16059
16164
  await writeFileIfChanged(
16060
16165
  insightsIndexPath,
16061
16166
  matter9.stringify(
16062
- (profile === "personal-research" ? [
16167
+ (isResearchProfile ? [
16063
16168
  "# Insights",
16064
16169
  "",
16065
16170
  "Human-authored research notes live here.",
16066
16171
  "",
16067
16172
  "- Use this folder for thesis notes, reading reflections, synthesis drafts, and decisions you want to keep explicitly human-authored.",
16068
- "- Guided sessions can stage `wiki/insights/` updates through the approval queue, but SwarmVault never applies them without review.",
16173
+ ...profile.guidedSessionMode === "canonical_review" ? [
16174
+ "- Guided sessions can stage approval-queued updates for canonical pages and fall back to `wiki/insights/` when a claim still needs judgment."
16175
+ ] : [
16176
+ "- Guided sessions fall back to `wiki/insights/` for exploratory synthesis until you decide what should become canonical."
16177
+ ],
16069
16178
  "- Treat these pages as the human judgment layer for your vault.",
16070
16179
  ""
16071
16180
  ] : [
@@ -16148,16 +16257,21 @@ async function initVault(rootDir, options = {}) {
16148
16257
  if (options.obsidian) {
16149
16258
  await ensureObsidianWorkspace(rootDir);
16150
16259
  }
16151
- if (profile === "personal-research") {
16260
+ if (isResearchProfile) {
16152
16261
  await writeFileIfChanged(
16153
16262
  path23.join(paths.wikiDir, "insights", "research-playbook.md"),
16154
16263
  matter9.stringify(
16155
16264
  [
16156
- "# Personal Research Playbook",
16265
+ `# ${requestedProfile === "personal-research" ? "Personal Research Playbook" : "Research Playbook"}`,
16157
16266
  "",
16158
16267
  "- Add one source at a time with `swarmvault ingest <input> --guide` or `swarmvault source add <input> --guide`.",
16159
16268
  "- Resume a guided session with `swarmvault source session <source-id-or-session-id>` whenever you want to answer the session prompts directly.",
16160
16269
  "- Review `wiki/outputs/source-briefs/`, `wiki/outputs/source-reviews/`, `wiki/outputs/source-guides/`, and `wiki/outputs/source-sessions/` before accepting staged updates.",
16270
+ ...profile.guidedSessionMode === "canonical_review" ? ["- Use `swarmvault review show --diff` to inspect staged canonical page edits before accepting them."] : ["- Keep exploratory synthesis in `wiki/insights/` until you are ready to promote it into canonical pages."],
16271
+ ...profile.dataviewBlocks ? [
16272
+ "- Dataview-friendly fields are enabled in the dashboards, but every generated page should still read cleanly as plain markdown."
16273
+ ] : [],
16274
+ ...profile.presets.length ? [`- Active profile presets: ${profile.presets.map((preset) => `\`${preset}\``).join(", ")}.`] : [],
16161
16275
  "- Keep unresolved questions visible in `wiki/dashboards/open-questions.md`.",
16162
16276
  "- Use `swarmvault review list` and `swarmvault review show --diff` to decide what becomes canonical.",
16163
16277
  ""
@@ -16165,7 +16279,7 @@ async function initVault(rootDir, options = {}) {
16165
16279
  {
16166
16280
  page_id: "insights:research-playbook",
16167
16281
  kind: "insight",
16168
- title: "Personal Research Playbook",
16282
+ title: requestedProfile === "personal-research" ? "Personal Research Playbook" : "Research Playbook",
16169
16283
  tags: ["insight", "research", "playbook"],
16170
16284
  source_ids: [],
16171
16285
  project_ids: [],
@@ -17098,7 +17212,7 @@ async function bootstrapDemo(rootDir, input) {
17098
17212
  }
17099
17213
 
17100
17214
  // src/mcp.ts
17101
- var SERVER_VERSION = "0.6.0";
17215
+ var SERVER_VERSION = "0.6.1";
17102
17216
  async function createMcpServer(rootDir) {
17103
17217
  const server = new McpServer({
17104
17218
  name: "swarmvault",
@@ -18417,6 +18531,9 @@ var GUIDED_SESSION_QUESTIONS = [
18417
18531
  function defaultGuidedSessionQuestions() {
18418
18532
  return GUIDED_SESSION_QUESTIONS.map((question) => ({ ...question }));
18419
18533
  }
18534
+ function splitDelimitedDetail(value) {
18535
+ return value ? value.split(",").map((item) => item.trim()).filter(Boolean) : [];
18536
+ }
18420
18537
  function normalizeGuidedAnswerValue(value) {
18421
18538
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
18422
18539
  }
@@ -18445,6 +18562,40 @@ function mergeGuidedSessionQuestions(questions, answers) {
18445
18562
  function answeredGuidedSessionQuestions(questions) {
18446
18563
  return questions.filter((question) => typeof question.answer === "string" && question.answer.trim().length > 0);
18447
18564
  }
18565
+ function questionStateForSession(session) {
18566
+ return answeredGuidedSessionQuestions(session.questions).length === session.questions.length ? "answered" : "awaiting_input";
18567
+ }
18568
+ function manifestsForScope(graph, scope) {
18569
+ if (!graph) {
18570
+ return [];
18571
+ }
18572
+ const scopeSet = new Set(scope.sourceIds);
18573
+ return graph.sources.filter((manifest) => scopeSet.has(manifest.sourceId));
18574
+ }
18575
+ function scopeSourceType(scope, manifests) {
18576
+ return scope.kind ?? manifests[0]?.sourceKind ?? manifests[0]?.sourceType;
18577
+ }
18578
+ function scopeOccurredAt(manifests) {
18579
+ return manifests.map((manifest) => manifest.details?.occurred_at).filter((value) => typeof value === "string" && value.trim().length > 0).sort((left, right) => right.localeCompare(left))[0];
18580
+ }
18581
+ function scopeParticipants(manifests) {
18582
+ return uniqueStrings4(manifests.flatMap((manifest) => splitDelimitedDetail(manifest.details?.participants)));
18583
+ }
18584
+ function scopeContainerTitle(manifests) {
18585
+ return manifests.find((manifest) => manifest.details?.container_title)?.details?.container_title ?? manifests[0]?.sourceGroupTitle;
18586
+ }
18587
+ function scopeConversationId(manifests) {
18588
+ return manifests.find((manifest) => manifest.details?.conversation_id)?.details?.conversation_id;
18589
+ }
18590
+ function classifyGuidedEvidenceState(scope, targetPage, contradictions) {
18591
+ if (contradictions.length) {
18592
+ return "conflicting";
18593
+ }
18594
+ if (!targetPage) {
18595
+ return "needs_judgment";
18596
+ }
18597
+ return targetPage.sourceIds.some((sourceId) => !scope.sourceIds.includes(sourceId)) ? "reinforcing" : "new";
18598
+ }
18448
18599
  function renderDeterministicSourceReview(input) {
18449
18600
  const canonicalPages = input.sourcePages.filter((page) => page.kind === "source" || page.kind === "concept" || page.kind === "entity").slice(0, 10);
18450
18601
  const modulePages = input.sourcePages.filter((page) => page.kind === "module").slice(0, 8);
@@ -18545,12 +18696,13 @@ Entities: ${analysis.entities.map((entity) => entity.name).join(", ") || "none"}
18545
18696
  }
18546
18697
  }
18547
18698
  async function buildSourceReviewStagedPage(rootDir, scope) {
18548
- const { paths } = await loadVaultConfig(rootDir);
18699
+ const { config, paths } = await loadVaultConfig(rootDir);
18549
18700
  const markdown = await generateSourceReviewMarkdown(rootDir, scope);
18550
18701
  if (!markdown) {
18551
18702
  throw new Error(`Could not generate a source review for ${scope.id}.`);
18552
18703
  }
18553
18704
  const graph = await readJsonFile(paths.graphPath);
18705
+ const scopeManifests = manifestsForScope(graph, scope);
18554
18706
  const relatedPages = graph ? scopedSourcePages(graph, scope.sourceIds) : [];
18555
18707
  const relatedPageIds = relatedPages.slice(0, 16).map((page) => page.id);
18556
18708
  const relatedNodeIds = graph ? scopedNodeIds(graph, scope.sourceIds).slice(0, 24) : [];
@@ -18577,13 +18729,26 @@ async function buildSourceReviewStagedPage(rootDir, scope) {
18577
18729
  compiledFrom: scope.sourceIds,
18578
18730
  managedBy: "system",
18579
18731
  confidence: 0.79
18732
+ },
18733
+ frontmatter: {
18734
+ profile_presets: config.profile.presets,
18735
+ source_type: scopeSourceType(scope, scopeManifests),
18736
+ occurred_at: scopeOccurredAt(scopeManifests),
18737
+ participants: scopeParticipants(scopeManifests),
18738
+ container_title: scopeContainerTitle(scopeManifests),
18739
+ conversation_id: scopeConversationId(scopeManifests),
18740
+ question_state: "answered",
18741
+ canonical_targets: relatedPages.filter((page) => page.kind === "source" || page.kind === "concept" || page.kind === "entity").slice(0, 8).map((page) => page.path),
18742
+ evidence_state: findContradictionsForScope(scope, await readGraphReport(rootDir)).length ? "conflicting" : "needs_judgment"
18580
18743
  }
18581
18744
  });
18582
18745
  return { page: output.page, content: output.content };
18583
18746
  }
18584
18747
  function classifySourceGuidePageBuckets(sourcePages, scopeSourceIds) {
18585
18748
  const scopeSet = new Set(scopeSourceIds);
18586
- const canonicalPages = sourcePages.filter((page) => page.kind === "source" || page.kind === "concept" || page.kind === "entity").slice(0, 12);
18749
+ const canonicalPages = sourcePages.filter(
18750
+ (page) => (page.kind === "source" || page.kind === "concept" || page.kind === "entity") && (page.kind === "source" || page.status !== "candidate")
18751
+ ).slice(0, 12);
18587
18752
  const newPages = canonicalPages.filter((page) => page.sourceIds.every((sourceId) => scopeSet.has(sourceId))).slice(0, 6);
18588
18753
  const reinforcingPages = canonicalPages.filter((page) => page.sourceIds.some((sourceId) => !scopeSet.has(sourceId))).slice(0, 6);
18589
18754
  return { canonicalPages, newPages, reinforcingPages };
@@ -18776,13 +18941,16 @@ Entities: ${analysis.entities.map((entity) => entity.name).join(", ") || "none"}
18776
18941
  }
18777
18942
  }
18778
18943
  async function buildSourceGuideStagedPage(rootDir, scope) {
18779
- const { paths } = await loadVaultConfig(rootDir);
18944
+ const { config, paths } = await loadVaultConfig(rootDir);
18780
18945
  const markdown = await generateSourceGuideMarkdown(rootDir, scope);
18781
18946
  if (!markdown) {
18782
18947
  throw new Error(`Could not generate a source guide for ${scope.id}.`);
18783
18948
  }
18784
18949
  const graph = await readJsonFile(paths.graphPath);
18950
+ const scopeManifests = manifestsForScope(graph, scope);
18785
18951
  const relatedPages = graph ? scopedSourcePages(graph, scope.sourceIds) : [];
18952
+ const contradictions = findContradictionsForScope(scope, await readGraphReport(rootDir));
18953
+ const selectedTargets = selectGuidedTargetPages(scope, relatedPages, defaultGuidedSessionQuestions());
18786
18954
  const relatedPageIds = relatedPages.slice(0, 18).map((page) => page.id);
18787
18955
  const relatedNodeIds = graph ? scopedNodeIds(graph, scope.sourceIds).slice(0, 28) : [];
18788
18956
  const projectIds = uniqueStrings4(relatedPages.flatMap((page) => page.projectIds));
@@ -18808,6 +18976,17 @@ async function buildSourceGuideStagedPage(rootDir, scope) {
18808
18976
  compiledFrom: scope.sourceIds,
18809
18977
  managedBy: "system",
18810
18978
  confidence: 0.8
18979
+ },
18980
+ frontmatter: {
18981
+ profile_presets: config.profile.presets,
18982
+ source_type: scopeSourceType(scope, scopeManifests),
18983
+ occurred_at: scopeOccurredAt(scopeManifests),
18984
+ participants: scopeParticipants(scopeManifests),
18985
+ container_title: scopeContainerTitle(scopeManifests),
18986
+ conversation_id: scopeConversationId(scopeManifests),
18987
+ question_state: "answered",
18988
+ canonical_targets: selectedTargets.map((page) => page.path),
18989
+ evidence_state: contradictions.length ? "conflicting" : selectedTargets.some((page) => page.sourceIds.some((sourceId) => !scope.sourceIds.includes(sourceId))) ? "reinforcing" : selectedTargets.length ? "new" : "needs_judgment"
18811
18990
  }
18812
18991
  });
18813
18992
  return { page: output.page, content: output.content };
@@ -18864,12 +19043,13 @@ async function prepareGuidedSourceSession(rootDir, scope, answers) {
18864
19043
  return { session, statePath };
18865
19044
  }
18866
19045
  async function buildSourceSessionSavedPage(rootDir, scope, session) {
18867
- const { paths } = await loadVaultConfig(rootDir);
19046
+ const { config, paths } = await loadVaultConfig(rootDir);
18868
19047
  let graph = await readJsonFile(paths.graphPath);
18869
19048
  if (!graph) {
18870
19049
  await compileVault(rootDir, {});
18871
19050
  graph = await readJsonFile(paths.graphPath);
18872
19051
  }
19052
+ const scopeManifests = manifestsForScope(graph, scope);
18873
19053
  const sourcePages = graph ? scopedSourcePages(graph, scope.sourceIds) : [];
18874
19054
  const analyses = await loadSourceAnalyses(rootDir, scope.sourceIds);
18875
19055
  const report = await readGraphReport(rootDir);
@@ -18883,6 +19063,9 @@ async function buildSourceSessionSavedPage(rootDir, scope, session) {
18883
19063
  ]);
18884
19064
  const relatedNodeIds = graph ? scopedNodeIds(graph, scope.sourceIds).slice(0, 28) : [];
18885
19065
  const projectIds = uniqueStrings4(sourcePages.flatMap((page) => page.projectIds));
19066
+ const evidenceState = contradictions.length > 0 ? "conflicting" : session.targetedPagePaths.some(
19067
+ (targetPath) => sourcePages.some((page) => page.path === targetPath && page.sourceIds.some((sourceId) => !scope.sourceIds.includes(sourceId)))
19068
+ ) ? "reinforcing" : session.targetedPagePaths.length ? "new" : "needs_judgment";
18886
19069
  const relativeBriefPath = session.briefPath && path26.isAbsolute(session.briefPath) ? path26.relative(paths.wikiDir, session.briefPath) : session.briefPath;
18887
19070
  const sessionMarkdown = [
18888
19071
  `# Guided Session: ${scope.title}`,
@@ -18947,6 +19130,18 @@ async function buildSourceSessionSavedPage(rootDir, scope, session) {
18947
19130
  compiledFrom: scope.sourceIds,
18948
19131
  managedBy: "system",
18949
19132
  confidence: 0.81
19133
+ },
19134
+ frontmatter: {
19135
+ profile_presets: config.profile.presets,
19136
+ source_type: scopeSourceType(scope, scopeManifests),
19137
+ occurred_at: scopeOccurredAt(scopeManifests),
19138
+ participants: scopeParticipants(scopeManifests),
19139
+ container_title: scopeContainerTitle(scopeManifests),
19140
+ conversation_id: scopeConversationId(scopeManifests),
19141
+ session_status: session.status,
19142
+ question_state: questionStateForSession(session),
19143
+ canonical_targets: session.targetedPagePaths,
19144
+ evidence_state: evidenceState
18950
19145
  }
18951
19146
  });
18952
19147
  return { page: output.page, content: output.content };
@@ -18959,8 +19154,8 @@ async function persistSourceSessionPage(rootDir, scope, session) {
18959
19154
  await fs22.writeFile(absolutePath, output.content, "utf8");
18960
19155
  return { pageId: output.page.id, sessionPath: absolutePath };
18961
19156
  }
18962
- async function buildGuidedInsightUpdatePages(rootDir, scope, session) {
18963
- const { paths } = await loadVaultConfig(rootDir);
19157
+ async function buildGuidedUpdatePages(rootDir, scope, session) {
19158
+ const { config, paths } = await loadVaultConfig(rootDir);
18964
19159
  let graph = await readJsonFile(paths.graphPath);
18965
19160
  if (!graph) {
18966
19161
  await compileVault(rootDir, {});
@@ -18970,14 +19165,20 @@ async function buildGuidedInsightUpdatePages(rootDir, scope, session) {
18970
19165
  return [];
18971
19166
  }
18972
19167
  const sourcePages = scopedSourcePages(graph, scope.sourceIds);
19168
+ const scopeManifests = manifestsForScope(graph, scope);
18973
19169
  const analyses = await loadSourceAnalyses(rootDir, scope.sourceIds);
18974
19170
  const report = await readGraphReport(rootDir);
18975
19171
  const contradictions = findContradictionsForScope(scope, report);
18976
19172
  const selectedTargets = selectGuidedTargetPages(scope, sourcePages, session.questions);
18977
- const targetPages = selectedTargets.length ? selectedTargets : [null];
19173
+ const useCanonicalTargets = config.profile.guidedSessionMode === "canonical_review" && selectedTargets.length > 0;
19174
+ const targetPages = useCanonicalTargets ? selectedTargets : [selectedTargets[0] ?? null];
19175
+ session.targetedPagePaths = uniqueStrings4(
19176
+ useCanonicalTargets ? selectedTargets.map((page) => page.path) : selectedTargets.length ? selectedTargets.map((page) => page.path) : session.targetedPagePaths
19177
+ );
18978
19178
  return await Promise.all(
18979
- targetPages.map(async (targetPage, index) => {
18980
- const relativePath = targetPage ? insightRelativePathForTarget(targetPage, scope) : `insights/topics/${slugify(scope.title)}.md`;
19179
+ targetPages.map(async (targetPage) => {
19180
+ const evidenceState = classifyGuidedEvidenceState(scope, targetPage, contradictions);
19181
+ const relativePath = useCanonicalTargets && targetPage ? targetPage.path : targetPage ? insightRelativePathForTarget(targetPage, scope) : `insights/topics/${slugify(scope.title)}.md`;
18981
19182
  const absolutePath = path26.join(paths.wikiDir, relativePath);
18982
19183
  const existingContent = await fileExists(absolutePath) ? await fs22.readFile(absolutePath, "utf8") : "";
18983
19184
  const parsed = existingContent ? matter10(existingContent) : { data: {}, content: "" };
@@ -18987,8 +19188,13 @@ async function buildGuidedInsightUpdatePages(rootDir, scope, session) {
18987
19188
  const existingNodeIds = Array.isArray(existingData.node_ids) ? existingData.node_ids.filter((value) => typeof value === "string") : [];
18988
19189
  const existingBacklinks = Array.isArray(existingData.backlinks) ? existingData.backlinks.filter((value) => typeof value === "string") : [];
18989
19190
  const createdAt = typeof existingData.created_at === "string" && existingData.created_at.trim() ? existingData.created_at : (/* @__PURE__ */ new Date()).toISOString();
18990
- const title = typeof existingData.title === "string" && existingData.title.trim() || (targetPage ? insightTitleForTarget(targetPage, scope) : `${scope.title} Notes`);
18991
- const baseBody = parsed.content.trim() ? parsed.content.trim() : [`# ${title}`, "", "Human-curated insight page. Guided sessions stage replaceable update blocks here.", ""].join("\n");
19191
+ const title = typeof existingData.title === "string" && existingData.title.trim() || (useCanonicalTargets && targetPage ? targetPage.title : targetPage ? insightTitleForTarget(targetPage, scope) : `${scope.title} Notes`);
19192
+ const baseBody = parsed.content.trim() ? parsed.content.trim() : [
19193
+ `# ${title}`,
19194
+ "",
19195
+ useCanonicalTargets ? "Canonical page maintained by SwarmVault. Guided sessions stage replaceable update blocks here for approval." : "Human-curated insight page. Guided sessions stage replaceable update blocks here.",
19196
+ ""
19197
+ ].join("\n");
18992
19198
  const importance = questionAnswer(
18993
19199
  session.questions,
18994
19200
  "importance",
@@ -19008,6 +19214,7 @@ async function buildGuidedInsightUpdatePages(rootDir, scope, session) {
19008
19214
  const updateBlock = [
19009
19215
  `## Guided Session Update: ${scope.title}`,
19010
19216
  "",
19217
+ `Evidence State: \`${evidenceState}\``,
19011
19218
  `Session: [[outputs/source-sessions/${scope.id}|Guided Session]]`,
19012
19219
  `Source Guide: [[outputs/source-guides/${scope.id}|Source Guide]]`,
19013
19220
  "",
@@ -19035,48 +19242,60 @@ async function buildGuidedInsightUpdatePages(rootDir, scope, session) {
19035
19242
  ""
19036
19243
  ].join("\n");
19037
19244
  const nextBody = replaceMarkedSection(baseBody, scope.id, updateBlock);
19038
- const content = matter10.stringify(`${nextBody.trimEnd()}
19039
- `, {
19040
- ...existingData,
19041
- page_id: typeof existingData.page_id === "string" && existingData.page_id.trim() || `insight:${slugify(relativePath.replace(/\.md$/, ""))}`,
19042
- kind: "insight",
19043
- title,
19044
- tags: uniqueStrings4([
19045
- ...Array.isArray(existingData.tags) ? existingData.tags.filter((value) => typeof value === "string") : [],
19046
- ...insightTagsForTarget(targetPage)
19047
- ]),
19048
- source_ids: uniqueStrings4([...existingSourceIds, ...scope.sourceIds]),
19049
- project_ids: uniqueStrings4([...existingProjectIds, ...targetPage?.projectIds ?? []]),
19050
- node_ids: uniqueStrings4([...existingNodeIds, ...targetPage?.nodeIds ?? []]),
19051
- freshness: "fresh",
19052
- status: existingData.status === "archived" ? "archived" : "active",
19053
- confidence: 0.83,
19054
- created_at: createdAt,
19055
- updated_at: (/* @__PURE__ */ new Date()).toISOString(),
19056
- compiled_from: uniqueStrings4([
19057
- ...Array.isArray(existingData.compiled_from) ? existingData.compiled_from.filter((value) => typeof value === "string") : [],
19058
- ...scope.sourceIds
19059
- ]),
19060
- managed_by: "human",
19061
- backlinks: uniqueStrings4([
19062
- ...existingBacklinks,
19063
- ...targetPage ? [targetPage.id] : [],
19064
- `output:source-sessions/${scope.id}`,
19065
- `output:source-guides/${scope.id}`
19066
- ]),
19067
- schema_hash: typeof existingData.schema_hash === "string" ? existingData.schema_hash : "",
19068
- source_hashes: existingData.source_hashes && typeof existingData.source_hashes === "object" ? existingData.source_hashes : {},
19069
- source_semantic_hashes: existingData.source_semantic_hashes && typeof existingData.source_semantic_hashes === "object" ? existingData.source_semantic_hashes : {}
19070
- });
19245
+ const content = matter10.stringify(
19246
+ `${nextBody.trimEnd()}
19247
+ `,
19248
+ JSON.parse(
19249
+ JSON.stringify({
19250
+ ...existingData,
19251
+ page_id: typeof existingData.page_id === "string" && existingData.page_id.trim() || (useCanonicalTargets && targetPage ? targetPage.id : `insight:${slugify(relativePath.replace(/\.md$/, ""))}`),
19252
+ kind: useCanonicalTargets && targetPage ? targetPage.kind : "insight",
19253
+ title,
19254
+ tags: uniqueStrings4([
19255
+ ...Array.isArray(existingData.tags) ? existingData.tags.filter((value) => typeof value === "string") : [],
19256
+ ...useCanonicalTargets ? ["guided-session", `guided/${targetPage?.kind ?? "page"}`] : insightTagsForTarget(targetPage)
19257
+ ]),
19258
+ source_ids: uniqueStrings4([...existingSourceIds, ...scope.sourceIds]),
19259
+ project_ids: uniqueStrings4([...existingProjectIds, ...targetPage?.projectIds ?? []]),
19260
+ node_ids: uniqueStrings4([...existingNodeIds, ...targetPage?.nodeIds ?? []]),
19261
+ freshness: "fresh",
19262
+ status: existingData.status === "archived" ? "archived" : "active",
19263
+ confidence: 0.83,
19264
+ created_at: createdAt,
19265
+ updated_at: (/* @__PURE__ */ new Date()).toISOString(),
19266
+ compiled_from: uniqueStrings4([
19267
+ ...Array.isArray(existingData.compiled_from) ? existingData.compiled_from.filter((value) => typeof value === "string") : [],
19268
+ ...scope.sourceIds
19269
+ ]),
19270
+ managed_by: typeof existingData.managed_by === "string" && (existingData.managed_by === "human" || existingData.managed_by === "system") ? existingData.managed_by : useCanonicalTargets ? "system" : "human",
19271
+ backlinks: uniqueStrings4([
19272
+ ...existingBacklinks,
19273
+ ...targetPage ? [targetPage.id] : [],
19274
+ `output:source-sessions/${scope.id}`,
19275
+ `output:source-guides/${scope.id}`
19276
+ ]),
19277
+ schema_hash: typeof existingData.schema_hash === "string" ? existingData.schema_hash : "",
19278
+ source_hashes: existingData.source_hashes && typeof existingData.source_hashes === "object" ? existingData.source_hashes : {},
19279
+ source_semantic_hashes: existingData.source_semantic_hashes && typeof existingData.source_semantic_hashes === "object" ? existingData.source_semantic_hashes : {},
19280
+ profile_presets: config.profile.presets,
19281
+ source_type: scopeSourceType(scope, scopeManifests),
19282
+ occurred_at: scopeOccurredAt(scopeManifests),
19283
+ participants: scopeParticipants(scopeManifests),
19284
+ container_title: scopeContainerTitle(scopeManifests),
19285
+ conversation_id: scopeConversationId(scopeManifests),
19286
+ session_status: session.status,
19287
+ question_state: questionStateForSession(session),
19288
+ canonical_targets: useCanonicalTargets ? selectedTargets.map((page2) => page2.path) : [],
19289
+ evidence_state: evidenceState
19290
+ })
19291
+ )
19292
+ );
19071
19293
  const page = parseStoredPage(relativePath, content, {
19072
19294
  createdAt,
19073
19295
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
19074
19296
  });
19075
- if (index === 0) {
19076
- session.targetedPagePaths = uniqueStrings4([
19077
- ...session.targetedPagePaths,
19078
- ...selectedTargets.length ? selectedTargets.map((page2) => page2.path) : [relativePath]
19079
- ]);
19297
+ if (!useCanonicalTargets && !selectedTargets.length) {
19298
+ session.targetedPagePaths = uniqueStrings4([...session.targetedPagePaths, relativePath]);
19080
19299
  }
19081
19300
  return { page, content, label: "guided-update" };
19082
19301
  })
@@ -19116,7 +19335,7 @@ async function stageSourceGuideForScope(rootDir, scope, options = {}) {
19116
19335
  ...scope,
19117
19336
  briefPath
19118
19337
  });
19119
- const guidedUpdates = await buildGuidedInsightUpdatePages(rootDir, scope, session);
19338
+ const guidedUpdates = await buildGuidedUpdatePages(rootDir, scope, session);
19120
19339
  session.stagedUpdatePaths = guidedUpdates.map((item) => item.page.path);
19121
19340
  const approval = await stageGeneratedOutputPages(
19122
19341
  rootDir,