@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.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { __privateAdd, EmbeddingService, SearchService, JobManager, PromptService, IngestionService, MaintenanceService, ImportExportService, RetrievalService, WriteService, __privateGet, __privateSet, normalizeSourceRef, normalizeSourceHash, generateId } from './chunk-24ANTHZB.mjs';
2
- export { HOOK_TIMEOUT_MARKER, PromptService, PrunePartialFailureError, WikiBusyError, parseEmbedding } from './chunk-24ANTHZB.mjs';
3
- import { buildConceptDocument, buildLogMd, buildIndexMd, buildRootIndexMd } from '@equationalapplications/core-okf';
1
+ import { __privateAdd, EmbeddingService, SearchService, JobManager, PromptService, IngestionService, MaintenanceService, ImportExportService, RetrievalService, WriteService, __privateGet, __privateSet, normalizeSourceRef, normalizeSourceHash, generateId } from './chunk-J4GBC6CP.mjs';
2
+ export { HOOK_TIMEOUT_MARKER, PromptService, PrunePartialFailureError, WikiBusyError, parseEmbedding } from './chunk-J4GBC6CP.mjs';
3
+ import { buildConceptDocument, buildLogMd, buildIndexMd, buildRootIndexMd, parseConcept, extractMarkdownLinks, parseLogMd } from '@equationalapplications/core-okf';
4
4
 
5
5
  // src/db/schema.ts
6
6
  async function setupDatabase(db, prefix) {
@@ -21,7 +21,8 @@ async function setupDatabase(db, prefix) {
21
21
  access_count INTEGER NOT NULL DEFAULT 0,
22
22
  deleted_at INTEGER,
23
23
  embedding TEXT,
24
- embedding_blob BLOB
24
+ embedding_blob BLOB,
25
+ okf_type TEXT
25
26
  );
26
27
 
27
28
  CREATE INDEX IF NOT EXISTS ${prefix}entries_entity_idx ON ${prefix}entries(entity_id);
@@ -38,11 +39,24 @@ async function setupDatabase(db, prefix) {
38
39
  created_at INTEGER NOT NULL,
39
40
  updated_at INTEGER NOT NULL,
40
41
  resolved_at INTEGER,
41
- deleted_at INTEGER
42
+ deleted_at INTEGER,
43
+ okf_type TEXT
42
44
  );
43
45
 
44
46
  CREATE INDEX IF NOT EXISTS ${prefix}tasks_entity_idx ON ${prefix}tasks(entity_id, status);
45
47
 
48
+ CREATE TABLE IF NOT EXISTS ${prefix}edges (
49
+ id TEXT PRIMARY KEY,
50
+ entity_id TEXT NOT NULL,
51
+ source_id TEXT NOT NULL,
52
+ target_id TEXT NOT NULL,
53
+ edge_type TEXT NOT NULL,
54
+ created_at INTEGER NOT NULL,
55
+ UNIQUE(entity_id, source_id, target_id, edge_type)
56
+ );
57
+
58
+ CREATE INDEX IF NOT EXISTS ${prefix}edges_entity_idx ON ${prefix}edges(entity_id);
59
+
46
60
  CREATE TABLE IF NOT EXISTS ${prefix}events (
47
61
  id TEXT PRIMARY KEY,
48
62
  entity_id TEXT NOT NULL,
@@ -143,6 +157,32 @@ var MIGRATIONS = [
143
157
  ON ${prefix}outbox (entity_id, created_at);
144
158
  `);
145
159
  }
160
+ },
161
+ {
162
+ version: 5,
163
+ description: "Add okf_type to entries/tasks for OKF type fidelity; create edges table for OKF graph import",
164
+ run: async (db, prefix) => {
165
+ for (const table of ["entries", "tasks"]) {
166
+ const cols = await db.getAllAsync(
167
+ `PRAGMA table_info(${prefix}${table})`
168
+ );
169
+ if (!cols.some((c) => c.name === "okf_type")) {
170
+ await db.execAsync(`ALTER TABLE ${prefix}${table} ADD COLUMN okf_type TEXT`);
171
+ }
172
+ }
173
+ await db.execAsync(`
174
+ CREATE TABLE IF NOT EXISTS ${prefix}edges (
175
+ id TEXT PRIMARY KEY,
176
+ entity_id TEXT NOT NULL,
177
+ source_id TEXT NOT NULL,
178
+ target_id TEXT NOT NULL,
179
+ edge_type TEXT NOT NULL,
180
+ created_at INTEGER NOT NULL,
181
+ UNIQUE(entity_id, source_id, target_id, edge_type)
182
+ );
183
+ CREATE INDEX IF NOT EXISTS ${prefix}edges_entity_idx ON ${prefix}edges (entity_id);
184
+ `);
185
+ }
146
186
  }
147
187
  ];
148
188
  for (let i = 1; i < MIGRATIONS.length; i++) {
@@ -194,7 +234,8 @@ function mapRowToFact(row) {
194
234
  updated_at: Number(row.updated_at),
195
235
  last_accessed_at: row.last_accessed_at === null || row.last_accessed_at === void 0 ? null : Number(row.last_accessed_at),
196
236
  deleted_at: row.deleted_at != null ? Number(row.deleted_at) : null,
197
- access_count: Number(row.access_count ?? 0)
237
+ access_count: Number(row.access_count ?? 0),
238
+ okf_type: row.okf_type ?? null
198
239
  };
199
240
  }
200
241
  function normalizeEmbeddingBlobValue(blob) {
@@ -367,8 +408,8 @@ var EntryRepository = class extends BaseRepository {
367
408
  `INSERT INTO ${this.prefix}entries (
368
409
  id, entity_id, title, body, tags, confidence, source_type,
369
410
  source_hash, source_ref, created_at, updated_at, last_accessed_at, access_count,
370
- deleted_at, embedding_blob, embedding
371
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
411
+ deleted_at, embedding_blob, embedding, okf_type
412
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
372
413
  ON CONFLICT(id) DO UPDATE SET
373
414
  entity_id = excluded.entity_id,
374
415
  title = excluded.title,
@@ -384,7 +425,8 @@ var EntryRepository = class extends BaseRepository {
384
425
  access_count = excluded.access_count,
385
426
  deleted_at = excluded.deleted_at,
386
427
  embedding_blob = excluded.embedding_blob,
387
- embedding = NULL`,
428
+ embedding = NULL,
429
+ okf_type = excluded.okf_type`,
388
430
  [
389
431
  fact.id,
390
432
  fact.entity_id,
@@ -401,7 +443,8 @@ var EntryRepository = class extends BaseRepository {
401
443
  fact.access_count,
402
444
  fact.deleted_at ?? null,
403
445
  embeddingBlob ?? null,
404
- null
446
+ null,
447
+ fact.okf_type ?? null
405
448
  ]
406
449
  );
407
450
  return result;
@@ -966,7 +1009,8 @@ function mapRowToTask(row) {
966
1009
  created_at: Number(row.created_at),
967
1010
  updated_at: Number(row.updated_at),
968
1011
  resolved_at: row.resolved_at != null ? Number(row.resolved_at) : null,
969
- deleted_at: row.deleted_at != null ? Number(row.deleted_at) : null
1012
+ deleted_at: row.deleted_at != null ? Number(row.deleted_at) : null,
1013
+ okf_type: row.okf_type ?? null
970
1014
  };
971
1015
  }
972
1016
  var TaskRepository = class extends BaseRepository {
@@ -1068,8 +1112,8 @@ var TaskRepository = class extends BaseRepository {
1068
1112
  await executor.runAsync(
1069
1113
  `INSERT INTO ${this.prefix}tasks (
1070
1114
  id, entity_id, description, status, priority,
1071
- created_at, updated_at, resolved_at, deleted_at
1072
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
1115
+ created_at, updated_at, resolved_at, deleted_at, okf_type
1116
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1073
1117
  ON CONFLICT(id) DO UPDATE SET
1074
1118
  entity_id = excluded.entity_id,
1075
1119
  description = excluded.description,
@@ -1077,7 +1121,8 @@ var TaskRepository = class extends BaseRepository {
1077
1121
  priority = excluded.priority,
1078
1122
  updated_at = excluded.updated_at,
1079
1123
  resolved_at = excluded.resolved_at,
1080
- deleted_at = excluded.deleted_at`,
1124
+ deleted_at = excluded.deleted_at,
1125
+ okf_type = excluded.okf_type`,
1081
1126
  [
1082
1127
  task.id,
1083
1128
  task.entity_id,
@@ -1087,7 +1132,8 @@ var TaskRepository = class extends BaseRepository {
1087
1132
  task.created_at,
1088
1133
  now,
1089
1134
  task.resolved_at ?? null,
1090
- task.deleted_at ?? null
1135
+ task.deleted_at ?? null,
1136
+ task.okf_type ?? null
1091
1137
  ]
1092
1138
  );
1093
1139
  }
@@ -1309,6 +1355,53 @@ var EventRepository = class extends BaseRepository {
1309
1355
  }
1310
1356
  };
1311
1357
 
1358
+ // src/repositories/EdgeRepository.ts
1359
+ var EdgeRepository = class extends BaseRepository {
1360
+ /**
1361
+ * Insert an edge, silently skipping on primary-key or uniqueness conflicts.
1362
+ * Throws when the insert was skipped due to an id collision with a different edge tuple.
1363
+ */
1364
+ async addIgnoreDuplicate(edge, tx) {
1365
+ const executor = this.getExecutor(tx);
1366
+ const result = await executor.runAsync(
1367
+ `INSERT OR IGNORE INTO ${this.prefix}edges (id, entity_id, source_id, target_id, edge_type, created_at)
1368
+ VALUES (?, ?, ?, ?, ?, ?)`,
1369
+ [edge.id, edge.entity_id, edge.source_id, edge.target_id, edge.edge_type, edge.created_at]
1370
+ );
1371
+ if (result.changes > 0) return;
1372
+ const existing = await executor.getFirstAsync(
1373
+ `SELECT entity_id, source_id, target_id, edge_type FROM ${this.prefix}edges WHERE id = ?`,
1374
+ [edge.id]
1375
+ );
1376
+ if (!existing) return;
1377
+ 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) {
1378
+ throw new Error(
1379
+ `Edge id collision: ${JSON.stringify(edge.id)} already exists with a different (entity_id, source_id, target_id, edge_type) tuple`
1380
+ );
1381
+ }
1382
+ }
1383
+ async getByEntityId(entityId, tx) {
1384
+ const executor = this.getExecutor(tx);
1385
+ const rows = await executor.getAllAsync(
1386
+ `SELECT * FROM ${this.prefix}edges WHERE entity_id = ? ORDER BY created_at ASC`,
1387
+ [entityId]
1388
+ );
1389
+ return rows.map((row) => ({
1390
+ id: String(row.id),
1391
+ entity_id: String(row.entity_id),
1392
+ source_id: String(row.source_id),
1393
+ target_id: String(row.target_id),
1394
+ edge_type: String(row.edge_type),
1395
+ created_at: Number(row.created_at)
1396
+ }));
1397
+ }
1398
+ /** Hard delete — edges have no soft-delete concept, only presence/absence. `tx` is REQUIRED. */
1399
+ async bulkDeleteByEntityId(entityId, tx) {
1400
+ const executor = this.getExecutor(tx);
1401
+ await executor.runAsync(`DELETE FROM ${this.prefix}edges WHERE entity_id = ?`, [entityId]);
1402
+ }
1403
+ };
1404
+
1312
1405
  // src/repositories/MetadataRepository.ts
1313
1406
  var MetadataRepository = class extends BaseRepository {
1314
1407
  // CHECKPOINTS TABLE METHODS
@@ -1428,6 +1521,7 @@ var WikiMemory = class {
1428
1521
  this.entryRepo = new EntryRepository(db, this.prefix, this.outboxRepo);
1429
1522
  this.taskRepo = new TaskRepository(db, this.prefix, this.outboxRepo);
1430
1523
  this.eventRepo = new EventRepository(db, this.prefix);
1524
+ this.edgeRepo = new EdgeRepository(db, this.prefix);
1431
1525
  this.metadataRepo = new MetadataRepository(db, this.prefix);
1432
1526
  this.embeddingService = new EmbeddingService(this.db, this.options, this.entryRepo, this.metadataRepo);
1433
1527
  this.searchService = new SearchService(this.entryRepo);
@@ -1461,6 +1555,7 @@ var WikiMemory = class {
1461
1555
  this.entryRepo,
1462
1556
  this.taskRepo,
1463
1557
  this.eventRepo,
1558
+ this.edgeRepo,
1464
1559
  this.metadataRepo,
1465
1560
  this.searchService,
1466
1561
  this.jobManager,
@@ -1921,7 +2016,7 @@ function formatMemoryDump(dump) {
1921
2016
  }
1922
2017
  function factFrontmatter(f) {
1923
2018
  return {
1924
- type: "fact",
2019
+ type: f.okf_type ?? "fact",
1925
2020
  title: f.title,
1926
2021
  tags: f.tags,
1927
2022
  timestamp: new Date(f.updated_at).toISOString(),
@@ -1939,7 +2034,7 @@ function factFrontmatter(f) {
1939
2034
  }
1940
2035
  function taskFrontmatter(t) {
1941
2036
  return {
1942
- type: "task",
2037
+ type: t.okf_type ?? "task",
1943
2038
  title: t.description,
1944
2039
  timestamp: new Date(t.updated_at).toISOString(),
1945
2040
  id: t.id,
@@ -2014,6 +2109,227 @@ function formatOkfBundle(dump) {
2014
2109
  });
2015
2110
  return { files };
2016
2111
  }
2112
+ var CONFIDENCE_VALUES = /* @__PURE__ */ new Set(["certain", "inferred", "tentative"]);
2113
+ var SOURCE_TYPES = /* @__PURE__ */ new Set([
2114
+ "user_stated",
2115
+ "librarian_inferred",
2116
+ "user_confirmed",
2117
+ "immutable_document"
2118
+ ]);
2119
+ var TASK_STATUSES = /* @__PURE__ */ new Set(["pending", "in_progress", "done", "abandoned"]);
2120
+ var EVENT_TYPES = /* @__PURE__ */ new Set(["observation", "decision", "action", "outcome"]);
2121
+ function basenameMd(filePath) {
2122
+ const name = filePath.slice(filePath.lastIndexOf("/") + 1);
2123
+ return name.endsWith(".md") ? name.slice(0, -3) : name;
2124
+ }
2125
+ function isConceptFile(filePath) {
2126
+ if (!filePath.endsWith(".md")) return false;
2127
+ if (filePath.endsWith("/index.md") || filePath === "index.md") return false;
2128
+ if (filePath.endsWith("/log.md") || filePath === "log.md") return false;
2129
+ return true;
2130
+ }
2131
+ function isStructuralPath(filePath) {
2132
+ return filePath.endsWith("/index.md") || filePath === "index.md" || filePath.endsWith("/log.md") || filePath === "log.md";
2133
+ }
2134
+ function posixDirname(filePath) {
2135
+ const idx = filePath.lastIndexOf("/");
2136
+ return idx === -1 ? "" : filePath.slice(0, idx);
2137
+ }
2138
+ function resolveRelativePath(fromFile, linkPath) {
2139
+ const baseDir = posixDirname(fromFile);
2140
+ const segments = [...baseDir ? baseDir.split("/") : [], ...linkPath.split("/")];
2141
+ const resolved = [];
2142
+ for (const seg of segments) {
2143
+ if (seg === "" || seg === ".") continue;
2144
+ if (seg === "..") {
2145
+ resolved.pop();
2146
+ continue;
2147
+ }
2148
+ resolved.push(seg);
2149
+ }
2150
+ return resolved.join("/");
2151
+ }
2152
+ function addPathAliases(map, filePath, resolvedId) {
2153
+ map.set(filePath, resolvedId);
2154
+ const withoutDot = filePath.replace(/^\.\//, "");
2155
+ if (withoutDot !== filePath) map.set(withoutDot, resolvedId);
2156
+ const entityRelative = filePath.replace(/^entities\/[^/]+\//, "");
2157
+ if (entityRelative !== filePath) {
2158
+ map.set(entityRelative, resolvedId);
2159
+ map.set(`./${entityRelative}`, resolvedId);
2160
+ }
2161
+ }
2162
+ function lookupResolvedId(map, path) {
2163
+ const normalized = path.replace(/^\.\//, "");
2164
+ return map.get(path) ?? map.get(normalized) ?? map.get(`./${normalized}`);
2165
+ }
2166
+ function stripLinkSuffix(linkPath) {
2167
+ const hashIdx = linkPath.indexOf("#");
2168
+ const queryIdx = linkPath.indexOf("?");
2169
+ if (hashIdx === -1 && queryIdx === -1) return linkPath;
2170
+ const cut = hashIdx === -1 ? queryIdx : queryIdx === -1 ? hashIdx : Math.min(hashIdx, queryIdx);
2171
+ return linkPath.slice(0, cut);
2172
+ }
2173
+ function resolveRoute(filePath, frontmatterType, options) {
2174
+ if (options?.typeMapping && Object.prototype.hasOwnProperty.call(options.typeMapping, frontmatterType)) {
2175
+ return options.typeMapping[frontmatterType];
2176
+ }
2177
+ if (filePath.includes("/facts/")) return "fact";
2178
+ if (filePath.includes("/tasks/")) return "task";
2179
+ return options?.defaultSchema ?? "fact";
2180
+ }
2181
+ function parseFrontmatterTimestamp(value, fallback) {
2182
+ if (typeof value === "number" && Number.isFinite(value)) return value;
2183
+ if (typeof value === "string") {
2184
+ const parsed = Date.parse(value);
2185
+ if (Number.isFinite(parsed)) return parsed;
2186
+ }
2187
+ return fallback;
2188
+ }
2189
+ function unescapeLogSummary(summary) {
2190
+ return summary.replace(/\\\]/g, "]").replace(/\\\[/g, "[").replace(/\\\\/g, "\\");
2191
+ }
2192
+ var LOG_LINE_PATTERN = /^\(([^)]+)\)\s*(?:\[((?:\\.|[^\]])*)\]\(([^)]+)\)|(.+))$/;
2193
+ function parseLogEntryText(text) {
2194
+ const match = LOG_LINE_PATTERN.exec(text.trim());
2195
+ if (!match) return null;
2196
+ const [, rawType, linkedSummary, linkPath, plainSummary] = match;
2197
+ const event_type = EVENT_TYPES.has(rawType) ? rawType : "observation";
2198
+ if (linkPath) {
2199
+ return { event_type, summary: unescapeLogSummary(linkedSummary), linkPath };
2200
+ }
2201
+ return { event_type, summary: (plainSummary ?? "").trim() };
2202
+ }
2203
+ function frontmatterToFact(entityId, id, frontmatter, body, now) {
2204
+ const created_at = parseFrontmatterTimestamp(frontmatter.created_at, now);
2205
+ const updated_at = parseFrontmatterTimestamp(
2206
+ frontmatter.timestamp,
2207
+ parseFrontmatterTimestamp(frontmatter.updated_at, now)
2208
+ );
2209
+ return {
2210
+ id,
2211
+ entity_id: entityId,
2212
+ title: typeof frontmatter.title === "string" ? frontmatter.title : "",
2213
+ body,
2214
+ tags: Array.isArray(frontmatter.tags) ? frontmatter.tags.filter((t) => typeof t === "string") : [],
2215
+ confidence: CONFIDENCE_VALUES.has(String(frontmatter.confidence)) ? frontmatter.confidence : "tentative",
2216
+ source_type: SOURCE_TYPES.has(String(frontmatter.source_type)) ? frontmatter.source_type : "user_stated",
2217
+ source_hash: typeof frontmatter.source_hash === "string" ? frontmatter.source_hash : null,
2218
+ source_ref: typeof frontmatter.resource === "string" ? frontmatter.resource : null,
2219
+ created_at,
2220
+ updated_at,
2221
+ last_accessed_at: frontmatter.last_accessed_at != null ? parseFrontmatterTimestamp(frontmatter.last_accessed_at, now) : null,
2222
+ access_count: typeof frontmatter.access_count === "number" ? frontmatter.access_count : 0,
2223
+ deleted_at: frontmatter.deleted_at != null ? parseFrontmatterTimestamp(frontmatter.deleted_at, 0) : null,
2224
+ okf_type: frontmatter.type
2225
+ };
2226
+ }
2227
+ function frontmatterToTask(entityId, id, frontmatter, now) {
2228
+ const created_at = parseFrontmatterTimestamp(frontmatter.created_at, now);
2229
+ const updated_at = parseFrontmatterTimestamp(
2230
+ frontmatter.timestamp,
2231
+ parseFrontmatterTimestamp(frontmatter.updated_at, now)
2232
+ );
2233
+ return {
2234
+ id,
2235
+ entity_id: entityId,
2236
+ description: typeof frontmatter.title === "string" ? frontmatter.title : "",
2237
+ status: TASK_STATUSES.has(String(frontmatter.status)) ? frontmatter.status : "pending",
2238
+ priority: typeof frontmatter.priority === "number" ? frontmatter.priority : 0,
2239
+ created_at,
2240
+ updated_at,
2241
+ resolved_at: frontmatter.resolved_at != null ? parseFrontmatterTimestamp(frontmatter.resolved_at, now) : null,
2242
+ deleted_at: frontmatter.deleted_at != null ? parseFrontmatterTimestamp(frontmatter.deleted_at, 0) : null,
2243
+ okf_type: frontmatter.type
2244
+ };
2245
+ }
2246
+ function findLogMdPath(files) {
2247
+ return files.find((f) => f.path.endsWith("/log.md") || f.path === "log.md")?.path;
2248
+ }
2249
+ function parseOkfBundle(entityId, files, options) {
2250
+ const now = Date.now();
2251
+ const pathToResolvedId = /* @__PURE__ */ new Map();
2252
+ for (const file of files) {
2253
+ if (!isConceptFile(file.path)) continue;
2254
+ const { frontmatter } = parseConcept(file.content);
2255
+ const route = resolveRoute(file.path, frontmatter.type ?? "", options);
2256
+ if (route === "ignore") continue;
2257
+ const resolvedId = typeof frontmatter.id === "string" && frontmatter.id ? frontmatter.id : basenameMd(file.path);
2258
+ addPathAliases(pathToResolvedId, file.path, resolvedId);
2259
+ }
2260
+ const facts = [];
2261
+ const tasks = [];
2262
+ const edges = [];
2263
+ let logContent = null;
2264
+ const logMdPath = findLogMdPath(files);
2265
+ for (const file of files) {
2266
+ if (file.path.endsWith("/log.md") || file.path === "log.md") {
2267
+ logContent = file.content;
2268
+ continue;
2269
+ }
2270
+ if (!isConceptFile(file.path)) continue;
2271
+ const { frontmatter, body } = parseConcept(file.content);
2272
+ const route = resolveRoute(file.path, frontmatter.type ?? "", options);
2273
+ if (route === "ignore") continue;
2274
+ const resolvedId = typeof frontmatter.id === "string" && frontmatter.id ? frontmatter.id : basenameMd(file.path);
2275
+ if (route === "fact") {
2276
+ facts.push(frontmatterToFact(entityId, resolvedId, frontmatter, body, now));
2277
+ } else {
2278
+ tasks.push(frontmatterToTask(entityId, resolvedId, frontmatter, now));
2279
+ }
2280
+ const seenEdges = /* @__PURE__ */ new Set();
2281
+ for (const link of extractMarkdownLinks(body)) {
2282
+ const strippedPath = stripLinkSuffix(link.path);
2283
+ const directTargetId = lookupResolvedId(pathToResolvedId, strippedPath);
2284
+ const resolvedTargetPath = resolveRelativePath(file.path, strippedPath);
2285
+ if (isStructuralPath(strippedPath) || isStructuralPath(resolvedTargetPath)) continue;
2286
+ const targetId = directTargetId ?? lookupResolvedId(pathToResolvedId, resolvedTargetPath);
2287
+ if (!targetId) continue;
2288
+ const edgeKey = `${resolvedId}\0${targetId}\0${link.text}`;
2289
+ if (seenEdges.has(edgeKey)) continue;
2290
+ seenEdges.add(edgeKey);
2291
+ edges.push({
2292
+ id: generateId(),
2293
+ entity_id: entityId,
2294
+ source_id: resolvedId,
2295
+ target_id: targetId,
2296
+ edge_type: link.text,
2297
+ created_at: now
2298
+ });
2299
+ }
2300
+ }
2301
+ const events = [];
2302
+ if (logContent != null) {
2303
+ const logPath = logMdPath ?? `entities/${entityId}/log.md`;
2304
+ for (const entry of parseLogMd(logContent)) {
2305
+ const parsed = parseLogEntryText(entry.text);
2306
+ if (!parsed) continue;
2307
+ let related_entry_id = null;
2308
+ if (parsed.linkPath) {
2309
+ const targetPath = resolveRelativePath(logPath, stripLinkSuffix(parsed.linkPath));
2310
+ if (!isStructuralPath(targetPath) && targetPath.includes("/facts/")) {
2311
+ related_entry_id = lookupResolvedId(pathToResolvedId, targetPath) ?? null;
2312
+ }
2313
+ }
2314
+ const created_at = (/* @__PURE__ */ new Date(`${entry.date}T00:00:00.000Z`)).getTime();
2315
+ if (!Number.isFinite(created_at)) continue;
2316
+ events.push({
2317
+ id: generateId("evt_"),
2318
+ entity_id: entityId,
2319
+ event_type: parsed.event_type,
2320
+ summary: parsed.summary,
2321
+ related_entry_id,
2322
+ created_at
2323
+ });
2324
+ }
2325
+ }
2326
+ return {
2327
+ generatedAt: now,
2328
+ entities: {
2329
+ [entityId]: { facts, tasks, events, edges }
2330
+ }
2331
+ };
2332
+ }
2017
2333
 
2018
2334
  // src/librarianPrompt.ts
2019
2335
  var DEFAULT_LIBRARIAN_SYNTHESIS_PROMPT = `You are a careful memory synthesis assistant.
@@ -2059,6 +2375,6 @@ function createWiki(db, options) {
2059
2375
  return new WikiMemory(db, options);
2060
2376
  }
2061
2377
 
2062
- export { DEFAULT_LIBRARIAN_SYNTHESIS_PROMPT, WikiMemory, createWiki, formatContext, formatMemoryDump, formatOkfBundle, hydrateLibrarianPrompt, mapLibrarianOptionsToReadOptions, validateLibrarianPromptTemplate };
2378
+ export { DEFAULT_LIBRARIAN_SYNTHESIS_PROMPT, WikiMemory, createWiki, formatContext, formatMemoryDump, formatOkfBundle, hydrateLibrarianPrompt, mapLibrarianOptionsToReadOptions, parseOkfBundle, validateLibrarianPromptTemplate };
2063
2379
  //# sourceMappingURL=index.mjs.map
2064
2380
  //# sourceMappingURL=index.mjs.map