@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
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import { mkdtemp, rm } from "node:fs/promises";
|
|
2
|
+
import { mkdtemp, rm, stat } from "node:fs/promises";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import test from "node:test";
|
|
6
6
|
import { parseConfig } from "./config.js";
|
|
7
7
|
import { Orchestrator } from "./orchestrator.js";
|
|
8
|
+
type ShutdownTestSurface = {
|
|
9
|
+
extractionQueueCoordinator: { pauseAndDrain(): Promise<boolean> };
|
|
10
|
+
dependencyPropagationDelivery: { shutdown(): Promise<void> };
|
|
11
|
+
};
|
|
12
|
+
function stubOrchestratorInit(orchestrator: Orchestrator): void {
|
|
13
|
+
const initCoordinator = (orchestrator as unknown as {
|
|
14
|
+
orchestratorInitCoordinator: { initialize: () => Promise<void> };
|
|
15
|
+
}).orchestratorInitCoordinator;
|
|
16
|
+
initCoordinator.initialize = async () => {};
|
|
17
|
+
}
|
|
8
18
|
|
|
9
19
|
test("destroy cancels maintenance and waits for tracked writes before disposing search backends", async () => {
|
|
10
20
|
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-background-writes-"));
|
|
@@ -53,6 +63,260 @@ test("destroy cancels maintenance and waits for tracked writes before disposing
|
|
|
53
63
|
}
|
|
54
64
|
});
|
|
55
65
|
|
|
66
|
+
test("disabled propagation does not construct the lazy delivery during initialize", async () => {
|
|
67
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-disabled-propagation-"));
|
|
68
|
+
const orchestrator = new Orchestrator(
|
|
69
|
+
parseConfig({
|
|
70
|
+
openaiApiKey: "sk-test",
|
|
71
|
+
memoryDir,
|
|
72
|
+
workspaceDir: path.join(memoryDir, "workspace"),
|
|
73
|
+
qmdEnabled: false,
|
|
74
|
+
knowledgeIndexEnabled: false,
|
|
75
|
+
identityContinuityEnabled: false,
|
|
76
|
+
transcriptEnabled: false,
|
|
77
|
+
hourlySummariesEnabled: false,
|
|
78
|
+
compoundingEnabled: false,
|
|
79
|
+
}),
|
|
80
|
+
);
|
|
81
|
+
stubOrchestratorInit(orchestrator);
|
|
82
|
+
const internals = orchestrator as unknown as {
|
|
83
|
+
_dependencyPropagationDelivery: unknown;
|
|
84
|
+
extractionPersistCoordinator: { dependencyPropagationDelivery: unknown };
|
|
85
|
+
};
|
|
86
|
+
try {
|
|
87
|
+
assert.equal(internals._dependencyPropagationDelivery, undefined);
|
|
88
|
+
assert.equal(internals.extractionPersistCoordinator.dependencyPropagationDelivery, undefined);
|
|
89
|
+
await orchestrator.initialize();
|
|
90
|
+
assert.equal(internals._dependencyPropagationDelivery, undefined);
|
|
91
|
+
assert.equal(internals.extractionPersistCoordinator.dependencyPropagationDelivery, undefined);
|
|
92
|
+
} finally {
|
|
93
|
+
await orchestrator.destroy();
|
|
94
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("destroy waits for startup propagation recovery before shutdown", async () => {
|
|
99
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-recovery-shutdown-"));
|
|
100
|
+
const orchestrator = new Orchestrator(
|
|
101
|
+
parseConfig({
|
|
102
|
+
openaiApiKey: "sk-test",
|
|
103
|
+
memoryDir,
|
|
104
|
+
workspaceDir: path.join(memoryDir, "workspace"),
|
|
105
|
+
qmdEnabled: false,
|
|
106
|
+
dependencyPropagation: {
|
|
107
|
+
enabled: true,
|
|
108
|
+
linkTypes: ["supports"],
|
|
109
|
+
maxDependents: 10,
|
|
110
|
+
timeoutMs: 100,
|
|
111
|
+
dryRun: false,
|
|
112
|
+
},
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
115
|
+
stubOrchestratorInit(orchestrator);
|
|
116
|
+
const recovery = Promise.withResolvers<void>();
|
|
117
|
+
let shutdown = false;
|
|
118
|
+
Reflect.set(orchestrator, "_dependencyPropagationDelivery", {
|
|
119
|
+
recover: () => recovery.promise,
|
|
120
|
+
shutdown: async () => {
|
|
121
|
+
shutdown = true;
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
let destroyPromise: Promise<void> | undefined;
|
|
126
|
+
try {
|
|
127
|
+
await orchestrator.initialize();
|
|
128
|
+
let destroySettled = false;
|
|
129
|
+
destroyPromise = orchestrator.destroy().then(() => {
|
|
130
|
+
destroySettled = true;
|
|
131
|
+
});
|
|
132
|
+
await new Promise<void>((resolve) => setImmediate(resolve));
|
|
133
|
+
assert.equal(destroySettled, false, "destroy must await startup recovery");
|
|
134
|
+
assert.equal(shutdown, false, "delivery must remain live during recovery");
|
|
135
|
+
|
|
136
|
+
recovery.resolve();
|
|
137
|
+
await destroyPromise;
|
|
138
|
+
assert.equal(shutdown, true);
|
|
139
|
+
} finally {
|
|
140
|
+
recovery.resolve();
|
|
141
|
+
await (destroyPromise ?? orchestrator.destroy());
|
|
142
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("enabled propagation recovery rejection does not block initialize", async () => {
|
|
147
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-recovery-rejection-"));
|
|
148
|
+
const orchestrator = new Orchestrator(
|
|
149
|
+
parseConfig({
|
|
150
|
+
openaiApiKey: "sk-test",
|
|
151
|
+
memoryDir,
|
|
152
|
+
workspaceDir: path.join(memoryDir, "workspace"),
|
|
153
|
+
qmdEnabled: false,
|
|
154
|
+
dependencyPropagation: {
|
|
155
|
+
enabled: true,
|
|
156
|
+
linkTypes: ["supports"],
|
|
157
|
+
maxDependents: 10,
|
|
158
|
+
timeoutMs: 100,
|
|
159
|
+
dryRun: false,
|
|
160
|
+
},
|
|
161
|
+
}),
|
|
162
|
+
);
|
|
163
|
+
stubOrchestratorInit(orchestrator);
|
|
164
|
+
let recoverCalls = 0;
|
|
165
|
+
Reflect.set(orchestrator, "_dependencyPropagationDelivery", {
|
|
166
|
+
recover: async () => {
|
|
167
|
+
recoverCalls += 1;
|
|
168
|
+
throw new Error("synthetic recovery failure");
|
|
169
|
+
},
|
|
170
|
+
shutdown: async () => {},
|
|
171
|
+
});
|
|
172
|
+
try {
|
|
173
|
+
await orchestrator.initialize();
|
|
174
|
+
assert.equal(recoverCalls, 1);
|
|
175
|
+
} finally {
|
|
176
|
+
await orchestrator.destroy();
|
|
177
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("enabled propagation recovery tolerates a missing queue directory", async () => {
|
|
182
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-recovery-no-queue-"));
|
|
183
|
+
const orchestrator = new Orchestrator(
|
|
184
|
+
parseConfig({
|
|
185
|
+
openaiApiKey: "sk-test",
|
|
186
|
+
memoryDir,
|
|
187
|
+
workspaceDir: path.join(memoryDir, "workspace"),
|
|
188
|
+
qmdEnabled: false,
|
|
189
|
+
dependencyPropagation: {
|
|
190
|
+
enabled: true,
|
|
191
|
+
linkTypes: ["supports"],
|
|
192
|
+
maxDependents: 10,
|
|
193
|
+
timeoutMs: 100,
|
|
194
|
+
dryRun: false,
|
|
195
|
+
},
|
|
196
|
+
}),
|
|
197
|
+
);
|
|
198
|
+
stubOrchestratorInit(orchestrator);
|
|
199
|
+
const queueRoot = path.join(memoryDir, "state", "dependency-propagation");
|
|
200
|
+
try {
|
|
201
|
+
await orchestrator.initialize();
|
|
202
|
+
const recovery = (orchestrator as unknown as {
|
|
203
|
+
_dependencyPropagationRecovery: Promise<void> | null;
|
|
204
|
+
})._dependencyPropagationRecovery;
|
|
205
|
+
assert.ok(recovery, "enabled initialization must retain its recovery promise");
|
|
206
|
+
await recovery;
|
|
207
|
+
await assert.rejects(() => stat(queueRoot), /ENOENT/);
|
|
208
|
+
} finally {
|
|
209
|
+
await orchestrator.destroy();
|
|
210
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("destroy drains consolidation producers before propagation delivery", async () => {
|
|
215
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-destroy-propagation-order-"));
|
|
216
|
+
const orchestrator = new Orchestrator(
|
|
217
|
+
parseConfig({
|
|
218
|
+
openaiApiKey: "sk-test",
|
|
219
|
+
memoryDir,
|
|
220
|
+
workspaceDir: path.join(memoryDir, "workspace"),
|
|
221
|
+
qmdEnabled: false,
|
|
222
|
+
knowledgeIndexEnabled: false,
|
|
223
|
+
identityContinuityEnabled: false,
|
|
224
|
+
transcriptEnabled: false,
|
|
225
|
+
hourlySummariesEnabled: false,
|
|
226
|
+
compoundingEnabled: false,
|
|
227
|
+
}),
|
|
228
|
+
);
|
|
229
|
+
const order: string[] = [];
|
|
230
|
+
const scheduler = (orchestrator as unknown as { maintenanceScheduler: { dispose(): Promise<void> } })
|
|
231
|
+
.maintenanceScheduler;
|
|
232
|
+
const extractionQueue = (orchestrator as unknown as { extractionQueueCoordinator: { waitForIdle(): Promise<boolean> } })
|
|
233
|
+
.extractionQueueCoordinator;
|
|
234
|
+
const delivery = (orchestrator as unknown as { dependencyPropagationDelivery: { shutdown(): Promise<void> } })
|
|
235
|
+
.dependencyPropagationDelivery;
|
|
236
|
+
const originalSchedulerDispose = scheduler.dispose.bind(scheduler);
|
|
237
|
+
const originalExtractionWait = extractionQueue.waitForIdle.bind(extractionQueue);
|
|
238
|
+
const originalDeliveryShutdown = delivery.shutdown.bind(delivery);
|
|
239
|
+
scheduler.dispose = async () => {
|
|
240
|
+
order.push("scheduler");
|
|
241
|
+
await originalSchedulerDispose();
|
|
242
|
+
};
|
|
243
|
+
extractionQueue.waitForIdle = async () => {
|
|
244
|
+
order.push("extraction");
|
|
245
|
+
return originalExtractionWait();
|
|
246
|
+
};
|
|
247
|
+
delivery.shutdown = async () => {
|
|
248
|
+
order.push("delivery");
|
|
249
|
+
await originalDeliveryShutdown();
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
await orchestrator.destroy();
|
|
254
|
+
assert.deepEqual(order.slice(0, 3), ["scheduler", "extraction", "delivery"]);
|
|
255
|
+
const internals = orchestrator as unknown as {
|
|
256
|
+
_dependencyPropagationDelivery: unknown;
|
|
257
|
+
dependencyPropagationDelivery: unknown;
|
|
258
|
+
};
|
|
259
|
+
assert.equal(internals._dependencyPropagationDelivery, undefined);
|
|
260
|
+
assert.throws(
|
|
261
|
+
() => internals.dependencyPropagationDelivery,
|
|
262
|
+
/orchestrator has been destroyed/,
|
|
263
|
+
"destroyed orchestrators must not recreate delivery",
|
|
264
|
+
);
|
|
265
|
+
} finally {
|
|
266
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
test("destroy continues cleanup when extraction drain times out", async () => {
|
|
270
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-destroy-propagation-timeout-"));
|
|
271
|
+
const orchestrator = new Orchestrator(
|
|
272
|
+
parseConfig({
|
|
273
|
+
openaiApiKey: "sk-test",
|
|
274
|
+
memoryDir,
|
|
275
|
+
workspaceDir: path.join(memoryDir, "workspace"),
|
|
276
|
+
qmdEnabled: false,
|
|
277
|
+
knowledgeIndexEnabled: false,
|
|
278
|
+
identityContinuityEnabled: false,
|
|
279
|
+
transcriptEnabled: false,
|
|
280
|
+
hourlySummariesEnabled: false,
|
|
281
|
+
compoundingEnabled: false,
|
|
282
|
+
}),
|
|
283
|
+
);
|
|
284
|
+
const internals = orchestrator as unknown as ShutdownTestSurface & {
|
|
285
|
+
_dependencyPropagationDelivery: unknown;
|
|
286
|
+
qmd: { dispose?: () => void | Promise<void> };
|
|
287
|
+
};
|
|
288
|
+
const extractionQueue = internals.extractionQueueCoordinator;
|
|
289
|
+
const delivery = internals.dependencyPropagationDelivery;
|
|
290
|
+
let shutdownCalls = 0;
|
|
291
|
+
let qmdDisposed = false;
|
|
292
|
+
const originalShutdown = delivery.shutdown.bind(delivery);
|
|
293
|
+
const originalQmdDispose = internals.qmd.dispose?.bind(internals.qmd);
|
|
294
|
+
delivery.shutdown = async () => {
|
|
295
|
+
shutdownCalls += 1;
|
|
296
|
+
await originalShutdown();
|
|
297
|
+
};
|
|
298
|
+
internals.qmd.dispose = async () => {
|
|
299
|
+
qmdDisposed = true;
|
|
300
|
+
await originalQmdDispose?.();
|
|
301
|
+
};
|
|
302
|
+
extractionQueue.pauseAndDrain = async () => false;
|
|
303
|
+
|
|
304
|
+
try {
|
|
305
|
+
await assert.rejects(
|
|
306
|
+
orchestrator.destroy(),
|
|
307
|
+
/extraction queue did not drain before teardown/,
|
|
308
|
+
);
|
|
309
|
+
assert.equal(shutdownCalls, 1, "delivery shutdown must run after a drain timeout");
|
|
310
|
+
assert.equal(internals._dependencyPropagationDelivery, undefined);
|
|
311
|
+
assert.equal(qmdDisposed, true, "later cleanup must run before the timeout is rethrown");
|
|
312
|
+
} finally {
|
|
313
|
+
extractionQueue.pauseAndDrain = async () => true;
|
|
314
|
+
await orchestrator.destroy();
|
|
315
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
|
|
56
320
|
test("destroy waits for a recall-initiated last-recall write", async () => {
|
|
57
321
|
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-recall-background-write-"));
|
|
58
322
|
const orchestrator = new Orchestrator(
|
|
@@ -183,3 +183,42 @@ test("logExtractionQueueFailure names the right layer (task vs processor)", asyn
|
|
|
183
183
|
),
|
|
184
184
|
);
|
|
185
185
|
});
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
test("pauseAndDrain waits for accepted work after its observation timeout", async () => {
|
|
189
|
+
const coordinator = createCoordinator();
|
|
190
|
+
const started = Promise.withResolvers<void>();
|
|
191
|
+
const release = Promise.withResolvers<void>();
|
|
192
|
+
const waitForIdle = coordinator.waitForIdle.bind(coordinator);
|
|
193
|
+
let waitCalls = 0;
|
|
194
|
+
let drainSettled = false;
|
|
195
|
+
coordinator.waitForIdle = async (timeoutMs?: number): Promise<boolean> => {
|
|
196
|
+
waitCalls += 1;
|
|
197
|
+
if (waitCalls === 1) return false;
|
|
198
|
+
return waitForIdle(timeoutMs);
|
|
199
|
+
};
|
|
200
|
+
coordinator.enqueue(async () => {
|
|
201
|
+
started.resolve();
|
|
202
|
+
await release.promise;
|
|
203
|
+
});
|
|
204
|
+
await started.promise;
|
|
205
|
+
const drain = coordinator.pauseAndDrain().then((result) => {
|
|
206
|
+
drainSettled = true;
|
|
207
|
+
return result;
|
|
208
|
+
});
|
|
209
|
+
await Promise.resolve();
|
|
210
|
+
assert.equal(drainSettled, false);
|
|
211
|
+
release.resolve();
|
|
212
|
+
assert.equal(await drain, true);
|
|
213
|
+
assert.equal(waitCalls, 2);
|
|
214
|
+
assert.equal(coordinator.isProcessing, false);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test("stopAccepting rejects new producers until resume", async () => {
|
|
218
|
+
const coordinator = createCoordinator();
|
|
219
|
+
coordinator.stopAccepting();
|
|
220
|
+
assert.equal(coordinator.enqueue(async () => {}), false);
|
|
221
|
+
coordinator.resumeAccepting();
|
|
222
|
+
assert.equal(coordinator.enqueue(async () => {}), true);
|
|
223
|
+
assert.equal(await coordinator.pauseAndDrain(), true);
|
|
224
|
+
});
|
package/src/orchestrator.ts
CHANGED
|
@@ -122,6 +122,13 @@ import {
|
|
|
122
122
|
} from "./orchestration/extraction-run.js";
|
|
123
123
|
import { ConsolidationRunCoordinator } from "./orchestration/consolidation-run.js";
|
|
124
124
|
import { ExtractionPersistCoordinator } from "./orchestration/extraction-persist.js";
|
|
125
|
+
import { drainExtractionAndShutdownDependencyPropagation } from "./orchestration/extraction-shutdown.js";
|
|
126
|
+
import {
|
|
127
|
+
DependencyPropagationDelivery,
|
|
128
|
+
type DependencyPropagationDeliveryPort,
|
|
129
|
+
} from "./orchestration/dependency-propagation-delivery.js";
|
|
130
|
+
import { isDependencyPropagationEnabled } from "./orchestration/dependency-propagation.js";
|
|
131
|
+
import { createDependencyPropagationReplayReconciliation } from "./orchestration/dependency-propagation-replay-reconciliation.js";
|
|
125
132
|
import { RecallInternalCoordinator } from "./orchestration/recall-internal.js";
|
|
126
133
|
import { RecallSearchPipelineCoordinator } from "./orchestration/recall-search-pipeline.js";
|
|
127
134
|
import type { GraphRecallExpansionOptions, GraphRecallExpansionResult } from "./orchestration/graph-recall-seam.js";
|
|
@@ -371,7 +378,6 @@ import { resolveNamespaceCapabilities,
|
|
|
371
378
|
resolveCapabilities,
|
|
372
379
|
resolveGraphConstructionCapabilities,
|
|
373
380
|
resolveMemoryLifecycleCapabilities,
|
|
374
|
-
resolveIndexingCapabilities,
|
|
375
381
|
resolveCreationMemoryCapabilities,
|
|
376
382
|
type CapabilitySet,
|
|
377
383
|
type GraphConstructionCapabilitySet,
|
|
@@ -733,26 +739,30 @@ export class Orchestrator {
|
|
|
733
739
|
suppressedReasonCounts: Record<string, number>;
|
|
734
740
|
} = { detected: 0, queued: 0, autoApplied: 0, suppressedReasonCounts: {} };
|
|
735
741
|
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
742
|
+
private _dependencyPropagationDelivery: DependencyPropagationDelivery | undefined;
|
|
743
|
+
private _dependencyPropagationRecovery: Promise<void> | null = null;
|
|
744
|
+
private destroyed = false;
|
|
745
|
+
|
|
746
|
+
private get dependencyPropagationDelivery(): DependencyPropagationDelivery {
|
|
747
|
+
if (this.destroyed) throw new Error("orchestrator has been destroyed");
|
|
748
|
+
if (!this._dependencyPropagationDelivery) {
|
|
749
|
+
this._dependencyPropagationDelivery = new DependencyPropagationDelivery({
|
|
750
|
+
queueRoot: path.join(this.config.memoryDir, "state", "dependency-propagation"),
|
|
751
|
+
config: this.config,
|
|
752
|
+
extraction: this.extraction,
|
|
753
|
+
getStorage: (namespace) => this.getStorage(namespace),
|
|
754
|
+
readQueueFile: (filePath) => this.storage.readDependencyPropagationQueueFile(filePath),
|
|
755
|
+
writeQueueFile: (filePath, content) => this.storage.writeDependencyPropagationQueueFile(filePath, content),
|
|
756
|
+
autoStart: true,
|
|
757
|
+
replayReconciliation: createDependencyPropagationReplayReconciliation({ getStorage: (namespace) => this.getStorage(namespace), indexPersistedMemory: (storage, memoryId) => this.indexPersistedMemory(storage, memoryId), removeFromIndex: (memoryId) => this.embeddingFallback.removeFromIndex(memoryId), config: this.config }),
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
return this._dependencyPropagationDelivery;
|
|
761
|
+
}
|
|
762
|
+
|
|
743
763
|
readonly extractionQueueCoordinator: ExtractionQueueCoordinator;
|
|
744
|
-
/**
|
|
745
|
-
* Extraction-run pipeline coordinator (issue #1526 seam 15). Owns
|
|
746
|
-
* `runExtraction` + dedupe fingerprint helpers + processed-fingerprint
|
|
747
|
-
* recording. The orchestrator delegates and injects its own methods.
|
|
748
|
-
*/
|
|
749
764
|
private _extractionRunCoordinator: ExtractionRunCoordinator | undefined;
|
|
750
765
|
|
|
751
|
-
/**
|
|
752
|
-
* Lazy getter: creates the ExtractionRunCoordinator on first access using
|
|
753
|
-
* the orchestrator's live field references. Supports Object.create(prototype)
|
|
754
|
-
* tests that set fields post-construction without invoking the constructor.
|
|
755
|
-
*/
|
|
756
766
|
private get extractionRunCoordinator(): ExtractionRunCoordinator {
|
|
757
767
|
if (!this._extractionRunCoordinator) {
|
|
758
768
|
this._extractionRunCoordinator = new ExtractionRunCoordinator({
|
|
@@ -779,15 +789,6 @@ export class Orchestrator {
|
|
|
779
789
|
}
|
|
780
790
|
return this._extractionRunCoordinator;
|
|
781
791
|
}
|
|
782
|
-
/**
|
|
783
|
-
* Consolidation-run coordinator (issue #1526 seam 17). Owns
|
|
784
|
-
* the full consolidation maintenance pass (LLM merge/invalidate/update,
|
|
785
|
-
* entity merge, commitment/TTL cleanup, lifecycle policy, compression
|
|
786
|
-
* guideline learning, tier migration, fact archival, semantic consolidation,
|
|
787
|
-
* identity consolidation, profile consolidation, summarization, topic
|
|
788
|
-
* extraction, TMT rebuild). The orchestrator delegates and injects
|
|
789
|
-
* its own methods + coordinators.
|
|
790
|
-
*/
|
|
791
792
|
private _consolidationRunCoordinator: ConsolidationRunCoordinator | undefined;
|
|
792
793
|
private get consolidationRunCoordinator(): ConsolidationRunCoordinator {
|
|
793
794
|
if (!this._consolidationRunCoordinator) {
|
|
@@ -797,6 +798,8 @@ export class Orchestrator {
|
|
|
797
798
|
getStorageRouter: () => this.storageRouter,
|
|
798
799
|
getNamespaceCatalog: () => this.namespaceCatalog,
|
|
799
800
|
getExtraction: () => this.extraction,
|
|
801
|
+
storageDirNamespace: (storageDir) => this.storageDirNamespace(storageDir),
|
|
802
|
+
getDependencyPropagationDelivery: () => this.dependencyPropagationDelivery,
|
|
800
803
|
embeddingFallback: this.embeddingFallback,
|
|
801
804
|
tmtBuilder: this.tmtBuilder,
|
|
802
805
|
consolidationObservers: this.consolidationObservers,
|
|
@@ -816,11 +819,6 @@ export class Orchestrator {
|
|
|
816
819
|
return this._consolidationRunCoordinator;
|
|
817
820
|
}
|
|
818
821
|
|
|
819
|
-
/**
|
|
820
|
-
* Extraction-persist coordinator (issue #1526 seam 16). Owns the
|
|
821
|
-
* `persistExtraction` pipeline. Lazy: created on first access so
|
|
822
|
-
* Object.create(prototype) tests that set fields post-construction work.
|
|
823
|
-
*/
|
|
824
822
|
private _extractionPersistCoordinator: ExtractionPersistCoordinator | undefined;
|
|
825
823
|
|
|
826
824
|
private get extractionPersistCoordinator(): ExtractionPersistCoordinator {
|
|
@@ -829,8 +827,8 @@ export class Orchestrator {
|
|
|
829
827
|
config: this.config,
|
|
830
828
|
getStorageRouter: () => this.storageRouter,
|
|
831
829
|
getThreading: () => this.threading,
|
|
830
|
+
getDependencyPropagationDelivery: () => this.dependencyPropagationDelivery,
|
|
832
831
|
getLocalLlm: () => this.localLlm,
|
|
833
|
-
getExtraction: () => this.extraction,
|
|
834
832
|
getQmd: () => this.qmd,
|
|
835
833
|
getJudgeVerdictCache: () => this.judgeVerdictCache,
|
|
836
834
|
getJudgeDeferCounts: () => this.judgeDeferCounts,
|
|
@@ -960,36 +958,26 @@ export class Orchestrator {
|
|
|
960
958
|
*/
|
|
961
959
|
async destroy(): Promise<void> {
|
|
962
960
|
this.abortDeferredInit();
|
|
961
|
+
this.extractionQueueCoordinator.stopAccepting();
|
|
963
962
|
if (this.wearablesAutoSyncHandle) {
|
|
964
|
-
// Aborts in-flight provider fetches and waits for the tick to
|
|
965
|
-
// settle, so nothing is writing or reindexing past destroy().
|
|
966
963
|
await this.wearablesAutoSyncHandle.stop();
|
|
967
964
|
this.wearablesAutoSyncHandle = null;
|
|
968
965
|
}
|
|
969
966
|
for (const svc of this.meetingsServiceByNamespace.values()) svc.dispose();
|
|
970
967
|
await this.maintenanceScheduler.dispose();
|
|
968
|
+
await this._dependencyPropagationRecovery;
|
|
969
|
+
this._dependencyPropagationRecovery = null;
|
|
970
|
+
const extractionDrainError = await drainExtractionAndShutdownDependencyPropagation(
|
|
971
|
+
this.extractionQueueCoordinator,
|
|
972
|
+
this._dependencyPropagationDelivery,
|
|
973
|
+
);
|
|
974
|
+
this.destroyed = true;
|
|
975
|
+
this._dependencyPropagationDelivery = undefined;
|
|
971
976
|
await drainRecallWrites(this);
|
|
972
|
-
// PR #2016 finding 3: drain any deferred lock-timeout hash-index retries so a
|
|
973
|
-
// short-lived writer's durable fact hash reaches disk before the process
|
|
974
|
-
// exits. The per-index background retry is unref'd (it never keeps a
|
|
975
|
-
// long-lived host alive), so a one-shot CLI could exit before it fires; this
|
|
976
|
-
// drives it to completion inline at the shutdown boundary. Best-effort.
|
|
977
977
|
await this.persistenceIndexCoordinator
|
|
978
978
|
.drainContentHashReconcileRetries()
|
|
979
979
|
.catch((err) => log.warn(`content-hash reconcile drain failed during destroy: ${err}`));
|
|
980
|
-
//
|
|
981
|
-
// flushing catalog touches (review round 11 finding 2). The buffer save fires
|
|
982
|
-
// a coalesced namespace-catalog touch on an unref'd timer; flushing touches
|
|
983
|
-
// first would let that shutdown-time touch queue after the flush and be lost.
|
|
984
|
-
// Ordering it before flushPendingTouches folds the buffer-save's touch into
|
|
985
|
-
// the flush below so both settle before destroy() returns.
|
|
986
|
-
//
|
|
987
|
-
// Graceful-shutdown durability contract (review round 14): the flush runs
|
|
988
|
-
// with throwOnFailure so a failed buffer write is NOT silently swallowed.
|
|
989
|
-
// flushPendingSave keeps the save pending on failure (in-memory turns are
|
|
990
|
-
// retained), so we finish the rest of teardown in a finally block and then
|
|
991
|
-
// rethrow — the host learns buffered turns did not reach disk instead of
|
|
992
|
-
// destroy() reporting a clean shutdown and losing them on exit.
|
|
980
|
+
// Save buffered turns before catalog flush; rethrow save failures after teardown.
|
|
993
981
|
let bufferFlushError: unknown;
|
|
994
982
|
try {
|
|
995
983
|
await this.buffer.flushPendingSave({ throwOnFailure: true });
|
|
@@ -997,8 +985,7 @@ export class Orchestrator {
|
|
|
997
985
|
bufferFlushError = err;
|
|
998
986
|
}
|
|
999
987
|
try {
|
|
1000
|
-
//
|
|
1001
|
-
// so a long-lived host does not drop buffered read/write timestamps.
|
|
988
|
+
// Flush catalog touches before backend disposal.
|
|
1002
989
|
await this.namespaceCatalog.flushPendingTouches().catch(() => undefined);
|
|
1003
990
|
this.prioritizedEmbedding?.dispose();
|
|
1004
991
|
await this.namespaceSearchRouter.dispose();
|
|
@@ -1013,6 +1000,12 @@ export class Orchestrator {
|
|
|
1013
1000
|
);
|
|
1014
1001
|
throw bufferFlushError;
|
|
1015
1002
|
}
|
|
1003
|
+
if (extractionDrainError !== undefined) {
|
|
1004
|
+
log.warn(
|
|
1005
|
+
`orchestrator.destroy: extraction queue did not drain before teardown: ${String(extractionDrainError)}`,
|
|
1006
|
+
);
|
|
1007
|
+
throw extractionDrainError;
|
|
1008
|
+
}
|
|
1016
1009
|
}
|
|
1017
1010
|
}
|
|
1018
1011
|
|
|
@@ -1297,7 +1290,6 @@ export class Orchestrator {
|
|
|
1297
1290
|
);
|
|
1298
1291
|
}
|
|
1299
1292
|
|
|
1300
|
-
|
|
1301
1293
|
invalidateLiveContentHashIndex(): void {
|
|
1302
1294
|
this.contentHashIndex = null;
|
|
1303
1295
|
this.contentHashIndexesByStorageDir.clear();
|
|
@@ -1551,6 +1543,8 @@ export class Orchestrator {
|
|
|
1551
1543
|
namespaceFromPath: (p) => this.namespaceFromPath(p),
|
|
1552
1544
|
storageForNamespace: (namespace) => this.storageRouter.storageFor(namespace),
|
|
1553
1545
|
getExtraction: () => this.extraction,
|
|
1546
|
+
storageDirNamespace: (storageDir) => this.storageDirNamespace(storageDir),
|
|
1547
|
+
getDependencyPropagationDelivery: () => this.dependencyPropagationDelivery,
|
|
1554
1548
|
});
|
|
1555
1549
|
this.modelRegistry = new ModelRegistry(config.memoryDir);
|
|
1556
1550
|
this.graphRecallCoordinator = new GraphRecallCoordinator({
|
|
@@ -1957,8 +1951,15 @@ export class Orchestrator {
|
|
|
1957
1951
|
}
|
|
1958
1952
|
|
|
1959
1953
|
async initialize(): Promise<void> {
|
|
1960
|
-
|
|
1961
|
-
);
|
|
1954
|
+
this.extractionQueueCoordinator.resumeAccepting();
|
|
1955
|
+
await this.orchestratorInitCoordinator.initialize();
|
|
1956
|
+
if (isDependencyPropagationEnabled(this.config)) {
|
|
1957
|
+
this._dependencyPropagationRecovery = this.dependencyPropagationDelivery
|
|
1958
|
+
.recover()
|
|
1959
|
+
.catch((err) => {
|
|
1960
|
+
log.warn(`dependency propagation startup recovery failed: ${err}`);
|
|
1961
|
+
});
|
|
1962
|
+
}
|
|
1962
1963
|
}
|
|
1963
1964
|
|
|
1964
1965
|
private async deferredInitialize(signal: AbortSignal): Promise<void> {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { log } from "../logger.js";
|
|
2
|
+
|
|
3
|
+
export interface CommittedInvalidationCallbacks {
|
|
4
|
+
memoryId: string;
|
|
5
|
+
recordProof: (() => Promise<void>) | undefined;
|
|
6
|
+
clearProof: (() => Promise<void>) | undefined;
|
|
7
|
+
deleteMemory: () => Promise<boolean>;
|
|
8
|
+
afterDelete: () => Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function clearProofFailOpen(callbacks: CommittedInvalidationCallbacks): Promise<void> {
|
|
12
|
+
await callbacks.clearProof?.().catch((error) => {
|
|
13
|
+
log.warn(`failed to clear invalidation proof for ${callbacks.memoryId}: ${error}`);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function runCommittedInvalidation(
|
|
18
|
+
callbacks: CommittedInvalidationCallbacks,
|
|
19
|
+
): Promise<boolean> {
|
|
20
|
+
let deletionCompleted = false;
|
|
21
|
+
try {
|
|
22
|
+
await callbacks.recordProof?.();
|
|
23
|
+
if (!await callbacks.deleteMemory()) {
|
|
24
|
+
await clearProofFailOpen(callbacks);
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
deletionCompleted = true;
|
|
28
|
+
await callbacks.afterDelete();
|
|
29
|
+
return true;
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (!deletionCompleted) await clearProofFailOpen(callbacks);
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|