@swarmvaultai/engine 3.16.0 → 3.17.0

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
@@ -121,6 +121,7 @@ import {
121
121
  searchVault,
122
122
  stageGeneratedOutputPages,
123
123
  startMemoryTask,
124
+ syncTrackedFiles,
124
125
  syncTrackedRepos,
125
126
  syncTrackedReposForWatch,
126
127
  updateMemoryTask,
@@ -130,7 +131,7 @@ import {
130
131
  writeGuidedSourceSession,
131
132
  writeRetrievalManifest,
132
133
  writeWatchStatusArtifact
133
- } from "./chunk-TQIIJVVG.js";
134
+ } from "./chunk-JEWLYIHN.js";
134
135
  import {
135
136
  LocalWhisperProviderAdapter,
136
137
  SWARMVAULT_OUT_ENV,
@@ -1235,6 +1236,68 @@ async function predictRemovedSourceIdsFromRepoSync(rootDir, options) {
1235
1236
  )
1236
1237
  ];
1237
1238
  }
1239
+ var REFRESH_LOCK_DIRNAME = "refresh.lock";
1240
+ var REFRESH_QUEUE_FILENAME = "refresh-queue.json";
1241
+ var REFRESH_LOCK_STALE_MS = 10 * 6e4;
1242
+ var MAX_FILE_REFRESH_ROUNDS = 5;
1243
+ function refreshLockDir(watchDir) {
1244
+ return path4.join(watchDir, REFRESH_LOCK_DIRNAME);
1245
+ }
1246
+ async function tryAcquireRefreshLock(watchDir) {
1247
+ const lockDir = refreshLockDir(watchDir);
1248
+ await fs4.mkdir(watchDir, { recursive: true });
1249
+ const writeOwner = () => fs4.writeFile(
1250
+ path4.join(lockDir, "owner.json"),
1251
+ `${JSON.stringify({ pid: process2.pid, acquiredAt: (/* @__PURE__ */ new Date()).toISOString() })}
1252
+ `,
1253
+ "utf8"
1254
+ );
1255
+ try {
1256
+ await fs4.mkdir(lockDir, { recursive: false });
1257
+ await writeOwner();
1258
+ return true;
1259
+ } catch {
1260
+ try {
1261
+ const stat = await fs4.stat(lockDir);
1262
+ if (Date.now() - stat.mtimeMs > REFRESH_LOCK_STALE_MS) {
1263
+ await fs4.rm(lockDir, { recursive: true, force: true });
1264
+ await fs4.mkdir(lockDir, { recursive: false });
1265
+ await writeOwner();
1266
+ return true;
1267
+ }
1268
+ } catch {
1269
+ }
1270
+ return false;
1271
+ }
1272
+ }
1273
+ async function releaseRefreshLock(watchDir) {
1274
+ await fs4.rm(refreshLockDir(watchDir), { recursive: true, force: true });
1275
+ }
1276
+ async function enqueueRefreshFiles(watchDir, files) {
1277
+ await fs4.mkdir(watchDir, { recursive: true });
1278
+ const queuePath = path4.join(watchDir, REFRESH_QUEUE_FILENAME);
1279
+ let existing = [];
1280
+ try {
1281
+ const parsed = JSON.parse(await fs4.readFile(queuePath, "utf8"));
1282
+ if (Array.isArray(parsed)) {
1283
+ existing = parsed.filter((entry) => typeof entry === "string");
1284
+ }
1285
+ } catch {
1286
+ }
1287
+ const merged = [.../* @__PURE__ */ new Set([...existing, ...files])];
1288
+ await fs4.writeFile(queuePath, `${JSON.stringify(merged, null, 2)}
1289
+ `, "utf8");
1290
+ }
1291
+ async function drainRefreshQueue(watchDir) {
1292
+ const queuePath = path4.join(watchDir, REFRESH_QUEUE_FILENAME);
1293
+ try {
1294
+ const parsed = JSON.parse(await fs4.readFile(queuePath, "utf8"));
1295
+ await fs4.rm(queuePath, { force: true });
1296
+ return Array.isArray(parsed) ? parsed.filter((entry) => typeof entry === "string") : [];
1297
+ } catch {
1298
+ return [];
1299
+ }
1300
+ }
1238
1301
  function hasIgnoredRepoSegment(baseDir, targetPath) {
1239
1302
  const relativePath = path4.relative(baseDir, targetPath);
1240
1303
  if (!relativePath || relativePath.startsWith("..")) {
@@ -1369,8 +1432,128 @@ async function performWatchCycle(rootDir, paths, options, codeOnly = false) {
1369
1432
  lintFindingCount
1370
1433
  };
1371
1434
  }
1435
+ async function runFileRefreshCycle(rootDir, paths, options, files) {
1436
+ const resolvedFiles = [...new Set(files.map((candidate) => path4.resolve(rootDir, candidate)))];
1437
+ const emptyResult = {
1438
+ watchedRepoRoots: [],
1439
+ importedCount: 0,
1440
+ scannedCount: 0,
1441
+ attachmentCount: 0,
1442
+ repoImportedCount: 0,
1443
+ repoUpdatedCount: 0,
1444
+ repoRemovedCount: 0,
1445
+ repoScannedCount: 0,
1446
+ pendingSemanticRefreshCount: 0,
1447
+ pendingSemanticRefreshPaths: [],
1448
+ changedPages: []
1449
+ };
1450
+ if (!await tryAcquireRefreshLock(paths.watchDir)) {
1451
+ await enqueueRefreshFiles(paths.watchDir, resolvedFiles);
1452
+ return { ...emptyResult, queuedFiles: resolvedFiles };
1453
+ }
1454
+ const startedAt = /* @__PURE__ */ new Date();
1455
+ const reasons = resolvedFiles.map((file) => `file:${path4.relative(rootDir, file) || file}`);
1456
+ let success = true;
1457
+ let error;
1458
+ const result = { ...emptyResult };
1459
+ try {
1460
+ const seenRoots = /* @__PURE__ */ new Set();
1461
+ const changedPages = /* @__PURE__ */ new Set();
1462
+ let pendingFiles = resolvedFiles;
1463
+ let rounds = 0;
1464
+ while (pendingFiles.length > 0 && rounds < MAX_FILE_REFRESH_ROUNDS) {
1465
+ rounds += 1;
1466
+ const repoSync = await syncTrackedFiles(rootDir, pendingFiles);
1467
+ const compile = await compileVault(rootDir, { codeOnly: true });
1468
+ const pendingSemanticRefresh = await mergePendingSemanticRefresh(rootDir, repoSync.pendingSemanticRefresh);
1469
+ const stalePagePaths = await markPagesStaleForSources(
1470
+ rootDir,
1471
+ pendingSemanticRefresh.map((entry) => entry.sourceId).filter((sourceId) => Boolean(sourceId))
1472
+ );
1473
+ for (const repoRoot of repoSync.repoRoots) {
1474
+ seenRoots.add(repoRoot);
1475
+ }
1476
+ for (const page of [...compile.changedPages, ...stalePagePaths]) {
1477
+ changedPages.add(page);
1478
+ }
1479
+ result.scannedCount += repoSync.scannedCount;
1480
+ result.repoScannedCount += repoSync.scannedCount;
1481
+ result.repoImportedCount += repoSync.imported.length;
1482
+ result.repoUpdatedCount += repoSync.updated.length;
1483
+ result.repoRemovedCount += repoSync.removed.length;
1484
+ result.pendingSemanticRefreshCount = pendingSemanticRefresh.length;
1485
+ result.pendingSemanticRefreshPaths = pendingSemanticRefresh.map((entry) => entry.path);
1486
+ pendingFiles = await drainRefreshQueue(paths.watchDir);
1487
+ }
1488
+ if (pendingFiles.length > 0) {
1489
+ await enqueueRefreshFiles(paths.watchDir, pendingFiles);
1490
+ result.queuedFiles = pendingFiles;
1491
+ }
1492
+ result.watchedRepoRoots = [...seenRoots].sort((left, right) => left.localeCompare(right));
1493
+ result.changedPages = [...changedPages];
1494
+ if (options.lint) {
1495
+ result.lintFindingCount = (await lintVault(rootDir)).length;
1496
+ }
1497
+ return result;
1498
+ } catch (caught) {
1499
+ success = false;
1500
+ error = caught instanceof Error ? caught.message : String(caught);
1501
+ throw caught;
1502
+ } finally {
1503
+ await releaseRefreshLock(paths.watchDir);
1504
+ const finishedAt = /* @__PURE__ */ new Date();
1505
+ const runSummary = {
1506
+ startedAt: startedAt.toISOString(),
1507
+ finishedAt: finishedAt.toISOString(),
1508
+ durationMs: finishedAt.getTime() - startedAt.getTime(),
1509
+ inputDir: paths.inboxDir,
1510
+ reasons,
1511
+ importedCount: result.repoImportedCount + result.repoUpdatedCount,
1512
+ scannedCount: result.scannedCount,
1513
+ attachmentCount: 0,
1514
+ changedPages: result.changedPages,
1515
+ repoImportedCount: result.repoImportedCount,
1516
+ repoUpdatedCount: result.repoUpdatedCount,
1517
+ repoRemovedCount: result.repoRemovedCount,
1518
+ repoScannedCount: result.repoScannedCount,
1519
+ pendingSemanticRefreshCount: result.pendingSemanticRefreshCount,
1520
+ pendingSemanticRefreshPaths: result.pendingSemanticRefreshPaths,
1521
+ lintFindingCount: result.lintFindingCount,
1522
+ success,
1523
+ error
1524
+ };
1525
+ await recordSession(rootDir, {
1526
+ operation: "watch",
1527
+ title: `File refresh for ${reasons.join(", ")}`,
1528
+ startedAt: startedAt.toISOString(),
1529
+ finishedAt: finishedAt.toISOString(),
1530
+ success,
1531
+ error,
1532
+ changedPages: result.changedPages,
1533
+ lintFindingCount: result.lintFindingCount,
1534
+ lines: [
1535
+ `reasons=${reasons.join(",")}`,
1536
+ `repo_imported=${result.repoImportedCount}`,
1537
+ `repo_updated=${result.repoUpdatedCount}`,
1538
+ `repo_removed=${result.repoRemovedCount}`,
1539
+ `pending_semantic_refresh=${result.pendingSemanticRefreshCount}`,
1540
+ `lint=${result.lintFindingCount ?? 0}`
1541
+ ]
1542
+ });
1543
+ await appendWatchRun(rootDir, runSummary);
1544
+ await writeWatchStatusArtifact(rootDir, {
1545
+ generatedAt: finishedAt.toISOString(),
1546
+ watchedRepoRoots: result.watchedRepoRoots,
1547
+ lastRun: runSummary,
1548
+ pendingSemanticRefresh: await readPendingSemanticRefresh(rootDir)
1549
+ });
1550
+ }
1551
+ }
1372
1552
  async function runWatchCycle(rootDir, options = {}) {
1373
1553
  const { paths } = await initWorkspace(rootDir);
1554
+ if (options.files && options.files.length > 0) {
1555
+ return runFileRefreshCycle(rootDir, paths, options, options.files);
1556
+ }
1374
1557
  const previousGraph = await readJsonFile(paths.graphPath);
1375
1558
  const startedAt = /* @__PURE__ */ new Date();
1376
1559
  let success = true;
@@ -3509,8 +3692,26 @@ function renderHtmlStandalone(graph) {
3509
3692
  var seedPageIds = coreUnique(matches.filter(function(m) { return m.type === "page"; }).map(function(m) { return m.id; }));
3510
3693
  var visitedEdgeIdList = Object.keys(visitedEdgeIds);
3511
3694
 
3695
+ var topMatchParts = matches.slice(0, 8).map(function(m) {
3696
+ var pagePath;
3697
+ if (m.type === "page") {
3698
+ pagePath = (CORE_PAGE_BY_ID[m.id] && CORE_PAGE_BY_ID[m.id].path) || m.id;
3699
+ } else if (m.type === "node") {
3700
+ var matchNode = CORE_NODE_BY_ID[m.id];
3701
+ var matchPageId = matchNode && matchNode.pageId;
3702
+ if (matchPageId) pagePath = (CORE_PAGE_BY_ID[matchPageId] && CORE_PAGE_BY_ID[matchPageId].path) || matchPageId;
3703
+ }
3704
+ return (m.label || m.id) + " (" + m.type + ", score " + m.score + (pagePath ? ", page " + pagePath : "") + ")";
3705
+ });
3706
+ var topMatchesLine = matches.length
3707
+ ? "Top matches: " + topMatchParts.join("; ") + (matches.length > 8 ? " (+" + (matches.length - 8) + " more)" : "")
3708
+ : "Top matches: none";
3709
+ var seedsLine = seeds.length
3710
+ ? "Seeds: " + seeds.slice(0, 15).join(", ") + (seeds.length > 15 ? " (+" + (seeds.length - 15) + " more)" : "")
3711
+ : "Seeds: none";
3512
3712
  var summary = [
3513
- "Seeds: " + (seeds.join(", ") || "none"),
3713
+ topMatchesLine,
3714
+ seedsLine,
3514
3715
  "Visited nodes: " + visitedNodeIds.length,
3515
3716
  "Visited edges: " + visitedEdgeIdList.length,
3516
3717
  "Touched group patterns: " + hyperedgeIds.length,
@@ -5510,7 +5711,7 @@ import path12 from "path";
5510
5711
  import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
5511
5712
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5512
5713
  import { z } from "zod";
5513
- var SERVER_VERSION = "3.16.0";
5714
+ var SERVER_VERSION = "3.17.0";
5514
5715
  var codeLanguageSchema = z.enum([
5515
5716
  "javascript",
5516
5717
  "jsx",
@@ -6175,6 +6376,38 @@ async function createMcpServer(rootDir) {
6175
6376
  return asToolText(status);
6176
6377
  })
6177
6378
  );
6379
+ server.registerTool(
6380
+ "graph_status",
6381
+ {
6382
+ description: "Read-only graph freshness check: graph/report presence, tracked repo changes since the last refresh, and the recommended refresh command.",
6383
+ inputSchema: {
6384
+ repoRoots: z.array(z.string()).optional().describe("Optional repo roots to check instead of configured/tracked roots")
6385
+ }
6386
+ },
6387
+ safeHandler(async ({ repoRoots }) => {
6388
+ const status = await getGraphStatus(rootDir, { repoRoots });
6389
+ return asToolText(status);
6390
+ })
6391
+ );
6392
+ server.registerTool(
6393
+ "update_graph",
6394
+ {
6395
+ description: "Code-only graph refresh. With files, refreshes just those files (fast path used after edits); otherwise walks tracked repo roots.",
6396
+ inputSchema: {
6397
+ files: z.array(z.string()).optional().describe("Refresh only these files instead of walking every tracked repo root"),
6398
+ force: z.boolean().optional().describe("Allow updates even when node or edge counts shrink sharply")
6399
+ }
6400
+ },
6401
+ safeHandler(async ({ files, force }) => {
6402
+ const result = await runWatchCycle(rootDir, {
6403
+ repo: true,
6404
+ codeOnly: true,
6405
+ force: force ?? false,
6406
+ files: files?.length ? files : void 0
6407
+ });
6408
+ return asToolText(result);
6409
+ })
6410
+ );
6178
6411
  server.registerTool(
6179
6412
  "consolidate",
6180
6413
  {
@@ -6438,6 +6671,12 @@ var PROVIDER_TASK_KEYS = [
6438
6671
  "embeddingProvider",
6439
6672
  "audioProvider"
6440
6673
  ];
6674
+ var REQUIRED_PROVIDER_TASK_KEYS = /* @__PURE__ */ new Set([
6675
+ "compileProvider",
6676
+ "queryProvider",
6677
+ "lintProvider",
6678
+ "visionProvider"
6679
+ ]);
6441
6680
  async function loadRawConfig(rootDir) {
6442
6681
  await initWorkspace(rootDir);
6443
6682
  const { paths } = await loadVaultConfig(rootDir);
@@ -6538,24 +6777,42 @@ async function removeProviderConfig(options) {
6538
6777
  const tasks = ensureTasks(raw);
6539
6778
  const removed = Boolean(providers[options.providerId]);
6540
6779
  delete providers[options.providerId];
6780
+ if (options.fallbackProviderId && !providers[options.fallbackProviderId]) {
6781
+ throw new Error(`Provider ${options.fallbackProviderId} is not configured; cannot reassign tasks to it.`);
6782
+ }
6541
6783
  const fallbackProviderId = options.fallbackProviderId ?? (providers.local ? "local" : Object.keys(providers).sort((left, right) => left.localeCompare(right))[0]);
6542
- const updatedTasks = [];
6543
- for (const taskKey of PROVIDER_TASK_KEYS) {
6544
- if (tasks[taskKey] === options.providerId) {
6545
- if (!fallbackProviderId) {
6546
- delete tasks[taskKey];
6547
- } else {
6548
- tasks[taskKey] = fallbackProviderId;
6549
- }
6550
- updatedTasks.push(taskKey);
6784
+ const assignedTaskKeys = PROVIDER_TASK_KEYS.filter((taskKey) => tasks[taskKey] === options.providerId);
6785
+ if (!fallbackProviderId) {
6786
+ const orphanedRequired = assignedTaskKeys.filter((taskKey) => REQUIRED_PROVIDER_TASK_KEYS.has(taskKey));
6787
+ if (orphanedRequired.length) {
6788
+ throw new Error(
6789
+ `Cannot remove provider ${options.providerId}; tasks ${orphanedRequired.join(", ")} would have no provider. Configure another provider first or pass a fallback.`
6790
+ );
6551
6791
  }
6552
6792
  }
6553
- await writeJsonFile(configPath, raw);
6793
+ const reassignedTasks = [];
6794
+ const clearedTasks = [];
6795
+ for (const taskKey of assignedTaskKeys) {
6796
+ if (!fallbackProviderId) {
6797
+ delete tasks[taskKey];
6798
+ clearedTasks.push(taskKey);
6799
+ } else {
6800
+ tasks[taskKey] = fallbackProviderId;
6801
+ reassignedTasks.push(taskKey);
6802
+ }
6803
+ }
6804
+ const updatedTasks = assignedTaskKeys;
6805
+ if (removed || updatedTasks.length) {
6806
+ await writeJsonFile(configPath, raw);
6807
+ }
6554
6808
  return {
6555
6809
  providerId: options.providerId,
6556
6810
  configPath,
6557
6811
  removed,
6558
- updatedTasks
6812
+ fallbackProviderId: fallbackProviderId ?? null,
6813
+ updatedTasks,
6814
+ reassignedTasks,
6815
+ clearedTasks
6559
6816
  };
6560
6817
  }
6561
6818
 
@@ -9905,6 +10162,7 @@ export {
9905
10162
  startMcpServer,
9906
10163
  startMemoryTask,
9907
10164
  summarizeLocalWhisperSetup,
10165
+ syncTrackedFiles,
9908
10166
  syncTrackedRepos,
9909
10167
  syncTrackedReposForWatch,
9910
10168
  synthesizeHyperedgeHubs,
@@ -0,0 +1,32 @@
1
+ import {
2
+ buildMemoryGraphElements,
3
+ ensureMemoryLedger,
4
+ estimateMemoryTaskTokens,
5
+ finishMemoryTask,
6
+ listMemoryTasks,
7
+ loadMemoryTaskPages,
8
+ memoryTaskHashes,
9
+ memoryTaskPageRecord,
10
+ readMemoryTask,
11
+ renderMemoryTaskMarkdown,
12
+ resumeMemoryTask,
13
+ startMemoryTask,
14
+ updateMemoryTask
15
+ } from "./chunk-VSDBQVSE.js";
16
+ import "./chunk-NON6BVEI.js";
17
+ import "./chunk-NAIERP4C.js";
18
+ export {
19
+ buildMemoryGraphElements,
20
+ ensureMemoryLedger,
21
+ estimateMemoryTaskTokens,
22
+ finishMemoryTask,
23
+ listMemoryTasks,
24
+ loadMemoryTaskPages,
25
+ memoryTaskHashes,
26
+ memoryTaskPageRecord,
27
+ readMemoryTask,
28
+ renderMemoryTaskMarkdown,
29
+ resumeMemoryTask,
30
+ startMemoryTask,
31
+ updateMemoryTask
32
+ };
@@ -0,0 +1,32 @@
1
+ import {
2
+ buildMemoryGraphElements,
3
+ ensureMemoryLedger,
4
+ estimateMemoryTaskTokens,
5
+ finishMemoryTask,
6
+ listMemoryTasks,
7
+ loadMemoryTaskPages,
8
+ memoryTaskHashes,
9
+ memoryTaskPageRecord,
10
+ readMemoryTask,
11
+ renderMemoryTaskMarkdown,
12
+ resumeMemoryTask,
13
+ startMemoryTask,
14
+ updateMemoryTask
15
+ } from "./chunk-HORJDLXV.js";
16
+ import "./chunk-NON6BVEI.js";
17
+ import "./chunk-NAIERP4C.js";
18
+ export {
19
+ buildMemoryGraphElements,
20
+ ensureMemoryLedger,
21
+ estimateMemoryTaskTokens,
22
+ finishMemoryTask,
23
+ listMemoryTasks,
24
+ loadMemoryTaskPages,
25
+ memoryTaskHashes,
26
+ memoryTaskPageRecord,
27
+ readMemoryTask,
28
+ renderMemoryTaskMarkdown,
29
+ resumeMemoryTask,
30
+ startMemoryTask,
31
+ updateMemoryTask
32
+ };
@@ -0,0 +1,32 @@
1
+ import {
2
+ buildMemoryGraphElements,
3
+ ensureMemoryLedger,
4
+ estimateMemoryTaskTokens,
5
+ finishMemoryTask,
6
+ listMemoryTasks,
7
+ loadMemoryTaskPages,
8
+ memoryTaskHashes,
9
+ memoryTaskPageRecord,
10
+ readMemoryTask,
11
+ renderMemoryTaskMarkdown,
12
+ resumeMemoryTask,
13
+ startMemoryTask,
14
+ updateMemoryTask
15
+ } from "./chunk-65IRGGXX.js";
16
+ import "./chunk-NON6BVEI.js";
17
+ import "./chunk-NAIERP4C.js";
18
+ export {
19
+ buildMemoryGraphElements,
20
+ ensureMemoryLedger,
21
+ estimateMemoryTaskTokens,
22
+ finishMemoryTask,
23
+ listMemoryTasks,
24
+ loadMemoryTaskPages,
25
+ memoryTaskHashes,
26
+ memoryTaskPageRecord,
27
+ readMemoryTask,
28
+ renderMemoryTaskMarkdown,
29
+ resumeMemoryTask,
30
+ startMemoryTask,
31
+ updateMemoryTask
32
+ };
@@ -0,0 +1,32 @@
1
+ import {
2
+ buildMemoryGraphElements,
3
+ ensureMemoryLedger,
4
+ estimateMemoryTaskTokens,
5
+ finishMemoryTask,
6
+ listMemoryTasks,
7
+ loadMemoryTaskPages,
8
+ memoryTaskHashes,
9
+ memoryTaskPageRecord,
10
+ readMemoryTask,
11
+ renderMemoryTaskMarkdown,
12
+ resumeMemoryTask,
13
+ startMemoryTask,
14
+ updateMemoryTask
15
+ } from "./chunk-JEWLYIHN.js";
16
+ import "./chunk-NON6BVEI.js";
17
+ import "./chunk-NAIERP4C.js";
18
+ export {
19
+ buildMemoryGraphElements,
20
+ ensureMemoryLedger,
21
+ estimateMemoryTaskTokens,
22
+ finishMemoryTask,
23
+ listMemoryTasks,
24
+ loadMemoryTaskPages,
25
+ memoryTaskHashes,
26
+ memoryTaskPageRecord,
27
+ readMemoryTask,
28
+ renderMemoryTaskMarkdown,
29
+ resumeMemoryTask,
30
+ startMemoryTask,
31
+ updateMemoryTask
32
+ };