wiki-graph-sdk 0.7.0 → 0.8.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.
@@ -8,14 +8,14 @@ import {
8
8
  getChapterDetails,
9
9
  openWikimediaResolver,
10
10
  recordBuildJobInputRevision,
11
- WikiGraphArchiveFile,
11
+ WikiGraphArchiveFile as WikiGraphArchiveFile3,
12
12
  WikiGraphScope,
13
13
  withLoggingContext
14
14
  } from "wiki-graph-core";
15
15
  import {
16
- applyChapterJobArtifactFile as applyChapterJobArtifactFile2,
16
+ applyChapterJobArtifactFile as applyChapterJobArtifactFile3,
17
17
  createLocalChapterJobFileExecutor as createLocalChapterJobFileExecutor2,
18
- writeChapterJobInputFile as writeChapterJobInputFile2
18
+ writeChapterJobInputFile as writeChapterJobInputFile3
19
19
  } from "wiki-graph-core/worker";
20
20
 
21
21
  // src/embedding.ts
@@ -289,6 +289,14 @@ function decodeNodeResourceIdentity(identity, kind) {
289
289
  return void 0;
290
290
  }
291
291
  }
292
+ function getNodeResourcePath(resource) {
293
+ const brandedPath = getBrandedNodeResourcePath(resource, resource.kind);
294
+ if (brandedPath !== void 0) return brandedPath;
295
+ if (typeof resource === "object" && resource !== null && "path" in resource && typeof resource.path === "string") {
296
+ return resource.path;
297
+ }
298
+ throw new TypeError("Expected a Node File or Directory resource");
299
+ }
292
300
  function assertChildName(name) {
293
301
  if (name.length === 0 || name === "." || name === ".." || pathModule.isAbsolute(name) || name.includes("/") || name.includes("\\")) {
294
302
  throw new TypeError(`Directory child must be a relative name: ${name}`);
@@ -300,42 +308,42 @@ var NodeDatabaseConnection = class {
300
308
  }
301
309
  database;
302
310
  async close() {
303
- await new Promise((resolve2, reject) => {
311
+ await new Promise((resolve4, reject) => {
304
312
  this.database.close((error) => {
305
313
  if (error) reject(error);
306
- else resolve2();
314
+ else resolve4();
307
315
  });
308
316
  });
309
317
  }
310
318
  async execute(sql) {
311
- await new Promise((resolve2, reject) => {
319
+ await new Promise((resolve4, reject) => {
312
320
  this.database.exec(sql, (error) => {
313
321
  if (error) reject(error);
314
- else resolve2();
322
+ else resolve4();
315
323
  });
316
324
  });
317
325
  }
318
326
  async queryAll(sql, params = []) {
319
- return await new Promise((resolve2, reject) => {
327
+ return await new Promise((resolve4, reject) => {
320
328
  this.database.all(sql, [...params], (error, rows) => {
321
329
  if (error) reject(error);
322
- else resolve2(rows);
330
+ else resolve4(rows);
323
331
  });
324
332
  });
325
333
  }
326
334
  async queryOne(sql, params = []) {
327
- return await new Promise((resolve2, reject) => {
335
+ return await new Promise((resolve4, reject) => {
328
336
  this.database.get(sql, [...params], (error, row) => {
329
337
  if (error) reject(error);
330
- else resolve2(row);
338
+ else resolve4(row);
331
339
  });
332
340
  });
333
341
  }
334
342
  async run(sql, params = []) {
335
- await new Promise((resolve2, reject) => {
343
+ await new Promise((resolve4, reject) => {
336
344
  this.database.run(sql, [...params], (error) => {
337
345
  if (error) reject(error);
338
- else resolve2();
346
+ else resolve4();
339
347
  });
340
348
  });
341
349
  }
@@ -349,10 +357,10 @@ async function openNativeNodeDatabase(file, flags) {
349
357
  if (path === void 0) {
350
358
  throw new TypeError("The Node database adapter requires a NodeFile");
351
359
  }
352
- return await new Promise((resolve2, reject) => {
360
+ return await new Promise((resolve4, reject) => {
353
361
  const database = new nodeSqlite3.Database(path, flags, (error) => {
354
362
  if (error) reject(error);
355
- else resolve2(database);
363
+ else resolve4(database);
356
364
  });
357
365
  });
358
366
  }
@@ -361,19 +369,19 @@ async function openNodeZip(file) {
361
369
  if (path === void 0) {
362
370
  throw new TypeError("The Node ZIP adapter requires a NodeFile");
363
371
  }
364
- const zipFile = await new Promise((resolve2, reject) => {
372
+ const zipFile = await new Promise((resolve4, reject) => {
365
373
  yauzl.open(
366
374
  path,
367
375
  { autoClose: false, lazyEntries: true },
368
376
  (error, opened) => {
369
377
  if (error || opened === void 0)
370
378
  reject(error ?? new Error("Cannot open ZIP"));
371
- else resolve2(opened);
379
+ else resolve4(opened);
372
380
  }
373
381
  );
374
382
  });
375
383
  const entries = await new Promise(
376
- (resolve2, reject) => {
384
+ (resolve4, reject) => {
377
385
  const discovered = /* @__PURE__ */ new Map();
378
386
  const rejectAndClose = (error) => {
379
387
  zipFile.close();
@@ -383,7 +391,7 @@ async function openNodeZip(file) {
383
391
  };
384
392
  const resolveEntries = () => {
385
393
  zipFile.off("error", rejectAndClose);
386
- resolve2(discovered);
394
+ resolve4(discovered);
387
395
  };
388
396
  zipFile.on("entry", (entry) => {
389
397
  if (!entry.fileName.endsWith("/")) {
@@ -493,12 +501,12 @@ async function collectDecodedNodeStreamRange(input, offset, length) {
493
501
  return Buffer.concat(chunks, collected);
494
502
  }
495
503
  async function openNodeZipEntryStream(zipFile, entry, options) {
496
- return await new Promise((resolve2, reject) => {
504
+ return await new Promise((resolve4, reject) => {
497
505
  const callback = (error, opened) => {
498
506
  if (error || opened === void 0) {
499
507
  reject(error ?? new Error(`Cannot read ZIP entry: ${entry.fileName}`));
500
508
  } else {
501
- resolve2(opened);
509
+ resolve4(opened);
502
510
  }
503
511
  };
504
512
  if (options === void 0) zipFile.openReadStream(entry, callback);
@@ -629,11 +637,11 @@ async function writeNodeStream(input, writer) {
629
637
  );
630
638
  }
631
639
  async function collectNodeStream(input) {
632
- return await new Promise((resolve2, reject) => {
640
+ return await new Promise((resolve4, reject) => {
633
641
  const chunks = [];
634
642
  input.on("data", (chunk) => chunks.push(chunk));
635
643
  input.once("error", reject);
636
- input.once("end", () => resolve2(Buffer.concat(chunks)));
644
+ input.once("end", () => resolve4(Buffer.concat(chunks)));
637
645
  });
638
646
  }
639
647
  var nodeWikiGraphPlatform = {
@@ -1380,12 +1388,13 @@ var DEFAULT_GENERATION_JOB_CONCURRENCY = 3;
1380
1388
  // src/wikispine.ts
1381
1389
  import { spawn } from "child_process";
1382
1390
  var nodeWikispineCommandRunner = {
1383
- run: async ({ args, command, input, onStdout, signal }) => await new Promise((resolve2, reject) => {
1391
+ run: async ({ args, command, input, onStdout, signal }) => await new Promise((resolve4, reject) => {
1384
1392
  signal?.throwIfAborted();
1385
1393
  const child = spawn(command, [...args], {
1386
1394
  stdio: ["pipe", "pipe", "pipe"]
1387
1395
  });
1388
1396
  const stderr = [];
1397
+ let stdoutQueue = Promise.resolve();
1389
1398
  let settled = false;
1390
1399
  const settle = (operation) => {
1391
1400
  if (settled) return;
@@ -1401,14 +1410,15 @@ var nodeWikispineCommandRunner = {
1401
1410
  child.stdout.setEncoding("utf8");
1402
1411
  child.stdout.on("data", (chunk) => {
1403
1412
  if (settled) return;
1404
- try {
1405
- onStdout(chunk);
1406
- } catch (error) {
1413
+ child.stdout.pause();
1414
+ stdoutQueue = stdoutQueue.then(async () => await onStdout(chunk)).then(() => {
1415
+ child.stdout.resume();
1416
+ }).catch((error) => {
1407
1417
  child.kill();
1408
1418
  settle(
1409
1419
  () => reject(error instanceof Error ? error : new Error(String(error)))
1410
1420
  );
1411
- }
1421
+ });
1412
1422
  });
1413
1423
  child.stderr.on("data", (chunk) => stderr.push(chunk));
1414
1424
  child.on("error", (error) => {
@@ -1419,12 +1429,14 @@ var nodeWikispineCommandRunner = {
1419
1429
  );
1420
1430
  });
1421
1431
  child.on("close", (exitCode) => {
1422
- settle(
1423
- () => resolve2({
1424
- exitCode,
1425
- stderr: Buffer.concat(stderr).toString("utf8")
1426
- })
1427
- );
1432
+ void stdoutQueue.then(() => {
1433
+ settle(
1434
+ () => resolve4({
1435
+ exitCode,
1436
+ stderr: Buffer.concat(stderr).toString("utf8")
1437
+ })
1438
+ );
1439
+ });
1428
1440
  });
1429
1441
  child.stdin.end(input);
1430
1442
  })
@@ -1435,12 +1447,244 @@ function abortError(signal) {
1435
1447
 
1436
1448
  // src/worker.ts
1437
1449
  import {
1438
- runBuildJobWorker as runCoreBuildJobWorker
1450
+ applyChapterJobArtifactFile,
1451
+ runBuildJobWorker as runCoreBuildJobWorker,
1452
+ writeChapterJobInputFile
1439
1453
  } from "wiki-graph-core/worker";
1440
1454
  import {
1441
- applyChapterJobArtifactFile,
1455
+ resolveChapterPathReadonly,
1456
+ WikiGraphArchiveFile as WikiGraphArchiveFile2
1457
+ } from "wiki-graph-core";
1458
+
1459
+ // src/archive/target.ts
1460
+ import { realpath, stat as stat2 } from "fs/promises";
1461
+ import { basename as basename2, dirname as dirname2, isAbsolute as isAbsolute3, join as join3, relative, resolve as resolve3 } from "path";
1462
+ import {
1463
+ formatLocatedWikiGraphUri,
1464
+ formatWikiGraphLibraryUri,
1465
+ listWikiGraphLibraries,
1466
+ listWikiGraphLibraryArchives,
1467
+ parseLocatedWikiGraphUri,
1468
+ parseWikiGraphLibraryUri,
1469
+ resolveWikiGraphLibrary,
1470
+ resolveWikiGraphLibraryArchiveFile
1471
+ } from "wiki-graph-core";
1472
+
1473
+ // src/runtime-path.ts
1474
+ import { homedir as homedir3 } from "os";
1475
+ import { isAbsolute as isAbsolute2, resolve as resolve2 } from "path";
1476
+ function resolveWikiGraphRuntimePath(path) {
1477
+ const context = getWikiGraphSDKRuntimeContext();
1478
+ const environmentHome = context.env.HOME?.trim();
1479
+ const home = environmentHome === void 0 || environmentHome === "" ? homedir3() : environmentHome;
1480
+ if (path === "~") return home;
1481
+ if (path.startsWith("~/")) return resolve2(home, path.slice(2));
1482
+ return isAbsolute2(path) ? path : resolve2(context.cwd, path);
1483
+ }
1484
+
1485
+ // src/archive/target.ts
1486
+ var WikiGraphArchiveOwnershipError = class extends Error {
1487
+ code = "WIKI_GRAPH_ARCHIVE_OWNERSHIP_MISMATCH";
1488
+ constructor(message) {
1489
+ super(message);
1490
+ this.name = "WikiGraphArchiveOwnershipError";
1491
+ }
1492
+ };
1493
+ async function resolveWikiGraphArchiveLocation(target) {
1494
+ if (target.kind === "standalone") {
1495
+ return await resolveStandaloneLocation(target);
1496
+ }
1497
+ return await resolveLibraryLocation(target);
1498
+ }
1499
+ async function assertStandaloneWikiGraphArchivePath(inputPath) {
1500
+ const path = resolveWikiGraphRuntimePath(inputPath);
1501
+ const candidate = await canonicalPath(path);
1502
+ const candidateIdentity = await readFileIdentity(path);
1503
+ const libraries = await listWikiGraphLibraries();
1504
+ const matches = await findMatchingMembers(
1505
+ libraries,
1506
+ candidate,
1507
+ candidateIdentity
1508
+ );
1509
+ if (matches.length > 0) {
1510
+ const match = requireUnambiguousManagedMatch(path, matches);
1511
+ throw new WikiGraphArchiveOwnershipError(
1512
+ `Standalone archive path belongs to registered library ${formatWikiGraphLibraryUri(
1513
+ match.library.isDefault ? void 0 : match.library.publicId
1514
+ )}: ${path}. Use the library UUID URI ${match.archive.uri} instead.`
1515
+ );
1516
+ }
1517
+ for (const library of libraries) {
1518
+ const rootPath = getNodeResourcePath(library.folder);
1519
+ const root = await canonicalPath(rootPath);
1520
+ if (!isWithin(root, candidate)) continue;
1521
+ throw new WikiGraphArchiveOwnershipError(
1522
+ `Standalone archive path belongs to registered library ${formatWikiGraphLibraryUri(
1523
+ library.isDefault ? void 0 : library.publicId
1524
+ )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
1525
+ );
1526
+ }
1527
+ return path;
1528
+ }
1529
+ async function resolveWikiGraphArchiveTarget(file) {
1530
+ const path = getNodeResourcePath(file);
1531
+ const candidate = await canonicalPath(path);
1532
+ const candidateIdentity = await readFileIdentity(path);
1533
+ const matches = await findMatchingMembers(
1534
+ await listWikiGraphLibraries(),
1535
+ candidate,
1536
+ candidateIdentity
1537
+ );
1538
+ if (matches.length > 0) {
1539
+ const match = requireUnambiguousManagedMatch(path, matches);
1540
+ return { kind: "library", uri: match.archive.uri };
1541
+ }
1542
+ return { kind: "standalone", path: getNodeResourcePath(file) };
1543
+ }
1544
+ async function resolveStandaloneLocation(target) {
1545
+ if (target.path.startsWith("wikg://lib/")) {
1546
+ throw new WikiGraphArchiveOwnershipError(
1547
+ `A library archive URI cannot be used as a standalone path: ${target.path}`
1548
+ );
1549
+ }
1550
+ const path = await assertStandaloneWikiGraphArchivePath(target.path);
1551
+ const normalized = {
1552
+ kind: "standalone",
1553
+ path,
1554
+ ...target.objectUri === void 0 ? {} : { objectUri: target.objectUri }
1555
+ };
1556
+ return {
1557
+ archiveFile: new NodeFile(path),
1558
+ archiveKey: path,
1559
+ archivePath: path,
1560
+ indexScope: { archiveKey: path, archivePath: path, kind: "archive-index" },
1561
+ locatedUri: formatLocatedWikiGraphUri(path, target.objectUri),
1562
+ target: normalized
1563
+ };
1564
+ }
1565
+ async function resolveLibraryLocation(target) {
1566
+ const parsedLocated = parseLocatedWikiGraphUri(target.uri);
1567
+ const archiveUri = parsedLocated.archivePath ?? target.uri;
1568
+ const parsed = parseWikiGraphLibraryUri(archiveUri);
1569
+ if (parsed?.kind !== "archive") {
1570
+ throw new WikiGraphArchiveOwnershipError(
1571
+ `Library archive target must be a UUID archive URI: ${target.uri}`
1572
+ );
1573
+ }
1574
+ await resolveWikiGraphLibrary(parsed);
1575
+ const archiveFile = await resolveWikiGraphLibraryArchiveFile(archiveUri);
1576
+ const normalized = {
1577
+ kind: "library",
1578
+ uri: target.uri
1579
+ };
1580
+ return {
1581
+ archiveFile,
1582
+ archiveKey: archiveUri,
1583
+ archivePath: archiveUri,
1584
+ indexScope: {
1585
+ archiveKey: archiveUri,
1586
+ archivePath: archiveUri,
1587
+ kind: "archive-index"
1588
+ },
1589
+ libraryArchiveTarget: parsed,
1590
+ locatedUri: target.uri,
1591
+ publicArchiveUri: archiveUri,
1592
+ target: normalized
1593
+ };
1594
+ }
1595
+ async function canonicalPath(path) {
1596
+ try {
1597
+ return await realpath(path);
1598
+ } catch {
1599
+ const absolute = resolve3(path);
1600
+ const parent = dirname2(absolute);
1601
+ return parent === absolute ? absolute : join3(await canonicalPath(parent), basename2(absolute));
1602
+ }
1603
+ }
1604
+ function isWithin(root, candidate) {
1605
+ const child = relative(root, candidate);
1606
+ return child === "" || !child.startsWith("..") && !isAbsolute3(child);
1607
+ }
1608
+ async function findMatchingMembers(libraries, candidate, candidateIdentity) {
1609
+ const members = [];
1610
+ for (const library of libraries) {
1611
+ const archives = await listWikiGraphLibraryArchives({
1612
+ isDefault: library.isDefault,
1613
+ kind: "scope",
1614
+ ...library.isDefault ? {} : { publicId: library.publicId }
1615
+ });
1616
+ for (const archive of archives) {
1617
+ if (archive.file === void 0) continue;
1618
+ members.push({
1619
+ archive,
1620
+ library,
1621
+ memberPath: getNodeResourcePath(archive.file)
1622
+ });
1623
+ }
1624
+ }
1625
+ const exact = [];
1626
+ for (const member of members) {
1627
+ if (await canonicalPath(member.memberPath) === candidate) {
1628
+ exact.push(member);
1629
+ }
1630
+ }
1631
+ if (exact.length > 0 || candidateIdentity === void 0) return exact;
1632
+ const identityMatches = [];
1633
+ for (const member of members) {
1634
+ if (await readFileIdentity(member.memberPath) === candidateIdentity) {
1635
+ identityMatches.push(member);
1636
+ }
1637
+ }
1638
+ return identityMatches;
1639
+ }
1640
+ function requireUnambiguousManagedMatch(path, matches) {
1641
+ if (matches.length === 1) return matches[0];
1642
+ throw new WikiGraphArchiveOwnershipError(
1643
+ `Archive file identity is ambiguous across managed archives: ${path}. Use an explicit library UUID URI (${matches.map((match) => match.archive.uri).join(", ")}).`
1644
+ );
1645
+ }
1646
+ async function readFileIdentity(path) {
1647
+ try {
1648
+ const value = await stat2(path);
1649
+ return `${value.dev}:${value.ino}`;
1650
+ } catch {
1651
+ return void 0;
1652
+ }
1653
+ }
1654
+
1655
+ // src/archive/write.ts
1656
+ import {
1657
+ WikiGraphArchiveFile,
1658
+ writeWikiGraphLibraryArchive
1659
+ } from "wiki-graph-core";
1660
+ async function writeWikiGraphArchiveLocation(location, operation, options = {}) {
1661
+ const cacheOnly = options.searchIndexWritebackPolicy === "cache";
1662
+ const write = async (archiveFile = location.archiveFile) => await new WikiGraphArchiveFile(archiveFile).write(operation, {
1663
+ ...cacheOnly ? {} : { derivedStateKeys: [location.archiveKey, location.archivePath] },
1664
+ ...options.invalidateContinuationCursors === void 0 ? {} : {
1665
+ invalidateContinuationCursors: options.invalidateContinuationCursors
1666
+ },
1667
+ ...options.searchIndexWritebackPolicy === void 0 ? {} : { searchIndexWritebackPolicy: options.searchIndexWritebackPolicy }
1668
+ });
1669
+ if (location.libraryArchiveTarget === void 0 || cacheOnly || options.refreshLibraryIndex === false) {
1670
+ return await write();
1671
+ }
1672
+ return await writeWikiGraphLibraryArchive({
1673
+ additionalDerivedStateKeys: [location.archiveKey, location.archivePath],
1674
+ ...options.invalidateContinuationCursors === void 0 ? {} : {
1675
+ invalidateContinuationCursors: options.invalidateContinuationCursors
1676
+ },
1677
+ ...options.onIndexSyncError === void 0 ? {} : { onIndexSyncError: options.onIndexSyncError },
1678
+ operation: async (archiveFile) => await write(archiveFile),
1679
+ target: location.libraryArchiveTarget
1680
+ });
1681
+ }
1682
+
1683
+ // src/worker.ts
1684
+ import {
1685
+ applyChapterJobArtifactFile as applyChapterJobArtifactFile2,
1442
1686
  createLocalChapterJobFileExecutor,
1443
- writeChapterJobInputFile
1687
+ writeChapterJobInputFile as writeChapterJobInputFile2
1444
1688
  } from "wiki-graph-core/worker";
1445
1689
  async function runBuildJobWorker(options) {
1446
1690
  installNodeWikiGraphPlatform();
@@ -1480,7 +1724,7 @@ async function executeBuildJob(job, reporter, context) {
1480
1724
  let stage = await readChapterStage(job);
1481
1725
  if (stage === "sourced") {
1482
1726
  await executeStep(job, "reading-graph", execution, reporter, context, {
1483
- ...execution.extractionPrompt === void 0 ? {} : { extractionPrompt: execution.extractionPrompt }
1727
+ ...execution.extractionPrompt === void 0 ? {} : { prompt: execution.extractionPrompt }
1484
1728
  });
1485
1729
  stage = await readChapterStage(job);
1486
1730
  }
@@ -1513,8 +1757,8 @@ async function executeBuildJob(job, reporter, context) {
1513
1757
  }
1514
1758
  }
1515
1759
  await executeStep(job, job.target, execution, reporter, context, {
1516
- ...job.target === "reading-graph" ? { extractionPrompt: execution.extractionPrompt } : {},
1517
- ...job.target === "knowledge-graph" ? { policyPrompt: execution.knowledgeGraphPrompt } : {}
1760
+ ...job.target === "reading-graph" ? { prompt: execution.extractionPrompt } : {},
1761
+ ...job.target === "knowledge-graph" ? { prompt: execution.knowledgeGraphPrompt } : {}
1518
1762
  });
1519
1763
  } finally {
1520
1764
  await execution.close();
@@ -1529,10 +1773,10 @@ async function executeStep(job, kind, execution, reporter, context, inputOptions
1529
1773
  const stepWorkspace = await resetStepWorkspace(job.workspace, kind);
1530
1774
  const inputFile = await stepWorkspace.createFile("input.jsonl");
1531
1775
  const outputWorkspace = await stepWorkspace.createDirectory("output");
1532
- const revision = await new WikiGraphArchiveFile(job.archive).readDocument(
1776
+ const revision = await new WikiGraphArchiveFile3(job.archive).readDocument(
1533
1777
  async (document) => {
1534
1778
  await getChapterDetails(document, job.chapterId);
1535
- return await writeChapterJobInputFile2(
1779
+ return await writeChapterJobInputFile3(
1536
1780
  document,
1537
1781
  job.chapterId,
1538
1782
  kind,
@@ -1571,16 +1815,23 @@ async function executeStep(job, kind, execution, reporter, context, inputOptions
1571
1815
  total: 1,
1572
1816
  unit: "item"
1573
1817
  });
1574
- await new WikiGraphArchiveFile(job.archive).write(async (document) => {
1575
- assertJobStillRunning(await getBuildJob(job.jobId));
1576
- await applyChapterJobArtifactFile2(
1577
- document,
1578
- job.chapterId,
1579
- kind,
1580
- revision,
1581
- result.artifactFile
1582
- );
1583
- });
1818
+ const target = await resolveWikiGraphArchiveTarget(job.archive);
1819
+ const resolvedLocation = await resolveWikiGraphArchiveLocation(target);
1820
+ const location = target.kind === "standalone" ? { ...resolvedLocation, archiveFile: job.archive } : resolvedLocation;
1821
+ await writeWikiGraphArchiveLocation(
1822
+ location,
1823
+ async (document) => {
1824
+ assertJobStillRunning(await getBuildJob(job.jobId));
1825
+ await applyChapterJobArtifactFile3(
1826
+ document,
1827
+ job.chapterId,
1828
+ kind,
1829
+ revision,
1830
+ result.artifactFile
1831
+ );
1832
+ },
1833
+ { invalidateContinuationCursors: true }
1834
+ );
1584
1835
  await reporter.updatePhase({
1585
1836
  done: 1,
1586
1837
  phase: "committing",
@@ -1619,6 +1870,10 @@ async function openJobExecutor(job, config, signal, reporter) {
1619
1870
  llmRequest: requireValue(
1620
1871
  request,
1621
1872
  "Local Wikimedia resolution requires an LLM."
1873
+ ),
1874
+ llmModelId: requireValue(
1875
+ stageConfig?.llm?.model,
1876
+ "Local Wikimedia resolution requires an LLM model."
1622
1877
  )
1623
1878
  } : {
1624
1879
  endpoint: config.wikimedia.endpoint,
@@ -1656,12 +1911,12 @@ async function resetStepWorkspace(workspace, kind) {
1656
1911
  return await workspace.createDirectory(name);
1657
1912
  }
1658
1913
  async function readChapterStage(job) {
1659
- return await new WikiGraphArchiveFile(job.archive).readDocument(
1914
+ return await new WikiGraphArchiveFile3(job.archive).readDocument(
1660
1915
  async (document) => (await getChapterDetails(document, job.chapterId)).stage
1661
1916
  );
1662
1917
  }
1663
1918
  async function assertCurrentFtsArtifact(job) {
1664
- await new WikiGraphArchiveFile(job.archive).readDocument(async (document) => {
1919
+ await new WikiGraphArchiveFile3(job.archive).readDocument(async (document) => {
1665
1920
  const revision = await document.serials.getRevision(job.chapterId);
1666
1921
  const artifact = await document.indexArtifacts.get(job.chapterId, "fts");
1667
1922
  if (artifact?.sourceRevision !== revision) {