@equationalapplications/core-llm-wiki 5.3.1 → 5.4.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 +24 -0
- package/dist/{chunk-KIJINNYU.mjs → chunk-N2NXN6PO.mjs} +33 -6
- package/dist/chunk-N2NXN6PO.mjs.map +1 -0
- package/dist/index.d.mts +9 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.js +657 -52
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +629 -50
- package/dist/index.mjs.map +1 -1
- package/dist/{testing-zf3Jp_RL.d.mts → testing-C71mnMxq.d.mts} +104 -0
- package/dist/{testing-zf3Jp_RL.d.ts → testing-C71mnMxq.d.ts} +104 -0
- package/dist/testing.d.mts +2 -1
- package/dist/testing.d.ts +2 -1
- package/dist/testing.js +31 -4
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +3 -3
- package/dist/chunk-KIJINNYU.mjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var MiniSearch = require('minisearch');
|
|
4
3
|
var coreOkf = require('@equationalapplications/core-okf');
|
|
4
|
+
var MiniSearch = require('minisearch');
|
|
5
5
|
|
|
6
6
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
7
|
|
|
@@ -37,7 +37,15 @@ async function setupDatabase(db, prefix) {
|
|
|
37
37
|
embedding_blob BLOB,
|
|
38
38
|
okf_type TEXT,
|
|
39
39
|
ontology_checked_at INTEGER,
|
|
40
|
-
heal_checked_at INTEGER
|
|
40
|
+
heal_checked_at INTEGER,
|
|
41
|
+
lifecycle_status TEXT NOT NULL DEFAULT 'stable',
|
|
42
|
+
stale_after INTEGER,
|
|
43
|
+
generated_by TEXT,
|
|
44
|
+
last_verified_at INTEGER,
|
|
45
|
+
last_verified_by TEXT,
|
|
46
|
+
okf_sources TEXT,
|
|
47
|
+
okf_verified TEXT,
|
|
48
|
+
okf_usage_window TEXT
|
|
41
49
|
);
|
|
42
50
|
|
|
43
51
|
CREATE INDEX IF NOT EXISTS ${prefix}entries_entity_idx ON ${prefix}entries(entity_id);
|
|
@@ -73,7 +81,15 @@ async function setupDatabase(db, prefix) {
|
|
|
73
81
|
updated_at INTEGER NOT NULL,
|
|
74
82
|
resolved_at INTEGER,
|
|
75
83
|
deleted_at INTEGER,
|
|
76
|
-
okf_type TEXT
|
|
84
|
+
okf_type TEXT,
|
|
85
|
+
lifecycle_status TEXT NOT NULL DEFAULT 'stable',
|
|
86
|
+
stale_after INTEGER,
|
|
87
|
+
generated_by TEXT,
|
|
88
|
+
last_verified_at INTEGER,
|
|
89
|
+
last_verified_by TEXT,
|
|
90
|
+
okf_sources TEXT,
|
|
91
|
+
okf_verified TEXT,
|
|
92
|
+
okf_usage_window TEXT
|
|
77
93
|
);
|
|
78
94
|
|
|
79
95
|
CREATE INDEX IF NOT EXISTS ${prefix}tasks_entity_idx ON ${prefix}tasks(entity_id, status);
|
|
@@ -135,6 +151,24 @@ async function setupDatabase(db, prefix) {
|
|
|
135
151
|
CREATE INDEX IF NOT EXISTS ${prefix}outbox_created_at
|
|
136
152
|
ON ${prefix}outbox (created_at);
|
|
137
153
|
`);
|
|
154
|
+
await createOkfV02IndexesIfColumnsExist(db, prefix);
|
|
155
|
+
}
|
|
156
|
+
async function createOkfV02IndexesIfColumnsExist(db, prefix) {
|
|
157
|
+
for (const table of ["entries", "tasks"]) {
|
|
158
|
+
const cols = await db.getAllAsync(
|
|
159
|
+
`PRAGMA table_info(${prefix}${table})`
|
|
160
|
+
);
|
|
161
|
+
const colNames = new Set(cols.map((c) => c.name));
|
|
162
|
+
if (colNames.has("lifecycle_status")) {
|
|
163
|
+
await db.execAsync(`CREATE INDEX IF NOT EXISTS ${prefix}${table}_lifecycle_status_idx ON ${prefix}${table}(lifecycle_status);`);
|
|
164
|
+
}
|
|
165
|
+
if (colNames.has("stale_after")) {
|
|
166
|
+
await db.execAsync(`CREATE INDEX IF NOT EXISTS ${prefix}${table}_stale_after_idx ON ${prefix}${table}(stale_after);`);
|
|
167
|
+
}
|
|
168
|
+
if (colNames.has("last_verified_at")) {
|
|
169
|
+
await db.execAsync(`CREATE INDEX IF NOT EXISTS ${prefix}${table}_last_verified_at_idx ON ${prefix}${table}(last_verified_at);`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
138
172
|
}
|
|
139
173
|
|
|
140
174
|
// src/db/sqliteCodes.ts
|
|
@@ -481,6 +515,39 @@ var MIGRATIONS = [
|
|
|
481
515
|
WHERE rn = 1;`
|
|
482
516
|
);
|
|
483
517
|
}
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
version: 10,
|
|
521
|
+
description: "OKF v0.2: add lifecycle_status, stale_after, generated_by, last_verified_*, okf_sources, okf_verified, okf_usage_window to entries and tasks",
|
|
522
|
+
run: async (db, prefix) => {
|
|
523
|
+
for (const table of ["entries", "tasks"]) {
|
|
524
|
+
const cols = await db.getAllAsync(
|
|
525
|
+
`PRAGMA table_info(${prefix}${table})`
|
|
526
|
+
);
|
|
527
|
+
const existing = new Set(cols.map((c) => c.name));
|
|
528
|
+
const adds = [
|
|
529
|
+
["lifecycle_status", `ALTER TABLE ${prefix}${table} ADD COLUMN lifecycle_status TEXT NOT NULL DEFAULT 'stable'`],
|
|
530
|
+
["stale_after", `ALTER TABLE ${prefix}${table} ADD COLUMN stale_after INTEGER`],
|
|
531
|
+
["generated_by", `ALTER TABLE ${prefix}${table} ADD COLUMN generated_by TEXT`],
|
|
532
|
+
["last_verified_at", `ALTER TABLE ${prefix}${table} ADD COLUMN last_verified_at INTEGER`],
|
|
533
|
+
["last_verified_by", `ALTER TABLE ${prefix}${table} ADD COLUMN last_verified_by TEXT`],
|
|
534
|
+
["okf_sources", `ALTER TABLE ${prefix}${table} ADD COLUMN okf_sources TEXT`],
|
|
535
|
+
["okf_verified", `ALTER TABLE ${prefix}${table} ADD COLUMN okf_verified TEXT`],
|
|
536
|
+
["okf_usage_window", `ALTER TABLE ${prefix}${table} ADD COLUMN okf_usage_window TEXT`]
|
|
537
|
+
];
|
|
538
|
+
for (const [name, sql] of adds) {
|
|
539
|
+
if (!existing.has(name)) await db.execAsync(sql);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
await db.execAsync(`
|
|
543
|
+
CREATE INDEX IF NOT EXISTS ${prefix}entries_lifecycle_status_idx ON ${prefix}entries(lifecycle_status);
|
|
544
|
+
CREATE INDEX IF NOT EXISTS ${prefix}entries_stale_after_idx ON ${prefix}entries(stale_after);
|
|
545
|
+
CREATE INDEX IF NOT EXISTS ${prefix}entries_last_verified_at_idx ON ${prefix}entries(last_verified_at);
|
|
546
|
+
CREATE INDEX IF NOT EXISTS ${prefix}tasks_lifecycle_status_idx ON ${prefix}tasks(lifecycle_status);
|
|
547
|
+
CREATE INDEX IF NOT EXISTS ${prefix}tasks_stale_after_idx ON ${prefix}tasks(stale_after);
|
|
548
|
+
CREATE INDEX IF NOT EXISTS ${prefix}tasks_last_verified_at_idx ON ${prefix}tasks(last_verified_at);
|
|
549
|
+
`);
|
|
550
|
+
}
|
|
484
551
|
}
|
|
485
552
|
];
|
|
486
553
|
for (let i = 1; i < MIGRATIONS.length; i++) {
|
|
@@ -507,7 +574,29 @@ var BaseRepository = class {
|
|
|
507
574
|
}
|
|
508
575
|
};
|
|
509
576
|
|
|
510
|
-
// src/repositories/
|
|
577
|
+
// src/repositories/rowMappers.ts
|
|
578
|
+
function parseJsonArray(s, fallback) {
|
|
579
|
+
if (Array.isArray(s)) return s;
|
|
580
|
+
if (typeof s === "string" && s.length > 0) {
|
|
581
|
+
try {
|
|
582
|
+
const p = JSON.parse(s);
|
|
583
|
+
if (Array.isArray(p)) return p;
|
|
584
|
+
} catch {
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return fallback;
|
|
588
|
+
}
|
|
589
|
+
function parseJsonObject(s, fallback = null) {
|
|
590
|
+
if (s && typeof s === "object" && !Array.isArray(s)) return s;
|
|
591
|
+
if (typeof s === "string" && s.length > 0) {
|
|
592
|
+
try {
|
|
593
|
+
const p = JSON.parse(s);
|
|
594
|
+
if (p && typeof p === "object" && !Array.isArray(p)) return p;
|
|
595
|
+
} catch {
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
return fallback;
|
|
599
|
+
}
|
|
511
600
|
function mapRowToFact(row) {
|
|
512
601
|
const tags = (() => {
|
|
513
602
|
if (Array.isArray(row.tags)) return row.tags;
|
|
@@ -518,6 +607,9 @@ function mapRowToFact(row) {
|
|
|
518
607
|
}
|
|
519
608
|
return [];
|
|
520
609
|
})();
|
|
610
|
+
const okf_verified = parseJsonArray(row.okf_verified, []);
|
|
611
|
+
const now = Date.now();
|
|
612
|
+
const staleAfterRaw = row.stale_after != null ? Number(row.stale_after) : null;
|
|
521
613
|
return {
|
|
522
614
|
id: row.id,
|
|
523
615
|
entity_id: row.entity_id,
|
|
@@ -533,7 +625,19 @@ function mapRowToFact(row) {
|
|
|
533
625
|
last_accessed_at: row.last_accessed_at === null || row.last_accessed_at === void 0 ? null : Number(row.last_accessed_at),
|
|
534
626
|
deleted_at: row.deleted_at != null ? Number(row.deleted_at) : null,
|
|
535
627
|
access_count: Number(row.access_count ?? 0),
|
|
536
|
-
okf_type: row.okf_type ?? null
|
|
628
|
+
okf_type: row.okf_type ?? null,
|
|
629
|
+
// OKF v0.2
|
|
630
|
+
lifecycle_status: row.lifecycle_status ?? "stable",
|
|
631
|
+
stale_after: staleAfterRaw,
|
|
632
|
+
generated_by: row.generated_by ?? null,
|
|
633
|
+
okf_sources: parseJsonArray(row.okf_sources, []),
|
|
634
|
+
okf_verified,
|
|
635
|
+
okf_usage_window: parseJsonObject(row.okf_usage_window),
|
|
636
|
+
last_verified_at: row.last_verified_at != null ? Number(row.last_verified_at) : null,
|
|
637
|
+
last_verified_by: row.last_verified_by ?? null,
|
|
638
|
+
// Spec §2.7 + §5.3: hydrate so read() consumers don't re-call the helpers.
|
|
639
|
+
isStale: coreOkf.isStaleAfter(staleAfterRaw, now),
|
|
640
|
+
trustTier: coreOkf.deriveTrustTier(okf_verified)
|
|
537
641
|
};
|
|
538
642
|
}
|
|
539
643
|
function normalizeEmbeddingBlobValue(blob) {
|
|
@@ -589,6 +693,21 @@ var EntryRepository = class extends BaseRepository {
|
|
|
589
693
|
* Upsert a WikiFact. Nullable fields set to null when fact value is null.
|
|
590
694
|
* Returns { changes, lastInsertRowId }.
|
|
591
695
|
* `tx` is REQUIRED to ensure atomic outbox staging.
|
|
696
|
+
*
|
|
697
|
+
* OKF metadata discipline (spec §2.5 + DAO checklist §8): content writes
|
|
698
|
+
* MUST NOT silently overwrite OKF metadata that a caller didn't supply. The
|
|
699
|
+
* previous CASE WHEN IS NULL guards around `lifecycle_status`, etc. were
|
|
700
|
+
* dead because the bind always supplied a non-null value (notably
|
|
701
|
+
* `fact.lifecycle_status ?? 'stable'`), so excluded.X was never NULL and
|
|
702
|
+
* an UPDATE rewrote an existing `deprecated` row to `stable`. We now omit
|
|
703
|
+
* these columns from the SET clause entirely — SQLite's ON CONFLICT UPDATE
|
|
704
|
+
* leaves untouched columns at their existing row value, which is exactly
|
|
705
|
+
* the preservation semantics we want for content writes. Callers that
|
|
706
|
+
* intentionally want to overwrite OKF metadata should use
|
|
707
|
+
* {@link upsertForImport} (always-replace semantics for that path).
|
|
708
|
+
* New-row INSERTs still bind a value so the column is populated; if the
|
|
709
|
+
* caller supplied nothing, the schema DEFAULT 'stable' applies when the
|
|
710
|
+
* bind is bound as 'stable' explicitly here.
|
|
592
711
|
*/
|
|
593
712
|
async upsert(fact, tx) {
|
|
594
713
|
const executor = this.getExecutor(tx);
|
|
@@ -596,7 +715,10 @@ var EntryRepository = class extends BaseRepository {
|
|
|
596
715
|
const tagsJson = JSON.stringify(fact.tags);
|
|
597
716
|
const embeddingBlob = this.normalizeEmbeddingBlob(fact.embedding_blob);
|
|
598
717
|
const existingRow = await executor.getFirstAsync(
|
|
599
|
-
`SELECT id
|
|
718
|
+
`SELECT id, lifecycle_status, stale_after, generated_by,
|
|
719
|
+
okf_sources, okf_verified, okf_usage_window,
|
|
720
|
+
last_verified_at, last_verified_by
|
|
721
|
+
FROM ${this.prefix}entries WHERE id = ?`,
|
|
600
722
|
[fact.id]
|
|
601
723
|
);
|
|
602
724
|
const operation = fact.deleted_at ? "DELETE" : existingRow ? "UPDATE" : "INSERT";
|
|
@@ -604,8 +726,10 @@ var EntryRepository = class extends BaseRepository {
|
|
|
604
726
|
`INSERT INTO ${this.prefix}entries (
|
|
605
727
|
id, entity_id, title, body, tags, confidence, source_type,
|
|
606
728
|
source_hash, source_ref, created_at, updated_at, last_accessed_at, access_count,
|
|
607
|
-
deleted_at, embedding_blob, embedding, okf_type
|
|
608
|
-
|
|
729
|
+
deleted_at, embedding_blob, embedding, okf_type,
|
|
730
|
+
lifecycle_status, stale_after, generated_by, last_verified_at, last_verified_by,
|
|
731
|
+
okf_sources, okf_verified, okf_usage_window
|
|
732
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
609
733
|
ON CONFLICT(id) DO UPDATE SET
|
|
610
734
|
entity_id = excluded.entity_id,
|
|
611
735
|
title = excluded.title,
|
|
@@ -639,15 +763,36 @@ var EntryRepository = class extends BaseRepository {
|
|
|
639
763
|
fact.deleted_at ?? null,
|
|
640
764
|
embeddingBlob ?? null,
|
|
641
765
|
null,
|
|
642
|
-
fact.okf_type ?? null
|
|
766
|
+
fact.okf_type ?? null,
|
|
767
|
+
// OKF v0.2 metadata — bound for INSERT only; ON CONFLICT UPDATE
|
|
768
|
+
// preserves the existing row values (these columns are intentionally
|
|
769
|
+
// absent from the SET clause above).
|
|
770
|
+
fact.lifecycle_status ?? "stable",
|
|
771
|
+
fact.stale_after ?? null,
|
|
772
|
+
fact.generated_by ?? null,
|
|
773
|
+
fact.last_verified_at ?? null,
|
|
774
|
+
fact.last_verified_by ?? null,
|
|
775
|
+
fact.okf_sources ? JSON.stringify(fact.okf_sources) : null,
|
|
776
|
+
fact.okf_verified ? JSON.stringify(fact.okf_verified) : null,
|
|
777
|
+
fact.okf_usage_window ? JSON.stringify(fact.okf_usage_window) : null
|
|
643
778
|
]
|
|
644
779
|
);
|
|
780
|
+
const persistedOkf = existingRow ? {
|
|
781
|
+
lifecycle_status: existingRow.lifecycle_status ?? "stable",
|
|
782
|
+
stale_after: existingRow.stale_after != null ? Number(existingRow.stale_after) : null,
|
|
783
|
+
generated_by: existingRow.generated_by ?? null,
|
|
784
|
+
okf_sources: parseJsonArray(existingRow.okf_sources, []),
|
|
785
|
+
okf_verified: parseJsonArray(existingRow.okf_verified, []),
|
|
786
|
+
okf_usage_window: parseJsonObject(existingRow.okf_usage_window),
|
|
787
|
+
last_verified_at: existingRow.last_verified_at != null ? Number(existingRow.last_verified_at) : null,
|
|
788
|
+
last_verified_by: existingRow.last_verified_by ?? null
|
|
789
|
+
} : null;
|
|
645
790
|
await this.outbox.push({
|
|
646
791
|
entityId: fact.entity_id,
|
|
647
792
|
tableName: "entries",
|
|
648
793
|
recordId: fact.id,
|
|
649
794
|
operation,
|
|
650
|
-
payload: fact
|
|
795
|
+
payload: persistedOkf ? { ...fact, ...persistedOkf } : fact
|
|
651
796
|
}, tx);
|
|
652
797
|
return result;
|
|
653
798
|
}
|
|
@@ -708,8 +853,10 @@ var EntryRepository = class extends BaseRepository {
|
|
|
708
853
|
`INSERT INTO ${this.prefix}entries (
|
|
709
854
|
id, entity_id, title, body, tags, confidence, source_type,
|
|
710
855
|
source_hash, source_ref, created_at, updated_at, last_accessed_at, access_count,
|
|
711
|
-
deleted_at, embedding_blob, embedding, okf_type
|
|
712
|
-
|
|
856
|
+
deleted_at, embedding_blob, embedding, okf_type,
|
|
857
|
+
lifecycle_status, stale_after, generated_by, last_verified_at, last_verified_by,
|
|
858
|
+
okf_sources, okf_verified, okf_usage_window
|
|
859
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
713
860
|
ON CONFLICT(id) DO UPDATE SET
|
|
714
861
|
entity_id = excluded.entity_id,
|
|
715
862
|
title = excluded.title,
|
|
@@ -726,7 +873,15 @@ var EntryRepository = class extends BaseRepository {
|
|
|
726
873
|
deleted_at = excluded.deleted_at,
|
|
727
874
|
embedding_blob = excluded.embedding_blob,
|
|
728
875
|
embedding = NULL,
|
|
729
|
-
okf_type = excluded.okf_type
|
|
876
|
+
okf_type = excluded.okf_type,
|
|
877
|
+
lifecycle_status = excluded.lifecycle_status,
|
|
878
|
+
stale_after = excluded.stale_after,
|
|
879
|
+
generated_by = excluded.generated_by,
|
|
880
|
+
last_verified_at = excluded.last_verified_at,
|
|
881
|
+
last_verified_by = excluded.last_verified_by,
|
|
882
|
+
okf_sources = excluded.okf_sources,
|
|
883
|
+
okf_verified = excluded.okf_verified,
|
|
884
|
+
okf_usage_window = excluded.okf_usage_window`,
|
|
730
885
|
[
|
|
731
886
|
fact.id,
|
|
732
887
|
fact.entity_id,
|
|
@@ -744,7 +899,16 @@ var EntryRepository = class extends BaseRepository {
|
|
|
744
899
|
fact.deleted_at ?? null,
|
|
745
900
|
embeddingBlob ?? null,
|
|
746
901
|
null,
|
|
747
|
-
fact.okf_type ?? null
|
|
902
|
+
fact.okf_type ?? null,
|
|
903
|
+
// Same NOT NULL DEFAULT note as in upsert above.
|
|
904
|
+
fact.lifecycle_status ?? "stable",
|
|
905
|
+
fact.stale_after ?? null,
|
|
906
|
+
fact.generated_by ?? null,
|
|
907
|
+
fact.last_verified_at ?? null,
|
|
908
|
+
fact.last_verified_by ?? null,
|
|
909
|
+
fact.okf_sources ? JSON.stringify(fact.okf_sources) : null,
|
|
910
|
+
fact.okf_verified ? JSON.stringify(fact.okf_verified) : null,
|
|
911
|
+
fact.okf_usage_window ? JSON.stringify(fact.okf_usage_window) : null
|
|
748
912
|
]
|
|
749
913
|
);
|
|
750
914
|
return result;
|
|
@@ -1696,9 +1860,10 @@ var SourceRefIndexRepository = class extends BaseRepository {
|
|
|
1696
1860
|
return row?.source_ref ?? null;
|
|
1697
1861
|
}
|
|
1698
1862
|
};
|
|
1699
|
-
|
|
1700
|
-
// src/repositories/TaskRepository.ts
|
|
1701
1863
|
function mapRowToTask(row) {
|
|
1864
|
+
const okf_verified = parseJsonArray(row.okf_verified, []);
|
|
1865
|
+
const now = Date.now();
|
|
1866
|
+
const staleAfterRaw = row.stale_after != null ? Number(row.stale_after) : null;
|
|
1702
1867
|
return {
|
|
1703
1868
|
id: row.id,
|
|
1704
1869
|
entity_id: row.entity_id,
|
|
@@ -1709,7 +1874,19 @@ function mapRowToTask(row) {
|
|
|
1709
1874
|
updated_at: Number(row.updated_at),
|
|
1710
1875
|
resolved_at: row.resolved_at != null ? Number(row.resolved_at) : null,
|
|
1711
1876
|
deleted_at: row.deleted_at != null ? Number(row.deleted_at) : null,
|
|
1712
|
-
okf_type: row.okf_type ?? null
|
|
1877
|
+
okf_type: row.okf_type ?? null,
|
|
1878
|
+
// OKF v0.2
|
|
1879
|
+
lifecycle_status: row.lifecycle_status ?? "stable",
|
|
1880
|
+
stale_after: staleAfterRaw,
|
|
1881
|
+
generated_by: row.generated_by ?? null,
|
|
1882
|
+
okf_sources: parseJsonArray(row.okf_sources, []),
|
|
1883
|
+
okf_verified,
|
|
1884
|
+
okf_usage_window: parseJsonObject(row.okf_usage_window),
|
|
1885
|
+
last_verified_at: row.last_verified_at != null ? Number(row.last_verified_at) : null,
|
|
1886
|
+
last_verified_by: row.last_verified_by ?? null,
|
|
1887
|
+
// Spec §2.7 + §5.3: hydrate so read() consumers don't re-call the helpers.
|
|
1888
|
+
isStale: coreOkf.isStaleAfter(staleAfterRaw, now),
|
|
1889
|
+
trustTier: coreOkf.deriveTrustTier(okf_verified)
|
|
1713
1890
|
};
|
|
1714
1891
|
}
|
|
1715
1892
|
var TaskRepository = class extends BaseRepository {
|
|
@@ -1759,20 +1936,31 @@ var TaskRepository = class extends BaseRepository {
|
|
|
1759
1936
|
* Uses ON CONFLICT(id) DO UPDATE (not INSERT OR REPLACE).
|
|
1760
1937
|
* Stages an outbox entry in the same transaction.
|
|
1761
1938
|
* `tx` is REQUIRED.
|
|
1939
|
+
*
|
|
1940
|
+
* OKF metadata (lifecycle_status, sources, etc.) is intentionally excluded
|
|
1941
|
+
* from the SET clause so content writes preserve any existing row values.
|
|
1942
|
+
* See {@link EntryRepository.upsert} for the rationale — the same applies
|
|
1943
|
+
* symmetrically to tasks (spec §2.5). Callers that need to overwrite OKF
|
|
1944
|
+
* metadata should use {@link upsertForImport}.
|
|
1762
1945
|
*/
|
|
1763
1946
|
async upsert(task, tx, updatedAt) {
|
|
1764
1947
|
const executor = this.getExecutor(tx);
|
|
1765
1948
|
const now = Number.isFinite(updatedAt) ? updatedAt : Date.now();
|
|
1766
1949
|
const existingRow = await executor.getFirstAsync(
|
|
1767
|
-
`SELECT id
|
|
1950
|
+
`SELECT id, lifecycle_status, stale_after, generated_by,
|
|
1951
|
+
okf_sources, okf_verified, okf_usage_window,
|
|
1952
|
+
last_verified_at, last_verified_by
|
|
1953
|
+
FROM ${this.prefix}tasks WHERE id = ?`,
|
|
1768
1954
|
[task.id]
|
|
1769
1955
|
);
|
|
1770
1956
|
const operation = task.deleted_at != null ? "DELETE" : existingRow ? "UPDATE" : "INSERT";
|
|
1771
1957
|
await executor.runAsync(
|
|
1772
1958
|
`INSERT INTO ${this.prefix}tasks (
|
|
1773
1959
|
id, entity_id, description, status, priority,
|
|
1774
|
-
created_at, updated_at, resolved_at, deleted_at
|
|
1775
|
-
|
|
1960
|
+
created_at, updated_at, resolved_at, deleted_at,
|
|
1961
|
+
lifecycle_status, stale_after, generated_by, last_verified_at, last_verified_by,
|
|
1962
|
+
okf_sources, okf_verified, okf_usage_window
|
|
1963
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1776
1964
|
ON CONFLICT(id) DO UPDATE SET
|
|
1777
1965
|
entity_id = excluded.entity_id,
|
|
1778
1966
|
description = excluded.description,
|
|
@@ -1791,16 +1979,37 @@ var TaskRepository = class extends BaseRepository {
|
|
|
1791
1979
|
now,
|
|
1792
1980
|
// updated_at set by repo or import override
|
|
1793
1981
|
task.resolved_at ?? null,
|
|
1794
|
-
task.deleted_at ?? null
|
|
1982
|
+
task.deleted_at ?? null,
|
|
1983
|
+
// OKF v0.2 metadata — bound for INSERT only; ON CONFLICT UPDATE
|
|
1984
|
+
// preserves the existing row values (these columns are intentionally
|
|
1985
|
+
// absent from the SET clause above).
|
|
1986
|
+
task.lifecycle_status ?? "stable",
|
|
1987
|
+
task.stale_after ?? null,
|
|
1988
|
+
task.generated_by ?? null,
|
|
1989
|
+
task.last_verified_at ?? null,
|
|
1990
|
+
task.last_verified_by ?? null,
|
|
1991
|
+
task.okf_sources ? JSON.stringify(task.okf_sources) : null,
|
|
1992
|
+
task.okf_verified ? JSON.stringify(task.okf_verified) : null,
|
|
1993
|
+
task.okf_usage_window ? JSON.stringify(task.okf_usage_window) : null
|
|
1795
1994
|
]
|
|
1796
1995
|
);
|
|
1996
|
+
const persistedOkf = existingRow ? {
|
|
1997
|
+
lifecycle_status: existingRow.lifecycle_status ?? "stable",
|
|
1998
|
+
stale_after: existingRow.stale_after != null ? Number(existingRow.stale_after) : null,
|
|
1999
|
+
generated_by: existingRow.generated_by ?? null,
|
|
2000
|
+
okf_sources: parseJsonArray(existingRow.okf_sources, []),
|
|
2001
|
+
okf_verified: parseJsonArray(existingRow.okf_verified, []),
|
|
2002
|
+
okf_usage_window: parseJsonObject(existingRow.okf_usage_window),
|
|
2003
|
+
last_verified_at: existingRow.last_verified_at != null ? Number(existingRow.last_verified_at) : null,
|
|
2004
|
+
last_verified_by: existingRow.last_verified_by ?? null
|
|
2005
|
+
} : null;
|
|
1797
2006
|
await this.outbox.push(
|
|
1798
2007
|
{
|
|
1799
2008
|
entityId: task.entity_id,
|
|
1800
2009
|
tableName: "tasks",
|
|
1801
2010
|
recordId: task.id,
|
|
1802
2011
|
operation,
|
|
1803
|
-
payload: task
|
|
2012
|
+
payload: persistedOkf ? { ...task, ...persistedOkf } : task
|
|
1804
2013
|
},
|
|
1805
2014
|
tx
|
|
1806
2015
|
);
|
|
@@ -1811,8 +2020,10 @@ var TaskRepository = class extends BaseRepository {
|
|
|
1811
2020
|
await executor.runAsync(
|
|
1812
2021
|
`INSERT INTO ${this.prefix}tasks (
|
|
1813
2022
|
id, entity_id, description, status, priority,
|
|
1814
|
-
created_at, updated_at, resolved_at, deleted_at, okf_type
|
|
1815
|
-
|
|
2023
|
+
created_at, updated_at, resolved_at, deleted_at, okf_type,
|
|
2024
|
+
lifecycle_status, stale_after, generated_by, last_verified_at, last_verified_by,
|
|
2025
|
+
okf_sources, okf_verified, okf_usage_window
|
|
2026
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1816
2027
|
ON CONFLICT(id) DO UPDATE SET
|
|
1817
2028
|
entity_id = excluded.entity_id,
|
|
1818
2029
|
description = excluded.description,
|
|
@@ -1821,7 +2032,15 @@ var TaskRepository = class extends BaseRepository {
|
|
|
1821
2032
|
updated_at = excluded.updated_at,
|
|
1822
2033
|
resolved_at = excluded.resolved_at,
|
|
1823
2034
|
deleted_at = excluded.deleted_at,
|
|
1824
|
-
okf_type = excluded.okf_type
|
|
2035
|
+
okf_type = excluded.okf_type,
|
|
2036
|
+
lifecycle_status = excluded.lifecycle_status,
|
|
2037
|
+
stale_after = excluded.stale_after,
|
|
2038
|
+
generated_by = excluded.generated_by,
|
|
2039
|
+
last_verified_at = excluded.last_verified_at,
|
|
2040
|
+
last_verified_by = excluded.last_verified_by,
|
|
2041
|
+
okf_sources = excluded.okf_sources,
|
|
2042
|
+
okf_verified = excluded.okf_verified,
|
|
2043
|
+
okf_usage_window = excluded.okf_usage_window`,
|
|
1825
2044
|
[
|
|
1826
2045
|
task.id,
|
|
1827
2046
|
task.entity_id,
|
|
@@ -1832,7 +2051,15 @@ var TaskRepository = class extends BaseRepository {
|
|
|
1832
2051
|
now,
|
|
1833
2052
|
task.resolved_at ?? null,
|
|
1834
2053
|
task.deleted_at ?? null,
|
|
1835
|
-
task.okf_type ?? null
|
|
2054
|
+
task.okf_type ?? null,
|
|
2055
|
+
task.lifecycle_status ?? "stable",
|
|
2056
|
+
task.stale_after ?? null,
|
|
2057
|
+
task.generated_by ?? null,
|
|
2058
|
+
task.last_verified_at ?? null,
|
|
2059
|
+
task.last_verified_by ?? null,
|
|
2060
|
+
task.okf_sources ? JSON.stringify(task.okf_sources) : null,
|
|
2061
|
+
task.okf_verified ? JSON.stringify(task.okf_verified) : null,
|
|
2062
|
+
task.okf_usage_window ? JSON.stringify(task.okf_usage_window) : null
|
|
1836
2063
|
]
|
|
1837
2064
|
);
|
|
1838
2065
|
}
|
|
@@ -4760,8 +4987,6 @@ var MaintenanceService = class {
|
|
|
4760
4987
|
return sanitizeRankerError(err, this.options.sanitizeRankerErrors);
|
|
4761
4988
|
}
|
|
4762
4989
|
};
|
|
4763
|
-
|
|
4764
|
-
// src/services/ImportExportService.ts
|
|
4765
4990
|
var MAX_EMBEDDING_BLOB_BYTES = 32 * 1024;
|
|
4766
4991
|
var IMPORT_TITLE_MAX = 500;
|
|
4767
4992
|
var IMPORT_BODY_MAX = 8e3;
|
|
@@ -4981,7 +5206,23 @@ var ImportExportService = class {
|
|
|
4981
5206
|
access_count: fact.access_count,
|
|
4982
5207
|
deleted_at: fact.deleted_at,
|
|
4983
5208
|
embedding_blob: blobData ?? void 0,
|
|
4984
|
-
okf_type: fact.okf_type ?? null
|
|
5209
|
+
okf_type: fact.okf_type ?? null,
|
|
5210
|
+
// OKF v0.2 — forwarded as-is; parseOkfBundle / the caller is responsible for
|
|
5211
|
+
// populating these per spec, importDump does not derive or validate them.
|
|
5212
|
+
lifecycle_status: fact.lifecycle_status ?? "stable",
|
|
5213
|
+
stale_after: fact.stale_after ?? null,
|
|
5214
|
+
generated_by: fact.generated_by ?? null,
|
|
5215
|
+
okf_sources: fact.okf_sources ?? [],
|
|
5216
|
+
okf_verified: fact.okf_verified ?? [],
|
|
5217
|
+
okf_usage_window: fact.okf_usage_window ?? null,
|
|
5218
|
+
// last_verified_at/_by are DERIVED mirrors of `okf_verified` per spec
|
|
5219
|
+
// §5.3. Recomputing here (instead of trusting the payload field) closes
|
|
5220
|
+
// the invariant that a raw importDump cannot carry a
|
|
5221
|
+
// last_verified_by that contradicts its own okf_verified list.
|
|
5222
|
+
...(() => {
|
|
5223
|
+
const latest = coreOkf.latestVerified(fact.okf_verified ?? [], Date.now());
|
|
5224
|
+
return { last_verified_at: latest?.at ?? null, last_verified_by: latest?.by ?? null };
|
|
5225
|
+
})()
|
|
4985
5226
|
};
|
|
4986
5227
|
await this.entryRepo.upsertForImport(factObj, tx);
|
|
4987
5228
|
if (blobData != null) {
|
|
@@ -5031,7 +5272,19 @@ var ImportExportService = class {
|
|
|
5031
5272
|
updated_at: safeUpdatedAt,
|
|
5032
5273
|
resolved_at: task.resolved_at,
|
|
5033
5274
|
deleted_at: task.deleted_at,
|
|
5034
|
-
okf_type: task.okf_type ?? null
|
|
5275
|
+
okf_type: task.okf_type ?? null,
|
|
5276
|
+
// OKF v0.2
|
|
5277
|
+
lifecycle_status: task.lifecycle_status ?? "stable",
|
|
5278
|
+
stale_after: task.stale_after ?? null,
|
|
5279
|
+
generated_by: task.generated_by ?? null,
|
|
5280
|
+
okf_sources: task.okf_sources ?? [],
|
|
5281
|
+
okf_verified: task.okf_verified ?? [],
|
|
5282
|
+
okf_usage_window: task.okf_usage_window ?? null,
|
|
5283
|
+
// Derived mirrors — see fact branch above; same invariant.
|
|
5284
|
+
...(() => {
|
|
5285
|
+
const latest = coreOkf.latestVerified(task.okf_verified ?? [], Date.now());
|
|
5286
|
+
return { last_verified_at: latest?.at ?? null, last_verified_by: latest?.by ?? null };
|
|
5287
|
+
})()
|
|
5035
5288
|
},
|
|
5036
5289
|
tx,
|
|
5037
5290
|
safeUpdatedAt
|
|
@@ -6157,6 +6410,164 @@ var GraphTraversalService = class {
|
|
|
6157
6410
|
return { nodes, edges: filteredEdges };
|
|
6158
6411
|
}
|
|
6159
6412
|
};
|
|
6413
|
+
var OkfTrustWritesRepository = class {
|
|
6414
|
+
constructor(db, prefix) {
|
|
6415
|
+
this.db = db;
|
|
6416
|
+
this.prefix = prefix;
|
|
6417
|
+
}
|
|
6418
|
+
getExecutor(tx) {
|
|
6419
|
+
return tx ?? this.db;
|
|
6420
|
+
}
|
|
6421
|
+
/**
|
|
6422
|
+
* Append one or more verification events to a fact's `okf_verified` JSON
|
|
6423
|
+
* array, atomically, in a single SQL statement.
|
|
6424
|
+
*
|
|
6425
|
+
* The implementation uses json_each over the existing array UNION ALL'd
|
|
6426
|
+
* with json_each over the new events wrapped in json_group_array — a single
|
|
6427
|
+
* UPDATE expression. SQLite serializes this against concurrent writers on
|
|
6428
|
+
* the same row, so two callers appending simultaneously cannot lose each
|
|
6429
|
+
* other's events (the previous implementation serialized only the supplied
|
|
6430
|
+
* array and overwrote, dropping any pre-existing history).
|
|
6431
|
+
*
|
|
6432
|
+
* Sets the `last_verified_*` convenience columns from the latest event in
|
|
6433
|
+
* the union (computed in code; see {@link latestVerified}).
|
|
6434
|
+
*/
|
|
6435
|
+
async writeOkfTrust(entryId, entityId, verified, tx) {
|
|
6436
|
+
const executor = this.getExecutor(tx);
|
|
6437
|
+
if (verified.length === 0) return;
|
|
6438
|
+
const latest = coreOkf.latestVerified(verified, Date.now());
|
|
6439
|
+
const newEventsJson = JSON.stringify(verified);
|
|
6440
|
+
await executor.runAsync(
|
|
6441
|
+
`UPDATE ${this.prefix}entries
|
|
6442
|
+
SET okf_verified = COALESCE(
|
|
6443
|
+
(SELECT json_group_array(json_object('by', s.by_val, 'at', s.at_val))
|
|
6444
|
+
FROM (
|
|
6445
|
+
SELECT json_extract(value, '$.by') AS by_val,
|
|
6446
|
+
json_extract(value, '$.at') AS at_val
|
|
6447
|
+
FROM json_each(okf_verified)
|
|
6448
|
+
UNION ALL
|
|
6449
|
+
SELECT json_extract(value, '$.by'),
|
|
6450
|
+
json_extract(value, '$.at')
|
|
6451
|
+
FROM json_each(?)
|
|
6452
|
+
) AS s
|
|
6453
|
+
ORDER BY s.at_val),
|
|
6454
|
+
'[]'
|
|
6455
|
+
),
|
|
6456
|
+
last_verified_by = ?,
|
|
6457
|
+
last_verified_at = ?
|
|
6458
|
+
WHERE id = ? AND entity_id = ?`,
|
|
6459
|
+
[newEventsJson, latest?.by ?? null, latest?.at ?? null, entryId, entityId]
|
|
6460
|
+
);
|
|
6461
|
+
}
|
|
6462
|
+
/** Replace the `sources` list (provenance). */
|
|
6463
|
+
async writeOkfSources(entryId, entityId, sources, tx) {
|
|
6464
|
+
const executor = this.getExecutor(tx);
|
|
6465
|
+
const json = coreOkf.formatSourcesJson(sources);
|
|
6466
|
+
await executor.runAsync(
|
|
6467
|
+
`UPDATE ${this.prefix}entries
|
|
6468
|
+
SET okf_sources = ?
|
|
6469
|
+
WHERE id = ? AND entity_id = ?`,
|
|
6470
|
+
[json, entryId, entityId]
|
|
6471
|
+
);
|
|
6472
|
+
}
|
|
6473
|
+
/** Set the OKF v0.2 lifecycle status. */
|
|
6474
|
+
async setLifecycleStatus(entryId, entityId, status, tx) {
|
|
6475
|
+
const executor = this.getExecutor(tx);
|
|
6476
|
+
await executor.runAsync(
|
|
6477
|
+
`UPDATE ${this.prefix}entries
|
|
6478
|
+
SET lifecycle_status = ?
|
|
6479
|
+
WHERE id = ? AND entity_id = ?`,
|
|
6480
|
+
[status, entryId, entityId]
|
|
6481
|
+
);
|
|
6482
|
+
}
|
|
6483
|
+
/** Set the absolute cutoff (epoch ms) for staleness, or NULL to clear. */
|
|
6484
|
+
async setStaleAfter(entryId, entityId, date, tx) {
|
|
6485
|
+
const executor = this.getExecutor(tx);
|
|
6486
|
+
await executor.runAsync(
|
|
6487
|
+
`UPDATE ${this.prefix}entries
|
|
6488
|
+
SET stale_after = ?
|
|
6489
|
+
WHERE id = ? AND entity_id = ?`,
|
|
6490
|
+
[date, entryId, entityId]
|
|
6491
|
+
);
|
|
6492
|
+
}
|
|
6493
|
+
/** Set the producer / verifier actor string. Rare. */
|
|
6494
|
+
async setGeneratedBy(entryId, entityId, actor, tx) {
|
|
6495
|
+
const executor = this.getExecutor(tx);
|
|
6496
|
+
await executor.runAsync(
|
|
6497
|
+
`UPDATE ${this.prefix}entries
|
|
6498
|
+
SET generated_by = ?
|
|
6499
|
+
WHERE id = ? AND entity_id = ?`,
|
|
6500
|
+
[actor, entryId, entityId]
|
|
6501
|
+
);
|
|
6502
|
+
}
|
|
6503
|
+
// The same five methods exist for tasks, with `tasks` instead of `entries`.
|
|
6504
|
+
// Tasks are symmetric per spec §2.5.
|
|
6505
|
+
/** Atomic append, task variant — mirrors {@link writeOkfTrust}. */
|
|
6506
|
+
async writeOkfTrustTask(taskId, entityId, verified, tx) {
|
|
6507
|
+
const executor = this.getExecutor(tx);
|
|
6508
|
+
if (verified.length === 0) return;
|
|
6509
|
+
const latest = coreOkf.latestVerified(verified, Date.now());
|
|
6510
|
+
const newEventsJson = JSON.stringify(verified);
|
|
6511
|
+
await executor.runAsync(
|
|
6512
|
+
`UPDATE ${this.prefix}tasks
|
|
6513
|
+
SET okf_verified = COALESCE(
|
|
6514
|
+
(SELECT json_group_array(json_object('by', s.by_val, 'at', s.at_val))
|
|
6515
|
+
FROM (
|
|
6516
|
+
SELECT json_extract(value, '$.by') AS by_val,
|
|
6517
|
+
json_extract(value, '$.at') AS at_val
|
|
6518
|
+
FROM json_each(okf_verified)
|
|
6519
|
+
UNION ALL
|
|
6520
|
+
SELECT json_extract(value, '$.by'),
|
|
6521
|
+
json_extract(value, '$.at')
|
|
6522
|
+
FROM json_each(?)
|
|
6523
|
+
) AS s
|
|
6524
|
+
ORDER BY s.at_val),
|
|
6525
|
+
'[]'
|
|
6526
|
+
),
|
|
6527
|
+
last_verified_by = ?,
|
|
6528
|
+
last_verified_at = ?
|
|
6529
|
+
WHERE id = ? AND entity_id = ?`,
|
|
6530
|
+
[newEventsJson, latest?.by ?? null, latest?.at ?? null, taskId, entityId]
|
|
6531
|
+
);
|
|
6532
|
+
}
|
|
6533
|
+
async writeOkfSourcesTask(taskId, entityId, sources, tx) {
|
|
6534
|
+
const executor = this.getExecutor(tx);
|
|
6535
|
+
const json = coreOkf.formatSourcesJson(sources);
|
|
6536
|
+
await executor.runAsync(
|
|
6537
|
+
`UPDATE ${this.prefix}tasks
|
|
6538
|
+
SET okf_sources = ?
|
|
6539
|
+
WHERE id = ? AND entity_id = ?`,
|
|
6540
|
+
[json, taskId, entityId]
|
|
6541
|
+
);
|
|
6542
|
+
}
|
|
6543
|
+
async setLifecycleStatusTask(taskId, entityId, status, tx) {
|
|
6544
|
+
const executor = this.getExecutor(tx);
|
|
6545
|
+
await executor.runAsync(
|
|
6546
|
+
`UPDATE ${this.prefix}tasks
|
|
6547
|
+
SET lifecycle_status = ?
|
|
6548
|
+
WHERE id = ? AND entity_id = ?`,
|
|
6549
|
+
[status, taskId, entityId]
|
|
6550
|
+
);
|
|
6551
|
+
}
|
|
6552
|
+
async setStaleAfterTask(taskId, entityId, date, tx) {
|
|
6553
|
+
const executor = this.getExecutor(tx);
|
|
6554
|
+
await executor.runAsync(
|
|
6555
|
+
`UPDATE ${this.prefix}tasks
|
|
6556
|
+
SET stale_after = ?
|
|
6557
|
+
WHERE id = ? AND entity_id = ?`,
|
|
6558
|
+
[date, taskId, entityId]
|
|
6559
|
+
);
|
|
6560
|
+
}
|
|
6561
|
+
async setGeneratedByTask(taskId, entityId, actor, tx) {
|
|
6562
|
+
const executor = this.getExecutor(tx);
|
|
6563
|
+
await executor.runAsync(
|
|
6564
|
+
`UPDATE ${this.prefix}tasks
|
|
6565
|
+
SET generated_by = ?
|
|
6566
|
+
WHERE id = ? AND entity_id = ?`,
|
|
6567
|
+
[actor, taskId, entityId]
|
|
6568
|
+
);
|
|
6569
|
+
}
|
|
6570
|
+
};
|
|
6160
6571
|
|
|
6161
6572
|
// src/WikiMemory.ts
|
|
6162
6573
|
var TABLE_PREFIX_PATTERN = /^[A-Za-z][A-Za-z0-9_]{0,30}_$/;
|
|
@@ -6251,6 +6662,7 @@ var WikiMemory = class {
|
|
|
6251
6662
|
this.entryRepo,
|
|
6252
6663
|
this.options.config ?? {}
|
|
6253
6664
|
);
|
|
6665
|
+
this.okfTrustWrites = new OkfTrustWritesRepository(this.db, this.prefix);
|
|
6254
6666
|
}
|
|
6255
6667
|
/**
|
|
6256
6668
|
* Explicit escape hatch for test suites: typed access to composed services for mocks/spies.
|
|
@@ -6618,6 +7030,42 @@ var WikiMemory = class {
|
|
|
6618
7030
|
);
|
|
6619
7031
|
this.ontologyService.invalidateCache(entityId);
|
|
6620
7032
|
}
|
|
7033
|
+
/** Append a verification event to a fact. Does NOT touch `updated_at`. */
|
|
7034
|
+
async writeOkfTrust(entryId, entityId, verified) {
|
|
7035
|
+
return this.okfTrustWrites.writeOkfTrust(entryId, entityId, verified);
|
|
7036
|
+
}
|
|
7037
|
+
/** Replace a fact's source list. Does NOT touch `updated_at`. */
|
|
7038
|
+
async writeOkfSources(entryId, entityId, sources) {
|
|
7039
|
+
return this.okfTrustWrites.writeOkfSources(entryId, entityId, sources);
|
|
7040
|
+
}
|
|
7041
|
+
/** Set a fact's OKF v0.2 lifecycle status. Does NOT touch `updated_at`. */
|
|
7042
|
+
async setLifecycleStatus(entryId, entityId, status) {
|
|
7043
|
+
return this.okfTrustWrites.setLifecycleStatus(entryId, entityId, status);
|
|
7044
|
+
}
|
|
7045
|
+
/** Set a fact's stale_after (epoch ms) or clear it. Does NOT touch `updated_at`. */
|
|
7046
|
+
async setStaleAfter(entryId, entityId, date) {
|
|
7047
|
+
return this.okfTrustWrites.setStaleAfter(entryId, entityId, date);
|
|
7048
|
+
}
|
|
7049
|
+
/** Set a fact's generated_by actor string. Does NOT touch `updated_at`. */
|
|
7050
|
+
async setGeneratedBy(entryId, entityId, actor) {
|
|
7051
|
+
return this.okfTrustWrites.setGeneratedBy(entryId, entityId, actor);
|
|
7052
|
+
}
|
|
7053
|
+
/** Task variants of the same five DAO methods. Symmetric per spec §2.5. */
|
|
7054
|
+
async writeOkfTrustTask(taskId, entityId, verified) {
|
|
7055
|
+
return this.okfTrustWrites.writeOkfTrustTask(taskId, entityId, verified);
|
|
7056
|
+
}
|
|
7057
|
+
async writeOkfSourcesTask(taskId, entityId, sources) {
|
|
7058
|
+
return this.okfTrustWrites.writeOkfSourcesTask(taskId, entityId, sources);
|
|
7059
|
+
}
|
|
7060
|
+
async setLifecycleStatusTask(taskId, entityId, status) {
|
|
7061
|
+
return this.okfTrustWrites.setLifecycleStatusTask(taskId, entityId, status);
|
|
7062
|
+
}
|
|
7063
|
+
async setStaleAfterTask(taskId, entityId, date) {
|
|
7064
|
+
return this.okfTrustWrites.setStaleAfterTask(taskId, entityId, date);
|
|
7065
|
+
}
|
|
7066
|
+
async setGeneratedByTask(taskId, entityId, actor) {
|
|
7067
|
+
return this.okfTrustWrites.setGeneratedByTask(taskId, entityId, actor);
|
|
7068
|
+
}
|
|
6621
7069
|
};
|
|
6622
7070
|
_testAccessNonTestEnvWarned = new WeakMap();
|
|
6623
7071
|
|
|
@@ -6921,13 +7369,87 @@ function formatMemoryDump(dump) {
|
|
|
6921
7369
|
files
|
|
6922
7370
|
};
|
|
6923
7371
|
}
|
|
6924
|
-
var
|
|
6925
|
-
function
|
|
7372
|
+
var DEFAULT_PROFILE = "llm-wiki/2";
|
|
7373
|
+
function isoOrFallback(updatedAt) {
|
|
7374
|
+
if (!Number.isFinite(updatedAt)) return (/* @__PURE__ */ new Date(0)).toISOString();
|
|
7375
|
+
const d = new Date(updatedAt);
|
|
7376
|
+
if (!Number.isFinite(d.getTime())) return (/* @__PURE__ */ new Date(0)).toISOString();
|
|
7377
|
+
return d.toISOString();
|
|
7378
|
+
}
|
|
7379
|
+
function isoDateOrOmit(ms) {
|
|
7380
|
+
if (ms == null || !Number.isFinite(ms)) return void 0;
|
|
7381
|
+
const d = new Date(ms);
|
|
7382
|
+
if (!Number.isFinite(d.getTime())) return void 0;
|
|
7383
|
+
return d.toISOString().slice(0, 10);
|
|
7384
|
+
}
|
|
7385
|
+
function factFrontmatterV2(f) {
|
|
6926
7386
|
return {
|
|
6927
7387
|
type: f.okf_type ?? "fact",
|
|
6928
7388
|
title: f.title,
|
|
6929
7389
|
tags: f.tags,
|
|
6930
|
-
timestamp
|
|
7390
|
+
// `timestamp` rides along deliberately even on the v0.2 path — a naive
|
|
7391
|
+
// consumer that only understands the v0.1 key still gets a readable date
|
|
7392
|
+
// without needing flow-mapping support. `generated.at` is the canonical
|
|
7393
|
+
// v0.2 field and always wins on import when both are present (spec §2.4).
|
|
7394
|
+
timestamp: isoOrFallback(f.updated_at),
|
|
7395
|
+
resource: f.source_ref ?? void 0,
|
|
7396
|
+
id: f.id,
|
|
7397
|
+
entity_id: f.entity_id,
|
|
7398
|
+
confidence: f.confidence,
|
|
7399
|
+
source_type: f.source_type,
|
|
7400
|
+
source_hash: f.source_hash,
|
|
7401
|
+
created_at: f.created_at,
|
|
7402
|
+
access_count: f.access_count,
|
|
7403
|
+
last_accessed_at: f.last_accessed_at,
|
|
7404
|
+
deleted_at: f.deleted_at,
|
|
7405
|
+
// OKF v0.2
|
|
7406
|
+
status: f.lifecycle_status ?? "stable",
|
|
7407
|
+
...isoDateOrOmit(f.stale_after) ? { stale_after: isoDateOrOmit(f.stale_after) } : {},
|
|
7408
|
+
// Omit `generated` entirely when there's no actor on record — never
|
|
7409
|
+
// fabricate a `generated_by` (mirrors the import side's rule in §4.8:
|
|
7410
|
+
// it never invents one either, so export must not manufacture one that
|
|
7411
|
+
// would then round-trip back in as if it had always been asserted).
|
|
7412
|
+
...f.generated_by ? { generated: { by: f.generated_by, at: isoOrFallback(f.updated_at) } } : {},
|
|
7413
|
+
// Omit `verified`/`sources` when empty — an emitted `[]` is not the same
|
|
7414
|
+
// shape as an absent key (spec §5.3 derives `unverified` from "no
|
|
7415
|
+
// `verified` key", not from "empty `verified` list").
|
|
7416
|
+
...f.okf_verified && f.okf_verified.length > 0 ? { verified: f.okf_verified } : {},
|
|
7417
|
+
...f.okf_sources && f.okf_sources.length > 0 ? { sources: f.okf_sources } : {},
|
|
7418
|
+
...f.okf_usage_window ? { usage_window: f.okf_usage_window } : {}
|
|
7419
|
+
};
|
|
7420
|
+
}
|
|
7421
|
+
function taskFrontmatterV2(t) {
|
|
7422
|
+
return {
|
|
7423
|
+
type: t.okf_type ?? "task",
|
|
7424
|
+
title: t.description,
|
|
7425
|
+
timestamp: isoOrFallback(t.updated_at),
|
|
7426
|
+
// see factFrontmatterV2 comment — deliberate back-compat duplication
|
|
7427
|
+
id: t.id,
|
|
7428
|
+
entity_id: t.entity_id,
|
|
7429
|
+
status: t.lifecycle_status ?? "stable",
|
|
7430
|
+
// OKF v0.2 lifecycle
|
|
7431
|
+
execution_status: t.status,
|
|
7432
|
+
// execution state under renamed key
|
|
7433
|
+
priority: t.priority,
|
|
7434
|
+
created_at: t.created_at,
|
|
7435
|
+
resolved_at: t.resolved_at,
|
|
7436
|
+
deleted_at: t.deleted_at,
|
|
7437
|
+
// OKF v0.2 — symmetric with facts (spec §2.5): stale_after applies to
|
|
7438
|
+
// tasks too (a hallucinated task needs a freshness signal as much as a
|
|
7439
|
+
// hallucinated fact does).
|
|
7440
|
+
...isoDateOrOmit(t.stale_after) ? { stale_after: isoDateOrOmit(t.stale_after) } : {},
|
|
7441
|
+
...t.generated_by ? { generated: { by: t.generated_by, at: isoOrFallback(t.updated_at) } } : {},
|
|
7442
|
+
...t.okf_verified && t.okf_verified.length > 0 ? { verified: t.okf_verified } : {},
|
|
7443
|
+
...t.okf_sources && t.okf_sources.length > 0 ? { sources: t.okf_sources } : {},
|
|
7444
|
+
...t.okf_usage_window ? { usage_window: t.okf_usage_window } : {}
|
|
7445
|
+
};
|
|
7446
|
+
}
|
|
7447
|
+
function factFrontmatterV1(f) {
|
|
7448
|
+
return {
|
|
7449
|
+
type: f.okf_type ?? "fact",
|
|
7450
|
+
title: f.title,
|
|
7451
|
+
tags: f.tags,
|
|
7452
|
+
timestamp: isoOrFallback(f.updated_at),
|
|
6931
7453
|
resource: f.source_ref ?? void 0,
|
|
6932
7454
|
id: f.id,
|
|
6933
7455
|
entity_id: f.entity_id,
|
|
@@ -6940,11 +7462,11 @@ function factFrontmatter(f) {
|
|
|
6940
7462
|
deleted_at: f.deleted_at
|
|
6941
7463
|
};
|
|
6942
7464
|
}
|
|
6943
|
-
function
|
|
7465
|
+
function taskFrontmatterV1(t) {
|
|
6944
7466
|
return {
|
|
6945
7467
|
type: t.okf_type ?? "task",
|
|
6946
7468
|
title: t.description,
|
|
6947
|
-
timestamp:
|
|
7469
|
+
timestamp: isoOrFallback(t.updated_at),
|
|
6948
7470
|
id: t.id,
|
|
6949
7471
|
entity_id: t.entity_id,
|
|
6950
7472
|
status: t.status,
|
|
@@ -6974,7 +7496,11 @@ function buildEventLogEntries(events, factIdToFilename) {
|
|
|
6974
7496
|
return { date: formatLogDate(e.created_at), text: coreOkf.appendEventIdComment(text, e.id) };
|
|
6975
7497
|
});
|
|
6976
7498
|
}
|
|
6977
|
-
function formatOkfBundle(dump) {
|
|
7499
|
+
function formatOkfBundle(dump, options) {
|
|
7500
|
+
const profile = options?.profile ?? DEFAULT_PROFILE;
|
|
7501
|
+
const okfVersion = profile === "llm-wiki/2" ? "0.2" : "0.1";
|
|
7502
|
+
const factFm = profile === "llm-wiki/2" ? factFrontmatterV2 : factFrontmatterV1;
|
|
7503
|
+
const taskFm = profile === "llm-wiki/2" ? taskFrontmatterV2 : taskFrontmatterV1;
|
|
6978
7504
|
const files = [];
|
|
6979
7505
|
const rootEntries = [];
|
|
6980
7506
|
for (const [entityId, bundle] of Object.entries(dump.entities)) {
|
|
@@ -7011,7 +7537,7 @@ function formatOkfBundle(dump) {
|
|
|
7011
7537
|
const factBody = coreOkf.appendRelatedSection(f.body, relatedLinksFor2(factPath, f.id));
|
|
7012
7538
|
files.push({
|
|
7013
7539
|
path: factPath,
|
|
7014
|
-
content: coreOkf.buildConceptDocument(
|
|
7540
|
+
content: coreOkf.buildConceptDocument(factFm(f), factBody)
|
|
7015
7541
|
});
|
|
7016
7542
|
}
|
|
7017
7543
|
const taskEntries = bundle.tasks.map((t) => ({
|
|
@@ -7023,7 +7549,7 @@ function formatOkfBundle(dump) {
|
|
|
7023
7549
|
const taskBody = coreOkf.appendRelatedSection("", relatedLinksFor2(taskPath, t.id));
|
|
7024
7550
|
files.push({
|
|
7025
7551
|
path: taskPath,
|
|
7026
|
-
content: coreOkf.buildConceptDocument(
|
|
7552
|
+
content: coreOkf.buildConceptDocument(taskFm(t), taskBody)
|
|
7027
7553
|
});
|
|
7028
7554
|
}
|
|
7029
7555
|
files.push({
|
|
@@ -7046,9 +7572,9 @@ function formatOkfBundle(dump) {
|
|
|
7046
7572
|
files.push({
|
|
7047
7573
|
path: "index.md",
|
|
7048
7574
|
content: coreOkf.buildRootIndexMd(
|
|
7049
|
-
|
|
7575
|
+
okfVersion,
|
|
7050
7576
|
rootEntries.length > 0 ? [{ heading: "Entities", entries: rootEntries }] : [],
|
|
7051
|
-
{ profile
|
|
7577
|
+
{ profile }
|
|
7052
7578
|
)
|
|
7053
7579
|
});
|
|
7054
7580
|
return { files };
|
|
@@ -7138,6 +7664,21 @@ function parseFrontmatterTimestamp(value, fallback) {
|
|
|
7138
7664
|
}
|
|
7139
7665
|
return fallback;
|
|
7140
7666
|
}
|
|
7667
|
+
function parseStaleAfterRaw(raw) {
|
|
7668
|
+
if (typeof raw !== "string") return null;
|
|
7669
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw);
|
|
7670
|
+
if (!m) return null;
|
|
7671
|
+
const year = Number(m[1]);
|
|
7672
|
+
const month = Number(m[2]);
|
|
7673
|
+
const day = Number(m[3]);
|
|
7674
|
+
const ms = Date.UTC(year, month - 1, day);
|
|
7675
|
+
if (!Number.isFinite(ms)) return null;
|
|
7676
|
+
const d = new Date(ms);
|
|
7677
|
+
if (d.getUTCFullYear() !== year || d.getUTCMonth() + 1 !== month || d.getUTCDate() !== day) {
|
|
7678
|
+
return null;
|
|
7679
|
+
}
|
|
7680
|
+
return ms;
|
|
7681
|
+
}
|
|
7141
7682
|
function unescapeLogSummary(summary) {
|
|
7142
7683
|
return summary.replace(/\\\]/g, "]").replace(/\\\[/g, "[").replace(/\\\\/g, "\\");
|
|
7143
7684
|
}
|
|
@@ -7155,9 +7696,29 @@ function parseLogEntryText(text) {
|
|
|
7155
7696
|
function frontmatterToFact(entityId, id, frontmatter, body, now) {
|
|
7156
7697
|
const created_at = parseFrontmatterTimestamp(frontmatter.created_at, now);
|
|
7157
7698
|
const updated_at = parseFrontmatterTimestamp(
|
|
7158
|
-
frontmatter.
|
|
7159
|
-
parseFrontmatterTimestamp(
|
|
7699
|
+
frontmatter.generated?.at,
|
|
7700
|
+
parseFrontmatterTimestamp(
|
|
7701
|
+
frontmatter.timestamp,
|
|
7702
|
+
parseFrontmatterTimestamp(frontmatter.updated_at, now)
|
|
7703
|
+
)
|
|
7160
7704
|
);
|
|
7705
|
+
const verified = coreOkf.parseVerifiedFlexible(frontmatter.verified);
|
|
7706
|
+
const rawSources = frontmatter.sources;
|
|
7707
|
+
const hasSourcesKey = rawSources !== void 0;
|
|
7708
|
+
const sources = Array.isArray(rawSources) ? rawSources : [];
|
|
7709
|
+
const usageWindow = frontmatter.usage_window && typeof frontmatter.usage_window === "object" ? frontmatter.usage_window : null;
|
|
7710
|
+
const lastV = coreOkf.latestVerified(verified, now);
|
|
7711
|
+
const lifecycleRaw = frontmatter.status;
|
|
7712
|
+
const lifecycle_status = lifecycleRaw === "draft" || lifecycleRaw === "stable" || lifecycleRaw === "deprecated" ? lifecycleRaw : "stable";
|
|
7713
|
+
const staleAfterRaw = frontmatter.stale_after;
|
|
7714
|
+
const stale_after = parseStaleAfterRaw(staleAfterRaw);
|
|
7715
|
+
const generated_by = frontmatter.generated?.by ?? null;
|
|
7716
|
+
if (!hasSourcesKey) {
|
|
7717
|
+
const urls = coreOkf.parseCitationsList(body);
|
|
7718
|
+
for (const url of urls) {
|
|
7719
|
+
sources.push({ resource: url });
|
|
7720
|
+
}
|
|
7721
|
+
}
|
|
7161
7722
|
return {
|
|
7162
7723
|
id,
|
|
7163
7724
|
entity_id: entityId,
|
|
@@ -7173,26 +7734,62 @@ function frontmatterToFact(entityId, id, frontmatter, body, now) {
|
|
|
7173
7734
|
last_accessed_at: frontmatter.last_accessed_at != null ? parseFrontmatterTimestamp(frontmatter.last_accessed_at, now) : null,
|
|
7174
7735
|
access_count: typeof frontmatter.access_count === "number" ? frontmatter.access_count : 0,
|
|
7175
7736
|
deleted_at: frontmatter.deleted_at != null ? parseFrontmatterTimestamp(frontmatter.deleted_at, 0) : null,
|
|
7176
|
-
okf_type: frontmatter.type
|
|
7737
|
+
okf_type: frontmatter.type,
|
|
7738
|
+
// OKF v0.2
|
|
7739
|
+
lifecycle_status,
|
|
7740
|
+
stale_after,
|
|
7741
|
+
generated_by,
|
|
7742
|
+
okf_sources: sources,
|
|
7743
|
+
okf_verified: verified,
|
|
7744
|
+
okf_usage_window: usageWindow,
|
|
7745
|
+
last_verified_at: lastV?.at ?? null,
|
|
7746
|
+
last_verified_by: lastV?.by ?? null
|
|
7177
7747
|
};
|
|
7178
7748
|
}
|
|
7179
|
-
function frontmatterToTask(entityId, id, frontmatter, now) {
|
|
7749
|
+
function frontmatterToTask(entityId, id, frontmatter, now, usesV1StatusRule) {
|
|
7180
7750
|
const created_at = parseFrontmatterTimestamp(frontmatter.created_at, now);
|
|
7181
7751
|
const updated_at = parseFrontmatterTimestamp(
|
|
7182
|
-
frontmatter.
|
|
7183
|
-
parseFrontmatterTimestamp(
|
|
7752
|
+
frontmatter.generated?.at,
|
|
7753
|
+
parseFrontmatterTimestamp(
|
|
7754
|
+
frontmatter.timestamp,
|
|
7755
|
+
parseFrontmatterTimestamp(frontmatter.updated_at, now)
|
|
7756
|
+
)
|
|
7184
7757
|
);
|
|
7758
|
+
const verified = coreOkf.parseVerifiedFlexible(frontmatter.verified);
|
|
7759
|
+
const sources = Array.isArray(frontmatter.sources) ? frontmatter.sources : [];
|
|
7760
|
+
const usageWindow = frontmatter.usage_window && typeof frontmatter.usage_window === "object" ? frontmatter.usage_window : null;
|
|
7761
|
+
const lastV = coreOkf.latestVerified(verified, now);
|
|
7762
|
+
const lifecycleRaw = frontmatter.status;
|
|
7763
|
+
const lifecycle_status = lifecycleRaw === "draft" || lifecycleRaw === "stable" || lifecycleRaw === "deprecated" ? lifecycleRaw : "stable";
|
|
7764
|
+
const staleAfterRaw = frontmatter.stale_after;
|
|
7765
|
+
const stale_after = parseStaleAfterRaw(staleAfterRaw);
|
|
7766
|
+
const executionRaw = frontmatter.execution_status;
|
|
7767
|
+
let executionState;
|
|
7768
|
+
if (usesV1StatusRule) {
|
|
7769
|
+
executionState = TASK_STATUSES.has(String(frontmatter.status)) ? frontmatter.status : "pending";
|
|
7770
|
+
} else {
|
|
7771
|
+
executionState = TASK_STATUSES.has(String(executionRaw)) ? executionRaw : TASK_STATUSES.has(String(frontmatter.status)) ? frontmatter.status : "pending";
|
|
7772
|
+
}
|
|
7185
7773
|
return {
|
|
7186
7774
|
id,
|
|
7187
7775
|
entity_id: entityId,
|
|
7188
7776
|
description: typeof frontmatter.title === "string" ? frontmatter.title : "",
|
|
7189
|
-
status:
|
|
7777
|
+
status: executionState,
|
|
7190
7778
|
priority: typeof frontmatter.priority === "number" ? frontmatter.priority : 0,
|
|
7191
7779
|
created_at,
|
|
7192
7780
|
updated_at,
|
|
7193
7781
|
resolved_at: frontmatter.resolved_at != null ? parseFrontmatterTimestamp(frontmatter.resolved_at, now) : null,
|
|
7194
7782
|
deleted_at: frontmatter.deleted_at != null ? parseFrontmatterTimestamp(frontmatter.deleted_at, 0) : null,
|
|
7195
|
-
okf_type: frontmatter.type
|
|
7783
|
+
okf_type: frontmatter.type,
|
|
7784
|
+
// OKF v0.2
|
|
7785
|
+
lifecycle_status,
|
|
7786
|
+
stale_after,
|
|
7787
|
+
generated_by: frontmatter.generated?.by ?? null,
|
|
7788
|
+
okf_sources: sources,
|
|
7789
|
+
okf_verified: verified,
|
|
7790
|
+
okf_usage_window: usageWindow,
|
|
7791
|
+
last_verified_at: lastV?.at ?? null,
|
|
7792
|
+
last_verified_by: lastV?.by ?? null
|
|
7196
7793
|
};
|
|
7197
7794
|
}
|
|
7198
7795
|
function findLogMdPath(files) {
|
|
@@ -7239,10 +7836,17 @@ function parseOkfBundle(entityId, files, options) {
|
|
|
7239
7836
|
);
|
|
7240
7837
|
const rootIndex = allowedFiles.find((f) => f.path === "index.md");
|
|
7241
7838
|
const profileMeta = rootIndex ? coreOkf.parseRootIndexMd(rootIndex.content) : {};
|
|
7242
|
-
const
|
|
7839
|
+
const okfVersion = profileMeta.okf_version;
|
|
7840
|
+
const profile = profileMeta.profile;
|
|
7841
|
+
const isProfile1 = profile === "llm-wiki/1";
|
|
7842
|
+
const isProfile2 = profile === "llm-wiki/2";
|
|
7843
|
+
const isLegacyV1 = profile === void 0 && okfVersion === "0.1";
|
|
7844
|
+
const isVersionOnlyV2 = profile === void 0 && okfVersion === "0.2";
|
|
7845
|
+
const isUnknownProfileV2 = profile !== void 0 && profile !== "llm-wiki/1" && profile !== "llm-wiki/2" && okfVersion === "0.2";
|
|
7846
|
+
const summaryEligible = isProfile1 || isProfile2 || isLegacyV1 || isVersionOnlyV2 || isUnknownProfileV2;
|
|
7243
7847
|
let entitySummary;
|
|
7244
7848
|
const entityIndex = allowedFiles.find((f) => f.path === `${entityPrefix}index.md`);
|
|
7245
|
-
if (
|
|
7849
|
+
if (summaryEligible && entityIndex) {
|
|
7246
7850
|
const summary = coreOkf.parseEntityIndexMd(entityIndex.content).summary;
|
|
7247
7851
|
entitySummary = summary || void 0;
|
|
7248
7852
|
}
|
|
@@ -7271,11 +7875,12 @@ function parseOkfBundle(entityId, files, options) {
|
|
|
7271
7875
|
if (route === "ignore") continue;
|
|
7272
7876
|
const resolvedId = typeof frontmatter.id === "string" && frontmatter.id ? frontmatter.id : basenameMd(file.path);
|
|
7273
7877
|
const { body: storedBody, relatedLinks } = coreOkf.splitRelatedSection(body);
|
|
7274
|
-
const edgeLinks = isProfile1 ? relatedLinks : [...relatedLinks, ...coreOkf.extractMarkdownLinks(storedBody)];
|
|
7878
|
+
const edgeLinks = isProfile1 || isLegacyV1 ? relatedLinks : [...relatedLinks, ...coreOkf.extractMarkdownLinks(storedBody)];
|
|
7879
|
+
const usesV1StatusRule = isProfile1 || isLegacyV1 || profile === void 0 && okfVersion === void 0;
|
|
7275
7880
|
if (route === "fact") {
|
|
7276
7881
|
facts.push(frontmatterToFact(entityId, resolvedId, frontmatter, storedBody, now));
|
|
7277
7882
|
} else {
|
|
7278
|
-
tasks.push(frontmatterToTask(entityId, resolvedId, frontmatter, now));
|
|
7883
|
+
tasks.push(frontmatterToTask(entityId, resolvedId, frontmatter, now, usesV1StatusRule));
|
|
7279
7884
|
}
|
|
7280
7885
|
const seenEdges = /* @__PURE__ */ new Set();
|
|
7281
7886
|
extractEdgesFromLinks(
|