@equationalapplications/core-llm-wiki 4.14.1 → 4.15.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.d.mts CHANGED
@@ -1,5 +1,5 @@
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-DW1qufP0.mjs';
2
- export { E as EntityStatus, d as ExtractedFact, e as ExtractedTask, H as HOOK_TIMEOUT_MARKER, L as LLMProvider, P as PromptOverrides, f as PromptService, g as PrunePartialFailureError, V as VectorRanker, h as VectorRankerFallback, i as VectorRankerRankArgs, j as VectorRankerSemanticResult, k as WikiBusyError, l as WikiBusyOperation, m as WikiCheckpoint, n as WikiConfig, o as WikiEvent, p as WikiFact, q as WikiMemoryTestAccess, r as WikiOutboxEvent, s as WikiTask } from './testing-DW1qufP0.mjs';
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-NH1_Aigh.mjs';
2
+ export { E as EntityStatus, d as ExtractedFact, e as ExtractedTask, H as HOOK_TIMEOUT_MARKER, L as LLMProvider, P as PromptOverrides, f as PromptService, g as PrunePartialFailureError, V as VectorRanker, h as VectorRankerFallback, i as VectorRankerRankArgs, j as VectorRankerSemanticResult, k as WikiBusyError, l as WikiBusyOperation, m as WikiCheckpoint, n as WikiConfig, o as WikiEdge, p as WikiEvent, q as WikiFact, r as WikiMemoryTestAccess, s as WikiOutboxEvent, t as WikiTask } from './testing-NH1_Aigh.mjs';
3
3
  import { OkfFile } from '@equationalapplications/core-okf';
4
4
  import 'minisearch';
5
5
 
@@ -11,6 +11,12 @@ declare function formatOkfBundle(dump: MemoryDump): {
11
11
  files: OkfFile[];
12
12
  };
13
13
 
14
+ interface OkfImportOptions {
15
+ typeMapping?: Record<string, 'fact' | 'task' | 'ignore'>;
16
+ defaultSchema?: 'fact' | 'task' | 'ignore';
17
+ }
18
+ declare function parseOkfBundle(entityId: string, files: OkfFile[], options?: OkfImportOptions): MemoryDump;
19
+
14
20
  declare function parseEmbedding(blob: Uint8Array | null | undefined, text: string | null | undefined): Float32Array | null;
15
21
 
16
22
  interface LibrarianOptions {
@@ -37,4 +43,4 @@ declare function mapLibrarianOptionsToReadOptions(options: LibrarianOptions): Pi
37
43
 
38
44
  declare function createWiki(db: SQLiteAdapter, options: WikiOptions): WikiMemory;
39
45
 
40
- export { DEFAULT_LIBRARIAN_SYNTHESIS_PROMPT, FormatContextOptions, FormattedMemoryDump, type LibrarianOptions, type LibrarianPromptVariables, MemoryBundle, MemoryDump, ReadOptions, SQLiteAdapter, WikiMemory, WikiOptions, createWiki, formatContext, formatMemoryDump, formatOkfBundle, hydrateLibrarianPrompt, mapLibrarianOptionsToReadOptions, parseEmbedding, validateLibrarianPromptTemplate };
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 };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
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-DW1qufP0.js';
2
- export { E as EntityStatus, d as ExtractedFact, e as ExtractedTask, H as HOOK_TIMEOUT_MARKER, L as LLMProvider, P as PromptOverrides, f as PromptService, g as PrunePartialFailureError, V as VectorRanker, h as VectorRankerFallback, i as VectorRankerRankArgs, j as VectorRankerSemanticResult, k as WikiBusyError, l as WikiBusyOperation, m as WikiCheckpoint, n as WikiConfig, o as WikiEvent, p as WikiFact, q as WikiMemoryTestAccess, r as WikiOutboxEvent, s as WikiTask } from './testing-DW1qufP0.js';
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-NH1_Aigh.js';
2
+ export { E as EntityStatus, d as ExtractedFact, e as ExtractedTask, H as HOOK_TIMEOUT_MARKER, L as LLMProvider, P as PromptOverrides, f as PromptService, g as PrunePartialFailureError, V as VectorRanker, h as VectorRankerFallback, i as VectorRankerRankArgs, j as VectorRankerSemanticResult, k as WikiBusyError, l as WikiBusyOperation, m as WikiCheckpoint, n as WikiConfig, o as WikiEdge, p as WikiEvent, q as WikiFact, r as WikiMemoryTestAccess, s as WikiOutboxEvent, t as WikiTask } from './testing-NH1_Aigh.js';
3
3
  import { OkfFile } from '@equationalapplications/core-okf';
4
4
  import 'minisearch';
5
5
 
@@ -11,6 +11,12 @@ declare function formatOkfBundle(dump: MemoryDump): {
11
11
  files: OkfFile[];
12
12
  };
13
13
 
14
+ interface OkfImportOptions {
15
+ typeMapping?: Record<string, 'fact' | 'task' | 'ignore'>;
16
+ defaultSchema?: 'fact' | 'task' | 'ignore';
17
+ }
18
+ declare function parseOkfBundle(entityId: string, files: OkfFile[], options?: OkfImportOptions): MemoryDump;
19
+
14
20
  declare function parseEmbedding(blob: Uint8Array | null | undefined, text: string | null | undefined): Float32Array | null;
15
21
 
16
22
  interface LibrarianOptions {
@@ -37,4 +43,4 @@ declare function mapLibrarianOptionsToReadOptions(options: LibrarianOptions): Pi
37
43
 
38
44
  declare function createWiki(db: SQLiteAdapter, options: WikiOptions): WikiMemory;
39
45
 
40
- export { DEFAULT_LIBRARIAN_SYNTHESIS_PROMPT, FormatContextOptions, FormattedMemoryDump, type LibrarianOptions, type LibrarianPromptVariables, MemoryBundle, MemoryDump, ReadOptions, SQLiteAdapter, WikiMemory, WikiOptions, createWiki, formatContext, formatMemoryDump, formatOkfBundle, hydrateLibrarianPrompt, mapLibrarianOptionsToReadOptions, parseEmbedding, validateLibrarianPromptTemplate };
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 };
package/dist/index.js CHANGED
@@ -34,7 +34,8 @@ async function setupDatabase(db, prefix) {
34
34
  access_count INTEGER NOT NULL DEFAULT 0,
35
35
  deleted_at INTEGER,
36
36
  embedding TEXT,
37
- embedding_blob BLOB
37
+ embedding_blob BLOB,
38
+ okf_type TEXT
38
39
  );
39
40
 
40
41
  CREATE INDEX IF NOT EXISTS ${prefix}entries_entity_idx ON ${prefix}entries(entity_id);
@@ -51,11 +52,24 @@ async function setupDatabase(db, prefix) {
51
52
  created_at INTEGER NOT NULL,
52
53
  updated_at INTEGER NOT NULL,
53
54
  resolved_at INTEGER,
54
- deleted_at INTEGER
55
+ deleted_at INTEGER,
56
+ okf_type TEXT
55
57
  );
56
58
 
57
59
  CREATE INDEX IF NOT EXISTS ${prefix}tasks_entity_idx ON ${prefix}tasks(entity_id, status);
58
60
 
61
+ CREATE TABLE IF NOT EXISTS ${prefix}edges (
62
+ id TEXT PRIMARY KEY,
63
+ entity_id TEXT NOT NULL,
64
+ source_id TEXT NOT NULL,
65
+ target_id TEXT NOT NULL,
66
+ edge_type TEXT NOT NULL,
67
+ created_at INTEGER NOT NULL,
68
+ UNIQUE(entity_id, source_id, target_id, edge_type)
69
+ );
70
+
71
+ CREATE INDEX IF NOT EXISTS ${prefix}edges_entity_idx ON ${prefix}edges(entity_id);
72
+
59
73
  CREATE TABLE IF NOT EXISTS ${prefix}events (
60
74
  id TEXT PRIMARY KEY,
61
75
  entity_id TEXT NOT NULL,
@@ -156,6 +170,32 @@ var MIGRATIONS = [
156
170
  ON ${prefix}outbox (entity_id, created_at);
157
171
  `);
158
172
  }
173
+ },
174
+ {
175
+ version: 5,
176
+ description: "Add okf_type to entries/tasks for OKF type fidelity; create edges table for OKF graph import",
177
+ run: async (db, prefix) => {
178
+ for (const table of ["entries", "tasks"]) {
179
+ const cols = await db.getAllAsync(
180
+ `PRAGMA table_info(${prefix}${table})`
181
+ );
182
+ if (!cols.some((c) => c.name === "okf_type")) {
183
+ await db.execAsync(`ALTER TABLE ${prefix}${table} ADD COLUMN okf_type TEXT`);
184
+ }
185
+ }
186
+ await db.execAsync(`
187
+ CREATE TABLE IF NOT EXISTS ${prefix}edges (
188
+ id TEXT PRIMARY KEY,
189
+ entity_id TEXT NOT NULL,
190
+ source_id TEXT NOT NULL,
191
+ target_id TEXT NOT NULL,
192
+ edge_type TEXT NOT NULL,
193
+ created_at INTEGER NOT NULL,
194
+ UNIQUE(entity_id, source_id, target_id, edge_type)
195
+ );
196
+ CREATE INDEX IF NOT EXISTS ${prefix}edges_entity_idx ON ${prefix}edges (entity_id);
197
+ `);
198
+ }
159
199
  }
160
200
  ];
161
201
  for (let i = 1; i < MIGRATIONS.length; i++) {
@@ -207,7 +247,8 @@ function mapRowToFact(row) {
207
247
  updated_at: Number(row.updated_at),
208
248
  last_accessed_at: row.last_accessed_at === null || row.last_accessed_at === void 0 ? null : Number(row.last_accessed_at),
209
249
  deleted_at: row.deleted_at != null ? Number(row.deleted_at) : null,
210
- access_count: Number(row.access_count ?? 0)
250
+ access_count: Number(row.access_count ?? 0),
251
+ okf_type: row.okf_type ?? null
211
252
  };
212
253
  }
213
254
  function normalizeEmbeddingBlobValue(blob) {
@@ -380,8 +421,8 @@ var EntryRepository = class extends BaseRepository {
380
421
  `INSERT INTO ${this.prefix}entries (
381
422
  id, entity_id, title, body, tags, confidence, source_type,
382
423
  source_hash, source_ref, created_at, updated_at, last_accessed_at, access_count,
383
- deleted_at, embedding_blob, embedding
384
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
424
+ deleted_at, embedding_blob, embedding, okf_type
425
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
385
426
  ON CONFLICT(id) DO UPDATE SET
386
427
  entity_id = excluded.entity_id,
387
428
  title = excluded.title,
@@ -397,7 +438,8 @@ var EntryRepository = class extends BaseRepository {
397
438
  access_count = excluded.access_count,
398
439
  deleted_at = excluded.deleted_at,
399
440
  embedding_blob = excluded.embedding_blob,
400
- embedding = NULL`,
441
+ embedding = NULL,
442
+ okf_type = excluded.okf_type`,
401
443
  [
402
444
  fact.id,
403
445
  fact.entity_id,
@@ -414,7 +456,8 @@ var EntryRepository = class extends BaseRepository {
414
456
  fact.access_count,
415
457
  fact.deleted_at ?? null,
416
458
  embeddingBlob ?? null,
417
- null
459
+ null,
460
+ fact.okf_type ?? null
418
461
  ]
419
462
  );
420
463
  return result;
@@ -994,7 +1037,8 @@ function mapRowToTask(row) {
994
1037
  created_at: Number(row.created_at),
995
1038
  updated_at: Number(row.updated_at),
996
1039
  resolved_at: row.resolved_at != null ? Number(row.resolved_at) : null,
997
- deleted_at: row.deleted_at != null ? Number(row.deleted_at) : null
1040
+ deleted_at: row.deleted_at != null ? Number(row.deleted_at) : null,
1041
+ okf_type: row.okf_type ?? null
998
1042
  };
999
1043
  }
1000
1044
  var TaskRepository = class extends BaseRepository {
@@ -1096,8 +1140,8 @@ var TaskRepository = class extends BaseRepository {
1096
1140
  await executor.runAsync(
1097
1141
  `INSERT INTO ${this.prefix}tasks (
1098
1142
  id, entity_id, description, status, priority,
1099
- created_at, updated_at, resolved_at, deleted_at
1100
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
1143
+ created_at, updated_at, resolved_at, deleted_at, okf_type
1144
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1101
1145
  ON CONFLICT(id) DO UPDATE SET
1102
1146
  entity_id = excluded.entity_id,
1103
1147
  description = excluded.description,
@@ -1105,7 +1149,8 @@ var TaskRepository = class extends BaseRepository {
1105
1149
  priority = excluded.priority,
1106
1150
  updated_at = excluded.updated_at,
1107
1151
  resolved_at = excluded.resolved_at,
1108
- deleted_at = excluded.deleted_at`,
1152
+ deleted_at = excluded.deleted_at,
1153
+ okf_type = excluded.okf_type`,
1109
1154
  [
1110
1155
  task.id,
1111
1156
  task.entity_id,
@@ -1115,7 +1160,8 @@ var TaskRepository = class extends BaseRepository {
1115
1160
  task.created_at,
1116
1161
  now,
1117
1162
  task.resolved_at ?? null,
1118
- task.deleted_at ?? null
1163
+ task.deleted_at ?? null,
1164
+ task.okf_type ?? null
1119
1165
  ]
1120
1166
  );
1121
1167
  }
@@ -1337,6 +1383,53 @@ var EventRepository = class extends BaseRepository {
1337
1383
  }
1338
1384
  };
1339
1385
 
1386
+ // src/repositories/EdgeRepository.ts
1387
+ var EdgeRepository = class extends BaseRepository {
1388
+ /**
1389
+ * Insert an edge, silently skipping on primary-key or uniqueness conflicts.
1390
+ * Throws when the insert was skipped due to an id collision with a different edge tuple.
1391
+ */
1392
+ async addIgnoreDuplicate(edge, tx) {
1393
+ const executor = this.getExecutor(tx);
1394
+ const result = await executor.runAsync(
1395
+ `INSERT OR IGNORE INTO ${this.prefix}edges (id, entity_id, source_id, target_id, edge_type, created_at)
1396
+ VALUES (?, ?, ?, ?, ?, ?)`,
1397
+ [edge.id, edge.entity_id, edge.source_id, edge.target_id, edge.edge_type, edge.created_at]
1398
+ );
1399
+ if (result.changes > 0) return;
1400
+ const existing = await executor.getFirstAsync(
1401
+ `SELECT entity_id, source_id, target_id, edge_type FROM ${this.prefix}edges WHERE id = ?`,
1402
+ [edge.id]
1403
+ );
1404
+ if (!existing) return;
1405
+ if (String(existing.entity_id) !== edge.entity_id || String(existing.source_id) !== edge.source_id || String(existing.target_id) !== edge.target_id || String(existing.edge_type) !== edge.edge_type) {
1406
+ throw new Error(
1407
+ `Edge id collision: ${JSON.stringify(edge.id)} already exists with a different (entity_id, source_id, target_id, edge_type) tuple`
1408
+ );
1409
+ }
1410
+ }
1411
+ async getByEntityId(entityId, tx) {
1412
+ const executor = this.getExecutor(tx);
1413
+ const rows = await executor.getAllAsync(
1414
+ `SELECT * FROM ${this.prefix}edges WHERE entity_id = ? ORDER BY created_at ASC`,
1415
+ [entityId]
1416
+ );
1417
+ return rows.map((row) => ({
1418
+ id: String(row.id),
1419
+ entity_id: String(row.entity_id),
1420
+ source_id: String(row.source_id),
1421
+ target_id: String(row.target_id),
1422
+ edge_type: String(row.edge_type),
1423
+ created_at: Number(row.created_at)
1424
+ }));
1425
+ }
1426
+ /** Hard delete — edges have no soft-delete concept, only presence/absence. `tx` is REQUIRED. */
1427
+ async bulkDeleteByEntityId(entityId, tx) {
1428
+ const executor = this.getExecutor(tx);
1429
+ await executor.runAsync(`DELETE FROM ${this.prefix}edges WHERE entity_id = ?`, [entityId]);
1430
+ }
1431
+ };
1432
+
1340
1433
  // src/repositories/MetadataRepository.ts
1341
1434
  var MetadataRepository = class extends BaseRepository {
1342
1435
  // CHECKPOINTS TABLE METHODS
@@ -2842,11 +2935,12 @@ var MAX_EMBEDDING_BLOB_BYTES = 32 * 1024;
2842
2935
  var IMPORT_TITLE_MAX = 500;
2843
2936
  var IMPORT_BODY_MAX = 8e3;
2844
2937
  var ImportExportService = class {
2845
- constructor(db, entryRepo, taskRepo, eventRepo, metadataRepo, searchService, jobManager, embeddingService) {
2938
+ constructor(db, entryRepo, taskRepo, eventRepo, edgeRepo, metadataRepo, searchService, jobManager, embeddingService) {
2846
2939
  this.db = db;
2847
2940
  this.entryRepo = entryRepo;
2848
2941
  this.taskRepo = taskRepo;
2849
2942
  this.eventRepo = eventRepo;
2943
+ this.edgeRepo = edgeRepo;
2850
2944
  this.metadataRepo = metadataRepo;
2851
2945
  this.searchService = searchService;
2852
2946
  this.jobManager = jobManager;
@@ -2891,10 +2985,11 @@ var ImportExportService = class {
2891
2985
  }
2892
2986
  }
2893
2987
  async getFullBundle(entityId, opts) {
2894
- const [factsRaw, tasks, events] = await Promise.all([
2988
+ const [factsRaw, tasks, events, edges] = await Promise.all([
2895
2989
  opts?.includeBlobs ? this.entryRepo.findAllByEntityIdWithBlobs(entityId) : this.entryRepo.findAllByEntityId(entityId),
2896
2990
  this.taskRepo.findAllByEntityId(entityId),
2897
- this.eventRepo.getByEntityId(entityId, opts?.maxEvents)
2991
+ this.eventRepo.getByEntityId(entityId, opts?.maxEvents),
2992
+ this.edgeRepo.getByEntityId(entityId)
2898
2993
  ]);
2899
2994
  const facts = factsRaw.map((f) => {
2900
2995
  const {
@@ -2913,7 +3008,7 @@ var ImportExportService = class {
2913
3008
  tags: typeof factBase.tags === "string" ? JSON.parse(factBase.tags) : factBase.tags
2914
3009
  };
2915
3010
  });
2916
- return { facts, tasks, events };
3011
+ return { facts, tasks, events, edges };
2917
3012
  }
2918
3013
  /** Single-entity import transaction + post-processing; package-internal hook for tests. */
2919
3014
  async doImportEntity(entityId, bundle, merge) {
@@ -2935,6 +3030,7 @@ var ImportExportService = class {
2935
3030
  softDeletedFactIds.push(...deletedLiveFactIds);
2936
3031
  await this.entryRepo.bulkSoftDeleteByEntityId(entityId, tx);
2937
3032
  await this.taskRepo.bulkSoftDeleteByEntityId(entityId, tx);
3033
+ await this.edgeRepo.bulkDeleteByEntityId(entityId, tx);
2938
3034
  await this.metadataRepo.deleteCheckpoint(entityId, tx);
2939
3035
  }
2940
3036
  const factIds = bundle.facts.map((fact) => fact.id);
@@ -3032,7 +3128,8 @@ var ImportExportService = class {
3032
3128
  last_accessed_at: fact.last_accessed_at,
3033
3129
  access_count: fact.access_count,
3034
3130
  deleted_at: fact.deleted_at,
3035
- embedding_blob: blobData ?? void 0
3131
+ embedding_blob: blobData ?? void 0,
3132
+ okf_type: fact.okf_type ?? null
3036
3133
  };
3037
3134
  await this.entryRepo.upsertForImport(factObj, tx);
3038
3135
  if (blobData != null) {
@@ -3081,7 +3178,8 @@ var ImportExportService = class {
3081
3178
  created_at: task.created_at,
3082
3179
  updated_at: safeUpdatedAt,
3083
3180
  resolved_at: task.resolved_at,
3084
- deleted_at: task.deleted_at
3181
+ deleted_at: task.deleted_at,
3182
+ okf_type: task.okf_type ?? null
3085
3183
  },
3086
3184
  tx,
3087
3185
  safeUpdatedAt
@@ -3105,6 +3203,19 @@ var ImportExportService = class {
3105
3203
  tx
3106
3204
  );
3107
3205
  }
3206
+ for (const edge of bundle.edges ?? []) {
3207
+ await this.edgeRepo.addIgnoreDuplicate(
3208
+ {
3209
+ id: edge.id,
3210
+ entity_id: entityId,
3211
+ source_id: edge.source_id,
3212
+ target_id: edge.target_id,
3213
+ edge_type: edge.edge_type,
3214
+ created_at: edge.created_at
3215
+ },
3216
+ tx
3217
+ );
3218
+ }
3108
3219
  });
3109
3220
  await this.searchService.sync(entityId);
3110
3221
  for (const fact of bundle.facts) {
@@ -3421,7 +3532,7 @@ var RetrievalService = class {
3421
3532
  const sanitizedTierWeights = shouldExposeReadMetadata(entityId) ? sanitizeTierWeights(entityIds, options?.tierWeights) : void 0;
3422
3533
  const exposeMetadata = shouldExposeReadMetadata(entityId);
3423
3534
  if (entityIds.length === 0) {
3424
- const empty = { facts: [], tasks: [], events: [] };
3535
+ const empty = { facts: [], tasks: [], events: [], edges: [] };
3425
3536
  if (exposeMetadata) {
3426
3537
  empty.metadata = { query, entityIds: [] };
3427
3538
  if (sanitizedTierWeights && Object.keys(sanitizedTierWeights).length > 0) empty.metadata.tierWeights = sanitizedTierWeights;
@@ -3811,7 +3922,7 @@ var RetrievalService = class {
3811
3922
  if (exposeMetadata && trimmedQuery && scoreByFactId) {
3812
3923
  factScores = Object.fromEntries(facts.map((fact) => [fact.id, scoreByFactId.get(fact.id) ?? 0]));
3813
3924
  }
3814
- const bundle = { facts, tasks, events: events.reverse() };
3925
+ const bundle = { facts, tasks, events: events.reverse(), edges: [] };
3815
3926
  if (exposeMetadata) {
3816
3927
  bundle.metadata = { query, entityIds };
3817
3928
  if (sanitizedTierWeights && Object.keys(sanitizedTierWeights).length > 0) bundle.metadata.tierWeights = sanitizedTierWeights;
@@ -4029,6 +4140,7 @@ var WikiMemory = class {
4029
4140
  this.entryRepo = new EntryRepository(db, this.prefix, this.outboxRepo);
4030
4141
  this.taskRepo = new TaskRepository(db, this.prefix, this.outboxRepo);
4031
4142
  this.eventRepo = new EventRepository(db, this.prefix);
4143
+ this.edgeRepo = new EdgeRepository(db, this.prefix);
4032
4144
  this.metadataRepo = new MetadataRepository(db, this.prefix);
4033
4145
  this.embeddingService = new EmbeddingService(this.db, this.options, this.entryRepo, this.metadataRepo);
4034
4146
  this.searchService = new SearchService(this.entryRepo);
@@ -4062,6 +4174,7 @@ var WikiMemory = class {
4062
4174
  this.entryRepo,
4063
4175
  this.taskRepo,
4064
4176
  this.eventRepo,
4177
+ this.edgeRepo,
4065
4178
  this.metadataRepo,
4066
4179
  this.searchService,
4067
4180
  this.jobManager,
@@ -4522,7 +4635,7 @@ function formatMemoryDump(dump) {
4522
4635
  }
4523
4636
  function factFrontmatter(f) {
4524
4637
  return {
4525
- type: "fact",
4638
+ type: f.okf_type ?? "fact",
4526
4639
  title: f.title,
4527
4640
  tags: f.tags,
4528
4641
  timestamp: new Date(f.updated_at).toISOString(),
@@ -4540,7 +4653,7 @@ function factFrontmatter(f) {
4540
4653
  }
4541
4654
  function taskFrontmatter(t) {
4542
4655
  return {
4543
- type: "task",
4656
+ type: t.okf_type ?? "task",
4544
4657
  title: t.description,
4545
4658
  timestamp: new Date(t.updated_at).toISOString(),
4546
4659
  id: t.id,
@@ -4615,6 +4728,227 @@ function formatOkfBundle(dump) {
4615
4728
  });
4616
4729
  return { files };
4617
4730
  }
4731
+ var CONFIDENCE_VALUES = /* @__PURE__ */ new Set(["certain", "inferred", "tentative"]);
4732
+ var SOURCE_TYPES = /* @__PURE__ */ new Set([
4733
+ "user_stated",
4734
+ "librarian_inferred",
4735
+ "user_confirmed",
4736
+ "immutable_document"
4737
+ ]);
4738
+ var TASK_STATUSES = /* @__PURE__ */ new Set(["pending", "in_progress", "done", "abandoned"]);
4739
+ var EVENT_TYPES = /* @__PURE__ */ new Set(["observation", "decision", "action", "outcome"]);
4740
+ function basenameMd(filePath) {
4741
+ const name = filePath.slice(filePath.lastIndexOf("/") + 1);
4742
+ return name.endsWith(".md") ? name.slice(0, -3) : name;
4743
+ }
4744
+ function isConceptFile(filePath) {
4745
+ if (!filePath.endsWith(".md")) return false;
4746
+ if (filePath.endsWith("/index.md") || filePath === "index.md") return false;
4747
+ if (filePath.endsWith("/log.md") || filePath === "log.md") return false;
4748
+ return true;
4749
+ }
4750
+ function isStructuralPath(filePath) {
4751
+ return filePath.endsWith("/index.md") || filePath === "index.md" || filePath.endsWith("/log.md") || filePath === "log.md";
4752
+ }
4753
+ function posixDirname(filePath) {
4754
+ const idx = filePath.lastIndexOf("/");
4755
+ return idx === -1 ? "" : filePath.slice(0, idx);
4756
+ }
4757
+ function resolveRelativePath(fromFile, linkPath) {
4758
+ const baseDir = posixDirname(fromFile);
4759
+ const segments = [...baseDir ? baseDir.split("/") : [], ...linkPath.split("/")];
4760
+ const resolved = [];
4761
+ for (const seg of segments) {
4762
+ if (seg === "" || seg === ".") continue;
4763
+ if (seg === "..") {
4764
+ resolved.pop();
4765
+ continue;
4766
+ }
4767
+ resolved.push(seg);
4768
+ }
4769
+ return resolved.join("/");
4770
+ }
4771
+ function addPathAliases(map, filePath, resolvedId) {
4772
+ map.set(filePath, resolvedId);
4773
+ const withoutDot = filePath.replace(/^\.\//, "");
4774
+ if (withoutDot !== filePath) map.set(withoutDot, resolvedId);
4775
+ const entityRelative = filePath.replace(/^entities\/[^/]+\//, "");
4776
+ if (entityRelative !== filePath) {
4777
+ map.set(entityRelative, resolvedId);
4778
+ map.set(`./${entityRelative}`, resolvedId);
4779
+ }
4780
+ }
4781
+ function lookupResolvedId(map, path) {
4782
+ const normalized = path.replace(/^\.\//, "");
4783
+ return map.get(path) ?? map.get(normalized) ?? map.get(`./${normalized}`);
4784
+ }
4785
+ function stripLinkSuffix(linkPath) {
4786
+ const hashIdx = linkPath.indexOf("#");
4787
+ const queryIdx = linkPath.indexOf("?");
4788
+ if (hashIdx === -1 && queryIdx === -1) return linkPath;
4789
+ const cut = hashIdx === -1 ? queryIdx : queryIdx === -1 ? hashIdx : Math.min(hashIdx, queryIdx);
4790
+ return linkPath.slice(0, cut);
4791
+ }
4792
+ function resolveRoute(filePath, frontmatterType, options) {
4793
+ if (options?.typeMapping && Object.prototype.hasOwnProperty.call(options.typeMapping, frontmatterType)) {
4794
+ return options.typeMapping[frontmatterType];
4795
+ }
4796
+ if (filePath.includes("/facts/")) return "fact";
4797
+ if (filePath.includes("/tasks/")) return "task";
4798
+ return options?.defaultSchema ?? "fact";
4799
+ }
4800
+ function parseFrontmatterTimestamp(value, fallback) {
4801
+ if (typeof value === "number" && Number.isFinite(value)) return value;
4802
+ if (typeof value === "string") {
4803
+ const parsed = Date.parse(value);
4804
+ if (Number.isFinite(parsed)) return parsed;
4805
+ }
4806
+ return fallback;
4807
+ }
4808
+ function unescapeLogSummary(summary) {
4809
+ return summary.replace(/\\\]/g, "]").replace(/\\\[/g, "[").replace(/\\\\/g, "\\");
4810
+ }
4811
+ var LOG_LINE_PATTERN = /^\(([^)]+)\)\s*(?:\[((?:\\.|[^\]])*)\]\(([^)]+)\)|(.+))$/;
4812
+ function parseLogEntryText(text) {
4813
+ const match = LOG_LINE_PATTERN.exec(text.trim());
4814
+ if (!match) return null;
4815
+ const [, rawType, linkedSummary, linkPath, plainSummary] = match;
4816
+ const event_type = EVENT_TYPES.has(rawType) ? rawType : "observation";
4817
+ if (linkPath) {
4818
+ return { event_type, summary: unescapeLogSummary(linkedSummary), linkPath };
4819
+ }
4820
+ return { event_type, summary: (plainSummary ?? "").trim() };
4821
+ }
4822
+ function frontmatterToFact(entityId, id, frontmatter, body, now) {
4823
+ const created_at = parseFrontmatterTimestamp(frontmatter.created_at, now);
4824
+ const updated_at = parseFrontmatterTimestamp(
4825
+ frontmatter.timestamp,
4826
+ parseFrontmatterTimestamp(frontmatter.updated_at, now)
4827
+ );
4828
+ return {
4829
+ id,
4830
+ entity_id: entityId,
4831
+ title: typeof frontmatter.title === "string" ? frontmatter.title : "",
4832
+ body,
4833
+ tags: Array.isArray(frontmatter.tags) ? frontmatter.tags.filter((t) => typeof t === "string") : [],
4834
+ confidence: CONFIDENCE_VALUES.has(String(frontmatter.confidence)) ? frontmatter.confidence : "tentative",
4835
+ source_type: SOURCE_TYPES.has(String(frontmatter.source_type)) ? frontmatter.source_type : "user_stated",
4836
+ source_hash: typeof frontmatter.source_hash === "string" ? frontmatter.source_hash : null,
4837
+ source_ref: typeof frontmatter.resource === "string" ? frontmatter.resource : null,
4838
+ created_at,
4839
+ updated_at,
4840
+ last_accessed_at: frontmatter.last_accessed_at != null ? parseFrontmatterTimestamp(frontmatter.last_accessed_at, now) : null,
4841
+ access_count: typeof frontmatter.access_count === "number" ? frontmatter.access_count : 0,
4842
+ deleted_at: frontmatter.deleted_at != null ? parseFrontmatterTimestamp(frontmatter.deleted_at, 0) : null,
4843
+ okf_type: frontmatter.type
4844
+ };
4845
+ }
4846
+ function frontmatterToTask(entityId, id, frontmatter, now) {
4847
+ const created_at = parseFrontmatterTimestamp(frontmatter.created_at, now);
4848
+ const updated_at = parseFrontmatterTimestamp(
4849
+ frontmatter.timestamp,
4850
+ parseFrontmatterTimestamp(frontmatter.updated_at, now)
4851
+ );
4852
+ return {
4853
+ id,
4854
+ entity_id: entityId,
4855
+ description: typeof frontmatter.title === "string" ? frontmatter.title : "",
4856
+ status: TASK_STATUSES.has(String(frontmatter.status)) ? frontmatter.status : "pending",
4857
+ priority: typeof frontmatter.priority === "number" ? frontmatter.priority : 0,
4858
+ created_at,
4859
+ updated_at,
4860
+ resolved_at: frontmatter.resolved_at != null ? parseFrontmatterTimestamp(frontmatter.resolved_at, now) : null,
4861
+ deleted_at: frontmatter.deleted_at != null ? parseFrontmatterTimestamp(frontmatter.deleted_at, 0) : null,
4862
+ okf_type: frontmatter.type
4863
+ };
4864
+ }
4865
+ function findLogMdPath(files) {
4866
+ return files.find((f) => f.path.endsWith("/log.md") || f.path === "log.md")?.path;
4867
+ }
4868
+ function parseOkfBundle(entityId, files, options) {
4869
+ const now = Date.now();
4870
+ const pathToResolvedId = /* @__PURE__ */ new Map();
4871
+ for (const file of files) {
4872
+ if (!isConceptFile(file.path)) continue;
4873
+ const { frontmatter } = coreOkf.parseConcept(file.content);
4874
+ const route = resolveRoute(file.path, frontmatter.type ?? "", options);
4875
+ if (route === "ignore") continue;
4876
+ const resolvedId = typeof frontmatter.id === "string" && frontmatter.id ? frontmatter.id : basenameMd(file.path);
4877
+ addPathAliases(pathToResolvedId, file.path, resolvedId);
4878
+ }
4879
+ const facts = [];
4880
+ const tasks = [];
4881
+ const edges = [];
4882
+ let logContent = null;
4883
+ const logMdPath = findLogMdPath(files);
4884
+ for (const file of files) {
4885
+ if (file.path.endsWith("/log.md") || file.path === "log.md") {
4886
+ logContent = file.content;
4887
+ continue;
4888
+ }
4889
+ if (!isConceptFile(file.path)) continue;
4890
+ const { frontmatter, body } = coreOkf.parseConcept(file.content);
4891
+ const route = resolveRoute(file.path, frontmatter.type ?? "", options);
4892
+ if (route === "ignore") continue;
4893
+ const resolvedId = typeof frontmatter.id === "string" && frontmatter.id ? frontmatter.id : basenameMd(file.path);
4894
+ if (route === "fact") {
4895
+ facts.push(frontmatterToFact(entityId, resolvedId, frontmatter, body, now));
4896
+ } else {
4897
+ tasks.push(frontmatterToTask(entityId, resolvedId, frontmatter, now));
4898
+ }
4899
+ const seenEdges = /* @__PURE__ */ new Set();
4900
+ for (const link of coreOkf.extractMarkdownLinks(body)) {
4901
+ const strippedPath = stripLinkSuffix(link.path);
4902
+ const directTargetId = lookupResolvedId(pathToResolvedId, strippedPath);
4903
+ const resolvedTargetPath = resolveRelativePath(file.path, strippedPath);
4904
+ if (isStructuralPath(strippedPath) || isStructuralPath(resolvedTargetPath)) continue;
4905
+ const targetId = directTargetId ?? lookupResolvedId(pathToResolvedId, resolvedTargetPath);
4906
+ if (!targetId) continue;
4907
+ const edgeKey = `${resolvedId}\0${targetId}\0${link.text}`;
4908
+ if (seenEdges.has(edgeKey)) continue;
4909
+ seenEdges.add(edgeKey);
4910
+ edges.push({
4911
+ id: generateId(),
4912
+ entity_id: entityId,
4913
+ source_id: resolvedId,
4914
+ target_id: targetId,
4915
+ edge_type: link.text,
4916
+ created_at: now
4917
+ });
4918
+ }
4919
+ }
4920
+ const events = [];
4921
+ if (logContent != null) {
4922
+ const logPath = logMdPath ?? `entities/${entityId}/log.md`;
4923
+ for (const entry of coreOkf.parseLogMd(logContent)) {
4924
+ const parsed = parseLogEntryText(entry.text);
4925
+ if (!parsed) continue;
4926
+ let related_entry_id = null;
4927
+ if (parsed.linkPath) {
4928
+ const targetPath = resolveRelativePath(logPath, stripLinkSuffix(parsed.linkPath));
4929
+ if (!isStructuralPath(targetPath) && targetPath.includes("/facts/")) {
4930
+ related_entry_id = lookupResolvedId(pathToResolvedId, targetPath) ?? null;
4931
+ }
4932
+ }
4933
+ const created_at = (/* @__PURE__ */ new Date(`${entry.date}T00:00:00.000Z`)).getTime();
4934
+ if (!Number.isFinite(created_at)) continue;
4935
+ events.push({
4936
+ id: generateId("evt_"),
4937
+ entity_id: entityId,
4938
+ event_type: parsed.event_type,
4939
+ summary: parsed.summary,
4940
+ related_entry_id,
4941
+ created_at
4942
+ });
4943
+ }
4944
+ }
4945
+ return {
4946
+ generatedAt: now,
4947
+ entities: {
4948
+ [entityId]: { facts, tasks, events, edges }
4949
+ }
4950
+ };
4951
+ }
4618
4952
 
4619
4953
  // src/librarianPrompt.ts
4620
4954
  var DEFAULT_LIBRARIAN_SYNTHESIS_PROMPT = `You are a careful memory synthesis assistant.
@@ -4673,6 +5007,7 @@ exports.formatOkfBundle = formatOkfBundle;
4673
5007
  exports.hydrateLibrarianPrompt = hydrateLibrarianPrompt;
4674
5008
  exports.mapLibrarianOptionsToReadOptions = mapLibrarianOptionsToReadOptions;
4675
5009
  exports.parseEmbedding = parseEmbedding;
5010
+ exports.parseOkfBundle = parseOkfBundle;
4676
5011
  exports.validateLibrarianPromptTemplate = validateLibrarianPromptTemplate;
4677
5012
  //# sourceMappingURL=index.js.map
4678
5013
  //# sourceMappingURL=index.js.map