@remnic/core 9.51.0 → 9.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-admin-ops-surface.d.ts +6 -5
- package/dist/access-admin-ops-surface.js +16 -16
- package/dist/access-authorization-probe.d.ts +6 -5
- package/dist/access-authorization-probe.js +17 -17
- package/dist/access-boundary.d.ts +6 -5
- package/dist/access-boundary.js +16 -16
- package/dist/access-cli.js +39 -39
- package/dist/access-extraction-force-flush.d.ts +6 -5
- package/dist/access-extraction-force-flush.js +16 -16
- package/dist/access-http-lcm-compaction.d.ts +6 -5
- package/dist/access-http-lifecycle-flush.d.ts +6 -5
- package/dist/access-http-offline-stream.d.ts +6 -5
- package/dist/access-http-query.js +16 -16
- package/dist/access-http.d.ts +6 -5
- package/dist/access-http.js +22 -22
- package/dist/access-identity-continuity-surface.d.ts +5 -4
- package/dist/access-identity-continuity-surface.js +16 -16
- package/dist/access-lcm-surface.d.ts +6 -5
- package/dist/access-lcm-surface.js +16 -16
- package/dist/access-mcp.d.ts +6 -5
- package/dist/access-mcp.js +20 -20
- package/dist/access-namespace-preflight.js +16 -16
- package/dist/access-observe-write-surface.d.ts +6 -5
- package/dist/access-observe-write-surface.js +16 -16
- package/dist/access-offline-manifest.d.ts +5 -4
- package/dist/access-operations-batch.js +17 -17
- package/dist/access-operations.d.ts +9 -8
- package/dist/access-operations.js +19 -19
- package/dist/access-recall-concurrency.d.ts +6 -5
- package/dist/access-recall-concurrency.js +16 -16
- package/dist/access-recall-response.d.ts +6 -5
- package/dist/access-recall-response.js +16 -16
- package/dist/access-recall-surface.d.ts +6 -5
- package/dist/access-recall-surface.js +16 -16
- package/dist/access-schema.d.ts +88 -88
- package/dist/{access-service-BY0KJ44c.d.ts → access-service-Cbk3bNbD.d.ts} +3 -3
- package/dist/access-service-helpers.d.ts +6 -5
- package/dist/access-service.d.ts +6 -5
- package/dist/access-service.js +16 -16
- package/dist/access-surface-catalog.d.ts +6 -5
- package/dist/access-wearables-meetings-surface.d.ts +2 -2
- package/dist/bootstrap.d.ts +5 -4
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +7 -7
- package/dist/buffer.d.ts +1 -1
- package/dist/{capsule-merge-XMFBXVDP.js → capsule-merge-HCJJMLPS.js} +4 -4
- package/dist/causal-consolidation.js +8 -8
- package/dist/{chunk-PWN32TAG.js → chunk-2ASAS3CQ.js} +2 -2
- package/dist/{chunk-WQM326GS.js → chunk-2LY6GIIE.js} +2 -2
- package/dist/{chunk-JL43BVDE.js → chunk-2OIQDTDC.js} +2 -2
- package/dist/{chunk-33SV3E43.js → chunk-3OY7KXHM.js} +3 -3
- package/dist/{chunk-AIUBBHSF.js → chunk-5UZ6UCOZ.js} +3 -3
- package/dist/chunk-5WITETWU.js +591 -0
- package/dist/chunk-5WITETWU.js.map +1 -0
- package/dist/{chunk-UT5BV5OG.js → chunk-7HXZJAID.js} +2 -2
- package/dist/{chunk-M7BXDKPR.js → chunk-7M6NYQWB.js} +3 -3
- package/dist/{chunk-TPTECFWR.js → chunk-B6LZ4IBH.js} +3 -3
- package/dist/{chunk-J5LLX6IL.js → chunk-BF6SXNQJ.js} +10 -10
- package/dist/{chunk-4PC4PRJE.js → chunk-BKIFTXHR.js} +2 -2
- package/dist/{chunk-VVG2BOKA.js → chunk-BMEYMKWX.js} +2 -2
- package/dist/{chunk-DCZBSE4F.js → chunk-D5YOZ66N.js} +18 -18
- package/dist/{chunk-BHXYAVUM.js → chunk-DLG5V6ND.js} +2 -2
- package/dist/{chunk-52UKFTUK.js → chunk-F4EU6VUC.js} +2 -2
- package/dist/{chunk-M4A44LUA.js → chunk-GSTIBXBW.js} +2 -2
- package/dist/{chunk-W5XVQFLM.js → chunk-GXFSQ6SD.js} +2 -2
- package/dist/{chunk-XADV5IZ3.js → chunk-HDIUYMSB.js} +5 -5
- package/dist/{chunk-FXTYIA46.js → chunk-J272WKX2.js} +2 -2
- package/dist/{chunk-ENTCVTQR.js → chunk-JJRVPWFY.js} +11 -1
- package/dist/chunk-JJRVPWFY.js.map +1 -0
- package/dist/{chunk-JYL3BVVZ.js → chunk-JSEJH2YQ.js} +2 -2
- package/dist/{chunk-5UDMFE3Q.js → chunk-MLXZD4WM.js} +2 -2
- package/dist/{chunk-PUSLMZBQ.js → chunk-NOEMGE56.js} +2 -2
- package/dist/{chunk-C5QLI33R.js → chunk-QINJITDS.js} +2 -2
- package/dist/{chunk-45BRYON6.js → chunk-QPOHMO6M.js} +2 -2
- package/dist/{chunk-ZQOCRIGA.js → chunk-QYN5PXDZ.js} +3 -3
- package/dist/{chunk-PA45XCUD.js → chunk-RANKJI5T.js} +2 -2
- package/dist/{chunk-ZHY5TMJG.js → chunk-RMHMUUBW.js} +2392 -628
- package/dist/chunk-RMHMUUBW.js.map +1 -0
- package/dist/{chunk-37ZO6LLJ.js → chunk-RYLEDKQ2.js} +2 -2
- package/dist/{chunk-CV6BBVYV.js → chunk-TEMSWHQD.js} +2 -2
- package/dist/{chunk-QGR6OVGT.js → chunk-VFSAWRMZ.js} +2 -2
- package/dist/{chunk-NLK2VZYZ.js → chunk-VXCNQYTC.js} +2 -2
- package/dist/{chunk-AZ2QBGYB.js → chunk-W7Y5JDVB.js} +1 -1
- package/dist/{chunk-VBHPOWBJ.js → chunk-WD5B5AIE.js} +1051 -591
- package/dist/chunk-WD5B5AIE.js.map +1 -0
- package/dist/{chunk-OYESWJ43.js → chunk-WLRD2TXF.js} +15 -10
- package/dist/chunk-WLRD2TXF.js.map +1 -0
- package/dist/{chunk-4F6X3RXW.js → chunk-X57M2Q4I.js} +2 -2
- package/dist/{chunk-6MTSFX7A.js → chunk-X7XEDOSL.js} +8 -8
- package/dist/{chunk-UEVLYVNW.js → chunk-YXKVLYXU.js} +2 -2
- package/dist/{chunk-OQAL6SIR.js → chunk-ZMSZVISA.js} +2 -2
- package/dist/{cli-CnOkH9hA.d.ts → cli-DMAFI0qt.d.ts} +3 -3
- package/dist/cli.d.ts +7 -6
- package/dist/cli.js +33 -33
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +7 -7
- package/dist/connectors/codex-materialize-runner.js +7 -7
- package/dist/connectors/index.js +7 -7
- package/dist/consolidation-provenance-check.d.ts +1 -1
- package/dist/consolidation-provenance-check.js +4 -4
- package/dist/consolidation-undo.d.ts +1 -1
- package/dist/consolidation-undo.js +3 -3
- package/dist/contradiction/index.d.ts +1 -1
- package/dist/convergence-refresh.d.ts +1 -1
- package/dist/convergence-refresh.js +9 -9
- package/dist/corpus-watermark.js +9 -9
- package/dist/dependency-propagation-delivery-D63r24pH.d.ts +41 -0
- package/dist/entity-retrieval.d.ts +1 -1
- package/dist/entity-retrieval.js +7 -7
- package/dist/explicit-capture.d.ts +5 -4
- package/dist/explicit-capture.js +4 -4
- package/dist/external-wiki-access.d.ts +6 -5
- package/dist/external-wiki-access.js +17 -17
- package/dist/external-wiki-mcp-tools.d.ts +6 -5
- package/dist/extraction.js +7 -7
- package/dist/{forget-UJDJHG6R.js → forget-HDS4N6CG.js} +3 -3
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/index.d.ts +687 -686
- package/dist/index.js +39 -39
- package/dist/maintenance/memory-governance.js +7 -7
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +1 -1
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +7 -7
- package/dist/maintenance/rebuild-memory-projection.d.ts +1 -1
- package/dist/maintenance/rebuild-memory-projection.js +8 -8
- package/dist/{maintenance-CKwxpdRx.d.ts → maintenance-kBII2qUI.d.ts} +3 -2
- package/dist/mcp-memory-inspector-app.d.ts +6 -5
- package/dist/memory-worth-outcomes.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +1 -1
- package/dist/namespaces/migrate.js +8 -8
- package/dist/namespaces/storage.d.ts +1 -1
- package/dist/namespaces/storage.js +7 -7
- package/dist/operator-doctor-corpus.js +10 -10
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +15 -15
- package/dist/orchestration/compression-guideline-coordinator.d.ts +1 -1
- package/dist/orchestration/maintenance.d.ts +2 -2
- package/dist/orchestration/maintenance.js +12 -12
- package/dist/{orchestrator-BshGam-o.d.ts → orchestrator-DHy5LtTy.d.ts} +20 -35
- package/dist/orchestrator.d.ts +5 -4
- package/dist/orchestrator.js +39 -39
- package/dist/page-versioning.js +3 -3
- package/dist/schemas.d.ts +138 -138
- package/dist/semantic-consolidation.js +8 -8
- package/dist/semantic-rule-promotion.js +7 -7
- package/dist/semantic-rule-verifier.js +7 -7
- package/dist/{service-BIgVM7fL.d.ts → service-PT2_z7xm.d.ts} +1 -1
- package/dist/shared-context/manager.d.ts +8 -8
- package/dist/source-agent-qualifier.js +7 -7
- package/dist/{storage-CVfGWqCI.d.ts → storage-BmVudhk4.d.ts} +46 -27
- package/dist/storage.d.ts +1 -1
- package/dist/storage.js +6 -6
- package/dist/temporal-supersession.d.ts +51 -26
- package/dist/temporal-supersession.js +3 -1
- package/dist/tier-migration.d.ts +1 -1
- package/dist/transfer/capsule-import.js +4 -4
- package/dist/transfer/types.d.ts +66 -66
- package/dist/verified-recall.js +7 -7
- package/package.json +2 -2
- package/src/lifecycle/tombstones.ts +30 -0
- package/src/orchestration/consolidation-run.ts +324 -106
- package/src/orchestration/contradiction-linking-coordinator.ts +237 -64
- package/src/orchestration/dependency-propagation-delivery.test.ts +2353 -0
- package/src/orchestration/dependency-propagation-delivery.ts +1143 -0
- package/src/orchestration/dependency-propagation-queue-state.ts +220 -0
- package/src/orchestration/dependency-propagation-recovery-replay.ts +237 -0
- package/src/orchestration/dependency-propagation-replay-reconciliation.ts +73 -0
- package/src/orchestration/dependency-propagation-triggers.test.ts +1069 -11
- package/src/orchestration/dependency-propagation.test.ts +82 -31
- package/src/orchestration/dependency-propagation.ts +55 -17
- package/src/orchestration/extraction-persist-deps.ts +2 -2
- package/src/orchestration/extraction-persist.ts +14 -15
- package/src/orchestration/extraction-queue-coordinator.ts +23 -1
- package/src/orchestration/extraction-shutdown.ts +29 -0
- package/src/orchestration/maintenance.ts +13 -7
- package/src/orchestration/turn-ingestion.ts +5 -1
- package/src/orchestrator-background-writes.test.ts +265 -1
- package/src/orchestrator-extraction-queue.test.ts +39 -0
- package/src/orchestrator.ts +59 -58
- package/src/storage/committed-invalidation.ts +34 -0
- package/src/storage/deletion-revision-store.ts +529 -0
- package/src/storage/supersession-audit.ts +41 -0
- package/src/storage/supersession-side-effects.ts +52 -25
- package/src/storage-contract/failure-semantics.test.ts +12 -0
- package/src/storage-contract/round-trip.test.ts +184 -2
- package/src/storage-deletion-revisions.test.ts +213 -1
- package/src/storage-frontmatter-cas.test.ts +141 -0
- package/src/storage-memory-lifecycle-ledger.test.ts +372 -0
- package/src/storage-replay.test.ts +149 -0
- package/src/storage.ts +345 -358
- package/src/temporal-supersession.test.ts +431 -2
- package/src/temporal-supersession.ts +567 -107
- package/src/testing/subjects/dependency-propagation.test.ts +179 -38
- package/dist/chunk-ENTCVTQR.js.map +0 -1
- package/dist/chunk-EWMTOVMY.js +0 -484
- package/dist/chunk-EWMTOVMY.js.map +0 -1
- package/dist/chunk-OYESWJ43.js.map +0 -1
- package/dist/chunk-VBHPOWBJ.js.map +0 -1
- package/dist/chunk-ZHY5TMJG.js.map +0 -1
- /package/dist/{capsule-merge-XMFBXVDP.js.map → capsule-merge-HCJJMLPS.js.map} +0 -0
- /package/dist/{chunk-PWN32TAG.js.map → chunk-2ASAS3CQ.js.map} +0 -0
- /package/dist/{chunk-WQM326GS.js.map → chunk-2LY6GIIE.js.map} +0 -0
- /package/dist/{chunk-JL43BVDE.js.map → chunk-2OIQDTDC.js.map} +0 -0
- /package/dist/{chunk-33SV3E43.js.map → chunk-3OY7KXHM.js.map} +0 -0
- /package/dist/{chunk-AIUBBHSF.js.map → chunk-5UZ6UCOZ.js.map} +0 -0
- /package/dist/{chunk-UT5BV5OG.js.map → chunk-7HXZJAID.js.map} +0 -0
- /package/dist/{chunk-M7BXDKPR.js.map → chunk-7M6NYQWB.js.map} +0 -0
- /package/dist/{chunk-TPTECFWR.js.map → chunk-B6LZ4IBH.js.map} +0 -0
- /package/dist/{chunk-J5LLX6IL.js.map → chunk-BF6SXNQJ.js.map} +0 -0
- /package/dist/{chunk-4PC4PRJE.js.map → chunk-BKIFTXHR.js.map} +0 -0
- /package/dist/{chunk-VVG2BOKA.js.map → chunk-BMEYMKWX.js.map} +0 -0
- /package/dist/{chunk-DCZBSE4F.js.map → chunk-D5YOZ66N.js.map} +0 -0
- /package/dist/{chunk-BHXYAVUM.js.map → chunk-DLG5V6ND.js.map} +0 -0
- /package/dist/{chunk-52UKFTUK.js.map → chunk-F4EU6VUC.js.map} +0 -0
- /package/dist/{chunk-M4A44LUA.js.map → chunk-GSTIBXBW.js.map} +0 -0
- /package/dist/{chunk-W5XVQFLM.js.map → chunk-GXFSQ6SD.js.map} +0 -0
- /package/dist/{chunk-XADV5IZ3.js.map → chunk-HDIUYMSB.js.map} +0 -0
- /package/dist/{chunk-FXTYIA46.js.map → chunk-J272WKX2.js.map} +0 -0
- /package/dist/{chunk-JYL3BVVZ.js.map → chunk-JSEJH2YQ.js.map} +0 -0
- /package/dist/{chunk-5UDMFE3Q.js.map → chunk-MLXZD4WM.js.map} +0 -0
- /package/dist/{chunk-PUSLMZBQ.js.map → chunk-NOEMGE56.js.map} +0 -0
- /package/dist/{chunk-C5QLI33R.js.map → chunk-QINJITDS.js.map} +0 -0
- /package/dist/{chunk-45BRYON6.js.map → chunk-QPOHMO6M.js.map} +0 -0
- /package/dist/{chunk-ZQOCRIGA.js.map → chunk-QYN5PXDZ.js.map} +0 -0
- /package/dist/{chunk-PA45XCUD.js.map → chunk-RANKJI5T.js.map} +0 -0
- /package/dist/{chunk-37ZO6LLJ.js.map → chunk-RYLEDKQ2.js.map} +0 -0
- /package/dist/{chunk-CV6BBVYV.js.map → chunk-TEMSWHQD.js.map} +0 -0
- /package/dist/{chunk-QGR6OVGT.js.map → chunk-VFSAWRMZ.js.map} +0 -0
- /package/dist/{chunk-NLK2VZYZ.js.map → chunk-VXCNQYTC.js.map} +0 -0
- /package/dist/{chunk-AZ2QBGYB.js.map → chunk-W7Y5JDVB.js.map} +0 -0
- /package/dist/{chunk-4F6X3RXW.js.map → chunk-X57M2Q4I.js.map} +0 -0
- /package/dist/{chunk-6MTSFX7A.js.map → chunk-X7XEDOSL.js.map} +0 -0
- /package/dist/{chunk-UEVLYVNW.js.map → chunk-YXKVLYXU.js.map} +0 -0
- /package/dist/{chunk-OQAL6SIR.js.map → chunk-ZMSZVISA.js.map} +0 -0
- /package/dist/{forget-UJDJHG6R.js.map → forget-HDS4N6CG.js.map} +0 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
import type { MemoryFile } from "../types.js";
|
|
4
|
+
import { isRecord } from "../store-contract.js";
|
|
5
|
+
import type { PropagationEvent } from "./dependency-propagation.js";
|
|
6
|
+
import type {
|
|
7
|
+
DependencyPropagationJob,
|
|
8
|
+
DependencyPropagationJobStatus,
|
|
9
|
+
} from "./dependency-propagation-delivery.js";
|
|
10
|
+
|
|
11
|
+
export const JOB_STATES: readonly DependencyPropagationJobStatus[] = [
|
|
12
|
+
"prepared",
|
|
13
|
+
"ready",
|
|
14
|
+
"leased",
|
|
15
|
+
"retryable",
|
|
16
|
+
"completed",
|
|
17
|
+
"dead_letter",
|
|
18
|
+
"canceled",
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const CAUSES: Record<PropagationEvent["cause"], true> = {
|
|
22
|
+
contradiction: true,
|
|
23
|
+
temporal_supersession: true,
|
|
24
|
+
consolidation_invalidate: true,
|
|
25
|
+
consolidation_merge: true,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export function isFiniteNumber(value: unknown): value is number {
|
|
29
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isPropagationEvent(value: unknown): value is PropagationEvent {
|
|
33
|
+
if (!isRecord(value) || !isRecord(value.oldMemory)) return false;
|
|
34
|
+
const oldMemory = value.oldMemory;
|
|
35
|
+
if (!isRecord(oldMemory.frontmatter)) return false;
|
|
36
|
+
const frontmatter = oldMemory.frontmatter;
|
|
37
|
+
if (typeof oldMemory.content !== "string" || typeof frontmatter.id !== "string") return false;
|
|
38
|
+
if (
|
|
39
|
+
!(value.replacementId === null || typeof value.replacementId === "string") ||
|
|
40
|
+
!(value.replacementContent === null || typeof value.replacementContent === "string") ||
|
|
41
|
+
typeof value.namespaceScope !== "string" ||
|
|
42
|
+
value.namespaceScope.length === 0 ||
|
|
43
|
+
typeof value.cause !== "string" ||
|
|
44
|
+
!Object.hasOwn(CAUSES, value.cause)
|
|
45
|
+
) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const temporalMutation = value.temporalMutation;
|
|
49
|
+
if (
|
|
50
|
+
temporalMutation !== undefined &&
|
|
51
|
+
(!isRecord(temporalMutation) ||
|
|
52
|
+
typeof temporalMutation.supersededAt !== "string" ||
|
|
53
|
+
(temporalMutation.invalidAt !== undefined && typeof temporalMutation.invalidAt !== "string") ||
|
|
54
|
+
!Array.isArray(temporalMutation.matchedKeys) ||
|
|
55
|
+
temporalMutation.matchedKeys.some((key) => typeof key !== "string"))
|
|
56
|
+
) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
if (frontmatter.links !== undefined) {
|
|
60
|
+
if (!Array.isArray(frontmatter.links)) return false;
|
|
61
|
+
for (const link of frontmatter.links) {
|
|
62
|
+
if (!isRecord(link) || typeof link.targetId !== "string" || typeof link.linkType !== "string") return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function validateJob(
|
|
69
|
+
value: unknown,
|
|
70
|
+
expectedState?: DependencyPropagationJobStatus,
|
|
71
|
+
): DependencyPropagationJob | null {
|
|
72
|
+
if (!isRecord(value)) return null;
|
|
73
|
+
const status = value.status;
|
|
74
|
+
const event = value.event;
|
|
75
|
+
const attempts = value.attempts;
|
|
76
|
+
const createdAt = value.createdAt;
|
|
77
|
+
const updatedAt = value.updatedAt;
|
|
78
|
+
if (
|
|
79
|
+
typeof value.jobId !== "string" ||
|
|
80
|
+
value.jobId.length === 0 ||
|
|
81
|
+
typeof value.namespace !== "string" ||
|
|
82
|
+
value.namespace.length === 0 ||
|
|
83
|
+
typeof value.sourceId !== "string" ||
|
|
84
|
+
value.sourceId.length === 0 ||
|
|
85
|
+
!isPropagationEvent(event) ||
|
|
86
|
+
value.jobId !== eventJobId(event) ||
|
|
87
|
+
value.namespace !== event.namespaceScope ||
|
|
88
|
+
value.sourceId !== event.oldMemory.frontmatter.id ||
|
|
89
|
+
typeof status !== "string" ||
|
|
90
|
+
!JOB_STATES.includes(status as DependencyPropagationJobStatus) ||
|
|
91
|
+
(expectedState !== undefined && status !== expectedState) ||
|
|
92
|
+
typeof attempts !== "number" ||
|
|
93
|
+
!Number.isInteger(attempts) ||
|
|
94
|
+
attempts < 0 ||
|
|
95
|
+
!isFiniteNumber(createdAt) ||
|
|
96
|
+
!isFiniteNumber(updatedAt)
|
|
97
|
+
) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const revision = value.revision === undefined ? 0 : value.revision;
|
|
101
|
+
if (typeof revision !== "number" || !Number.isInteger(revision) || revision < 0) return null;
|
|
102
|
+
const reservationIdsValue = value.reservationIds;
|
|
103
|
+
if (!Array.isArray(reservationIdsValue)) return null;
|
|
104
|
+
const reservationIds = reservationIdsValue as unknown[];
|
|
105
|
+
if (
|
|
106
|
+
reservationIds.some(
|
|
107
|
+
(reservationId) => typeof reservationId !== "string" || reservationId.trim().length === 0,
|
|
108
|
+
) || new Set(reservationIds).size !== reservationIds.length
|
|
109
|
+
) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const reservations = value.reservations;
|
|
113
|
+
if (
|
|
114
|
+
typeof reservations !== "number" ||
|
|
115
|
+
!Number.isInteger(reservations) ||
|
|
116
|
+
reservations < 0 ||
|
|
117
|
+
reservations !== reservationIds.length
|
|
118
|
+
) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
const nextAttemptAt = value.nextAttemptAt;
|
|
122
|
+
const leaseOwner = value.leaseOwner;
|
|
123
|
+
const leaseExpiresAt = value.leaseExpiresAt;
|
|
124
|
+
const lastError = value.lastError;
|
|
125
|
+
const preparedReplacementFingerprint = value.preparedReplacementFingerprint;
|
|
126
|
+
if (nextAttemptAt !== undefined && !isFiniteNumber(nextAttemptAt)) return null;
|
|
127
|
+
if (leaseOwner !== undefined && typeof leaseOwner !== "string") return null;
|
|
128
|
+
if (leaseExpiresAt !== undefined && !isFiniteNumber(leaseExpiresAt)) return null;
|
|
129
|
+
if (lastError !== undefined && typeof lastError !== "string") return null;
|
|
130
|
+
if (
|
|
131
|
+
preparedReplacementFingerprint !== undefined &&
|
|
132
|
+
(typeof preparedReplacementFingerprint !== "string" || preparedReplacementFingerprint.length === 0)
|
|
133
|
+
) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
if (status === "retryable" && nextAttemptAt === undefined) return null;
|
|
137
|
+
if (status === "leased" && (typeof leaseOwner !== "string" || leaseExpiresAt === undefined)) return null;
|
|
138
|
+
return {
|
|
139
|
+
jobId: value.jobId as string,
|
|
140
|
+
namespace: value.namespace as string,
|
|
141
|
+
sourceId: value.sourceId as string,
|
|
142
|
+
event,
|
|
143
|
+
status: status as DependencyPropagationJobStatus,
|
|
144
|
+
attempts,
|
|
145
|
+
createdAt,
|
|
146
|
+
updatedAt,
|
|
147
|
+
reservations,
|
|
148
|
+
reservationIds: [...(reservationIds as string[])],
|
|
149
|
+
revision,
|
|
150
|
+
...(nextAttemptAt === undefined ? {} : { nextAttemptAt: nextAttemptAt as number }),
|
|
151
|
+
...(leaseOwner === undefined ? {} : { leaseOwner: leaseOwner as string }),
|
|
152
|
+
...(leaseExpiresAt === undefined ? {} : { leaseExpiresAt: leaseExpiresAt as number }),
|
|
153
|
+
...(lastError === undefined ? {} : { lastError: lastError as string }),
|
|
154
|
+
...(preparedReplacementFingerprint === undefined
|
|
155
|
+
? {}
|
|
156
|
+
: { preparedReplacementFingerprint: preparedReplacementFingerprint as string }),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
export function canonicalize(value: unknown): unknown {
|
|
160
|
+
if (Array.isArray(value)) return value.map((item) => canonicalize(item));
|
|
161
|
+
if (isRecord(value)) {
|
|
162
|
+
const result: Record<string, unknown> = {};
|
|
163
|
+
for (const key of Object.keys(value).sort()) {
|
|
164
|
+
if (value[key] === undefined) continue;
|
|
165
|
+
result[key] = canonicalize(value[key]);
|
|
166
|
+
}
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
return value === undefined ? null : value;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function compareByteStable(left: string, right: string): number {
|
|
173
|
+
return Buffer.from(left, "utf8").compare(Buffer.from(right, "utf8"));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function canonicalEvent(event: PropagationEvent): PropagationEvent {
|
|
177
|
+
return canonicalize(event) as PropagationEvent;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function stableLinks(links: unknown): unknown {
|
|
181
|
+
if (!Array.isArray(links)) return links;
|
|
182
|
+
return [...links].sort((left, right) => {
|
|
183
|
+
const leftKey = JSON.stringify(canonicalize(left));
|
|
184
|
+
const rightKey = JSON.stringify(canonicalize(right));
|
|
185
|
+
return compareByteStable(leftKey, rightKey);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function matchesPreparedSource(
|
|
190
|
+
current: MemoryFile,
|
|
191
|
+
prepared: Pick<MemoryFile, "content" | "frontmatter">,
|
|
192
|
+
): boolean {
|
|
193
|
+
const { accessCount: _currentAccessCount, lastAccessed: _currentLastAccessed, ...currentSemantic } =
|
|
194
|
+
current.frontmatter;
|
|
195
|
+
const { accessCount: _preparedAccessCount, lastAccessed: _preparedLastAccessed, ...preparedSemantic } =
|
|
196
|
+
prepared.frontmatter;
|
|
197
|
+
const normalizedCurrent = { ...currentSemantic, links: stableLinks(currentSemantic.links) };
|
|
198
|
+
const normalizedPrepared = { ...preparedSemantic, links: stableLinks(preparedSemantic.links) };
|
|
199
|
+
return (
|
|
200
|
+
current.content === prepared.content &&
|
|
201
|
+
JSON.stringify(canonicalize(normalizedCurrent)) === JSON.stringify(canonicalize(normalizedPrepared))
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function eventJobId(event: PropagationEvent): string {
|
|
206
|
+
const { accessCount: _accessCount, lastAccessed: _lastAccessed, links, ...semanticFrontmatter } =
|
|
207
|
+
event.oldMemory.frontmatter;
|
|
208
|
+
const semanticEvent = {
|
|
209
|
+
...event,
|
|
210
|
+
oldMemory: {
|
|
211
|
+
...event.oldMemory,
|
|
212
|
+
frontmatter: { ...semanticFrontmatter, links: stableLinks(links) },
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
return createHash("sha256").update(JSON.stringify(canonicalize(semanticEvent))).digest("hex");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function isNotFound(error: unknown): boolean {
|
|
219
|
+
return isRecord(error) && error.code === "ENOENT";
|
|
220
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recovery primary-mutation replay for durable dependency propagation.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from the delivery module so the recovery concern — resolving a
|
|
5
|
+
* memory across hot/cold/archived stores and replaying the primary mutation
|
|
6
|
+
* (supersede / invalidate / temporal) that a durable job represents — lives in
|
|
7
|
+
* one cohesive place. These functions are stateless with respect to the
|
|
8
|
+
* delivery class: they operate on an injected storage handle plus pure helpers,
|
|
9
|
+
* so the delivery delegates here without wrapper indirection. The module
|
|
10
|
+
* imports only package-local, host-agnostic sources (no index backend).
|
|
11
|
+
*/
|
|
12
|
+
import { createHash } from "node:crypto";
|
|
13
|
+
|
|
14
|
+
import { sanitizeMemoryContent } from "../sanitize.js";
|
|
15
|
+
import type { MemoryFile } from "../types.js";
|
|
16
|
+
import {
|
|
17
|
+
applyTemporalSupersessionPrimaryMutation,
|
|
18
|
+
type TemporalSupersessionStorage,
|
|
19
|
+
} from "../temporal-supersession.js";
|
|
20
|
+
import {
|
|
21
|
+
type DependencyPropagationStorage,
|
|
22
|
+
type PropagationEvent,
|
|
23
|
+
} from "./dependency-propagation.js";
|
|
24
|
+
import { canonicalize, matchesPreparedSource } from "./dependency-propagation-queue-state.js";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Storage capabilities the recovery replay needs beyond the base
|
|
28
|
+
* {@link DependencyPropagationStorage}: cold/archived memory reads, committed
|
|
29
|
+
* invalidation bookkeeping, and the supersede/invalidate primitives.
|
|
30
|
+
*/
|
|
31
|
+
export type DependencyPropagationRecoveryStorage = DependencyPropagationStorage & {
|
|
32
|
+
readAllColdMemories?: () => Promise<MemoryFile[]>;
|
|
33
|
+
readArchivedMemories?: () => Promise<MemoryFile[]>;
|
|
34
|
+
hasCommittedInvalidation?: (memory: Pick<MemoryFile, "content" | "frontmatter">) => Promise<boolean>;
|
|
35
|
+
clearCommittedInvalidation?: (
|
|
36
|
+
memory: Pick<MemoryFile, "content" | "frontmatter">,
|
|
37
|
+
) => Promise<void>;
|
|
38
|
+
invalidateMemory?: (
|
|
39
|
+
id: string,
|
|
40
|
+
snapshot?: Pick<MemoryFile, "content" | "frontmatter"> & Partial<Pick<MemoryFile, "path">>,
|
|
41
|
+
options?: { recordCommitProof?: boolean },
|
|
42
|
+
) => Promise<boolean>;
|
|
43
|
+
updateMemoryIfUnchanged?: (
|
|
44
|
+
expected: MemoryFile,
|
|
45
|
+
content: string,
|
|
46
|
+
options?: {
|
|
47
|
+
supersedes?: string;
|
|
48
|
+
lineage?: string[];
|
|
49
|
+
actor?: string;
|
|
50
|
+
sourceConnector?: string;
|
|
51
|
+
},
|
|
52
|
+
) => Promise<boolean>;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Content fingerprint captured at preparation time. Access-count /
|
|
57
|
+
* last-accessed bookkeeping is excluded so a replay can prove the survivor has
|
|
58
|
+
* not drifted.
|
|
59
|
+
*/
|
|
60
|
+
export function memoryFingerprint(memory: Pick<MemoryFile, "content" | "frontmatter">): string {
|
|
61
|
+
const { accessCount: _accessCount, lastAccessed: _lastAccessed, ...frontmatter } = memory.frontmatter;
|
|
62
|
+
return createHash("sha256")
|
|
63
|
+
.update(JSON.stringify(canonicalize({ content: memory.content, frontmatter })))
|
|
64
|
+
.digest("hex");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Resolve a memory by id across hot, cold, and archived stores. Returns `null`
|
|
69
|
+
* when no source record survives anywhere — recovery then cannot replay.
|
|
70
|
+
*/
|
|
71
|
+
export async function findRecoverySource(
|
|
72
|
+
storage: DependencyPropagationRecoveryStorage,
|
|
73
|
+
sourceId: string,
|
|
74
|
+
): Promise<MemoryFile | null> {
|
|
75
|
+
const hot = await storage.getMemoryById(sourceId);
|
|
76
|
+
if (hot?.frontmatter.id === sourceId) return hot;
|
|
77
|
+
if (storage.readAllColdMemories) {
|
|
78
|
+
const cold = await storage.readAllColdMemories();
|
|
79
|
+
const coldMatch = cold.find((memory) => memory.frontmatter.id === sourceId);
|
|
80
|
+
if (coldMatch) return coldMatch;
|
|
81
|
+
}
|
|
82
|
+
if (storage.readArchivedMemories) {
|
|
83
|
+
const archived = await storage.readArchivedMemories();
|
|
84
|
+
const archivedMatch = archived.find((memory) => memory.frontmatter.id === sourceId);
|
|
85
|
+
if (archivedMatch) return archivedMatch;
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Refresh a propagation event's replacement content from the current store for
|
|
92
|
+
* replay. Returns the event unchanged when it has no replacement; returns
|
|
93
|
+
* `null` when a replacement was expected but is no longer resolvable.
|
|
94
|
+
*/
|
|
95
|
+
export async function readCurrentReplacementEvent(
|
|
96
|
+
storage: DependencyPropagationStorage,
|
|
97
|
+
event: PropagationEvent,
|
|
98
|
+
): Promise<PropagationEvent | null> {
|
|
99
|
+
if (event.replacementId === null) return event;
|
|
100
|
+
const replacement = await findRecoverySource(
|
|
101
|
+
storage as DependencyPropagationRecoveryStorage,
|
|
102
|
+
event.replacementId,
|
|
103
|
+
);
|
|
104
|
+
if (!replacement) return null;
|
|
105
|
+
return {
|
|
106
|
+
...event,
|
|
107
|
+
replacementContent: replacement.content,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Replay the primary mutation a durable job represents against the current
|
|
113
|
+
* store, by cause: temporal supersession, contradiction supersede,
|
|
114
|
+
* consolidation invalidate, or consolidation merge. Idempotent — returns
|
|
115
|
+
* `true` when the mutation is already applied or is applied now, `false` when
|
|
116
|
+
* the source/replacement no longer matches the prepared snapshot (so the caller
|
|
117
|
+
* keeps the job retryable instead of marking it complete).
|
|
118
|
+
*/
|
|
119
|
+
export async function replayPrimaryMutation(
|
|
120
|
+
sourceStorage: DependencyPropagationStorage,
|
|
121
|
+
event: PropagationEvent,
|
|
122
|
+
preparedReplacementFingerprint?: string,
|
|
123
|
+
): Promise<boolean> {
|
|
124
|
+
const storage = sourceStorage as DependencyPropagationRecoveryStorage;
|
|
125
|
+
const source = await findRecoverySource(storage, event.oldMemory.frontmatter.id);
|
|
126
|
+
const primaryApplied =
|
|
127
|
+
source?.frontmatter.status === "superseded" &&
|
|
128
|
+
source.frontmatter.supersededBy === event.replacementId;
|
|
129
|
+
|
|
130
|
+
if (event.cause === "temporal_supersession") {
|
|
131
|
+
if (!event.replacementId || !event.temporalMutation || !source) return false;
|
|
132
|
+
const temporalPrimaryApplied =
|
|
133
|
+
primaryApplied &&
|
|
134
|
+
source.frontmatter.supersededAt === event.temporalMutation.supersededAt &&
|
|
135
|
+
(event.temporalMutation.invalidAt === undefined ||
|
|
136
|
+
source.frontmatter.invalid_at === event.temporalMutation.invalidAt);
|
|
137
|
+
if (!temporalPrimaryApplied) {
|
|
138
|
+
const replacement = await findRecoverySource(storage, event.replacementId);
|
|
139
|
+
if (
|
|
140
|
+
event.replacementContent === null ||
|
|
141
|
+
!replacement ||
|
|
142
|
+
replacement.frontmatter.id !== event.replacementId ||
|
|
143
|
+
replacement.content !== event.replacementContent
|
|
144
|
+
)
|
|
145
|
+
return false;
|
|
146
|
+
if (!matchesPreparedSource(source, event.oldMemory)) return false;
|
|
147
|
+
}
|
|
148
|
+
return await applyTemporalSupersessionPrimaryMutation({
|
|
149
|
+
storage: sourceStorage as unknown as TemporalSupersessionStorage,
|
|
150
|
+
oldMemory: source,
|
|
151
|
+
replacementId: event.replacementId,
|
|
152
|
+
mutation: event.temporalMutation,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (event.cause === "contradiction") {
|
|
157
|
+
if (!event.replacementId || !source) return false;
|
|
158
|
+
if (primaryApplied) return true;
|
|
159
|
+
const replacement = await findRecoverySource(storage, event.replacementId);
|
|
160
|
+
if (
|
|
161
|
+
event.replacementContent === null ||
|
|
162
|
+
!replacement ||
|
|
163
|
+
replacement.frontmatter.id !== event.replacementId ||
|
|
164
|
+
replacement.content !== event.replacementContent
|
|
165
|
+
)
|
|
166
|
+
return false;
|
|
167
|
+
if (!matchesPreparedSource(source, event.oldMemory)) return false;
|
|
168
|
+
return await storage.supersedeMemory(
|
|
169
|
+
event.oldMemory.frontmatter.id,
|
|
170
|
+
event.replacementId,
|
|
171
|
+
`dependency_propagation:${event.cause}`,
|
|
172
|
+
undefined,
|
|
173
|
+
{ requireActive: true, acceptExactReplay: true, expectedSnapshot: source },
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (event.cause === "consolidation_invalidate") {
|
|
178
|
+
if (!source) {
|
|
179
|
+
return storage.hasCommittedInvalidation
|
|
180
|
+
? await storage.hasCommittedInvalidation(event.oldMemory)
|
|
181
|
+
: false;
|
|
182
|
+
}
|
|
183
|
+
if (!storage.invalidateMemory || !matchesPreparedSource(source, event.oldMemory)) return false;
|
|
184
|
+
return await storage.invalidateMemory(source.frontmatter.id, source, {
|
|
185
|
+
recordCommitProof: true,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (
|
|
190
|
+
!event.replacementId ||
|
|
191
|
+
event.replacementContent === null ||
|
|
192
|
+
!storage.updateMemoryIfUnchanged
|
|
193
|
+
) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
const replacement = await findRecoverySource(storage, event.replacementId);
|
|
197
|
+
const persistedReplacementContent = sanitizeMemoryContent(event.replacementContent).text;
|
|
198
|
+
if (
|
|
199
|
+
replacement &&
|
|
200
|
+
replacement.frontmatter.supersedes === event.oldMemory.frontmatter.id &&
|
|
201
|
+
replacement.content !== persistedReplacementContent
|
|
202
|
+
) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
const mergeAlreadyApplied =
|
|
206
|
+
replacement !== null &&
|
|
207
|
+
replacement.frontmatter.supersedes === event.oldMemory.frontmatter.id &&
|
|
208
|
+
replacement.content === persistedReplacementContent;
|
|
209
|
+
if (!source) {
|
|
210
|
+
if (!storage.hasCommittedInvalidation) return false;
|
|
211
|
+
return await storage.hasCommittedInvalidation(event.oldMemory);
|
|
212
|
+
}
|
|
213
|
+
if (
|
|
214
|
+
preparedReplacementFingerprint &&
|
|
215
|
+
source &&
|
|
216
|
+
!mergeAlreadyApplied &&
|
|
217
|
+
(!replacement || memoryFingerprint(replacement) !== preparedReplacementFingerprint)
|
|
218
|
+
) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
if (!replacement || !matchesPreparedSource(source, event.oldMemory)) return false;
|
|
222
|
+
if (!mergeAlreadyApplied) {
|
|
223
|
+
const updated = await storage.updateMemoryIfUnchanged(
|
|
224
|
+
replacement,
|
|
225
|
+
event.replacementContent,
|
|
226
|
+
{
|
|
227
|
+
supersedes: event.oldMemory.frontmatter.id,
|
|
228
|
+
lineage: [event.replacementId, event.oldMemory.frontmatter.id],
|
|
229
|
+
},
|
|
230
|
+
);
|
|
231
|
+
if (!updated) return false;
|
|
232
|
+
}
|
|
233
|
+
if (!storage.invalidateMemory) return false;
|
|
234
|
+
return await storage.invalidateMemory(source.frontmatter.id, source, {
|
|
235
|
+
recordCommitProof: true,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replay reconciliation for dependency-propagation recovery.
|
|
3
|
+
*
|
|
4
|
+
* Reconciles the index effects — reindex the survivor, deindex the superseded
|
|
5
|
+
* set — of a replayed consolidation merge so a recovery pass matches what a
|
|
6
|
+
* normal consolidation MERGE/INVALIDATE performs inline. Host-agnostic: it
|
|
7
|
+
* imports no index backend and never references the orchestrator. The namespace
|
|
8
|
+
* storage lookup, survivor reindex, embedding deindex, and query-aware-indexing
|
|
9
|
+
* gate all flow through core APIs plus the injected
|
|
10
|
+
* {@link DependencyPropagationReplayHost}. Cause-gating mirrors consolidation
|
|
11
|
+
* exactly: reindex runs only for a consolidation_merge survivor; deindex runs
|
|
12
|
+
* for consolidation_merge and consolidation_invalidate superseded memories,
|
|
13
|
+
* dropping embedding visibility first then the temporal index entry. Any
|
|
14
|
+
* rejection propagates so the delivery keeps recovery retryable rather than
|
|
15
|
+
* marking the job safely complete.
|
|
16
|
+
*/
|
|
17
|
+
import { deindexMemoriesBatchAsync } from "../temporal-index.js";
|
|
18
|
+
import { resolveIndexingCapabilities } from "../capabilities.js";
|
|
19
|
+
import type { StorageManager } from "../storage.js";
|
|
20
|
+
import type { PluginConfig } from "../types.js";
|
|
21
|
+
import type { DependencyPropagationReplayReconciliationPort } from "./dependency-propagation-delivery.js";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Narrow host port: the index primitives a recovery replay needs. The host
|
|
25
|
+
* (orchestrator) supplies them; this module owns the cause-gating and
|
|
26
|
+
* deindex/reindex ordering and imports no index backend of its own.
|
|
27
|
+
*/
|
|
28
|
+
export interface DependencyPropagationReplayHost {
|
|
29
|
+
/** Resolve the storage owning a propagation event's namespace scope. */
|
|
30
|
+
getStorage(namespace: string): Promise<StorageManager>;
|
|
31
|
+
/** Reindex/refresh a still-active survivor's index visibility. */
|
|
32
|
+
indexPersistedMemory(storage: StorageManager, memoryId: string): Promise<void>;
|
|
33
|
+
/** Remove a superseded memory's stale embedding index entry. */
|
|
34
|
+
removeFromIndex(memoryId: string): Promise<void>;
|
|
35
|
+
/** Host config; read for the query-aware-indexing gate. */
|
|
36
|
+
readonly config: PluginConfig;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Build the replay-reconciliation port bound to a host's index primitives. The
|
|
41
|
+
* returned port is handed to {@link DependencyPropagationDelivery} as
|
|
42
|
+
* `replayReconciliation`; it is inert until the delivery replays a job.
|
|
43
|
+
*/
|
|
44
|
+
export function createDependencyPropagationReplayReconciliation(
|
|
45
|
+
host: DependencyPropagationReplayHost,
|
|
46
|
+
): DependencyPropagationReplayReconciliationPort {
|
|
47
|
+
return {
|
|
48
|
+
reindex: async (event, survivorId) => {
|
|
49
|
+
if (event.cause !== "consolidation_merge") return;
|
|
50
|
+
const storage = await host.getStorage(event.namespaceScope);
|
|
51
|
+
await host.indexPersistedMemory(storage, survivorId);
|
|
52
|
+
},
|
|
53
|
+
deindex: async (event, memoryIds) => {
|
|
54
|
+
if (
|
|
55
|
+
event.cause !== "consolidation_merge" &&
|
|
56
|
+
event.cause !== "consolidation_invalidate"
|
|
57
|
+
) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
await Promise.all(
|
|
61
|
+
memoryIds.map((memoryId) => host.removeFromIndex(memoryId)),
|
|
62
|
+
);
|
|
63
|
+
if (!resolveIndexingCapabilities(host.config).queryAwareIndexing) return;
|
|
64
|
+
if (!event.oldMemory.path || !event.oldMemory.frontmatter.created) return;
|
|
65
|
+
const storage = await host.getStorage(event.namespaceScope);
|
|
66
|
+
await deindexMemoriesBatchAsync(storage.dir, [{
|
|
67
|
+
path: event.oldMemory.path,
|
|
68
|
+
createdAt: event.oldMemory.frontmatter.created,
|
|
69
|
+
tags: event.oldMemory.frontmatter.tags ?? [],
|
|
70
|
+
}]);
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|