@equationalapplications/core-llm-wiki 4.14.0 → 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/README.md +12 -0
- package/dist/{chunk-6FWG2DG4.mjs → chunk-J4GBC6CP.mjs} +103 -31
- package/dist/chunk-J4GBC6CP.mjs.map +1 -0
- package/dist/index.d.mts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +441 -45
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +343 -20
- package/dist/index.mjs.map +1 -1
- package/dist/{testing-CDIDE4Jd.d.mts → testing-NH1_Aigh.d.mts} +47 -3
- package/dist/{testing-CDIDE4Jd.d.ts → testing-NH1_Aigh.d.ts} +47 -3
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +316 -244
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-6FWG2DG4.mjs.map +0 -1
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;
|
|
@@ -926,7 +969,9 @@ function generateId(prefix = "") {
|
|
|
926
969
|
crypto.getRandomValues(bytes);
|
|
927
970
|
return prefix + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("").substring(0, 24);
|
|
928
971
|
}
|
|
929
|
-
|
|
972
|
+
throw new Error(
|
|
973
|
+
"generateId: no cryptographically secure random source available (crypto.randomUUID and crypto.getRandomValues are both missing)."
|
|
974
|
+
);
|
|
930
975
|
}
|
|
931
976
|
|
|
932
977
|
// src/repositories/OutboxRepository.ts
|
|
@@ -992,7 +1037,8 @@ function mapRowToTask(row) {
|
|
|
992
1037
|
created_at: Number(row.created_at),
|
|
993
1038
|
updated_at: Number(row.updated_at),
|
|
994
1039
|
resolved_at: row.resolved_at != null ? Number(row.resolved_at) : null,
|
|
995
|
-
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
|
|
996
1042
|
};
|
|
997
1043
|
}
|
|
998
1044
|
var TaskRepository = class extends BaseRepository {
|
|
@@ -1094,8 +1140,8 @@ var TaskRepository = class extends BaseRepository {
|
|
|
1094
1140
|
await executor.runAsync(
|
|
1095
1141
|
`INSERT INTO ${this.prefix}tasks (
|
|
1096
1142
|
id, entity_id, description, status, priority,
|
|
1097
|
-
created_at, updated_at, resolved_at, deleted_at
|
|
1098
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1143
|
+
created_at, updated_at, resolved_at, deleted_at, okf_type
|
|
1144
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1099
1145
|
ON CONFLICT(id) DO UPDATE SET
|
|
1100
1146
|
entity_id = excluded.entity_id,
|
|
1101
1147
|
description = excluded.description,
|
|
@@ -1103,7 +1149,8 @@ var TaskRepository = class extends BaseRepository {
|
|
|
1103
1149
|
priority = excluded.priority,
|
|
1104
1150
|
updated_at = excluded.updated_at,
|
|
1105
1151
|
resolved_at = excluded.resolved_at,
|
|
1106
|
-
deleted_at = excluded.deleted_at
|
|
1152
|
+
deleted_at = excluded.deleted_at,
|
|
1153
|
+
okf_type = excluded.okf_type`,
|
|
1107
1154
|
[
|
|
1108
1155
|
task.id,
|
|
1109
1156
|
task.entity_id,
|
|
@@ -1113,7 +1160,8 @@ var TaskRepository = class extends BaseRepository {
|
|
|
1113
1160
|
task.created_at,
|
|
1114
1161
|
now,
|
|
1115
1162
|
task.resolved_at ?? null,
|
|
1116
|
-
task.deleted_at ?? null
|
|
1163
|
+
task.deleted_at ?? null,
|
|
1164
|
+
task.okf_type ?? null
|
|
1117
1165
|
]
|
|
1118
1166
|
);
|
|
1119
1167
|
}
|
|
@@ -1335,6 +1383,53 @@ var EventRepository = class extends BaseRepository {
|
|
|
1335
1383
|
}
|
|
1336
1384
|
};
|
|
1337
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
|
+
|
|
1338
1433
|
// src/repositories/MetadataRepository.ts
|
|
1339
1434
|
var MetadataRepository = class extends BaseRepository {
|
|
1340
1435
|
// CHECKPOINTS TABLE METHODS
|
|
@@ -1711,6 +1806,20 @@ var JobManager = class {
|
|
|
1711
1806
|
this.activeMaintenanceJobs = /* @__PURE__ */ new Set();
|
|
1712
1807
|
this.activeIngestJobs = /* @__PURE__ */ new Map();
|
|
1713
1808
|
this.statusSubscribers = /* @__PURE__ */ new Map();
|
|
1809
|
+
/**
|
|
1810
|
+
* Lookup table for acquireLock/releaseLock's dynamic-dispatch branch.
|
|
1811
|
+
* Excludes 'ingest' | 'global_reembed' | 'global_import', which those
|
|
1812
|
+
* methods already handle via explicit if/else branches before reaching
|
|
1813
|
+
* this table.
|
|
1814
|
+
*/
|
|
1815
|
+
this.lockKeyFns = {
|
|
1816
|
+
prune: (id) => this._pruneKey(id),
|
|
1817
|
+
librarian: (id) => this._librarianKey(id),
|
|
1818
|
+
heal: (id) => this._healKey(id),
|
|
1819
|
+
reembed: (id) => this._reembedKey(id),
|
|
1820
|
+
import: (id) => this._importKey(id),
|
|
1821
|
+
forget: (id) => this._forgetKey(id)
|
|
1822
|
+
};
|
|
1714
1823
|
}
|
|
1715
1824
|
_pruneKey(entityId) {
|
|
1716
1825
|
return `${this.prefix}:${entityId}:prune`;
|
|
@@ -1860,9 +1969,7 @@ var JobManager = class {
|
|
|
1860
1969
|
} else if (operation === "global_import") {
|
|
1861
1970
|
this.activeMaintenanceJobs.add(this._globalImportKey());
|
|
1862
1971
|
} else {
|
|
1863
|
-
|
|
1864
|
-
const keyFn = this[keyFnName];
|
|
1865
|
-
this.activeMaintenanceJobs.add(keyFn.call(this, entityId));
|
|
1972
|
+
this.activeMaintenanceJobs.add(this.lockKeyFns[operation](entityId));
|
|
1866
1973
|
}
|
|
1867
1974
|
this._notifyStatusSubscribers(entityId);
|
|
1868
1975
|
}
|
|
@@ -1874,9 +1981,7 @@ var JobManager = class {
|
|
|
1874
1981
|
} else if (operation === "global_import") {
|
|
1875
1982
|
this.activeMaintenanceJobs.delete(this._globalImportKey());
|
|
1876
1983
|
} else {
|
|
1877
|
-
|
|
1878
|
-
const keyFn = this[keyFnName];
|
|
1879
|
-
this.activeMaintenanceJobs.delete(keyFn.call(this, entityId));
|
|
1984
|
+
this.activeMaintenanceJobs.delete(this.lockKeyFns[operation](entityId));
|
|
1880
1985
|
}
|
|
1881
1986
|
this._notifyStatusSubscribers(entityId);
|
|
1882
1987
|
}
|
|
@@ -2826,12 +2931,16 @@ var MaintenanceService = class {
|
|
|
2826
2931
|
};
|
|
2827
2932
|
|
|
2828
2933
|
// src/services/ImportExportService.ts
|
|
2934
|
+
var MAX_EMBEDDING_BLOB_BYTES = 32 * 1024;
|
|
2935
|
+
var IMPORT_TITLE_MAX = 500;
|
|
2936
|
+
var IMPORT_BODY_MAX = 8e3;
|
|
2829
2937
|
var ImportExportService = class {
|
|
2830
|
-
constructor(db, entryRepo, taskRepo, eventRepo, metadataRepo, searchService, jobManager, embeddingService) {
|
|
2938
|
+
constructor(db, entryRepo, taskRepo, eventRepo, edgeRepo, metadataRepo, searchService, jobManager, embeddingService) {
|
|
2831
2939
|
this.db = db;
|
|
2832
2940
|
this.entryRepo = entryRepo;
|
|
2833
2941
|
this.taskRepo = taskRepo;
|
|
2834
2942
|
this.eventRepo = eventRepo;
|
|
2943
|
+
this.edgeRepo = edgeRepo;
|
|
2835
2944
|
this.metadataRepo = metadataRepo;
|
|
2836
2945
|
this.searchService = searchService;
|
|
2837
2946
|
this.jobManager = jobManager;
|
|
@@ -2876,10 +2985,11 @@ var ImportExportService = class {
|
|
|
2876
2985
|
}
|
|
2877
2986
|
}
|
|
2878
2987
|
async getFullBundle(entityId, opts) {
|
|
2879
|
-
const [factsRaw, tasks, events] = await Promise.all([
|
|
2988
|
+
const [factsRaw, tasks, events, edges] = await Promise.all([
|
|
2880
2989
|
opts?.includeBlobs ? this.entryRepo.findAllByEntityIdWithBlobs(entityId) : this.entryRepo.findAllByEntityId(entityId),
|
|
2881
2990
|
this.taskRepo.findAllByEntityId(entityId),
|
|
2882
|
-
this.eventRepo.getByEntityId(entityId, opts?.maxEvents)
|
|
2991
|
+
this.eventRepo.getByEntityId(entityId, opts?.maxEvents),
|
|
2992
|
+
this.edgeRepo.getByEntityId(entityId)
|
|
2883
2993
|
]);
|
|
2884
2994
|
const facts = factsRaw.map((f) => {
|
|
2885
2995
|
const {
|
|
@@ -2898,7 +3008,7 @@ var ImportExportService = class {
|
|
|
2898
3008
|
tags: typeof factBase.tags === "string" ? JSON.parse(factBase.tags) : factBase.tags
|
|
2899
3009
|
};
|
|
2900
3010
|
});
|
|
2901
|
-
return { facts, tasks, events };
|
|
3011
|
+
return { facts, tasks, events, edges };
|
|
2902
3012
|
}
|
|
2903
3013
|
/** Single-entity import transaction + post-processing; package-internal hook for tests. */
|
|
2904
3014
|
async doImportEntity(entityId, bundle, merge) {
|
|
@@ -2907,6 +3017,7 @@ var ImportExportService = class {
|
|
|
2907
3017
|
const factsWithPreservedBlob = /* @__PURE__ */ new Map();
|
|
2908
3018
|
const preservedBlobDims = /* @__PURE__ */ new Set();
|
|
2909
3019
|
const softDeletedFactIds = [];
|
|
3020
|
+
const clippedTextByFactId = /* @__PURE__ */ new Map();
|
|
2910
3021
|
await this.db.withTransactionAsync(async (tx) => {
|
|
2911
3022
|
if (!merge) {
|
|
2912
3023
|
const deletedLiveFactIds = await this.entryRepo.findIdsBySource(
|
|
@@ -2919,6 +3030,7 @@ var ImportExportService = class {
|
|
|
2919
3030
|
softDeletedFactIds.push(...deletedLiveFactIds);
|
|
2920
3031
|
await this.entryRepo.bulkSoftDeleteByEntityId(entityId, tx);
|
|
2921
3032
|
await this.taskRepo.bulkSoftDeleteByEntityId(entityId, tx);
|
|
3033
|
+
await this.edgeRepo.bulkDeleteByEntityId(entityId, tx);
|
|
2922
3034
|
await this.metadataRepo.deleteCheckpoint(entityId, tx);
|
|
2923
3035
|
}
|
|
2924
3036
|
const factIds = bundle.facts.map((fact) => fact.id);
|
|
@@ -2943,21 +3055,32 @@ var ImportExportService = class {
|
|
|
2943
3055
|
const rawBlobRaw = fact.embedding_blob;
|
|
2944
3056
|
let rawBlob = null;
|
|
2945
3057
|
if (rawBlobRaw instanceof Uint8Array) {
|
|
2946
|
-
|
|
3058
|
+
if (rawBlobRaw.byteLength <= MAX_EMBEDDING_BLOB_BYTES) {
|
|
3059
|
+
rawBlob = rawBlobRaw;
|
|
3060
|
+
}
|
|
2947
3061
|
} else if (rawBlobRaw !== null && rawBlobRaw !== void 0 && typeof rawBlobRaw === "object") {
|
|
2948
3062
|
const obj = rawBlobRaw;
|
|
2949
3063
|
if (obj["type"] === "Buffer" && Array.isArray(obj["data"])) {
|
|
2950
|
-
|
|
3064
|
+
const data = obj["data"];
|
|
3065
|
+
if (data.length <= MAX_EMBEDDING_BLOB_BYTES) {
|
|
3066
|
+
rawBlob = new Uint8Array(data);
|
|
3067
|
+
}
|
|
2951
3068
|
} else if (!Array.isArray(rawBlobRaw)) {
|
|
2952
3069
|
const entries = Object.keys(obj);
|
|
2953
3070
|
if (entries.length > 0 && entries.every((k) => /^\d+$/.test(k))) {
|
|
2954
3071
|
const len = entries.length;
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
3072
|
+
if (len <= MAX_EMBEDDING_BLOB_BYTES) {
|
|
3073
|
+
rawBlob = new Uint8Array(len);
|
|
3074
|
+
for (let i = 0; i < len; i++) {
|
|
3075
|
+
rawBlob[i] = obj[String(i)] ?? 0;
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
2958
3078
|
}
|
|
2959
3079
|
}
|
|
2960
3080
|
}
|
|
3081
|
+
if (rawBlob !== null && rawBlob.byteLength > MAX_EMBEDDING_BLOB_BYTES) {
|
|
3082
|
+
rawBlob = null;
|
|
3083
|
+
}
|
|
2961
3084
|
let blobData = null;
|
|
2962
3085
|
if (rawBlob !== null && rawBlob.byteLength > 0 && rawBlob.byteLength % 4 === 0) {
|
|
2963
3086
|
const copy = new ArrayBuffer(rawBlob.byteLength);
|
|
@@ -2987,11 +3110,14 @@ var ImportExportService = class {
|
|
|
2987
3110
|
}
|
|
2988
3111
|
if (merge && safeUpdatedAt <= existing.updated_at) continue;
|
|
2989
3112
|
}
|
|
3113
|
+
const safeTitle = clip(String(fact.title ?? ""), IMPORT_TITLE_MAX);
|
|
3114
|
+
const safeBody = clip(String(fact.body ?? ""), IMPORT_BODY_MAX);
|
|
3115
|
+
clippedTextByFactId.set(fact.id, { title: safeTitle, body: safeBody });
|
|
2990
3116
|
const factObj = {
|
|
2991
3117
|
id: fact.id,
|
|
2992
3118
|
entity_id: entityId,
|
|
2993
|
-
title:
|
|
2994
|
-
body:
|
|
3119
|
+
title: safeTitle,
|
|
3120
|
+
body: safeBody,
|
|
2995
3121
|
tags: Array.isArray(fact.tags) ? fact.tags : [],
|
|
2996
3122
|
confidence: fact.confidence,
|
|
2997
3123
|
source_type: sourceType,
|
|
@@ -3002,7 +3128,8 @@ var ImportExportService = class {
|
|
|
3002
3128
|
last_accessed_at: fact.last_accessed_at,
|
|
3003
3129
|
access_count: fact.access_count,
|
|
3004
3130
|
deleted_at: fact.deleted_at,
|
|
3005
|
-
embedding_blob: blobData ?? void 0
|
|
3131
|
+
embedding_blob: blobData ?? void 0,
|
|
3132
|
+
okf_type: fact.okf_type ?? null
|
|
3006
3133
|
};
|
|
3007
3134
|
await this.entryRepo.upsertForImport(factObj, tx);
|
|
3008
3135
|
if (blobData != null) {
|
|
@@ -3051,7 +3178,8 @@ var ImportExportService = class {
|
|
|
3051
3178
|
created_at: task.created_at,
|
|
3052
3179
|
updated_at: safeUpdatedAt,
|
|
3053
3180
|
resolved_at: task.resolved_at,
|
|
3054
|
-
deleted_at: task.deleted_at
|
|
3181
|
+
deleted_at: task.deleted_at,
|
|
3182
|
+
okf_type: task.okf_type ?? null
|
|
3055
3183
|
},
|
|
3056
3184
|
tx,
|
|
3057
3185
|
safeUpdatedAt
|
|
@@ -3075,15 +3203,29 @@ var ImportExportService = class {
|
|
|
3075
3203
|
tx
|
|
3076
3204
|
);
|
|
3077
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
|
+
}
|
|
3078
3219
|
});
|
|
3079
3220
|
await this.searchService.sync(entityId);
|
|
3080
3221
|
for (const fact of bundle.facts) {
|
|
3081
3222
|
if (!fact.deleted_at && upsertedFactIds.has(fact.id) && !factsWithPreservedBlob.has(fact.id)) {
|
|
3223
|
+
const clipped = clippedTextByFactId.get(fact.id);
|
|
3082
3224
|
const embedded = await this.embeddingService.embedFact({
|
|
3083
3225
|
id: fact.id,
|
|
3084
3226
|
entity_id: entityId,
|
|
3085
|
-
title: fact.title,
|
|
3086
|
-
body: fact.body,
|
|
3227
|
+
title: clipped?.title ?? fact.title,
|
|
3228
|
+
body: clipped?.body ?? fact.body,
|
|
3087
3229
|
tags: Array.isArray(fact.tags) || typeof fact.tags === "string" ? fact.tags : []
|
|
3088
3230
|
});
|
|
3089
3231
|
if (!embedded) {
|
|
@@ -3183,7 +3325,7 @@ var ImportExportService = class {
|
|
|
3183
3325
|
}
|
|
3184
3326
|
_warnCrossEntityCollision(type, id, existingEntityId, targetEntityId) {
|
|
3185
3327
|
console.warn(
|
|
3186
|
-
`[WikiMemory] importDump: ${type} id
|
|
3328
|
+
`[WikiMemory] importDump: ${type} id ${JSON.stringify(id)} already belongs to entity ${JSON.stringify(existingEntityId)}; skipping for entity ${JSON.stringify(targetEntityId)}`
|
|
3187
3329
|
);
|
|
3188
3330
|
}
|
|
3189
3331
|
_normalizeImportedSourceType(raw, ctx) {
|
|
@@ -3262,7 +3404,7 @@ var EmbeddingService = class {
|
|
|
3262
3404
|
tagsStr = fact.tags;
|
|
3263
3405
|
}
|
|
3264
3406
|
}
|
|
3265
|
-
const text = `${fact.title} ${fact.body} ${tagsStr}`.trim();
|
|
3407
|
+
const text = clip(`${fact.title} ${fact.body} ${tagsStr}`.trim(), 16e3);
|
|
3266
3408
|
try {
|
|
3267
3409
|
const vector = await embedFn(text);
|
|
3268
3410
|
if (vector.length === 0 || !vector.every((v) => typeof v === "number" && isFinite(v))) {
|
|
@@ -3390,7 +3532,7 @@ var RetrievalService = class {
|
|
|
3390
3532
|
const sanitizedTierWeights = shouldExposeReadMetadata(entityId) ? sanitizeTierWeights(entityIds, options?.tierWeights) : void 0;
|
|
3391
3533
|
const exposeMetadata = shouldExposeReadMetadata(entityId);
|
|
3392
3534
|
if (entityIds.length === 0) {
|
|
3393
|
-
const empty = { facts: [], tasks: [], events: [] };
|
|
3535
|
+
const empty = { facts: [], tasks: [], events: [], edges: [] };
|
|
3394
3536
|
if (exposeMetadata) {
|
|
3395
3537
|
empty.metadata = { query, entityIds: [] };
|
|
3396
3538
|
if (sanitizedTierWeights && Object.keys(sanitizedTierWeights).length > 0) empty.metadata.tierWeights = sanitizedTierWeights;
|
|
@@ -3780,7 +3922,7 @@ var RetrievalService = class {
|
|
|
3780
3922
|
if (exposeMetadata && trimmedQuery && scoreByFactId) {
|
|
3781
3923
|
factScores = Object.fromEntries(facts.map((fact) => [fact.id, scoreByFactId.get(fact.id) ?? 0]));
|
|
3782
3924
|
}
|
|
3783
|
-
const bundle = { facts, tasks, events: events.reverse() };
|
|
3925
|
+
const bundle = { facts, tasks, events: events.reverse(), edges: [] };
|
|
3784
3926
|
if (exposeMetadata) {
|
|
3785
3927
|
bundle.metadata = { query, entityIds };
|
|
3786
3928
|
if (sanitizedTierWeights && Object.keys(sanitizedTierWeights).length > 0) bundle.metadata.tierWeights = sanitizedTierWeights;
|
|
@@ -3876,15 +4018,38 @@ var RetrievalService = class {
|
|
|
3876
4018
|
|
|
3877
4019
|
// src/services/WriteService.ts
|
|
3878
4020
|
var WriteService = class {
|
|
3879
|
-
constructor(db, options, eventRepo, metadataRepo, jobManager, maintenanceService) {
|
|
4021
|
+
constructor(db, options, entryRepo, eventRepo, metadataRepo, jobManager, maintenanceService) {
|
|
3880
4022
|
this.db = db;
|
|
3881
4023
|
this.options = options;
|
|
4024
|
+
this.entryRepo = entryRepo;
|
|
3882
4025
|
this.eventRepo = eventRepo;
|
|
3883
4026
|
this.metadataRepo = metadataRepo;
|
|
3884
4027
|
this.jobManager = jobManager;
|
|
3885
4028
|
this.maintenanceService = maintenanceService;
|
|
3886
4029
|
}
|
|
3887
4030
|
async write(entityId, event) {
|
|
4031
|
+
if (typeof entityId !== "string" || entityId.length === 0 || entityId.length > 200 || entityId.includes("\0")) {
|
|
4032
|
+
throw new TypeError(
|
|
4033
|
+
`Invalid entityId: must be a non-empty string at most 200 chars with no null bytes; got ${JSON.stringify(entityId)}.`
|
|
4034
|
+
);
|
|
4035
|
+
}
|
|
4036
|
+
if (event === null || typeof event !== "object" || Array.isArray(event)) {
|
|
4037
|
+
throw new TypeError("Invalid event: must be a non-null object.");
|
|
4038
|
+
}
|
|
4039
|
+
if (typeof event.summary !== "string") {
|
|
4040
|
+
throw new TypeError("Invalid event.summary: must be a string.");
|
|
4041
|
+
}
|
|
4042
|
+
const summary = clip(event.summary, 4e3);
|
|
4043
|
+
let relatedEntryId = null;
|
|
4044
|
+
const rawRelatedEntryId = event.related_entry_id;
|
|
4045
|
+
if (rawRelatedEntryId != null && rawRelatedEntryId !== "") {
|
|
4046
|
+
if (typeof rawRelatedEntryId !== "string" || rawRelatedEntryId.length > 200 || rawRelatedEntryId.includes("\0")) {
|
|
4047
|
+
relatedEntryId = null;
|
|
4048
|
+
} else {
|
|
4049
|
+
const existing = await this.entryRepo.findByIds([rawRelatedEntryId], [entityId]);
|
|
4050
|
+
relatedEntryId = existing.length > 0 ? rawRelatedEntryId : null;
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
3888
4053
|
const id = generateId("evt_");
|
|
3889
4054
|
const now = Date.now();
|
|
3890
4055
|
let eventType = event.event_type;
|
|
@@ -3895,8 +4060,8 @@ var WriteService = class {
|
|
|
3895
4060
|
id,
|
|
3896
4061
|
entity_id: entityId,
|
|
3897
4062
|
event_type: eventType,
|
|
3898
|
-
summary
|
|
3899
|
-
related_entry_id:
|
|
4063
|
+
summary,
|
|
4064
|
+
related_entry_id: relatedEntryId,
|
|
3900
4065
|
created_at: now
|
|
3901
4066
|
};
|
|
3902
4067
|
let shouldRunLibrarian = false;
|
|
@@ -3957,6 +4122,7 @@ var WriteService = class {
|
|
|
3957
4122
|
};
|
|
3958
4123
|
|
|
3959
4124
|
// src/WikiMemory.ts
|
|
4125
|
+
var TABLE_PREFIX_PATTERN = /^[A-Za-z][A-Za-z0-9_]{0,30}_$/;
|
|
3960
4126
|
var _testAccessNonTestEnvWarned;
|
|
3961
4127
|
var WikiMemory = class {
|
|
3962
4128
|
constructor(db, options) {
|
|
@@ -3964,11 +4130,17 @@ var WikiMemory = class {
|
|
|
3964
4130
|
__privateAdd(this, _testAccessNonTestEnvWarned, false);
|
|
3965
4131
|
this.db = db;
|
|
3966
4132
|
this.options = options;
|
|
3967
|
-
this.prefix = options.config?.tablePrefix
|
|
4133
|
+
this.prefix = options.config?.tablePrefix ?? "llm_wiki_";
|
|
4134
|
+
if (!TABLE_PREFIX_PATTERN.test(this.prefix)) {
|
|
4135
|
+
throw new Error(
|
|
4136
|
+
`Invalid tablePrefix: ${JSON.stringify(this.prefix)}. Must match ${TABLE_PREFIX_PATTERN} (letter, then alphanumeric/underscore, ending in "_", max 32 chars total).`
|
|
4137
|
+
);
|
|
4138
|
+
}
|
|
3968
4139
|
this.outboxRepo = new OutboxRepository(db, this.prefix, !!options.config?.enableOutbox);
|
|
3969
4140
|
this.entryRepo = new EntryRepository(db, this.prefix, this.outboxRepo);
|
|
3970
4141
|
this.taskRepo = new TaskRepository(db, this.prefix, this.outboxRepo);
|
|
3971
4142
|
this.eventRepo = new EventRepository(db, this.prefix);
|
|
4143
|
+
this.edgeRepo = new EdgeRepository(db, this.prefix);
|
|
3972
4144
|
this.metadataRepo = new MetadataRepository(db, this.prefix);
|
|
3973
4145
|
this.embeddingService = new EmbeddingService(this.db, this.options, this.entryRepo, this.metadataRepo);
|
|
3974
4146
|
this.searchService = new SearchService(this.entryRepo);
|
|
@@ -4002,6 +4174,7 @@ var WikiMemory = class {
|
|
|
4002
4174
|
this.entryRepo,
|
|
4003
4175
|
this.taskRepo,
|
|
4004
4176
|
this.eventRepo,
|
|
4177
|
+
this.edgeRepo,
|
|
4005
4178
|
this.metadataRepo,
|
|
4006
4179
|
this.searchService,
|
|
4007
4180
|
this.jobManager,
|
|
@@ -4018,6 +4191,7 @@ var WikiMemory = class {
|
|
|
4018
4191
|
this.writeService = new WriteService(
|
|
4019
4192
|
this.db,
|
|
4020
4193
|
this.options,
|
|
4194
|
+
this.entryRepo,
|
|
4021
4195
|
this.eventRepo,
|
|
4022
4196
|
this.metadataRepo,
|
|
4023
4197
|
this.jobManager,
|
|
@@ -4096,7 +4270,7 @@ var WikiMemory = class {
|
|
|
4096
4270
|
async hasChanged(entityId, sourceRef, sourceHash) {
|
|
4097
4271
|
const normalizedRef = normalizeSourceRef(sourceRef);
|
|
4098
4272
|
if (!normalizedRef) {
|
|
4099
|
-
throw new Error(`Invalid sourceRef:
|
|
4273
|
+
throw new Error(`Invalid sourceRef: ${JSON.stringify(sourceRef)}`);
|
|
4100
4274
|
}
|
|
4101
4275
|
const normalizedHash = normalizeSourceHash(sourceHash);
|
|
4102
4276
|
if (!normalizedHash) {
|
|
@@ -4461,7 +4635,7 @@ function formatMemoryDump(dump) {
|
|
|
4461
4635
|
}
|
|
4462
4636
|
function factFrontmatter(f) {
|
|
4463
4637
|
return {
|
|
4464
|
-
type: "fact",
|
|
4638
|
+
type: f.okf_type ?? "fact",
|
|
4465
4639
|
title: f.title,
|
|
4466
4640
|
tags: f.tags,
|
|
4467
4641
|
timestamp: new Date(f.updated_at).toISOString(),
|
|
@@ -4479,7 +4653,7 @@ function factFrontmatter(f) {
|
|
|
4479
4653
|
}
|
|
4480
4654
|
function taskFrontmatter(t) {
|
|
4481
4655
|
return {
|
|
4482
|
-
type: "task",
|
|
4656
|
+
type: t.okf_type ?? "task",
|
|
4483
4657
|
title: t.description,
|
|
4484
4658
|
timestamp: new Date(t.updated_at).toISOString(),
|
|
4485
4659
|
id: t.id,
|
|
@@ -4554,6 +4728,227 @@ function formatOkfBundle(dump) {
|
|
|
4554
4728
|
});
|
|
4555
4729
|
return { files };
|
|
4556
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
|
+
}
|
|
4557
4952
|
|
|
4558
4953
|
// src/librarianPrompt.ts
|
|
4559
4954
|
var DEFAULT_LIBRARIAN_SYNTHESIS_PROMPT = `You are a careful memory synthesis assistant.
|
|
@@ -4612,6 +5007,7 @@ exports.formatOkfBundle = formatOkfBundle;
|
|
|
4612
5007
|
exports.hydrateLibrarianPrompt = hydrateLibrarianPrompt;
|
|
4613
5008
|
exports.mapLibrarianOptionsToReadOptions = mapLibrarianOptionsToReadOptions;
|
|
4614
5009
|
exports.parseEmbedding = parseEmbedding;
|
|
5010
|
+
exports.parseOkfBundle = parseOkfBundle;
|
|
4615
5011
|
exports.validateLibrarianPromptTemplate = validateLibrarianPromptTemplate;
|
|
4616
5012
|
//# sourceMappingURL=index.js.map
|
|
4617
5013
|
//# sourceMappingURL=index.js.map
|