@swarmvaultai/engine 0.6.3 → 0.6.5

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/README.md CHANGED
@@ -292,7 +292,7 @@ Compile and repo-refresh runs also keep benchmark artifacts current by default,
292
292
  ## Notes
293
293
 
294
294
  - The engine expects Node `>=24`
295
- - The local search layer currently uses the built-in `node:sqlite` module, which may emit an experimental warning in Node 24
295
+ - The local search layer uses the built-in `node:sqlite` module on Node `>=24`; current CLI releases suppress the upstream experimental warning during normal runs
296
296
  - The viewer source lives in the companion `@swarmvaultai/viewer` package, and the built assets are bundled into the engine package for CLI installs
297
297
 
298
298
  ## Links
package/dist/index.d.ts CHANGED
@@ -46,7 +46,7 @@ type Freshness = "fresh" | "stale";
46
46
  type ClaimStatus = "extracted" | "inferred" | "conflicted" | "stale";
47
47
  type EvidenceClass = "extracted" | "inferred" | "ambiguous";
48
48
  type Polarity = "positive" | "negative" | "neutral";
49
- type OutputOrigin = "query" | "explore";
49
+ type OutputOrigin = "query" | "explore" | "source_brief" | "source_review" | "source_guide" | "source_session";
50
50
  type OutputFormat = "markdown" | "report" | "slides" | "chart" | "image";
51
51
  type OutputAssetRole = "primary" | "preview" | "manifest" | "poster";
52
52
  type GraphExportFormat = "html" | "svg" | "graphml" | "cypher";
@@ -359,6 +359,7 @@ interface ManagedSourceRecord {
359
359
  lastSyncStatus?: "success" | "error";
360
360
  lastSyncCounts?: ManagedSourceSyncCounts;
361
361
  lastError?: string;
362
+ changed?: boolean;
362
363
  }
363
364
  interface ManagedSourcesArtifact {
364
365
  version: 1;
package/dist/index.js CHANGED
@@ -9099,6 +9099,7 @@ import { z as z7 } from "zod";
9099
9099
 
9100
9100
  // src/analysis.ts
9101
9101
  import path14 from "path";
9102
+ import { fromMarkdown } from "mdast-util-from-markdown";
9102
9103
  import { z as z2 } from "zod";
9103
9104
  var ANALYSIS_FORMAT_VERSION = 7;
9104
9105
  var sourceAnalysisSchema = z2.object({
@@ -9158,6 +9159,12 @@ var STOPWORDS = /* @__PURE__ */ new Set([
9158
9159
  "would",
9159
9160
  "your"
9160
9161
  ]);
9162
+ var HEURISTIC_SECTION_SOURCE_KINDS = /* @__PURE__ */ new Map([
9163
+ ["transcript", "Transcript"],
9164
+ ["chat_export", "Messages"],
9165
+ ["email", "Message"],
9166
+ ["calendar", "Description"]
9167
+ ]);
9161
9168
  function extractTopTerms(text, count) {
9162
9169
  const frequency = /* @__PURE__ */ new Map();
9163
9170
  for (const token of text.toLowerCase().match(/[a-z][a-z0-9-]{3,}/g) ?? []) {
@@ -9184,18 +9191,112 @@ function detectPolarity(text) {
9184
9191
  }
9185
9192
  return "neutral";
9186
9193
  }
9187
- function deriveTitle(manifest, text) {
9188
- const heading = text.match(/^#\s+(.+)$/m)?.[1]?.trim();
9189
- return heading || manifest.title;
9194
+ function parseMarkdownNodes(text) {
9195
+ try {
9196
+ const root = fromMarkdown(text);
9197
+ return Array.isArray(root.children) ? root.children : [];
9198
+ } catch {
9199
+ return [];
9200
+ }
9201
+ }
9202
+ function markdownNodeText(node) {
9203
+ if (node.type === "text" || node.type === "inlineCode" || node.type === "code") {
9204
+ return normalizeWhitespace(node.value ?? "");
9205
+ }
9206
+ if (node.type === "image") {
9207
+ return normalizeWhitespace(node.alt ?? "");
9208
+ }
9209
+ if (node.type === "break" || node.type === "thematicBreak") {
9210
+ return " ";
9211
+ }
9212
+ return normalizeWhitespace((node.children ?? []).map((child) => markdownNodeText(child)).join(" "));
9213
+ }
9214
+ function markdownNodesText(nodes) {
9215
+ return normalizeWhitespace(nodes.map((node) => markdownNodeText(node)).join("\n"));
9216
+ }
9217
+ function stripLeadingTitleNodes(nodes, title) {
9218
+ const normalizedTitle = normalizeWhitespace(title);
9219
+ if (!normalizedTitle || !nodes.length) {
9220
+ return nodes;
9221
+ }
9222
+ for (let index = 0; index < nodes.length; index += 1) {
9223
+ const node = nodes[index];
9224
+ if (!node) {
9225
+ continue;
9226
+ }
9227
+ const nodeText2 = markdownNodeText(node);
9228
+ if (node.type === "heading" && node.depth === 1 && nodeText2 === normalizedTitle) {
9229
+ return nodes.slice(index + 1);
9230
+ }
9231
+ if (node.type === "paragraph" && nodeText2 === normalizedTitle) {
9232
+ return nodes.slice(index + 1);
9233
+ }
9234
+ return nodes;
9235
+ }
9236
+ return nodes;
9237
+ }
9238
+ function markdownSectionNodes(nodes, heading) {
9239
+ const normalizedHeading = normalizeWhitespace(heading);
9240
+ for (let index = 0; index < nodes.length; index += 1) {
9241
+ const node = nodes[index];
9242
+ if (node?.type !== "heading" || node.depth !== 2) {
9243
+ continue;
9244
+ }
9245
+ if (markdownNodeText(node) !== normalizedHeading) {
9246
+ continue;
9247
+ }
9248
+ const sectionNodes = [];
9249
+ for (let cursor = index + 1; cursor < nodes.length; cursor += 1) {
9250
+ const candidate = nodes[cursor];
9251
+ if (candidate?.type === "heading" && typeof candidate.depth === "number" && candidate.depth <= 2) {
9252
+ break;
9253
+ }
9254
+ if (candidate) {
9255
+ sectionNodes.push(candidate);
9256
+ }
9257
+ }
9258
+ return sectionNodes;
9259
+ }
9260
+ return [];
9261
+ }
9262
+ function textForHeuristicAnalysis(manifest, text) {
9263
+ const nodes = parseMarkdownNodes(text);
9264
+ if (!nodes.length) {
9265
+ return normalizeWhitespace(text);
9266
+ }
9267
+ const sectionHeading = HEURISTIC_SECTION_SOURCE_KINDS.get(manifest.sourceKind);
9268
+ const scopedNodes = sectionHeading ? markdownSectionNodes(nodes, sectionHeading) : nodes;
9269
+ const relevantNodes = scopedNodes.length ? scopedNodes : nodes;
9270
+ const contentNodes = stripLeadingTitleNodes(relevantNodes, manifest.title);
9271
+ const normalized = markdownNodesText(contentNodes.length ? contentNodes : relevantNodes);
9272
+ return normalized || normalizeWhitespace(text);
9273
+ }
9274
+ function normalizeAnalysisTitle(manifest, candidate) {
9275
+ if (manifest.sourceKind !== "code") {
9276
+ return manifest.title;
9277
+ }
9278
+ const normalized = normalizeWhitespace(candidate.replace(/^#+\s+/, ""));
9279
+ if (!normalized) {
9280
+ return manifest.title;
9281
+ }
9282
+ if (normalized.length > 140 || normalized.includes(" ## ")) {
9283
+ return manifest.title;
9284
+ }
9285
+ return normalized;
9286
+ }
9287
+ function normalizeSourceAnalysis(manifest, analysis) {
9288
+ const title = normalizeAnalysisTitle(manifest, analysis.title);
9289
+ return title === analysis.title ? analysis : { ...analysis, title };
9190
9290
  }
9191
9291
  function heuristicAnalysis(manifest, text, schemaHash) {
9192
- const normalized = normalizeWhitespace(text);
9292
+ const analysisText = textForHeuristicAnalysis(manifest, text);
9293
+ const normalized = normalizeWhitespace(analysisText);
9193
9294
  const concepts = extractTopTerms(normalized, 6).map((term) => ({
9194
9295
  id: `concept:${slugify(term)}`,
9195
9296
  name: term,
9196
9297
  description: `Frequently referenced concept in ${manifest.title}.`
9197
9298
  }));
9198
- const entities = extractEntities(text, 6).map((term) => ({
9299
+ const entities = extractEntities(analysisText, 6).map((term) => ({
9199
9300
  id: `entity:${slugify(term)}`,
9200
9301
  name: term,
9201
9302
  description: `Named entity mentioned in ${manifest.title}.`
@@ -9208,7 +9309,7 @@ function heuristicAnalysis(manifest, text, schemaHash) {
9208
9309
  semanticHash: manifest.semanticHash,
9209
9310
  extractionHash: manifest.extractionHash,
9210
9311
  schemaHash,
9211
- title: deriveTitle(manifest, text),
9312
+ title: manifest.title,
9212
9313
  summary: firstSentences(normalized, 3) || truncate(normalized, 280) || `Imported ${manifest.sourceKind} source.`,
9213
9314
  concepts,
9214
9315
  entities,
@@ -9333,7 +9434,11 @@ async function analyzeSource(manifest, extractedText, provider, paths, schema) {
9333
9434
  const cachePath = path14.join(paths.analysesDir, `${manifest.sourceId}.json`);
9334
9435
  const cached = await readJsonFile(cachePath);
9335
9436
  if (cached && cached.analysisVersion === ANALYSIS_FORMAT_VERSION && (cached.semanticHash ?? cached.sourceHash) === manifest.semanticHash && cached.extractionHash === manifest.extractionHash && cached.schemaHash === schema.hash) {
9336
- return cached;
9437
+ const normalizedCached = normalizeSourceAnalysis(manifest, cached);
9438
+ if (normalizedCached !== cached) {
9439
+ await writeJsonFile(cachePath, normalizedCached);
9440
+ }
9441
+ return normalizedCached;
9337
9442
  }
9338
9443
  const extraction = await readExtractionArtifact(paths.rootDir, manifest);
9339
9444
  const content = normalizeWhitespace(extractedText ?? "");
@@ -9398,8 +9503,9 @@ async function analyzeSource(manifest, extractedText, provider, paths, schema) {
9398
9503
  analysis = heuristicAnalysis(manifest, content, schema.hash);
9399
9504
  }
9400
9505
  }
9401
- await writeJsonFile(cachePath, analysis);
9402
- return analysis;
9506
+ const normalized = normalizeSourceAnalysis(manifest, analysis);
9507
+ await writeJsonFile(cachePath, normalized);
9508
+ return normalized;
9403
9509
  }
9404
9510
  function analysisSignature(analysis) {
9405
9511
  return sha256(JSON.stringify(analysis));
@@ -13019,8 +13125,37 @@ async function loadSavedOutputPages(wikiDir) {
13019
13125
  import fs17 from "fs/promises";
13020
13126
  import path21 from "path";
13021
13127
  import matter8 from "gray-matter";
13128
+ function warningMessage(warning) {
13129
+ return warning instanceof Error ? warning.message : String(warning);
13130
+ }
13131
+ function warningType(warning, type) {
13132
+ if (warning instanceof Error) {
13133
+ return warning.name;
13134
+ }
13135
+ return typeof type === "string" ? type : void 0;
13136
+ }
13137
+ function isSqliteExperimentalWarning(warning, type) {
13138
+ return warningType(warning, type) === "ExperimentalWarning" && warningMessage(warning).includes("SQLite is an experimental feature");
13139
+ }
13140
+ function withSuppressedSqliteExperimentalWarning(run) {
13141
+ const originalEmitWarning = process.emitWarning.bind(process);
13142
+ process.emitWarning = ((warning, options, ...args) => {
13143
+ const type = typeof options === "string" ? options : typeof options?.type === "string" ? options.type ?? void 0 : void 0;
13144
+ if (isSqliteExperimentalWarning(warning, type)) {
13145
+ return;
13146
+ }
13147
+ return originalEmitWarning(warning, options, ...args);
13148
+ });
13149
+ try {
13150
+ return run();
13151
+ } finally {
13152
+ process.emitWarning = originalEmitWarning;
13153
+ }
13154
+ }
13022
13155
  function getDatabaseSync() {
13023
- const builtin = process.getBuiltinModule?.("node:sqlite");
13156
+ const builtin = withSuppressedSqliteExperimentalWarning(
13157
+ () => process.getBuiltinModule?.("node:sqlite")
13158
+ );
13024
13159
  if (!builtin?.DatabaseSync) {
13025
13160
  throw new Error("node:sqlite is unavailable in this Node runtime.");
13026
13161
  }
@@ -13045,7 +13180,7 @@ function normalizeSourceClass2(value) {
13045
13180
  async function rebuildSearchIndex(dbPath, pages, wikiDir) {
13046
13181
  await ensureDir(path21.dirname(dbPath));
13047
13182
  const DatabaseSync = getDatabaseSync();
13048
- const db = new DatabaseSync(dbPath);
13183
+ const db = withSuppressedSqliteExperimentalWarning(() => new DatabaseSync(dbPath));
13049
13184
  db.exec("PRAGMA journal_mode = WAL;");
13050
13185
  db.exec(`
13051
13186
  DROP TABLE IF EXISTS page_search;
@@ -13123,7 +13258,7 @@ function searchPages(dbPath, query, limitOrOptions = 5) {
13123
13258
  return [];
13124
13259
  }
13125
13260
  const DatabaseSync = getDatabaseSync();
13126
- const db = new DatabaseSync(dbPath, { readOnly: true });
13261
+ const db = withSuppressedSqliteExperimentalWarning(() => new DatabaseSync(dbPath, { readOnly: true }));
13127
13262
  const clauses = ["page_search MATCH ?"];
13128
13263
  const params = [ftsQuery];
13129
13264
  if (options.kind && options.kind !== "all") {
@@ -17688,7 +17823,7 @@ async function bootstrapDemo(rootDir, input) {
17688
17823
  }
17689
17824
 
17690
17825
  // src/mcp.ts
17691
- var SERVER_VERSION = "0.6.3";
17826
+ var SERVER_VERSION = "0.6.5";
17692
17827
  async function createMcpServer(rootDir) {
17693
17828
  const server = new McpServer({
17694
17829
  name: "swarmvault",
@@ -18365,6 +18500,15 @@ function uniqueStrings4(values) {
18365
18500
  function normalizeManagedStatus(value) {
18366
18501
  return value === "missing" || value === "error" ? value : "ready";
18367
18502
  }
18503
+ function emptyManagedSourceSyncCounts() {
18504
+ return {
18505
+ scannedCount: 0,
18506
+ importedCount: 0,
18507
+ updatedCount: 0,
18508
+ removedCount: 0,
18509
+ skippedCount: 0
18510
+ };
18511
+ }
18368
18512
  function withinRoot2(rootPath, targetPath) {
18369
18513
  const relative = path26.relative(rootPath, targetPath);
18370
18514
  return relative === "" || !relative.startsWith("..") && !path26.isAbsolute(relative);
@@ -18740,6 +18884,7 @@ async function syncManagedSource(rootDir, entry, options) {
18740
18884
  updatedAt: now,
18741
18885
  lastSyncAt: now,
18742
18886
  lastSyncStatus: "error",
18887
+ lastSyncCounts: emptyManagedSourceSyncCounts(),
18743
18888
  lastError: `Directory not found: ${entry.path}`,
18744
18889
  changed: false
18745
18890
  };
@@ -18756,6 +18901,7 @@ async function syncManagedSource(rootDir, entry, options) {
18756
18901
  updatedAt: now,
18757
18902
  lastSyncAt: now,
18758
18903
  lastSyncStatus: "error",
18904
+ lastSyncCounts: emptyManagedSourceSyncCounts(),
18759
18905
  lastError: `File not found: ${entry.path}`,
18760
18906
  changed: false
18761
18907
  };
@@ -18785,6 +18931,7 @@ async function syncManagedSource(rootDir, entry, options) {
18785
18931
  updatedAt: now,
18786
18932
  lastSyncAt: now,
18787
18933
  lastSyncStatus: "error",
18934
+ lastSyncCounts: emptyManagedSourceSyncCounts(),
18788
18935
  lastError: error instanceof Error ? error.message : String(error),
18789
18936
  changed: false
18790
18937
  };
@@ -18950,7 +19097,7 @@ async function writeSourceBriefForScope(rootDir, source) {
18950
19097
  relatedSourceIds: source.sourceIds,
18951
19098
  projectIds,
18952
19099
  extraTags: ["source-brief"],
18953
- origin: "query",
19100
+ origin: "source_brief",
18954
19101
  slug: `source-briefs/${source.id}`,
18955
19102
  metadata: {
18956
19103
  status: "active",
@@ -19196,7 +19343,7 @@ async function buildSourceReviewStagedPage(rootDir, scope) {
19196
19343
  relatedSourceIds: scope.sourceIds,
19197
19344
  projectIds,
19198
19345
  extraTags: ["source-review"],
19199
- origin: "query",
19346
+ origin: "source_review",
19200
19347
  slug: `source-reviews/${scope.id}`,
19201
19348
  metadata: {
19202
19349
  status: "draft",
@@ -19443,7 +19590,7 @@ async function buildSourceGuideStagedPage(rootDir, scope) {
19443
19590
  relatedSourceIds: scope.sourceIds,
19444
19591
  projectIds,
19445
19592
  extraTags: ["source-guide", "guided-ingest"],
19446
- origin: "query",
19593
+ origin: "source_guide",
19447
19594
  slug: `source-guides/${scope.id}`,
19448
19595
  metadata: {
19449
19596
  status: "draft",
@@ -19597,7 +19744,7 @@ async function buildSourceSessionSavedPage(rootDir, scope, session) {
19597
19744
  relatedSourceIds: scope.sourceIds,
19598
19745
  projectIds,
19599
19746
  extraTags: ["source-session", "guided-session"],
19600
- origin: "query",
19747
+ origin: "source_session",
19601
19748
  slug: `source-sessions/${scope.id}`,
19602
19749
  metadata: {
19603
19750
  status: "active",
@@ -19943,6 +20090,15 @@ async function resumeSourceSession(rootDir, id, options = {}) {
19943
20090
  function shouldCompile(changedSources, graphExists, compileRequested) {
19944
20091
  return compileRequested && (!graphExists || changedSources.length > 0);
19945
20092
  }
20093
+ async function shouldRefreshBriefForManagedSource(source, options) {
20094
+ if (options.compilePerformed || options.changed) {
20095
+ return true;
20096
+ }
20097
+ if (!source.briefPath) {
20098
+ return true;
20099
+ }
20100
+ return !await fileExists(source.briefPath);
20101
+ }
19946
20102
  async function listManagedSourceRecords(rootDir) {
19947
20103
  await ensureManagedSourcesArtifact(rootDir);
19948
20104
  return await loadManagedSources(rootDir);
@@ -19974,12 +20130,15 @@ async function addManagedSource(rootDir, input, options = {}) {
19974
20130
  }
19975
20131
  const graphExists = await loadVaultConfig(rootDir).then(({ paths }) => fileExists(paths.graphPath));
19976
20132
  let compile;
19977
- if (shouldCompile([synced], graphExists, compileRequested)) {
20133
+ if (shouldCompile(synced.changed ? [synced] : [], graphExists, compileRequested)) {
19978
20134
  compile = await compileVault(rootDir, {});
19979
20135
  }
19980
20136
  let briefGenerated = false;
19981
20137
  let briefPath;
19982
- if (compileRequested && briefRequested && synced.status === "ready") {
20138
+ if (compileRequested && briefRequested && synced.status === "ready" && await shouldRefreshBriefForManagedSource(synced, {
20139
+ compilePerformed: Boolean(compile),
20140
+ changed: synced.changed
20141
+ })) {
19983
20142
  const briefs = await generateBriefsForSources(rootDir, [synced]);
19984
20143
  briefPath = briefs.get(synced.id);
19985
20144
  briefGenerated = Boolean(briefPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swarmvaultai/engine",
3
- "version": "0.6.3",
3
+ "version": "0.6.5",
4
4
  "description": "Core engine for SwarmVault: ingest, compile, query, lint, and provider abstractions.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -57,6 +57,7 @@
57
57
  "jsdom": "^27.0.0",
58
58
  "mailparser": "^3.9.8",
59
59
  "mammoth": "^1.12.0",
60
+ "mdast-util-from-markdown": "^2.0.3",
60
61
  "mime-types": "^3.0.1",
61
62
  "neo4j-driver": "^5.28.3",
62
63
  "node-ical": "^0.26.0",