@equationalapplications/core-llm-wiki 4.16.0 → 4.17.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.d.mts CHANGED
@@ -1,10 +1,17 @@
1
- import { M as MemoryBundle, F as FormatContextOptions, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-BMsplvLy.mjs';
2
- export { E as EntityStatus, d as ExtractedFact, e as ExtractedFactEdge, f as ExtractedFactWithOntology, g as ExtractedTask, H as HOOK_TIMEOUT_MARKER, L as LLMProvider, O as OntologyConfig, h as OntologyEdgeType, i as OntologyManifest, j as OntologyMode, k as OntologyNodeType, l as OntologyPromptContext, m as OntologyUpdates, P as PromptOverrides, n as PromptService, o as PrunePartialFailureError, V as VectorRanker, p as VectorRankerFallback, q as VectorRankerRankArgs, r as VectorRankerSemanticResult, s as WikiBusyError, t as WikiBusyOperation, u as WikiCheckpoint, v as WikiConfig, w as WikiEdge, x as WikiEvent, y as WikiFact, z as WikiMemoryTestAccess, A as WikiOutboxEvent, B as WikiTask } from './testing-BMsplvLy.mjs';
1
+ import { M as MemoryBundle, F as FormatContextOptions, G as GraphNeighborhood, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-D02cdI9A.mjs';
2
+ export { E as EntityStatus, d as ExtractedFact, e as ExtractedFactEdge, f as ExtractedFactWithOntology, g as ExtractedTask, h as GraphTraversalOptions, H as HOOK_TIMEOUT_MARKER, L as LLMProvider, O as OntologyConfig, i as OntologyEdgeType, j as OntologyManifest, k as OntologyMode, l as OntologyNodeType, m as OntologyPromptContext, n as OntologyUpdates, P as PromptOverrides, o as PromptService, p as PrunePartialFailureError, V as VectorRanker, q as VectorRankerFallback, r as VectorRankerRankArgs, s as VectorRankerSemanticResult, t as WikiBusyError, u as WikiBusyOperation, v as WikiCheckpoint, w as WikiConfig, x as WikiEdge, y as WikiEvent, z as WikiFact, A as WikiMemoryTestAccess, B as WikiOutboxEvent, C as WikiTask } from './testing-D02cdI9A.mjs';
3
3
  import { OkfFile } from '@equationalapplications/core-okf';
4
4
  import 'minisearch';
5
5
 
6
6
  declare function formatContext(bundle: MemoryBundle, options?: FormatContextOptions): string;
7
7
 
8
+ /**
9
+ * Pure presenter — dense text serialization of a GraphNeighborhood for LLM prompt
10
+ * injection. Deterministic: same input always produces byte-identical output
11
+ * (matters for prompt caching).
12
+ */
13
+ declare function formatGraphContext(neighborhood: GraphNeighborhood): string;
14
+
8
15
  declare function formatMemoryDump(dump: MemoryDump): FormattedMemoryDump;
9
16
 
10
17
  declare function formatOkfBundle(dump: MemoryDump): {
@@ -19,6 +26,16 @@ declare function parseOkfBundle(entityId: string, files: OkfFile[], options?: Ok
19
26
 
20
27
  declare function parseEmbedding(blob: Uint8Array | null | undefined, text: string | null | undefined): Float32Array | null;
21
28
 
29
+ type GetRandomValues = (bytes: Uint8Array) => void;
30
+ /**
31
+ * Inject a platform-specific `getRandomValues` implementation.
32
+ * Call this once at module load time from the platform adapter
33
+ * (e.g. expo-crypto's getRandomValues) when the global `crypto` API is
34
+ * absent — Hermes / React Native being the primary case.
35
+ * Pass `null` to clear a previously injected source.
36
+ */
37
+ declare function configureRandomSource(fn: GetRandomValues | null): void;
38
+
22
39
  interface LibrarianOptions {
23
40
  /** If provided, replaces the default Librarian system instructions. */
24
41
  systemPrompt?: string;
@@ -43,4 +60,4 @@ declare function mapLibrarianOptionsToReadOptions(options: LibrarianOptions): Pi
43
60
 
44
61
  declare function createWiki(db: SQLiteAdapter, options: WikiOptions): WikiMemory;
45
62
 
46
- export { DEFAULT_LIBRARIAN_SYNTHESIS_PROMPT, FormatContextOptions, FormattedMemoryDump, type LibrarianOptions, type LibrarianPromptVariables, MemoryBundle, MemoryDump, type OkfImportOptions, ReadOptions, SQLiteAdapter, WikiMemory, WikiOptions, createWiki, formatContext, formatMemoryDump, formatOkfBundle, hydrateLibrarianPrompt, mapLibrarianOptionsToReadOptions, parseEmbedding, parseOkfBundle, validateLibrarianPromptTemplate };
63
+ export { DEFAULT_LIBRARIAN_SYNTHESIS_PROMPT, FormatContextOptions, FormattedMemoryDump, GraphNeighborhood, type LibrarianOptions, type LibrarianPromptVariables, MemoryBundle, MemoryDump, type OkfImportOptions, ReadOptions, SQLiteAdapter, WikiMemory, WikiOptions, configureRandomSource, createWiki, formatContext, formatGraphContext, formatMemoryDump, formatOkfBundle, hydrateLibrarianPrompt, mapLibrarianOptionsToReadOptions, parseEmbedding, parseOkfBundle, validateLibrarianPromptTemplate };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,17 @@
1
- import { M as MemoryBundle, F as FormatContextOptions, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-BMsplvLy.js';
2
- export { E as EntityStatus, d as ExtractedFact, e as ExtractedFactEdge, f as ExtractedFactWithOntology, g as ExtractedTask, H as HOOK_TIMEOUT_MARKER, L as LLMProvider, O as OntologyConfig, h as OntologyEdgeType, i as OntologyManifest, j as OntologyMode, k as OntologyNodeType, l as OntologyPromptContext, m as OntologyUpdates, P as PromptOverrides, n as PromptService, o as PrunePartialFailureError, V as VectorRanker, p as VectorRankerFallback, q as VectorRankerRankArgs, r as VectorRankerSemanticResult, s as WikiBusyError, t as WikiBusyOperation, u as WikiCheckpoint, v as WikiConfig, w as WikiEdge, x as WikiEvent, y as WikiFact, z as WikiMemoryTestAccess, A as WikiOutboxEvent, B as WikiTask } from './testing-BMsplvLy.js';
1
+ import { M as MemoryBundle, F as FormatContextOptions, G as GraphNeighborhood, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-D02cdI9A.js';
2
+ export { E as EntityStatus, d as ExtractedFact, e as ExtractedFactEdge, f as ExtractedFactWithOntology, g as ExtractedTask, h as GraphTraversalOptions, H as HOOK_TIMEOUT_MARKER, L as LLMProvider, O as OntologyConfig, i as OntologyEdgeType, j as OntologyManifest, k as OntologyMode, l as OntologyNodeType, m as OntologyPromptContext, n as OntologyUpdates, P as PromptOverrides, o as PromptService, p as PrunePartialFailureError, V as VectorRanker, q as VectorRankerFallback, r as VectorRankerRankArgs, s as VectorRankerSemanticResult, t as WikiBusyError, u as WikiBusyOperation, v as WikiCheckpoint, w as WikiConfig, x as WikiEdge, y as WikiEvent, z as WikiFact, A as WikiMemoryTestAccess, B as WikiOutboxEvent, C as WikiTask } from './testing-D02cdI9A.js';
3
3
  import { OkfFile } from '@equationalapplications/core-okf';
4
4
  import 'minisearch';
5
5
 
6
6
  declare function formatContext(bundle: MemoryBundle, options?: FormatContextOptions): string;
7
7
 
8
+ /**
9
+ * Pure presenter — dense text serialization of a GraphNeighborhood for LLM prompt
10
+ * injection. Deterministic: same input always produces byte-identical output
11
+ * (matters for prompt caching).
12
+ */
13
+ declare function formatGraphContext(neighborhood: GraphNeighborhood): string;
14
+
8
15
  declare function formatMemoryDump(dump: MemoryDump): FormattedMemoryDump;
9
16
 
10
17
  declare function formatOkfBundle(dump: MemoryDump): {
@@ -19,6 +26,16 @@ declare function parseOkfBundle(entityId: string, files: OkfFile[], options?: Ok
19
26
 
20
27
  declare function parseEmbedding(blob: Uint8Array | null | undefined, text: string | null | undefined): Float32Array | null;
21
28
 
29
+ type GetRandomValues = (bytes: Uint8Array) => void;
30
+ /**
31
+ * Inject a platform-specific `getRandomValues` implementation.
32
+ * Call this once at module load time from the platform adapter
33
+ * (e.g. expo-crypto's getRandomValues) when the global `crypto` API is
34
+ * absent — Hermes / React Native being the primary case.
35
+ * Pass `null` to clear a previously injected source.
36
+ */
37
+ declare function configureRandomSource(fn: GetRandomValues | null): void;
38
+
22
39
  interface LibrarianOptions {
23
40
  /** If provided, replaces the default Librarian system instructions. */
24
41
  systemPrompt?: string;
@@ -43,4 +60,4 @@ declare function mapLibrarianOptionsToReadOptions(options: LibrarianOptions): Pi
43
60
 
44
61
  declare function createWiki(db: SQLiteAdapter, options: WikiOptions): WikiMemory;
45
62
 
46
- export { DEFAULT_LIBRARIAN_SYNTHESIS_PROMPT, FormatContextOptions, FormattedMemoryDump, type LibrarianOptions, type LibrarianPromptVariables, MemoryBundle, MemoryDump, type OkfImportOptions, ReadOptions, SQLiteAdapter, WikiMemory, WikiOptions, createWiki, formatContext, formatMemoryDump, formatOkfBundle, hydrateLibrarianPrompt, mapLibrarianOptionsToReadOptions, parseEmbedding, parseOkfBundle, validateLibrarianPromptTemplate };
63
+ export { DEFAULT_LIBRARIAN_SYNTHESIS_PROMPT, FormatContextOptions, FormattedMemoryDump, GraphNeighborhood, type LibrarianOptions, type LibrarianPromptVariables, MemoryBundle, MemoryDump, type OkfImportOptions, ReadOptions, SQLiteAdapter, WikiMemory, WikiOptions, configureRandomSource, createWiki, formatContext, formatGraphContext, formatMemoryDump, formatOkfBundle, hydrateLibrarianPrompt, mapLibrarianOptionsToReadOptions, parseEmbedding, parseOkfBundle, validateLibrarianPromptTemplate };
package/dist/index.js CHANGED
@@ -983,6 +983,10 @@ var EntryRepository = class extends BaseRepository {
983
983
  };
984
984
 
985
985
  // src/utils/ids.ts
986
+ var _injectedGetRandomValues = null;
987
+ function configureRandomSource(fn) {
988
+ _injectedGetRandomValues = fn;
989
+ }
986
990
  function generateId(prefix = "") {
987
991
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
988
992
  return prefix + crypto.randomUUID().replace(/-/g, "").substring(0, 24);
@@ -992,8 +996,13 @@ function generateId(prefix = "") {
992
996
  crypto.getRandomValues(bytes);
993
997
  return prefix + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("").substring(0, 24);
994
998
  }
999
+ if (_injectedGetRandomValues) {
1000
+ const bytes = new Uint8Array(16);
1001
+ _injectedGetRandomValues(bytes);
1002
+ return prefix + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("").substring(0, 24);
1003
+ }
995
1004
  throw new Error(
996
- "generateId: no cryptographically secure random source available (crypto.randomUUID and crypto.getRandomValues are both missing)."
1005
+ "generateId: no cryptographically secure random source available (crypto.randomUUID and crypto.getRandomValues are both missing, and no configureRandomSource() injection was provided)."
997
1006
  );
998
1007
  }
999
1008
 
@@ -1407,6 +1416,11 @@ var EventRepository = class extends BaseRepository {
1407
1416
  };
1408
1417
 
1409
1418
  // src/repositories/EdgeRepository.ts
1419
+ var CONFIDENCE_RANK = {
1420
+ tentative: 0,
1421
+ inferred: 1,
1422
+ certain: 2
1423
+ };
1410
1424
  var EdgeRepository = class extends BaseRepository {
1411
1425
  /**
1412
1426
  * Insert an edge, silently skipping on primary-key or uniqueness conflicts.
@@ -1437,21 +1451,111 @@ var EdgeRepository = class extends BaseRepository {
1437
1451
  `SELECT * FROM ${this.prefix}edges WHERE entity_id = ? ORDER BY created_at ASC`,
1438
1452
  [entityId]
1439
1453
  );
1440
- return rows.map((row) => ({
1441
- id: String(row.id),
1442
- entity_id: String(row.entity_id),
1443
- source_id: String(row.source_id),
1444
- target_id: String(row.target_id),
1445
- edge_type: String(row.edge_type),
1446
- created_at: Number(row.created_at)
1447
- }));
1454
+ return rows.map(mapRowToEdge);
1448
1455
  }
1449
1456
  /** Hard delete — edges have no soft-delete concept, only presence/absence. `tx` is REQUIRED. */
1450
1457
  async bulkDeleteByEntityId(entityId, tx) {
1451
1458
  const executor = this.getExecutor(tx);
1452
1459
  await executor.runAsync(`DELETE FROM ${this.prefix}edges WHERE entity_id = ?`, [entityId]);
1453
1460
  }
1461
+ /**
1462
+ * Multi-hop traversal from `sourceId` via SQLite `WITH RECURSIVE`. All filtering,
1463
+ * dead-ending, cycle-guarding, capping, and ordering happens in this one query.
1464
+ * The anchor is validated (exists, right entity, not soft-deleted) but never gated
1465
+ * by confidence/source_type — only nodes discovered beyond it are.
1466
+ */
1467
+ async getNeighborhood(entityId, sourceId, opts, tx) {
1468
+ const executor = this.getExecutor(tx);
1469
+ if (opts.edgeTypes && opts.edgeTypes.length === 0) {
1470
+ const anchor = await executor.getFirstAsync(
1471
+ `SELECT id FROM ${this.prefix}entries WHERE id = ? AND entity_id = ? AND deleted_at IS NULL`,
1472
+ [sourceId, entityId]
1473
+ );
1474
+ return { nodeIds: anchor ? [anchor.id] : [], edges: [] };
1475
+ }
1476
+ const edgeTypesClause = opts.edgeTypes ? `e.edge_type IN (${opts.edgeTypes.map(() => "?").join(",")})` : "1=1";
1477
+ const excludeSourceTypesPlaceholders = opts.excludeSourceTypes.map(() => "?").join(",");
1478
+ const minConfidenceRank = CONFIDENCE_RANK[opts.minConfidence];
1479
+ const sql = `
1480
+ WITH RECURSIVE walk(node_id, depth, visited) AS (
1481
+ SELECT id, 0, ',' || id || ','
1482
+ FROM ${this.prefix}entries
1483
+ WHERE id = ? AND entity_id = ? AND deleted_at IS NULL
1484
+
1485
+ UNION
1486
+
1487
+ SELECT
1488
+ CASE WHEN e.source_id = w.node_id THEN e.target_id ELSE e.source_id END,
1489
+ w.depth + 1,
1490
+ w.visited || (CASE WHEN e.source_id = w.node_id THEN e.target_id ELSE e.source_id END) || ','
1491
+ FROM walk w
1492
+ JOIN ${this.prefix}edges e
1493
+ ON e.entity_id = ?
1494
+ AND (
1495
+ (? != 'inbound' AND e.source_id = w.node_id) OR
1496
+ (? != 'outbound' AND e.target_id = w.node_id)
1497
+ )
1498
+ AND (${edgeTypesClause})
1499
+ JOIN ${this.prefix}entries n
1500
+ ON n.id = (CASE WHEN e.source_id = w.node_id THEN e.target_id ELSE e.source_id END)
1501
+ AND n.entity_id = ?
1502
+ AND n.deleted_at IS NULL
1503
+ AND (
1504
+ CASE n.confidence
1505
+ WHEN 'tentative' THEN 0
1506
+ WHEN 'inferred' THEN 1
1507
+ WHEN 'certain' THEN 2
1508
+ ELSE -1
1509
+ END
1510
+ ) >= ?
1511
+ AND n.source_type NOT IN (${excludeSourceTypesPlaceholders})
1512
+ WHERE w.depth < ?
1513
+ AND instr(w.visited, ',' || (CASE WHEN e.source_id = w.node_id THEN e.target_id ELSE e.source_id END) || ',') = 0
1514
+ )
1515
+ SELECT node_id, MIN(depth) AS depth
1516
+ FROM walk
1517
+ GROUP BY node_id
1518
+ ORDER BY depth ASC, (SELECT updated_at FROM ${this.prefix}entries WHERE id = node_id) DESC
1519
+ LIMIT ?
1520
+ `;
1521
+ const params = [
1522
+ sourceId,
1523
+ entityId,
1524
+ entityId,
1525
+ opts.direction,
1526
+ opts.direction,
1527
+ ...opts.edgeTypes ?? [],
1528
+ entityId,
1529
+ minConfidenceRank,
1530
+ ...opts.excludeSourceTypes,
1531
+ opts.maxDepth,
1532
+ opts.maxNodes
1533
+ ];
1534
+ const rows = await executor.getAllAsync(sql, params);
1535
+ const nodeIds = rows.map((r) => r.node_id);
1536
+ if (nodeIds.length === 0) return { nodeIds: [], edges: [] };
1537
+ const valueRows = nodeIds.map(() => "(?)").join(", ");
1538
+ const edgeRows = await executor.getAllAsync(
1539
+ `WITH neighborhood(node_id) AS (VALUES ${valueRows})
1540
+ SELECT e.* FROM ${this.prefix}edges e
1541
+ JOIN neighborhood ns ON e.source_id = ns.node_id
1542
+ JOIN neighborhood nt ON e.target_id = nt.node_id
1543
+ WHERE e.entity_id = ?`,
1544
+ [...nodeIds, entityId]
1545
+ );
1546
+ return { nodeIds, edges: edgeRows.map(mapRowToEdge) };
1547
+ }
1454
1548
  };
1549
+ function mapRowToEdge(row) {
1550
+ return {
1551
+ id: String(row.id),
1552
+ entity_id: String(row.entity_id),
1553
+ source_id: String(row.source_id),
1554
+ target_id: String(row.target_id),
1555
+ edge_type: String(row.edge_type),
1556
+ created_at: Number(row.created_at)
1557
+ };
1558
+ }
1455
1559
 
1456
1560
  // src/utils/ontology.ts
1457
1561
  function emptyManifest() {
@@ -4481,6 +4585,38 @@ var OntologyService = class {
4481
4585
  }
4482
4586
  };
4483
4587
 
4588
+ // src/services/GraphTraversalService.ts
4589
+ var GraphTraversalService = class {
4590
+ constructor(edgeRepo, entryRepo, config) {
4591
+ this.edgeRepo = edgeRepo;
4592
+ this.entryRepo = entryRepo;
4593
+ this.config = config;
4594
+ }
4595
+ async traverseGraph(entityId, options) {
4596
+ const fallbackMaxNodes = 20;
4597
+ const rawConfigDefault = this.config.maxTraversalNodes ?? fallbackMaxNodes;
4598
+ const defaultMaxNodes = Number.isFinite(rawConfigDefault) && rawConfigDefault >= 1 ? Math.floor(rawConfigDefault) : fallbackMaxNodes;
4599
+ const rawMaxNodes = options.maxTraversalNodes ?? defaultMaxNodes;
4600
+ const maxNodes = Number.isFinite(rawMaxNodes) && rawMaxNodes >= 1 ? Math.floor(rawMaxNodes) : defaultMaxNodes;
4601
+ const opts = {
4602
+ maxDepth: Math.max(1, Math.min(options.maxDepth ?? 1, 3)),
4603
+ direction: options.direction ?? this.config.traversalDirection ?? "both",
4604
+ edgeTypes: options.edgeTypes,
4605
+ minConfidence: options.minTraversalConfidence ?? this.config.minTraversalConfidence ?? "tentative",
4606
+ excludeSourceTypes: options.excludeSourceTypes ?? this.config.excludeSourceTypes ?? [],
4607
+ maxNodes
4608
+ };
4609
+ const { nodeIds, edges } = await this.edgeRepo.getNeighborhood(entityId, options.sourceId, opts);
4610
+ if (nodeIds.length === 0) return { nodes: [], edges: [] };
4611
+ const nodes = await this.entryRepo.findByIds(nodeIds, [entityId]);
4612
+ const hydratedIds = new Set(nodes.map((node) => node.id));
4613
+ const filteredEdges = edges.filter(
4614
+ (edge) => hydratedIds.has(edge.source_id) && hydratedIds.has(edge.target_id)
4615
+ );
4616
+ return { nodes, edges: filteredEdges };
4617
+ }
4618
+ };
4619
+
4484
4620
  // src/WikiMemory.ts
4485
4621
  var TABLE_PREFIX_PATTERN = /^[A-Za-z][A-Za-z0-9_]{0,30}_$/;
4486
4622
  var _testAccessNonTestEnvWarned;
@@ -4564,6 +4700,11 @@ var WikiMemory = class {
4564
4700
  this.jobManager,
4565
4701
  this.maintenanceService
4566
4702
  );
4703
+ this.graphTraversalService = new GraphTraversalService(
4704
+ this.edgeRepo,
4705
+ this.entryRepo,
4706
+ this.options.config ?? {}
4707
+ );
4567
4708
  }
4568
4709
  /**
4569
4710
  * Explicit escape hatch for test suites: typed access to composed services for mocks/spies.
@@ -4584,6 +4725,7 @@ var WikiMemory = class {
4584
4725
  searchService: this.searchService,
4585
4726
  writeService: this.writeService,
4586
4727
  promptService: this.promptService,
4728
+ graphTraversalService: this.graphTraversalService,
4587
4729
  entryRepo: this.entryRepo,
4588
4730
  metadataRepo: this.metadataRepo,
4589
4731
  jobManager: this.jobManager
@@ -4654,6 +4796,9 @@ var WikiMemory = class {
4654
4796
  async read(entityId, query, options) {
4655
4797
  return this.retrievalService.read(entityId, query, options);
4656
4798
  }
4799
+ async traverseGraph(entityId, options) {
4800
+ return this.graphTraversalService.traverseGraph(entityId, options);
4801
+ }
4657
4802
  async getMemoryBundle(entityId) {
4658
4803
  return this.importExportService.getFullBundle(entityId, { maxEvents: 10 });
4659
4804
  }
@@ -4881,6 +5026,38 @@ function formatContext(bundle, options) {
4881
5026
  return lines.join("\n");
4882
5027
  }
4883
5028
 
5029
+ // src/utils/formatGraphContext.ts
5030
+ function formatGraphContext(neighborhood) {
5031
+ const { nodes, edges } = neighborhood;
5032
+ if (nodes.length === 0) return "";
5033
+ const nodeById = new Map(nodes.map((n) => [n.id, n]));
5034
+ const nodeIndex = new Map(nodes.map((n, i) => [n.id, i]));
5035
+ const lines = [];
5036
+ for (const node of nodes) {
5037
+ lines.push(`[${node.okf_type ?? "fact"}] ${node.title} (ID: ${node.id})`);
5038
+ const outbound = edgesByDirection(edges, node.id, "source_id", nodeById, nodeIndex);
5039
+ const inbound = edgesByDirection(edges, node.id, "target_id", nodeById, nodeIndex);
5040
+ for (const { edge, other } of outbound) {
5041
+ lines.push(` -[${edge.edge_type}]-> [${other.okf_type ?? "fact"}] ${other.title}`);
5042
+ }
5043
+ for (const { edge, other } of inbound) {
5044
+ lines.push(` <-[${edge.edge_type}]- [${other.okf_type ?? "fact"}] ${other.title}`);
5045
+ }
5046
+ }
5047
+ return lines.join("\n");
5048
+ }
5049
+ function edgesByDirection(edges, nodeId, endpoint, nodeById, nodeIndex) {
5050
+ const otherEndpoint = endpoint === "source_id" ? "target_id" : "source_id";
5051
+ return edges.filter((e) => e[endpoint] === nodeId).filter((e) => {
5052
+ const otherId = e[otherEndpoint];
5053
+ const selfIdx = nodeIndex.get(nodeId);
5054
+ const otherIdx = nodeIndex.get(otherId);
5055
+ return otherIdx !== void 0 && selfIdx < otherIdx;
5056
+ }).map((edge) => ({ edge, other: nodeById.get(edge[otherEndpoint]) })).sort(
5057
+ (a, b) => a.edge.edge_type.localeCompare(b.edge.edge_type) || a.other.title.localeCompare(b.other.title) || a.other.id.localeCompare(b.other.id) || a.edge.id.localeCompare(b.edge.id)
5058
+ );
5059
+ }
5060
+
4884
5061
  // src/utils/sanitizeForFilename.ts
4885
5062
  function shortHash(value) {
4886
5063
  let h1 = 5381;
@@ -5395,8 +5572,10 @@ exports.PromptService = PromptService;
5395
5572
  exports.PrunePartialFailureError = PrunePartialFailureError;
5396
5573
  exports.WikiBusyError = WikiBusyError;
5397
5574
  exports.WikiMemory = WikiMemory;
5575
+ exports.configureRandomSource = configureRandomSource;
5398
5576
  exports.createWiki = createWiki;
5399
5577
  exports.formatContext = formatContext;
5578
+ exports.formatGraphContext = formatGraphContext;
5400
5579
  exports.formatMemoryDump = formatMemoryDump;
5401
5580
  exports.formatOkfBundle = formatOkfBundle;
5402
5581
  exports.hydrateLibrarianPrompt = hydrateLibrarianPrompt;