@equationalapplications/core-llm-wiki 7.2.1 → 7.3.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 +23 -0
- package/dist/{chunk-TTECJ3S3.mjs → chunk-IAYO63DQ.mjs} +346 -41
- package/dist/chunk-IAYO63DQ.mjs.map +1 -0
- package/dist/index.d.mts +3 -5
- package/dist/index.d.ts +3 -5
- package/dist/index.js +388 -47
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +47 -11
- package/dist/index.mjs.map +1 -1
- package/dist/{testing-DYeMd9d1.d.mts → testing-ht1qOvcD.d.mts} +98 -6
- package/dist/{testing-DYeMd9d1.d.ts → testing-ht1qOvcD.d.ts} +98 -6
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +336 -38
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-TTECJ3S3.mjs.map +0 -1
package/dist/testing.js
CHANGED
|
@@ -621,6 +621,17 @@ function validateTask(task) {
|
|
|
621
621
|
priority
|
|
622
622
|
};
|
|
623
623
|
}
|
|
624
|
+
function factRejectionReason(raw) {
|
|
625
|
+
const fact = raw;
|
|
626
|
+
if (typeof fact?.title !== "string" || typeof fact?.body !== "string") return "invalid_shape";
|
|
627
|
+
if (!clip(fact.title, 80)) return "missing_title";
|
|
628
|
+
return "missing_body";
|
|
629
|
+
}
|
|
630
|
+
function taskRejectionReason(raw) {
|
|
631
|
+
const task = raw;
|
|
632
|
+
if (typeof task?.description !== "string") return "invalid_shape";
|
|
633
|
+
return "missing_description";
|
|
634
|
+
}
|
|
624
635
|
function normalizeSourceRef(value) {
|
|
625
636
|
if (typeof value !== "string") return null;
|
|
626
637
|
const cleaned = value.replace(/[^A-Za-z0-9._\- ]/g, "").trim().slice(0, 255);
|
|
@@ -644,6 +655,104 @@ function jaccardScore(a, b) {
|
|
|
644
655
|
var DEFAULT_MAX_EMBED_CHARS = 6e3;
|
|
645
656
|
var EMBED_CHARS_CEILING = 16e3;
|
|
646
657
|
|
|
658
|
+
// src/utils/diagnostics.ts
|
|
659
|
+
var SEVERITY = {
|
|
660
|
+
ingest_chunk_failed: "warn",
|
|
661
|
+
fact_rejected: "warn",
|
|
662
|
+
task_rejected: "warn",
|
|
663
|
+
fact_deduplicated: "info",
|
|
664
|
+
edge_dropped: "warn",
|
|
665
|
+
embedding_failed: "warn",
|
|
666
|
+
hook_failed: "warn",
|
|
667
|
+
background_job_failed: "error",
|
|
668
|
+
heal_skipped: "warn",
|
|
669
|
+
grounding_missing: "warn",
|
|
670
|
+
grounding_failed: "warn",
|
|
671
|
+
classification_low_confidence: "info",
|
|
672
|
+
classification_invalid: "warn"
|
|
673
|
+
};
|
|
674
|
+
var MESSAGE = {
|
|
675
|
+
ingest_chunk_failed: "One or more document chunks failed to parse or generate.",
|
|
676
|
+
fact_rejected: "An extracted fact failed validation and was not written.",
|
|
677
|
+
task_rejected: "An extracted task failed validation and was not written.",
|
|
678
|
+
fact_deduplicated: "An extracted fact duplicated an existing fact and was not written.",
|
|
679
|
+
edge_dropped: "An extracted edge could not be resolved against the ontology and was not written.",
|
|
680
|
+
embedding_failed: "A fact could not be embedded.",
|
|
681
|
+
hook_failed: "A host hook threw or rejected.",
|
|
682
|
+
background_job_failed: "A background maintenance job failed.",
|
|
683
|
+
heal_skipped: "Heal skipped a candidate fact.",
|
|
684
|
+
grounding_missing: "A fact carried no qualifying evidence and was stored as a draft.",
|
|
685
|
+
grounding_failed: "A fact carried evidence not found in its source and was stored as a draft.",
|
|
686
|
+
classification_low_confidence: "A classifier answer was below the confidence threshold and was not applied.",
|
|
687
|
+
classification_invalid: "A classifier answer was invalid and was not applied."
|
|
688
|
+
};
|
|
689
|
+
var warnedNonFunction = /* @__PURE__ */ new WeakSet();
|
|
690
|
+
function copyDetail(detail) {
|
|
691
|
+
const copy = { ...detail };
|
|
692
|
+
if (detail.chunkIndexes) copy.chunkIndexes = detail.chunkIndexes.slice();
|
|
693
|
+
return copy;
|
|
694
|
+
}
|
|
695
|
+
function emitDiagnostic(options, input) {
|
|
696
|
+
const hook = options.onDiagnostic;
|
|
697
|
+
if (hook === void 0 || hook === null) return;
|
|
698
|
+
if (typeof hook !== "function") {
|
|
699
|
+
if (!warnedNonFunction.has(options)) {
|
|
700
|
+
warnedNonFunction.add(options);
|
|
701
|
+
console.warn("[WikiMemory] onDiagnostic is not a function; diagnostics are disabled.");
|
|
702
|
+
}
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
const diagnostic = {
|
|
706
|
+
code: input.code,
|
|
707
|
+
severity: SEVERITY[input.code],
|
|
708
|
+
operation: input.operation,
|
|
709
|
+
trigger: input.trigger,
|
|
710
|
+
entityId: input.entityId,
|
|
711
|
+
at: Date.now(),
|
|
712
|
+
message: MESSAGE[input.code],
|
|
713
|
+
...input.detail ? { detail: copyDetail(input.detail) } : {}
|
|
714
|
+
};
|
|
715
|
+
try {
|
|
716
|
+
const returned = hook(diagnostic);
|
|
717
|
+
if (returned !== void 0) {
|
|
718
|
+
Promise.resolve(returned).catch((err) => {
|
|
719
|
+
console.warn("[WikiMemory] onDiagnostic hook rejected:", err);
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
} catch (err) {
|
|
723
|
+
console.warn("[WikiMemory] onDiagnostic hook threw:", err);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
var DiagnosticBuffer = class {
|
|
727
|
+
constructor() {
|
|
728
|
+
this.items = [];
|
|
729
|
+
}
|
|
730
|
+
push(input) {
|
|
731
|
+
this.items.push(input);
|
|
732
|
+
}
|
|
733
|
+
get size() {
|
|
734
|
+
return this.items.length;
|
|
735
|
+
}
|
|
736
|
+
flush(options) {
|
|
737
|
+
const pending = this.items;
|
|
738
|
+
this.items = [];
|
|
739
|
+
for (const input of pending) emitDiagnostic(options, input);
|
|
740
|
+
}
|
|
741
|
+
discard() {
|
|
742
|
+
this.items = [];
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
function edgeDropDiagnostic(drop, ctx) {
|
|
746
|
+
const detail = { reason: drop.reason };
|
|
747
|
+
const factId = ctx.factId ?? drop.sourceId;
|
|
748
|
+
if (factId) detail.factId = factId;
|
|
749
|
+
if (drop.edgeType) detail.edgeType = drop.edgeType;
|
|
750
|
+
if (drop.sourceNodeType) detail.sourceNodeType = drop.sourceNodeType;
|
|
751
|
+
if (drop.targetNodeType) detail.targetNodeType = drop.targetNodeType;
|
|
752
|
+
if (ctx.sourceRef) detail.sourceRef = ctx.sourceRef;
|
|
753
|
+
return { code: "edge_dropped", operation: ctx.operation, trigger: ctx.trigger, entityId: ctx.entityId, detail };
|
|
754
|
+
}
|
|
755
|
+
|
|
647
756
|
// src/services/EmbeddingService.ts
|
|
648
757
|
var EmbeddingService = class {
|
|
649
758
|
constructor(db, options, entryRepo, metadataRepo) {
|
|
@@ -707,7 +816,7 @@ var EmbeddingService = class {
|
|
|
707
816
|
});
|
|
708
817
|
}
|
|
709
818
|
}
|
|
710
|
-
async tryEmbedFact(fact) {
|
|
819
|
+
async tryEmbedFact(fact, ctx) {
|
|
711
820
|
const embedFn = this.options.llmProvider.embed;
|
|
712
821
|
if (typeof embedFn !== "function") return { ok: false, kind: "no_provider" };
|
|
713
822
|
let tagsStr;
|
|
@@ -729,6 +838,7 @@ var EmbeddingService = class {
|
|
|
729
838
|
const vector = await embedFn(text);
|
|
730
839
|
if (vector.length === 0 || !vector.every((v) => typeof v === "number" && isFinite(v))) {
|
|
731
840
|
console.warn(`[WikiMemory] embedFact: embed() returned an invalid vector for ${fact.id}; skipping.`);
|
|
841
|
+
this.reportEmbed(ctx, fact, "embedding_failed", "invalid_vector");
|
|
732
842
|
await this.markFailure(fact.id, "invalid_vector");
|
|
733
843
|
return { ok: false, kind: "invalid_vector" };
|
|
734
844
|
}
|
|
@@ -742,11 +852,13 @@ var EmbeddingService = class {
|
|
|
742
852
|
}
|
|
743
853
|
if (hasNonFinite) {
|
|
744
854
|
console.warn(`[WikiMemory] embedFact: embed() returned values that overflow float32 for ${fact.id}; skipping.`);
|
|
855
|
+
this.reportEmbed(ctx, fact, "embedding_failed", "float32_overflow");
|
|
745
856
|
await this.markFailure(fact.id, "float32_overflow");
|
|
746
857
|
return { ok: false, kind: "float32_overflow" };
|
|
747
858
|
}
|
|
748
859
|
} catch (err) {
|
|
749
860
|
console.warn(`[WikiMemory] embedFact failed for ${fact.id}:`, err);
|
|
861
|
+
this.reportEmbed(ctx, fact, "embedding_failed", "embed_threw");
|
|
750
862
|
await this.markFailure(fact.id, "provider_error");
|
|
751
863
|
return { ok: false, kind: "provider_error" };
|
|
752
864
|
}
|
|
@@ -756,15 +868,31 @@ var EmbeddingService = class {
|
|
|
756
868
|
await this.entryRepo.updateEmbeddingBlob(fact.id, blob);
|
|
757
869
|
} catch (err) {
|
|
758
870
|
console.warn(`[WikiMemory] embedFact: persisting embedding failed for ${fact.id}:`, err);
|
|
871
|
+
this.reportEmbed(ctx, fact, "embedding_failed", "persist_failed");
|
|
759
872
|
return { ok: false, kind: "storage_error" };
|
|
760
873
|
}
|
|
761
874
|
try {
|
|
762
875
|
await this.notifyEmbeddingPersisted(fact.entity_id, fact.id, float32Vector);
|
|
763
876
|
} catch (hookErr) {
|
|
764
877
|
console.warn(`[WikiMemory] onEmbeddingPersisted hook failed for ${fact.id}:`, hookErr);
|
|
878
|
+
this.reportEmbed(ctx, fact, "hook_failed", "on_embedding_persisted");
|
|
765
879
|
}
|
|
766
880
|
return { ok: true, dimension: float32Vector.length };
|
|
767
881
|
}
|
|
882
|
+
reportEmbed(ctx, fact, code, reason) {
|
|
883
|
+
if (!ctx) return;
|
|
884
|
+
emitDiagnostic(this.options, {
|
|
885
|
+
code,
|
|
886
|
+
operation: ctx.operation,
|
|
887
|
+
trigger: ctx.trigger,
|
|
888
|
+
entityId: fact.entity_id,
|
|
889
|
+
detail: { factId: fact.id, reason }
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
/** For callers that invoke `notifyEmbeddingPersisted` directly and catch its failure. */
|
|
893
|
+
reportHookFailed(ctx, entityId, factId) {
|
|
894
|
+
this.reportEmbed(ctx, { id: factId, entity_id: entityId }, "hook_failed", "on_embedding_persisted");
|
|
895
|
+
}
|
|
768
896
|
/** Marker writes must never fail the caller. Only marker-eligible kinds reach here. */
|
|
769
897
|
async markFailure(id, kind) {
|
|
770
898
|
try {
|
|
@@ -778,8 +906,8 @@ var EmbeddingService = class {
|
|
|
778
906
|
* ImportExportService branch on `if (!embedded)`; a discriminated result
|
|
779
907
|
* object is always truthy and would silently disable that branch.
|
|
780
908
|
*/
|
|
781
|
-
async embedFact(fact) {
|
|
782
|
-
const result = await this.tryEmbedFact(fact);
|
|
909
|
+
async embedFact(fact, ctx) {
|
|
910
|
+
const result = await this.tryEmbedFact(fact, ctx);
|
|
783
911
|
return result.ok;
|
|
784
912
|
}
|
|
785
913
|
async notifyEmbeddingPersisted(entityId, factId, vector) {
|
|
@@ -1192,7 +1320,7 @@ var ImportExportService = class {
|
|
|
1192
1320
|
title: clipped?.title ?? fact.title,
|
|
1193
1321
|
body: clipped?.body ?? fact.body,
|
|
1194
1322
|
tags: Array.isArray(fact.tags) || typeof fact.tags === "string" ? fact.tags : []
|
|
1195
|
-
});
|
|
1323
|
+
}, { operation: "importDump", trigger: "call" });
|
|
1196
1324
|
if (!embedded) {
|
|
1197
1325
|
await this.embeddingService.notifyEmbeddingPersisted(entityId, fact.id, null);
|
|
1198
1326
|
}
|
|
@@ -1217,6 +1345,11 @@ var ImportExportService = class {
|
|
|
1217
1345
|
`[WikiMemory] onEmbeddingPersisted hook failed for preserved-blob fact ${fact.id}:`,
|
|
1218
1346
|
hookErr
|
|
1219
1347
|
);
|
|
1348
|
+
this.embeddingService.reportHookFailed(
|
|
1349
|
+
{ operation: "importDump", trigger: "call" },
|
|
1350
|
+
entityId,
|
|
1351
|
+
fact.id
|
|
1352
|
+
);
|
|
1220
1353
|
}
|
|
1221
1354
|
}
|
|
1222
1355
|
}
|
|
@@ -1233,6 +1366,11 @@ var ImportExportService = class {
|
|
|
1233
1366
|
`[WikiMemory] onEmbeddingPersisted(vector=null) hook failed for soft-deleted fact ${factId}:`,
|
|
1234
1367
|
hookErr
|
|
1235
1368
|
);
|
|
1369
|
+
this.embeddingService.reportHookFailed(
|
|
1370
|
+
{ operation: "importDump", trigger: "call" },
|
|
1371
|
+
entityId,
|
|
1372
|
+
factId
|
|
1373
|
+
);
|
|
1236
1374
|
}
|
|
1237
1375
|
}
|
|
1238
1376
|
}
|
|
@@ -1628,9 +1766,24 @@ var IngestionService = class {
|
|
|
1628
1766
|
try {
|
|
1629
1767
|
const responseText = await this.options.llmProvider.generateText({ systemPrompt, userPrompt });
|
|
1630
1768
|
const result2 = parseJsonResponse(responseText);
|
|
1769
|
+
const rawFacts = Array.isArray(result2.facts) ? result2.facts : [];
|
|
1770
|
+
const facts = [];
|
|
1771
|
+
const itemIndexes = [];
|
|
1772
|
+
const rejected = [];
|
|
1773
|
+
rawFacts.forEach((raw, itemIndex) => {
|
|
1774
|
+
const valid = validateFact(raw);
|
|
1775
|
+
if (valid) {
|
|
1776
|
+
facts.push(valid);
|
|
1777
|
+
itemIndexes.push(itemIndex);
|
|
1778
|
+
} else {
|
|
1779
|
+
rejected.push({ itemIndex, reason: factRejectionReason(raw) });
|
|
1780
|
+
}
|
|
1781
|
+
});
|
|
1631
1782
|
return {
|
|
1632
1783
|
status: "ok",
|
|
1633
|
-
facts
|
|
1784
|
+
facts,
|
|
1785
|
+
itemIndexes,
|
|
1786
|
+
rejected,
|
|
1634
1787
|
ontology_updates: result2.ontology_updates
|
|
1635
1788
|
};
|
|
1636
1789
|
} catch (e) {
|
|
@@ -1675,23 +1828,48 @@ var IngestionService = class {
|
|
|
1675
1828
|
const failures = [];
|
|
1676
1829
|
const seen = /* @__PURE__ */ new Set();
|
|
1677
1830
|
const orderedChunkFacts = [];
|
|
1678
|
-
|
|
1831
|
+
const diagBuffer = new DiagnosticBuffer();
|
|
1832
|
+
const diagBase = { entityId, operation: "ingest", trigger: "call" };
|
|
1833
|
+
for (const [chunkIndex, slot] of chunkResults.entries()) {
|
|
1679
1834
|
if (slot.status === "failed") {
|
|
1680
1835
|
failedChunks++;
|
|
1681
1836
|
failures.push(slot.error);
|
|
1682
1837
|
continue;
|
|
1683
1838
|
}
|
|
1684
1839
|
ingestedChunks++;
|
|
1840
|
+
for (const r of slot.rejected) {
|
|
1841
|
+
diagBuffer.push({ ...diagBase, code: "fact_rejected", detail: { sourceRef, chunkIndex, itemIndex: r.itemIndex, reason: r.reason } });
|
|
1842
|
+
}
|
|
1685
1843
|
const dedupedFacts = [];
|
|
1686
|
-
|
|
1844
|
+
slot.facts.forEach((fact, k) => {
|
|
1687
1845
|
const normalizedTitle = normalizeTitleKey(fact.title);
|
|
1688
1846
|
if (!seen.has(normalizedTitle)) {
|
|
1689
1847
|
seen.add(normalizedTitle);
|
|
1690
1848
|
dedupedFacts.push(fact);
|
|
1849
|
+
} else {
|
|
1850
|
+
diagBuffer.push({
|
|
1851
|
+
...diagBase,
|
|
1852
|
+
code: "fact_deduplicated",
|
|
1853
|
+
detail: { sourceRef, chunkIndex, itemIndex: slot.itemIndexes[k], reason: "exact_title" }
|
|
1854
|
+
});
|
|
1691
1855
|
}
|
|
1692
|
-
}
|
|
1856
|
+
});
|
|
1693
1857
|
orderedChunkFacts.push({ facts: dedupedFacts, ontology_updates: slot.ontology_updates });
|
|
1694
1858
|
}
|
|
1859
|
+
for (const reason of ["parse", "llm"]) {
|
|
1860
|
+
const ofReason = failures.filter((f) => f.source === reason);
|
|
1861
|
+
if (ofReason.length === 0) continue;
|
|
1862
|
+
diagBuffer.push({
|
|
1863
|
+
...diagBase,
|
|
1864
|
+
code: "ingest_chunk_failed",
|
|
1865
|
+
detail: {
|
|
1866
|
+
sourceRef,
|
|
1867
|
+
reason,
|
|
1868
|
+
count: ofReason.length,
|
|
1869
|
+
chunkIndexes: ofReason.map((f) => f.chunkIndex).sort((a, b) => a - b).slice(0, 20)
|
|
1870
|
+
}
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1695
1873
|
if (failedChunks === chunks.length) {
|
|
1696
1874
|
throw new WikiIngestEmptyError({
|
|
1697
1875
|
parseFailures: failures,
|
|
@@ -1704,7 +1882,7 @@ var IngestionService = class {
|
|
|
1704
1882
|
try {
|
|
1705
1883
|
if (failedChunks === 0) {
|
|
1706
1884
|
const fullResult = await this.db.withTransactionAsync(async (tx) => {
|
|
1707
|
-
return await this.runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx);
|
|
1885
|
+
return await this.runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx, diagBuffer);
|
|
1708
1886
|
});
|
|
1709
1887
|
deletedSourceFactIds.push(...fullResult.deletedSourceFactIds);
|
|
1710
1888
|
insertedFacts.push(...fullResult.insertedFacts);
|
|
@@ -1712,7 +1890,7 @@ var IngestionService = class {
|
|
|
1712
1890
|
const partialResult = await this.db.withTransactionAsync(async (tx) => {
|
|
1713
1891
|
const flat = [];
|
|
1714
1892
|
for (const slot of orderedChunkFacts) flat.push(...slot.facts);
|
|
1715
|
-
return await this.appendPartialFacts(entityId, sourceRef, flat, tx);
|
|
1893
|
+
return await this.appendPartialFacts(entityId, sourceRef, flat, tx, diagBuffer);
|
|
1716
1894
|
});
|
|
1717
1895
|
insertedFacts.push(...partialResult.insertedDescriptors);
|
|
1718
1896
|
}
|
|
@@ -1726,6 +1904,7 @@ var IngestionService = class {
|
|
|
1726
1904
|
}
|
|
1727
1905
|
return zeroChunkResult(canonical);
|
|
1728
1906
|
}
|
|
1907
|
+
diagBuffer.flush(this.options);
|
|
1729
1908
|
await this.searchService.sync(entityId);
|
|
1730
1909
|
if (failedChunks === 0) {
|
|
1731
1910
|
const uniqueDeletedSourceFactIds = Array.from(new Set(deletedSourceFactIds));
|
|
@@ -1734,11 +1913,18 @@ var IngestionService = class {
|
|
|
1734
1913
|
await this.embeddingService.notifyEmbeddingPersisted(entityId, factId, null);
|
|
1735
1914
|
} catch (hookErr) {
|
|
1736
1915
|
console.warn(`[WikiMemory] onEmbeddingPersisted hook failed during ingest for ${factId}:`, hookErr);
|
|
1916
|
+
emitDiagnostic(this.options, {
|
|
1917
|
+
entityId,
|
|
1918
|
+
operation: "ingest",
|
|
1919
|
+
trigger: "call",
|
|
1920
|
+
code: "hook_failed",
|
|
1921
|
+
detail: { factId, reason: "on_embedding_persisted" }
|
|
1922
|
+
});
|
|
1737
1923
|
}
|
|
1738
1924
|
}
|
|
1739
1925
|
}
|
|
1740
1926
|
for (const fact of insertedFacts) {
|
|
1741
|
-
await this.embeddingService.embedFact(fact);
|
|
1927
|
+
await this.embeddingService.embedFact(fact, { operation: "ingest", trigger: "call" });
|
|
1742
1928
|
}
|
|
1743
1929
|
this.searchService.evictCache(entityId);
|
|
1744
1930
|
const result = {
|
|
@@ -1884,6 +2070,19 @@ var IngestionService = class {
|
|
|
1884
2070
|
const match = candidates[0];
|
|
1885
2071
|
if (!match) {
|
|
1886
2072
|
if (strictEffective) throw new WikiStrictOntologyViolation(entityId, "edge", edge.type);
|
|
2073
|
+
opts?.diag?.buffer.push({
|
|
2074
|
+
entityId,
|
|
2075
|
+
operation: opts.diag.operation,
|
|
2076
|
+
trigger: "call",
|
|
2077
|
+
code: "edge_dropped",
|
|
2078
|
+
detail: {
|
|
2079
|
+
reason: "manifest_violation",
|
|
2080
|
+
factId: edge.sourceId,
|
|
2081
|
+
edgeType: edge.type,
|
|
2082
|
+
...sourceType ? { sourceNodeType: sourceType } : {},
|
|
2083
|
+
sourceRef: params.sourceRef
|
|
2084
|
+
}
|
|
2085
|
+
});
|
|
1887
2086
|
continue;
|
|
1888
2087
|
}
|
|
1889
2088
|
validEdges.push({
|
|
@@ -1924,7 +2123,7 @@ var IngestionService = class {
|
|
|
1924
2123
|
* On the partial path the caller never invokes this method; the empty
|
|
1925
2124
|
* arrays stay empty.
|
|
1926
2125
|
*/
|
|
1927
|
-
async runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx) {
|
|
2126
|
+
async runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx, diagBuffer) {
|
|
1928
2127
|
const deletedSourceFactIds = [];
|
|
1929
2128
|
const insertedFacts = [];
|
|
1930
2129
|
deletedSourceFactIds.push(...await this.entryRepo.findIdsBySource(entityId, sourceRef, null, tx, false));
|
|
@@ -1949,8 +2148,12 @@ var IngestionService = class {
|
|
|
1949
2148
|
}
|
|
1950
2149
|
for (const fact of facts) {
|
|
1951
2150
|
const ontologyFact = fact;
|
|
1952
|
-
const normalized = this.ontologyService?.validateAndNormalizeFact(ontologyFact, manifest, { strict: false }) ?? { okf_type: null, edges: [] };
|
|
1953
2151
|
const id = generateId("fact_");
|
|
2152
|
+
const validationDrops = [];
|
|
2153
|
+
const normalized = this.ontologyService?.validateAndNormalizeFact(ontologyFact, manifest, { strict: false, drops: validationDrops }) ?? { okf_type: null, edges: [] };
|
|
2154
|
+
for (const drop of validationDrops) {
|
|
2155
|
+
diagBuffer.push(edgeDropDiagnostic(drop, { entityId, operation: "ingest", trigger: "call", sourceRef, factId: id }));
|
|
2156
|
+
}
|
|
1954
2157
|
hostNodes.push({
|
|
1955
2158
|
id,
|
|
1956
2159
|
type: ontologyFact.okf_type ?? "",
|
|
@@ -1975,6 +2178,7 @@ var IngestionService = class {
|
|
|
1975
2178
|
}
|
|
1976
2179
|
const hostEdges = [];
|
|
1977
2180
|
for (const req of rawEdgeRequests) {
|
|
2181
|
+
const resolveDrops = [];
|
|
1978
2182
|
const resolved = this.ontologyService?.resolveEdges(
|
|
1979
2183
|
entityId,
|
|
1980
2184
|
req.sourceId,
|
|
@@ -1982,8 +2186,12 @@ var IngestionService = class {
|
|
|
1982
2186
|
req.edges,
|
|
1983
2187
|
manifest,
|
|
1984
2188
|
titleIndex,
|
|
1985
|
-
now
|
|
2189
|
+
now,
|
|
2190
|
+
resolveDrops
|
|
1986
2191
|
) ?? [];
|
|
2192
|
+
for (const drop of resolveDrops) {
|
|
2193
|
+
diagBuffer.push(edgeDropDiagnostic(drop, { entityId, operation: "ingest", trigger: "call", sourceRef }));
|
|
2194
|
+
}
|
|
1987
2195
|
for (const e of resolved) {
|
|
1988
2196
|
hostEdges.push({ type: e.edge_type, sourceId: e.source_id, targetId: e.target_id });
|
|
1989
2197
|
}
|
|
@@ -1992,7 +2200,7 @@ var IngestionService = class {
|
|
|
1992
2200
|
entityId,
|
|
1993
2201
|
{ sourceRef, sourceHash, nodes: hostNodes, edges: hostEdges },
|
|
1994
2202
|
tx,
|
|
1995
|
-
{ strict: false }
|
|
2203
|
+
{ strict: false, diag: { buffer: diagBuffer, operation: "ingest" } }
|
|
1996
2204
|
);
|
|
1997
2205
|
return { deletedSourceFactIds, insertedFacts };
|
|
1998
2206
|
}
|
|
@@ -2016,7 +2224,7 @@ var IngestionService = class {
|
|
|
2016
2224
|
* Runs INSIDE the caller's `tx`. Does not open a nested transaction.
|
|
2017
2225
|
* Returns `{ inserted, skippedDuplicate }` for observability.
|
|
2018
2226
|
*/
|
|
2019
|
-
async appendPartialFacts(entityId, sourceRef, dedupedFacts, tx) {
|
|
2227
|
+
async appendPartialFacts(entityId, sourceRef, dedupedFacts, tx, diagBuffer) {
|
|
2020
2228
|
const liveIds = await this.entryRepo.findIdsBySource(entityId, sourceRef, null, tx, false);
|
|
2021
2229
|
const liveFacts = liveIds.length === 0 ? [] : await this.entryRepo.findByIds(liveIds, void 0, tx);
|
|
2022
2230
|
const liveTitles = new Set(liveFacts.map((f) => normalizeTitleKey(f.title)));
|
|
@@ -2027,6 +2235,13 @@ var IngestionService = class {
|
|
|
2027
2235
|
for (const fact of dedupedFacts) {
|
|
2028
2236
|
const normalizedTitle = normalizeTitleKey(fact.title);
|
|
2029
2237
|
if (liveTitles.has(normalizedTitle)) {
|
|
2238
|
+
diagBuffer.push({
|
|
2239
|
+
entityId,
|
|
2240
|
+
operation: "ingest",
|
|
2241
|
+
trigger: "call",
|
|
2242
|
+
code: "fact_deduplicated",
|
|
2243
|
+
detail: { sourceRef, reason: "exact_title" }
|
|
2244
|
+
});
|
|
2030
2245
|
skippedDuplicate++;
|
|
2031
2246
|
continue;
|
|
2032
2247
|
}
|
|
@@ -2455,7 +2670,7 @@ var MaintenanceService = class {
|
|
|
2455
2670
|
permanentlyFailed++;
|
|
2456
2671
|
continue;
|
|
2457
2672
|
}
|
|
2458
|
-
const result = await this.embeddingService.tryEmbedFact(row);
|
|
2673
|
+
const result = await this.embeddingService.tryEmbedFact(row, { operation: "reembed", trigger: "call" });
|
|
2459
2674
|
if (result.ok) embedded++;
|
|
2460
2675
|
else failed++;
|
|
2461
2676
|
}
|
|
@@ -2576,7 +2791,7 @@ var MaintenanceService = class {
|
|
|
2576
2791
|
return { deleted: { entries, tasks: 0 } };
|
|
2577
2792
|
}
|
|
2578
2793
|
/** Core librarian pass (locks handled by {@link runLibrarian}). Package-internal orchestration hook. */
|
|
2579
|
-
async doRunLibrarian(entityId, promptOverride) {
|
|
2794
|
+
async doRunLibrarian(entityId, promptOverride, trigger = "call") {
|
|
2580
2795
|
const events = await this.eventRepo.getRecent(entityId, 50);
|
|
2581
2796
|
const currentFactsRows = await this.entryRepo.findRecentByEntityId(entityId, 100);
|
|
2582
2797
|
const currentFacts = currentFactsRows.map((f) => {
|
|
@@ -2598,8 +2813,25 @@ var MaintenanceService = class {
|
|
|
2598
2813
|
const facts = Array.isArray(result.facts) ? result.facts : [];
|
|
2599
2814
|
const tasks = Array.isArray(result.tasks) ? result.tasks : [];
|
|
2600
2815
|
const ontologyUpdates = result.ontology_updates;
|
|
2601
|
-
const
|
|
2602
|
-
const
|
|
2816
|
+
const diagBuffer = new DiagnosticBuffer();
|
|
2817
|
+
const diagBase = { entityId, operation: "librarian", trigger };
|
|
2818
|
+
const validFacts = [];
|
|
2819
|
+
const validFactItemIndexes = [];
|
|
2820
|
+
facts.forEach((raw, itemIndex) => {
|
|
2821
|
+
const valid = validateFact(raw);
|
|
2822
|
+
if (valid) {
|
|
2823
|
+
validFacts.push(valid);
|
|
2824
|
+
validFactItemIndexes.push(itemIndex);
|
|
2825
|
+
} else {
|
|
2826
|
+
diagBuffer.push({ ...diagBase, code: "fact_rejected", detail: { itemIndex, reason: factRejectionReason(raw) } });
|
|
2827
|
+
}
|
|
2828
|
+
});
|
|
2829
|
+
const validTasks = [];
|
|
2830
|
+
tasks.forEach((raw, itemIndex) => {
|
|
2831
|
+
const valid = validateTask(raw);
|
|
2832
|
+
if (valid) validTasks.push(valid);
|
|
2833
|
+
else diagBuffer.push({ ...diagBase, code: "task_rejected", detail: { itemIndex, reason: taskRejectionReason(raw) } });
|
|
2834
|
+
});
|
|
2603
2835
|
const now = Date.now();
|
|
2604
2836
|
const insertedFacts = [];
|
|
2605
2837
|
await this.db.withTransactionAsync(async (tx) => {
|
|
@@ -2616,7 +2848,7 @@ var MaintenanceService = class {
|
|
|
2616
2848
|
}
|
|
2617
2849
|
const factsForDedupe = await this.entryRepo.findRecentByEntityId(entityId, 100, tx);
|
|
2618
2850
|
const pendingEdges = [];
|
|
2619
|
-
for (const fact of validFacts) {
|
|
2851
|
+
for (const [k, fact] of validFacts.entries()) {
|
|
2620
2852
|
const newTokens = titleTokens(fact.title);
|
|
2621
2853
|
let skip = false;
|
|
2622
2854
|
if (newTokens.size >= MIN_TOKENS_TO_QUALIFY) {
|
|
@@ -2631,10 +2863,15 @@ var MaintenanceService = class {
|
|
|
2631
2863
|
}
|
|
2632
2864
|
}
|
|
2633
2865
|
}
|
|
2634
|
-
if (skip)
|
|
2866
|
+
if (skip) {
|
|
2867
|
+
diagBuffer.push({ ...diagBase, code: "fact_deduplicated", detail: { itemIndex: validFactItemIndexes[k], reason: "fuzzy_title" } });
|
|
2868
|
+
continue;
|
|
2869
|
+
}
|
|
2635
2870
|
const ontologyFact = fact;
|
|
2636
|
-
const normalized = this.ontologyService?.validateAndNormalizeFact(ontologyFact, manifest, { strict: false }) ?? { okf_type: null, edges: [] };
|
|
2637
2871
|
const id = generateId("fact_");
|
|
2872
|
+
const validationDrops = [];
|
|
2873
|
+
const normalized = this.ontologyService?.validateAndNormalizeFact(ontologyFact, manifest, { strict: false, drops: validationDrops }) ?? { okf_type: null, edges: [] };
|
|
2874
|
+
for (const drop of validationDrops) diagBuffer.push(edgeDropDiagnostic(drop, { ...diagBase, factId: id }));
|
|
2638
2875
|
const factObj = {
|
|
2639
2876
|
id,
|
|
2640
2877
|
entity_id: entityId,
|
|
@@ -2661,6 +2898,7 @@ var MaintenanceService = class {
|
|
|
2661
2898
|
}
|
|
2662
2899
|
}
|
|
2663
2900
|
for (const item of pendingEdges) {
|
|
2901
|
+
const resolveDrops = [];
|
|
2664
2902
|
await this.ontologyService?.resolveAndPersistEdges(
|
|
2665
2903
|
entityId,
|
|
2666
2904
|
item.sourceId,
|
|
@@ -2669,8 +2907,10 @@ var MaintenanceService = class {
|
|
|
2669
2907
|
manifest,
|
|
2670
2908
|
titleIndex,
|
|
2671
2909
|
tx,
|
|
2672
|
-
now
|
|
2910
|
+
now,
|
|
2911
|
+
resolveDrops
|
|
2673
2912
|
);
|
|
2913
|
+
for (const drop of resolveDrops) diagBuffer.push(edgeDropDiagnostic(drop, diagBase));
|
|
2674
2914
|
}
|
|
2675
2915
|
for (const task of validTasks) {
|
|
2676
2916
|
const id = generateId("task_");
|
|
@@ -2688,9 +2928,10 @@ var MaintenanceService = class {
|
|
|
2688
2928
|
await this.taskRepo.upsert(taskObj, tx);
|
|
2689
2929
|
}
|
|
2690
2930
|
});
|
|
2931
|
+
diagBuffer.flush(this.options);
|
|
2691
2932
|
await this.searchService.sync(entityId);
|
|
2692
2933
|
for (const fact of insertedFacts) {
|
|
2693
|
-
await this.embeddingService.embedFact(fact);
|
|
2934
|
+
await this.embeddingService.embedFact(fact, { operation: "librarian", trigger });
|
|
2694
2935
|
}
|
|
2695
2936
|
this.searchService.evictCache(entityId);
|
|
2696
2937
|
}
|
|
@@ -2704,6 +2945,8 @@ var MaintenanceService = class {
|
|
|
2704
2945
|
async doRunHeal(entityId, options) {
|
|
2705
2946
|
const promptOverride = options?.promptOverride;
|
|
2706
2947
|
const batchSize = options?.batchSize ?? HEAL_BATCH_SIZE;
|
|
2948
|
+
const trigger = options?.trigger ?? "call";
|
|
2949
|
+
const diagBase = { entityId, operation: "heal", trigger };
|
|
2707
2950
|
if (!Number.isInteger(batchSize) || batchSize < 1) {
|
|
2708
2951
|
throw new Error("Invalid batchSize: must be an integer >= 1");
|
|
2709
2952
|
}
|
|
@@ -2739,6 +2982,7 @@ var MaintenanceService = class {
|
|
|
2739
2982
|
await this.embeddingService.notifyEmbeddingPersisted(entityId, factId, null);
|
|
2740
2983
|
} catch (hookErr) {
|
|
2741
2984
|
console.warn(`[WikiMemory] onEmbeddingPersisted hook failed during heal orphan pass for ${factId}:`, hookErr);
|
|
2985
|
+
emitDiagnostic(this.options, { ...diagBase, code: "hook_failed", detail: { factId, reason: "on_embedding_persisted" } });
|
|
2742
2986
|
}
|
|
2743
2987
|
}
|
|
2744
2988
|
const healCandidates = await this.entryRepo.findHealCandidatesByEntityId(entityId, batchSize, recheckCutoff);
|
|
@@ -2811,22 +3055,38 @@ var MaintenanceService = class {
|
|
|
2811
3055
|
const safeDowngradedSet = /* @__PURE__ */ new Set();
|
|
2812
3056
|
const safeDeletedSet = /* @__PURE__ */ new Set();
|
|
2813
3057
|
const newFacts = [];
|
|
3058
|
+
const newFactItemIndexes = [];
|
|
2814
3059
|
for (const batchResult of outcome.results) {
|
|
2815
3060
|
const mutableIds = new Set(batchResult.batch.map((f) => f.id));
|
|
2816
3061
|
for (const id of batchResult.downgraded) if (mutableIds.has(id)) safeDowngradedSet.add(id);
|
|
2817
3062
|
for (const id of batchResult.deleted) if (mutableIds.has(id)) safeDeletedSet.add(id);
|
|
2818
|
-
newFacts.
|
|
3063
|
+
batchResult.newFacts.forEach((raw, itemIndex) => {
|
|
3064
|
+
newFacts.push(raw);
|
|
3065
|
+
newFactItemIndexes.push(itemIndex);
|
|
3066
|
+
});
|
|
2819
3067
|
}
|
|
2820
3068
|
const safeDowngraded = Array.from(safeDowngradedSet);
|
|
2821
3069
|
const safeDeleted = Array.from(safeDeletedSet);
|
|
2822
|
-
const
|
|
3070
|
+
const diagBuffer = new DiagnosticBuffer();
|
|
3071
|
+
const validNewFacts = [];
|
|
3072
|
+
const validNewFactItemIndexes = [];
|
|
3073
|
+
newFacts.forEach((raw, k) => {
|
|
3074
|
+
const itemIndex = newFactItemIndexes[k];
|
|
3075
|
+
const valid = validateFact(raw);
|
|
3076
|
+
if (valid) {
|
|
3077
|
+
validNewFacts.push(valid);
|
|
3078
|
+
validNewFactItemIndexes.push(itemIndex);
|
|
3079
|
+
} else {
|
|
3080
|
+
diagBuffer.push({ ...diagBase, code: "fact_rejected", detail: { itemIndex, reason: factRejectionReason(raw) } });
|
|
3081
|
+
}
|
|
3082
|
+
});
|
|
2823
3083
|
const insertedFacts = [];
|
|
2824
3084
|
const uniqueDeletedFactIds = Array.from(new Set(safeDeleted));
|
|
2825
3085
|
await this.db.withTransactionAsync(async (tx) => {
|
|
2826
3086
|
await this.entryRepo.downgradeByIds(safeDowngraded, entityId, tx);
|
|
2827
3087
|
await this.entryRepo.softDeleteByIds(safeDeleted, entityId, tx);
|
|
2828
3088
|
const healFactsForDedupe = await this.entryRepo.findInferredTitlesByEntityId(entityId, tx);
|
|
2829
|
-
for (const fact of validNewFacts) {
|
|
3089
|
+
for (const [k, fact] of validNewFacts.entries()) {
|
|
2830
3090
|
const newTokens = titleTokens(fact.title);
|
|
2831
3091
|
let skip = false;
|
|
2832
3092
|
if (newTokens.size >= MIN_TOKENS_TO_QUALIFY) {
|
|
@@ -2840,7 +3100,10 @@ var MaintenanceService = class {
|
|
|
2840
3100
|
}
|
|
2841
3101
|
}
|
|
2842
3102
|
}
|
|
2843
|
-
if (skip)
|
|
3103
|
+
if (skip) {
|
|
3104
|
+
diagBuffer.push({ ...diagBase, code: "fact_deduplicated", detail: { itemIndex: validNewFactItemIndexes[k], reason: "fuzzy_title" } });
|
|
3105
|
+
continue;
|
|
3106
|
+
}
|
|
2844
3107
|
const id = generateId("fact_");
|
|
2845
3108
|
const factObj = {
|
|
2846
3109
|
id,
|
|
@@ -2875,16 +3138,21 @@ var MaintenanceService = class {
|
|
|
2875
3138
|
tx
|
|
2876
3139
|
);
|
|
2877
3140
|
});
|
|
3141
|
+
for (const { item, reason } of outcome.skipped) {
|
|
3142
|
+
diagBuffer.push({ ...diagBase, code: "heal_skipped", detail: { factId: item.id, reason } });
|
|
3143
|
+
}
|
|
3144
|
+
diagBuffer.flush(this.options);
|
|
2878
3145
|
await this.searchService.sync(entityId);
|
|
2879
3146
|
for (const factId of uniqueDeletedFactIds) {
|
|
2880
3147
|
try {
|
|
2881
3148
|
await this.embeddingService.notifyEmbeddingPersisted(entityId, factId, null);
|
|
2882
3149
|
} catch (hookErr) {
|
|
2883
3150
|
console.warn(`[WikiMemory] onEmbeddingPersisted hook failed during heal for ${factId}:`, hookErr);
|
|
3151
|
+
emitDiagnostic(this.options, { ...diagBase, code: "hook_failed", detail: { factId, reason: "on_embedding_persisted" } });
|
|
2884
3152
|
}
|
|
2885
3153
|
}
|
|
2886
3154
|
for (const fact of insertedFacts) {
|
|
2887
|
-
await this.embeddingService.embedFact(fact);
|
|
3155
|
+
await this.embeddingService.embedFact(fact, { operation: "heal", trigger });
|
|
2888
3156
|
}
|
|
2889
3157
|
this.searchService.evictCache(entityId);
|
|
2890
3158
|
const skippedIds = new Set(outcome.skipped.map(({ item }) => item.id));
|
|
@@ -3024,6 +3292,8 @@ var MaintenanceService = class {
|
|
|
3024
3292
|
let failedValidation = 0;
|
|
3025
3293
|
let edgesAdded = 0;
|
|
3026
3294
|
let abortedOntologyOff = false;
|
|
3295
|
+
const diagBuffer = new DiagnosticBuffer();
|
|
3296
|
+
const diagBase = { entityId, operation: "ontologyBackfill", trigger: "call" };
|
|
3027
3297
|
await this.db.withTransactionAsync(async (tx) => {
|
|
3028
3298
|
let { mode: txMode, manifest } = await ontologyService.getEffectiveState(entityId, tx);
|
|
3029
3299
|
if (txMode === "off") {
|
|
@@ -3048,6 +3318,7 @@ var MaintenanceService = class {
|
|
|
3048
3318
|
continue;
|
|
3049
3319
|
}
|
|
3050
3320
|
applied.add(fact.id);
|
|
3321
|
+
const validationDrops = [];
|
|
3051
3322
|
const normalized = ontologyService.validateAndNormalizeFact(
|
|
3052
3323
|
{
|
|
3053
3324
|
title: fact.title,
|
|
@@ -3058,8 +3329,11 @@ var MaintenanceService = class {
|
|
|
3058
3329
|
edges: classification.edges
|
|
3059
3330
|
},
|
|
3060
3331
|
manifest,
|
|
3061
|
-
{ strict: false }
|
|
3332
|
+
{ strict: false, drops: validationDrops }
|
|
3062
3333
|
);
|
|
3334
|
+
for (const drop of validationDrops) {
|
|
3335
|
+
diagBuffer.push(edgeDropDiagnostic(drop, { ...diagBase, factId: fact.id }));
|
|
3336
|
+
}
|
|
3063
3337
|
if (!normalized.okf_type) {
|
|
3064
3338
|
failedValidation++;
|
|
3065
3339
|
continue;
|
|
@@ -3073,7 +3347,8 @@ var MaintenanceService = class {
|
|
|
3073
3347
|
}
|
|
3074
3348
|
}
|
|
3075
3349
|
for (const item of pendingEdges) {
|
|
3076
|
-
|
|
3350
|
+
const resolveDrops = [];
|
|
3351
|
+
const added = await ontologyService.resolveAndPersistEdges(
|
|
3077
3352
|
entityId,
|
|
3078
3353
|
item.sourceId,
|
|
3079
3354
|
item.sourceType,
|
|
@@ -3081,11 +3356,17 @@ var MaintenanceService = class {
|
|
|
3081
3356
|
manifest,
|
|
3082
3357
|
titleIndex,
|
|
3083
3358
|
tx,
|
|
3084
|
-
now
|
|
3359
|
+
now,
|
|
3360
|
+
resolveDrops
|
|
3085
3361
|
);
|
|
3362
|
+
edgesAdded += added;
|
|
3363
|
+
for (const drop of resolveDrops) {
|
|
3364
|
+
diagBuffer.push(edgeDropDiagnostic(drop, diagBase));
|
|
3365
|
+
}
|
|
3086
3366
|
}
|
|
3087
3367
|
await this.entryRepo.markOntologyChecked(batch.map((f) => f.id), entityId, now, tx);
|
|
3088
3368
|
});
|
|
3369
|
+
diagBuffer.flush(this.options);
|
|
3089
3370
|
return { typed, failedValidation, edgesAdded, abortedOntologyOff };
|
|
3090
3371
|
}
|
|
3091
3372
|
_validatePruneDuration(value, name) {
|
|
@@ -4569,7 +4850,11 @@ var WriteService = class {
|
|
|
4569
4850
|
if (shouldRunLibrarian) {
|
|
4570
4851
|
try {
|
|
4571
4852
|
this.jobManager.acquireLock("librarian", entityId);
|
|
4572
|
-
|
|
4853
|
+
const stage = { job: "librarian" };
|
|
4854
|
+
this.runLibrarianThenMaybeHeal(entityId, librarianCount, prevMemoryCheckpoint, stage).catch((err) => {
|
|
4855
|
+
console.error(err);
|
|
4856
|
+
this.reportBackgroundFailure(entityId, stage.job);
|
|
4857
|
+
}).finally(() => {
|
|
4573
4858
|
this.jobManager.releaseLock("librarian", entityId);
|
|
4574
4859
|
});
|
|
4575
4860
|
} catch (e) {
|
|
@@ -4577,17 +4862,21 @@ var WriteService = class {
|
|
|
4577
4862
|
await this.metadataRepo.updateCheckpoint(entityId, { memory: prevMemoryCheckpoint }, this.db);
|
|
4578
4863
|
}
|
|
4579
4864
|
} else if (!this.jobManager.isBlocked("librarian", entityId)) {
|
|
4580
|
-
this.maybeRunHeal(entityId, eventCount).catch(
|
|
4865
|
+
this.maybeRunHeal(entityId, eventCount).catch((err) => {
|
|
4866
|
+
console.error(err);
|
|
4867
|
+
this.reportBackgroundFailure(entityId, "heal");
|
|
4868
|
+
});
|
|
4581
4869
|
}
|
|
4582
4870
|
}
|
|
4583
|
-
async runLibrarianThenMaybeHeal(entityId, currentEventCount, prevCheckpoint) {
|
|
4871
|
+
async runLibrarianThenMaybeHeal(entityId, currentEventCount, prevCheckpoint, stage) {
|
|
4584
4872
|
try {
|
|
4585
|
-
await this.maintenanceService.doRunLibrarian(entityId);
|
|
4873
|
+
await this.maintenanceService.doRunLibrarian(entityId, void 0, "auto");
|
|
4586
4874
|
await this.metadataRepo.updateCheckpoint(entityId, { memory: currentEventCount }, this.db);
|
|
4587
4875
|
} catch (e) {
|
|
4588
4876
|
await this.metadataRepo.updateCheckpoint(entityId, { memory: prevCheckpoint }, this.db);
|
|
4589
4877
|
throw e;
|
|
4590
4878
|
}
|
|
4879
|
+
stage.job = "heal";
|
|
4591
4880
|
await this.maybeRunHeal(entityId, currentEventCount);
|
|
4592
4881
|
}
|
|
4593
4882
|
/**
|
|
@@ -4603,7 +4892,7 @@ var WriteService = class {
|
|
|
4603
4892
|
const shouldRunHeal = currentEventCount - healCheckpoint >= autoHealThreshold;
|
|
4604
4893
|
if (shouldRunHeal && this.jobManager.tryAcquireAutoHealLock(entityId)) {
|
|
4605
4894
|
try {
|
|
4606
|
-
const result = await this.maintenanceService.doRunHeal(entityId);
|
|
4895
|
+
const result = await this.maintenanceService.doRunHeal(entityId, { trigger: "auto" });
|
|
4607
4896
|
if (result.remaining === 0) {
|
|
4608
4897
|
await this.metadataRepo.updateCheckpoint(entityId, { heal: currentEventCount }, this.db);
|
|
4609
4898
|
}
|
|
@@ -4612,6 +4901,15 @@ var WriteService = class {
|
|
|
4612
4901
|
}
|
|
4613
4902
|
}
|
|
4614
4903
|
}
|
|
4904
|
+
reportBackgroundFailure(entityId, job) {
|
|
4905
|
+
emitDiagnostic(this.options, {
|
|
4906
|
+
code: "background_job_failed",
|
|
4907
|
+
operation: job,
|
|
4908
|
+
trigger: "auto",
|
|
4909
|
+
entityId,
|
|
4910
|
+
detail: { reason: "unhandled_rejection" }
|
|
4911
|
+
});
|
|
4912
|
+
}
|
|
4615
4913
|
};
|
|
4616
4914
|
|
|
4617
4915
|
exports.EmbeddingService = EmbeddingService;
|