@remnic/core 9.3.640 → 9.3.642
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-cli.js +16 -16
- package/dist/access-http.d.ts +5 -5
- package/dist/access-http.js +9 -9
- package/dist/access-mcp.d.ts +5 -5
- package/dist/access-mcp.js +8 -8
- package/dist/{access-service-jyYute38.d.ts → access-service-DFXIlGvZ.d.ts} +2 -2
- package/dist/access-service.d.ts +5 -5
- package/dist/access-service.js +7 -7
- package/dist/action-confidence.d.ts +2 -2
- package/dist/active-memory-bridge.d.ts +2 -2
- package/dist/active-recall.d.ts +2 -2
- package/dist/behavior-learner.d.ts +2 -2
- package/dist/behavior-signals.d.ts +2 -2
- package/dist/bootstrap.d.ts +4 -4
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +3 -3
- package/dist/buffer-surprise-report.d.ts +2 -2
- package/dist/buffer.d.ts +2 -2
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/bulk-import/index.js +2 -2
- package/dist/calibration.d.ts +2 -2
- package/dist/causal-behavior.d.ts +2 -2
- package/dist/causal-consolidation.d.ts +2 -2
- package/dist/causal-consolidation.js +4 -4
- package/dist/{chunk-WAKPKNEX.js → chunk-3UR4SBZF.js} +307 -70
- package/dist/chunk-3UR4SBZF.js.map +1 -0
- package/dist/{chunk-RJGPH2Q3.js → chunk-4HYLRROV.js} +2 -2
- package/dist/{chunk-3AZDLWDW.js → chunk-4HYWWO3W.js} +3 -3
- package/dist/{chunk-ISMZIMD3.js → chunk-4MNDVDBS.js} +2 -2
- package/dist/{chunk-JFHEEDRY.js → chunk-5IDVOWBE.js} +3 -3
- package/dist/{chunk-4GYGEKUB.js → chunk-6DDBW2V4.js} +4 -4
- package/dist/{chunk-LUDUFZTV.js → chunk-ABP6QMVM.js} +2 -2
- package/dist/chunk-ABP6QMVM.js.map +1 -0
- package/dist/{chunk-UZBK2JR6.js → chunk-ACNU2UBF.js} +16 -16
- package/dist/{chunk-UDZYZ5Q5.js → chunk-AK4DSORQ.js} +7 -7
- package/dist/{chunk-CMZLMIZI.js → chunk-BEMWL2FZ.js} +2 -2
- package/dist/{chunk-3F7RMHC4.js → chunk-C43KEWEV.js} +1 -1
- package/dist/{chunk-3F7RMHC4.js.map → chunk-C43KEWEV.js.map} +1 -1
- package/dist/{chunk-H2NCNXMS.js → chunk-CKITWVWJ.js} +2 -2
- package/dist/{chunk-C4YAIWDG.js → chunk-DM4US77W.js} +2 -2
- package/dist/{chunk-SBHQBC37.js → chunk-ECMMM2VE.js} +2 -2
- package/dist/{chunk-YMI4NRV7.js → chunk-EGBH5IPJ.js} +4 -4
- package/dist/{chunk-SHHT4JTC.js → chunk-GYITUZFT.js} +2 -2
- package/dist/{chunk-EKIQBGAY.js → chunk-IENGGY2C.js} +2 -2
- package/dist/{chunk-UU5LHD6J.js → chunk-NLPH23IQ.js} +2 -2
- package/dist/{chunk-A3UPMOJZ.js → chunk-NV2MGIW7.js} +2 -2
- package/dist/{chunk-PU44GBL4.js → chunk-OUELRE5E.js} +1 -1
- package/dist/chunk-OUELRE5E.js.map +1 -0
- package/dist/{chunk-J2KFC47S.js → chunk-RFSCIBCB.js} +2 -2
- package/dist/{chunk-IXSTQGYV.js → chunk-RREI7CNM.js} +2 -2
- package/dist/{chunk-C6XO6ONP.js → chunk-SZL2GGFY.js} +2 -2
- package/dist/{chunk-7XRSLMUD.js → chunk-THWT7RJU.js} +2 -2
- package/dist/{chunk-H4RWKZE2.js → chunk-TKNTAUA3.js} +2 -2
- package/dist/{chunk-7OH7KPF5.js → chunk-TTOYT5RB.js} +2 -2
- package/dist/{chunk-SEOTBRO4.js → chunk-U3GQ33JC.js} +35 -6
- package/dist/chunk-U3GQ33JC.js.map +1 -0
- package/dist/{chunk-C3EJ3UIS.js → chunk-WLGE6KEO.js} +3 -3
- package/dist/{cli-A4AIhv9e.d.ts → cli-DrL2Nv4j.d.ts} +5 -5
- package/dist/cli.d.ts +7 -7
- package/dist/cli.js +21 -21
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +3 -3
- package/dist/compounding/preference-consolidator.d.ts +2 -2
- package/dist/compression-optimizer.d.ts +2 -2
- package/dist/config.d.ts +2 -2
- package/dist/connectors/codex-materialize-runner.d.ts +2 -2
- package/dist/connectors/codex-materialize-runner.js +3 -3
- package/dist/connectors/codex-materialize.d.ts +2 -2
- package/dist/connectors/index.d.ts +2 -2
- package/dist/connectors/index.js +3 -3
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/contradiction/index.d.ts +2 -2
- package/dist/conversation-index/backend.d.ts +2 -2
- package/dist/conversation-index/chunker.d.ts +2 -2
- package/dist/conversation-index/faiss-adapter.d.ts +2 -2
- package/dist/conversation-index/indexer.d.ts +2 -2
- package/dist/conversation-index/search.d.ts +2 -2
- package/dist/day-summary.d.ts +2 -2
- package/dist/delinearize.d.ts +2 -2
- package/dist/direct-answer-wiring.d.ts +2 -2
- package/dist/direct-answer.d.ts +2 -2
- package/dist/embedding-fallback.d.ts +2 -2
- package/dist/enrichment/index.d.ts +2 -2
- package/dist/entity-retrieval.d.ts +2 -2
- package/dist/entity-retrieval.js +3 -3
- package/dist/entity-schema.d.ts +2 -2
- package/dist/explicit-capture.d.ts +4 -4
- package/dist/extraction-judge-telemetry.d.ts +2 -2
- package/dist/extraction-judge-training.d.ts +2 -2
- package/dist/extraction-judge.d.ts +2 -2
- package/dist/extraction.d.ts +2 -2
- package/dist/extraction.js +1 -1
- package/dist/fallback-llm.d.ts +2 -2
- package/dist/identity-continuity.d.ts +2 -2
- package/dist/importance.d.ts +2 -2
- package/dist/importers/index.d.ts +2 -2
- package/dist/importers/index.js +2 -2
- package/dist/index.d.ts +138 -11
- package/dist/index.js +940 -92
- package/dist/index.js.map +1 -1
- package/dist/intent.d.ts +2 -2
- package/dist/lcm/engine.d.ts +2 -2
- package/dist/lcm/index.d.ts +2 -2
- package/dist/lcm/tools.d.ts +2 -2
- package/dist/lifecycle.d.ts +2 -2
- package/dist/live-connectors-runner.d.ts +2 -2
- package/dist/local-llm.d.ts +2 -2
- package/dist/maintenance/memory-governance.d.ts +2 -2
- package/dist/maintenance/memory-governance.js +3 -3
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
- package/dist/maintenance/rebuild-memory-projection.js +4 -4
- package/dist/mcp-memory-inspector-app.d.ts +5 -5
- package/dist/memory-action-policy.d.ts +2 -2
- package/dist/memory-cache.d.ts +2 -2
- package/dist/memory-lifecycle-ledger-utils.d.ts +2 -2
- package/dist/memory-projection-store.d.ts +2 -2
- package/dist/memory-provenance.d.ts +2 -2
- package/dist/memory-worth-outcomes.d.ts +2 -2
- package/dist/models-json.d.ts +2 -2
- package/dist/namespaces/migrate.d.ts +2 -2
- package/dist/namespaces/migrate.js +4 -4
- package/dist/namespaces/principal.d.ts +2 -2
- package/dist/namespaces/search.d.ts +2 -2
- package/dist/namespaces/storage.d.ts +2 -2
- package/dist/namespaces/storage.js +3 -3
- package/dist/native-knowledge.d.ts +2 -2
- package/dist/operator-toolkit.d.ts +2 -2
- package/dist/operator-toolkit.js +6 -6
- package/dist/{orchestrator-BEDTiuEO.d.ts → orchestrator-DEQW9j0Z.d.ts} +22 -5
- package/dist/orchestrator.d.ts +4 -4
- package/dist/orchestrator.js +15 -13
- package/dist/patterns-cli.d.ts +2 -2
- package/dist/{pipeline-D18UAKlN.d.ts → pipeline-BSTuml0d.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +2 -2
- package/dist/qmd-recall-cache.d.ts +2 -2
- package/dist/qmd.d.ts +2 -2
- package/dist/recall-disclosure-escalation.d.ts +2 -2
- package/dist/recall-explain-renderer.d.ts +2 -2
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-planner-llm.d.ts +2 -2
- package/dist/recall-state.d.ts +2 -2
- package/dist/recall-tag-filter.d.ts +2 -2
- package/dist/recall-xray-cli.d.ts +2 -2
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +2 -2
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +2 -2
- package/dist/recall-xray.js +2 -2
- package/dist/resolve-auth-token.d.ts +2 -2
- package/dist/retrieval-agents.d.ts +2 -2
- package/dist/retrieval-tiers.d.ts +2 -2
- package/dist/routing/engine.d.ts +2 -2
- package/dist/routing/store.d.ts +2 -2
- package/dist/search/embed-helper.d.ts +2 -2
- package/dist/search/factory.d.ts +2 -2
- package/dist/search/index.d.ts +2 -2
- package/dist/search/lancedb-backend.d.ts +2 -2
- package/dist/search/meilisearch-backend.d.ts +2 -2
- package/dist/search/noop-backend.d.ts +2 -2
- package/dist/search/orama-backend.d.ts +2 -2
- package/dist/search/port.d.ts +2 -2
- package/dist/search/remote-backend.d.ts +2 -2
- package/dist/{semantic-consolidation-Byut1ufK.d.ts → semantic-consolidation-DKdYzQOg.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +3 -3
- package/dist/semantic-consolidation.js +4 -4
- package/dist/semantic-rule-promotion.js +3 -3
- package/dist/semantic-rule-verifier.d.ts +2 -2
- package/dist/semantic-rule-verifier.js +3 -3
- package/dist/session-observer-bands.d.ts +2 -2
- package/dist/session-observer-state.d.ts +2 -2
- package/dist/shared-context/manager.d.ts +2 -2
- package/dist/signal.d.ts +2 -2
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +2 -2
- package/dist/summarizer.d.ts +2 -2
- package/dist/summary-snapshot.d.ts +2 -2
- package/dist/temporal-supersession.d.ts +2 -2
- package/dist/temporal-validity.d.ts +2 -2
- package/dist/threading.d.ts +2 -2
- package/dist/tier-migration.d.ts +2 -2
- package/dist/tier-routing.d.ts +2 -2
- package/dist/topics.d.ts +2 -2
- package/dist/transcript.d.ts +2 -2
- package/dist/{types-BliCnURB.d.ts → types-ByK7T3L6.d.ts} +2 -0
- package/dist/{types-BzA1FKbg.d.ts → types-D8yUmSik.d.ts} +2 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +2 -2
- package/dist/verified-recall.js +3 -3
- package/package.json +1 -1
- package/src/bulk-import/types.ts +2 -0
- package/src/extraction-timeout.test.ts +6 -1
- package/src/extraction.ts +34 -5
- package/src/importers/base.ts +1 -1
- package/src/index.ts +7 -4
- package/src/orchestrator-flush.test.ts +640 -42
- package/src/orchestrator.ts +389 -70
- package/src/session-summaries/adapters.ts +438 -0
- package/src/session-summaries/index.ts +401 -0
- package/src/session-summaries/redaction.ts +160 -0
- package/src/session-summaries/session-summaries.test.ts +1748 -0
- package/src/session-summaries/types.ts +130 -0
- package/src/types.ts +1 -0
- package/dist/chunk-LUDUFZTV.js.map +0 -1
- package/dist/chunk-PU44GBL4.js.map +0 -1
- package/dist/chunk-SEOTBRO4.js.map +0 -1
- package/dist/chunk-WAKPKNEX.js.map +0 -1
- /package/dist/{chunk-RJGPH2Q3.js.map → chunk-4HYLRROV.js.map} +0 -0
- /package/dist/{chunk-3AZDLWDW.js.map → chunk-4HYWWO3W.js.map} +0 -0
- /package/dist/{chunk-ISMZIMD3.js.map → chunk-4MNDVDBS.js.map} +0 -0
- /package/dist/{chunk-JFHEEDRY.js.map → chunk-5IDVOWBE.js.map} +0 -0
- /package/dist/{chunk-4GYGEKUB.js.map → chunk-6DDBW2V4.js.map} +0 -0
- /package/dist/{chunk-UZBK2JR6.js.map → chunk-ACNU2UBF.js.map} +0 -0
- /package/dist/{chunk-UDZYZ5Q5.js.map → chunk-AK4DSORQ.js.map} +0 -0
- /package/dist/{chunk-CMZLMIZI.js.map → chunk-BEMWL2FZ.js.map} +0 -0
- /package/dist/{chunk-H2NCNXMS.js.map → chunk-CKITWVWJ.js.map} +0 -0
- /package/dist/{chunk-C4YAIWDG.js.map → chunk-DM4US77W.js.map} +0 -0
- /package/dist/{chunk-SBHQBC37.js.map → chunk-ECMMM2VE.js.map} +0 -0
- /package/dist/{chunk-YMI4NRV7.js.map → chunk-EGBH5IPJ.js.map} +0 -0
- /package/dist/{chunk-SHHT4JTC.js.map → chunk-GYITUZFT.js.map} +0 -0
- /package/dist/{chunk-EKIQBGAY.js.map → chunk-IENGGY2C.js.map} +0 -0
- /package/dist/{chunk-UU5LHD6J.js.map → chunk-NLPH23IQ.js.map} +0 -0
- /package/dist/{chunk-A3UPMOJZ.js.map → chunk-NV2MGIW7.js.map} +0 -0
- /package/dist/{chunk-J2KFC47S.js.map → chunk-RFSCIBCB.js.map} +0 -0
- /package/dist/{chunk-IXSTQGYV.js.map → chunk-RREI7CNM.js.map} +0 -0
- /package/dist/{chunk-C6XO6ONP.js.map → chunk-SZL2GGFY.js.map} +0 -0
- /package/dist/{chunk-7XRSLMUD.js.map → chunk-THWT7RJU.js.map} +0 -0
- /package/dist/{chunk-H4RWKZE2.js.map → chunk-TKNTAUA3.js.map} +0 -0
- /package/dist/{chunk-7OH7KPF5.js.map → chunk-TTOYT5RB.js.map} +0 -0
- /package/dist/{chunk-C3EJ3UIS.js.map → chunk-WLGE6KEO.js.map} +0 -0
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
BulkImportBatchPartialFailureError,
|
|
5
|
+
Orchestrator,
|
|
6
|
+
} from "./orchestrator.js";
|
|
4
7
|
import { parseConfig } from "./config.js";
|
|
5
8
|
import type { BufferTurn } from "./types.js";
|
|
9
|
+
import type { ImportTurn } from "./bulk-import/types.js";
|
|
6
10
|
|
|
7
11
|
function makeTurn(sessionKey: string, content: string): BufferTurn {
|
|
8
12
|
return {
|
|
@@ -134,6 +138,304 @@ test("flushSession waits for queued extraction task completion", async () => {
|
|
|
134
138
|
assert.equal(flushSettled, true);
|
|
135
139
|
});
|
|
136
140
|
|
|
141
|
+
test("ingestBulkImportBatch rejects when the extraction deadline expires in the queue", async () => {
|
|
142
|
+
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
143
|
+
orchestrator.config = parseConfig({});
|
|
144
|
+
orchestrator.extractionQueue = [];
|
|
145
|
+
orchestrator.queueProcessing = true;
|
|
146
|
+
let runExtractionCalls = 0;
|
|
147
|
+
orchestrator.runExtraction = async () => {
|
|
148
|
+
runExtractionCalls += 1;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const turns: ImportTurn[] = [
|
|
152
|
+
{
|
|
153
|
+
role: "user",
|
|
154
|
+
timestamp: "2026-06-24T12:00:00.000Z",
|
|
155
|
+
content: "Remember this queued bulk import.",
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
const startedAt = Date.now();
|
|
159
|
+
const outcome = await Promise.race([
|
|
160
|
+
orchestrator
|
|
161
|
+
.ingestBulkImportBatch(turns, {
|
|
162
|
+
deadlineMs: Date.now() + 25,
|
|
163
|
+
failOnExtractionFailure: true,
|
|
164
|
+
})
|
|
165
|
+
.then(
|
|
166
|
+
() => new Error("bulk import unexpectedly resolved"),
|
|
167
|
+
(error: unknown) => error,
|
|
168
|
+
),
|
|
169
|
+
new Promise<Error>((resolve) =>
|
|
170
|
+
setTimeout(() => resolve(new Error("bulk import did not time out")), 300),
|
|
171
|
+
),
|
|
172
|
+
]);
|
|
173
|
+
|
|
174
|
+
assert.ok(outcome instanceof Error);
|
|
175
|
+
assert.match(outcome.message, /deadline exceeded \(queue_wait\)/);
|
|
176
|
+
assert.ok(
|
|
177
|
+
Date.now() - startedAt < 250,
|
|
178
|
+
"bulk import should not wait behind the extraction queue past its deadline",
|
|
179
|
+
);
|
|
180
|
+
assert.equal(runExtractionCalls, 0);
|
|
181
|
+
|
|
182
|
+
const queuedTask = orchestrator.extractionQueue.shift();
|
|
183
|
+
assert.ok(queuedTask);
|
|
184
|
+
await queuedTask();
|
|
185
|
+
assert.equal(
|
|
186
|
+
runExtractionCalls,
|
|
187
|
+
0,
|
|
188
|
+
"the expired bulk-import task should be a no-op when the queue later drains",
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("ingestBulkImportBatch does not report queue wait timeout after extraction starts", async () => {
|
|
193
|
+
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
194
|
+
orchestrator.config = parseConfig({});
|
|
195
|
+
orchestrator.extractionQueue = [];
|
|
196
|
+
orchestrator.queueProcessing = false;
|
|
197
|
+
let runExtractionCalls = 0;
|
|
198
|
+
orchestrator.runExtraction = async () => {
|
|
199
|
+
runExtractionCalls += 1;
|
|
200
|
+
await new Promise((resolve) => setTimeout(resolve, 60));
|
|
201
|
+
return {
|
|
202
|
+
status: "completed",
|
|
203
|
+
persistedCount: 1,
|
|
204
|
+
durableOutputCount: 1,
|
|
205
|
+
};
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const result = await orchestrator.ingestBulkImportBatch(
|
|
209
|
+
[
|
|
210
|
+
{
|
|
211
|
+
role: "user",
|
|
212
|
+
timestamp: "2026-06-24T12:00:00.000Z",
|
|
213
|
+
content: "Remember this active bulk import.",
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
{
|
|
217
|
+
deadlineMs: Date.now() + 25,
|
|
218
|
+
failOnExtractionFailure: true,
|
|
219
|
+
},
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
assert.equal(runExtractionCalls, 1);
|
|
223
|
+
assert.equal(result.extractionCount, 1);
|
|
224
|
+
assert.equal(result.persistedCount, 1);
|
|
225
|
+
assert.equal(result.failedCount, 0);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("ingestBulkImportBatch reports post-persist metadata failures separately", async () => {
|
|
229
|
+
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
230
|
+
orchestrator.config = parseConfig({});
|
|
231
|
+
orchestrator.extractionQueue = [];
|
|
232
|
+
orchestrator.queueProcessing = false;
|
|
233
|
+
orchestrator.runExtraction = async () => ({
|
|
234
|
+
status: "completed",
|
|
235
|
+
persistedCount: 1,
|
|
236
|
+
durableOutputCount: 1,
|
|
237
|
+
postPersistMetadataFailed: true,
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const result = await orchestrator.ingestBulkImportBatch(
|
|
241
|
+
[
|
|
242
|
+
{
|
|
243
|
+
role: "user",
|
|
244
|
+
timestamp: "2026-06-24T12:00:00.000Z",
|
|
245
|
+
content: "Remember this import, but fail the replay metadata marker.",
|
|
246
|
+
turnFingerprint: "flush-plan-fp-1",
|
|
247
|
+
persistProcessedFingerprint: true,
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
{
|
|
251
|
+
failOnExtractionFailure: true,
|
|
252
|
+
},
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
assert.equal(result.extractionCount, 1);
|
|
256
|
+
assert.equal(result.persistedCount, 1);
|
|
257
|
+
assert.equal(result.failedCount, 0);
|
|
258
|
+
assert.equal(result.postPersistMetadataFailureCount, 1);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test("ingestBulkImportBatch can disable source-valid-at replay context", async () => {
|
|
262
|
+
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
263
|
+
orchestrator.config = parseConfig({});
|
|
264
|
+
orchestrator.extractionQueue = [];
|
|
265
|
+
orchestrator.queueProcessing = false;
|
|
266
|
+
const capturedSlices: BufferTurn[][] = [];
|
|
267
|
+
orchestrator.runExtraction = async (turns: BufferTurn[]) => {
|
|
268
|
+
capturedSlices.push(turns);
|
|
269
|
+
return {
|
|
270
|
+
status: "completed",
|
|
271
|
+
persistedCount: 1,
|
|
272
|
+
durableOutputCount: 1,
|
|
273
|
+
};
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
await orchestrator.ingestBulkImportBatch(
|
|
277
|
+
[
|
|
278
|
+
{
|
|
279
|
+
role: "user",
|
|
280
|
+
timestamp: "2026-06-24T12:00:00.000Z",
|
|
281
|
+
content: "Remember the first chunk.",
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
role: "user",
|
|
285
|
+
timestamp: "2026-06-24T12:00:00.001Z",
|
|
286
|
+
content: "Remember the second chunk.",
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
role: "user",
|
|
290
|
+
timestamp: "2026-06-24T12:00:00.002Z",
|
|
291
|
+
content: "Remember the third chunk.",
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
{
|
|
295
|
+
includeSourceValidAtContext: false,
|
|
296
|
+
},
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
assert.equal(capturedSlices.length, 3);
|
|
300
|
+
assert.deepEqual(
|
|
301
|
+
capturedSlices.map((slice) =>
|
|
302
|
+
slice.map((turn) => ({
|
|
303
|
+
content: turn.content,
|
|
304
|
+
contextOnly: turn.extractionContextOnly === true,
|
|
305
|
+
})),
|
|
306
|
+
),
|
|
307
|
+
[
|
|
308
|
+
[{ content: "Remember the first chunk.", contextOnly: false }],
|
|
309
|
+
[{ content: "Remember the second chunk.", contextOnly: false }],
|
|
310
|
+
[{ content: "Remember the third chunk.", contextOnly: false }],
|
|
311
|
+
],
|
|
312
|
+
);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test("ingestBulkImportBatch preserves partial metadata failure before a later slice rejects", async () => {
|
|
316
|
+
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
317
|
+
orchestrator.config = parseConfig({});
|
|
318
|
+
orchestrator.extractionQueue = [];
|
|
319
|
+
orchestrator.queueProcessing = false;
|
|
320
|
+
let runExtractionCalls = 0;
|
|
321
|
+
orchestrator.runExtraction = async () => {
|
|
322
|
+
runExtractionCalls += 1;
|
|
323
|
+
if (runExtractionCalls <= 2) {
|
|
324
|
+
return {
|
|
325
|
+
status: "completed",
|
|
326
|
+
persistedCount: 1,
|
|
327
|
+
durableOutputCount: 1,
|
|
328
|
+
postPersistMetadataFailed: runExtractionCalls === 1,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
throw new Error("backend unavailable");
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
const error = await orchestrator
|
|
335
|
+
.ingestBulkImportBatch(
|
|
336
|
+
[
|
|
337
|
+
{
|
|
338
|
+
role: "user",
|
|
339
|
+
timestamp: "2026-06-24T12:00:00.000Z",
|
|
340
|
+
content: "Remember the first chunk.",
|
|
341
|
+
turnFingerprint: "flush-plan-fp-1",
|
|
342
|
+
persistProcessedFingerprint: true,
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
role: "user",
|
|
346
|
+
timestamp: "2026-06-24T12:00:00.001Z",
|
|
347
|
+
content: "Remember the second chunk.",
|
|
348
|
+
turnFingerprint: "flush-plan-fp-2",
|
|
349
|
+
persistProcessedFingerprint: true,
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
role: "user",
|
|
353
|
+
timestamp: "2026-06-24T12:00:00.002Z",
|
|
354
|
+
content: "Remember the third chunk.",
|
|
355
|
+
turnFingerprint: "flush-plan-fp-3",
|
|
356
|
+
persistProcessedFingerprint: true,
|
|
357
|
+
},
|
|
358
|
+
],
|
|
359
|
+
{
|
|
360
|
+
failOnExtractionFailure: true,
|
|
361
|
+
},
|
|
362
|
+
)
|
|
363
|
+
.then(
|
|
364
|
+
() => undefined,
|
|
365
|
+
(rejection: unknown) => rejection,
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
assert.ok(error instanceof BulkImportBatchPartialFailureError);
|
|
369
|
+
assert.equal(error.partialResult.attemptedTurnCount, 3);
|
|
370
|
+
assert.equal(error.partialResult.extractionCount, 2);
|
|
371
|
+
assert.equal(error.partialResult.persistedCount, 2);
|
|
372
|
+
assert.equal(error.partialResult.failedCount, 1);
|
|
373
|
+
assert.equal(error.partialResult.postPersistMetadataFailureCount, 1);
|
|
374
|
+
assert.equal(error.partialResult.processedTurnCount, 2);
|
|
375
|
+
assert.equal(runExtractionCalls, 3);
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
test("ingestBulkImportBatch stops after the first failed source-valid-at slice", async () => {
|
|
379
|
+
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
380
|
+
orchestrator.config = parseConfig({});
|
|
381
|
+
orchestrator.extractionQueue = [];
|
|
382
|
+
orchestrator.queueProcessing = false;
|
|
383
|
+
let runExtractionCalls = 0;
|
|
384
|
+
orchestrator.runExtraction = async () => {
|
|
385
|
+
runExtractionCalls += 1;
|
|
386
|
+
if (runExtractionCalls === 1) {
|
|
387
|
+
return {
|
|
388
|
+
status: "completed",
|
|
389
|
+
persistedCount: 1,
|
|
390
|
+
durableOutputCount: 1,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
throw new Error("backend unavailable");
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
const error = await orchestrator
|
|
397
|
+
.ingestBulkImportBatch(
|
|
398
|
+
[
|
|
399
|
+
{
|
|
400
|
+
role: "user",
|
|
401
|
+
timestamp: "2026-06-24T12:00:00.000Z",
|
|
402
|
+
content: "Remember the first chunk.",
|
|
403
|
+
turnFingerprint: "flush-plan-fp-1",
|
|
404
|
+
persistProcessedFingerprint: true,
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
role: "user",
|
|
408
|
+
timestamp: "2026-06-24T12:00:00.001Z",
|
|
409
|
+
content: "Remember the second chunk.",
|
|
410
|
+
turnFingerprint: "flush-plan-fp-2",
|
|
411
|
+
persistProcessedFingerprint: true,
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
role: "user",
|
|
415
|
+
timestamp: "2026-06-24T12:00:00.002Z",
|
|
416
|
+
content: "Remember the third chunk.",
|
|
417
|
+
turnFingerprint: "flush-plan-fp-3",
|
|
418
|
+
persistProcessedFingerprint: true,
|
|
419
|
+
},
|
|
420
|
+
],
|
|
421
|
+
{
|
|
422
|
+
failOnExtractionFailure: true,
|
|
423
|
+
},
|
|
424
|
+
)
|
|
425
|
+
.then(
|
|
426
|
+
() => undefined,
|
|
427
|
+
(rejection: unknown) => rejection,
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
assert.ok(error instanceof BulkImportBatchPartialFailureError);
|
|
431
|
+
assert.equal(error.partialResult.attemptedTurnCount, 3);
|
|
432
|
+
assert.equal(error.partialResult.extractionCount, 1);
|
|
433
|
+
assert.equal(error.partialResult.persistedCount, 1);
|
|
434
|
+
assert.equal(error.partialResult.failedCount, 1);
|
|
435
|
+
assert.equal(error.partialResult.processedTurnCount, 1);
|
|
436
|
+
assert.equal(runExtractionCalls, 2);
|
|
437
|
+
});
|
|
438
|
+
|
|
137
439
|
test("processTurn preserves the original sessionKey on buffered turns", async () => {
|
|
138
440
|
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
139
441
|
let capturedTurn: BufferTurn | undefined;
|
|
@@ -396,6 +698,298 @@ test("runExtraction skips batches whose persisted fingerprint already exists in
|
|
|
396
698
|
assert.equal(clearCalls, 1);
|
|
397
699
|
});
|
|
398
700
|
|
|
701
|
+
test("runExtraction fails closed on invalid extraction results when required", async () => {
|
|
702
|
+
const cases: Array<[string, unknown]> = [
|
|
703
|
+
["null", null],
|
|
704
|
+
[
|
|
705
|
+
"malformed collections",
|
|
706
|
+
{
|
|
707
|
+
facts: "not an array",
|
|
708
|
+
entities: [],
|
|
709
|
+
questions: [],
|
|
710
|
+
profileUpdates: [],
|
|
711
|
+
},
|
|
712
|
+
],
|
|
713
|
+
];
|
|
714
|
+
|
|
715
|
+
for (const [label, extractionResult] of cases) {
|
|
716
|
+
const config = parseConfig({});
|
|
717
|
+
config.extractionMinChars = 0;
|
|
718
|
+
config.extractionMinUserTurns = 1;
|
|
719
|
+
|
|
720
|
+
let clearCalls = 0;
|
|
721
|
+
let persistCalls = 0;
|
|
722
|
+
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
723
|
+
orchestrator.config = config;
|
|
724
|
+
orchestrator.buffer = {
|
|
725
|
+
clearAfterExtraction: async () => {
|
|
726
|
+
clearCalls += 1;
|
|
727
|
+
},
|
|
728
|
+
};
|
|
729
|
+
orchestrator.storageRouter = {
|
|
730
|
+
storageFor: async () => ({
|
|
731
|
+
listEntityNames: async () => [],
|
|
732
|
+
loadMeta: async () => ({
|
|
733
|
+
extractionCount: 0,
|
|
734
|
+
lastExtractionAt: null,
|
|
735
|
+
lastConsolidationAt: null,
|
|
736
|
+
totalMemories: 0,
|
|
737
|
+
totalEntities: 0,
|
|
738
|
+
processedExtractionFingerprints: [],
|
|
739
|
+
}),
|
|
740
|
+
saveMeta: async () => undefined,
|
|
741
|
+
}),
|
|
742
|
+
};
|
|
743
|
+
orchestrator.extraction = {
|
|
744
|
+
extract: async () => extractionResult,
|
|
745
|
+
};
|
|
746
|
+
orchestrator.persistExtraction = async () => {
|
|
747
|
+
persistCalls += 1;
|
|
748
|
+
return ["fact-1"];
|
|
749
|
+
};
|
|
750
|
+
|
|
751
|
+
await assert.rejects(
|
|
752
|
+
orchestrator.runExtraction([makeTurn("session-invalid", "remember bad output")], {
|
|
753
|
+
bufferKey: `bulk-import:batch:${label}`,
|
|
754
|
+
failOnExtractionFailure: true,
|
|
755
|
+
}),
|
|
756
|
+
/extraction failed: invalid_extraction_result/,
|
|
757
|
+
);
|
|
758
|
+
|
|
759
|
+
assert.equal(clearCalls, 0);
|
|
760
|
+
assert.equal(persistCalls, 0);
|
|
761
|
+
}
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
test("runExtraction persists processed fingerprints for empty extraction results", async () => {
|
|
765
|
+
const config = parseConfig({});
|
|
766
|
+
config.extractionMinChars = 0;
|
|
767
|
+
config.extractionMinUserTurns = 1;
|
|
768
|
+
|
|
769
|
+
let clearCalls = 0;
|
|
770
|
+
let persistCalls = 0;
|
|
771
|
+
let saveMetaCalls = 0;
|
|
772
|
+
let savedMeta:
|
|
773
|
+
| {
|
|
774
|
+
extractionCount: number;
|
|
775
|
+
lastExtractionAt: string | null;
|
|
776
|
+
totalMemories: number;
|
|
777
|
+
totalEntities: number;
|
|
778
|
+
processedExtractionFingerprints: Array<{
|
|
779
|
+
fingerprint: string;
|
|
780
|
+
observedAt: string;
|
|
781
|
+
}>;
|
|
782
|
+
}
|
|
783
|
+
| undefined;
|
|
784
|
+
|
|
785
|
+
const meta = {
|
|
786
|
+
extractionCount: 0,
|
|
787
|
+
lastExtractionAt: null,
|
|
788
|
+
lastConsolidationAt: null,
|
|
789
|
+
totalMemories: 0,
|
|
790
|
+
totalEntities: 0,
|
|
791
|
+
processedExtractionFingerprints: [] as Array<{
|
|
792
|
+
fingerprint: string;
|
|
793
|
+
observedAt: string;
|
|
794
|
+
}>,
|
|
795
|
+
};
|
|
796
|
+
|
|
797
|
+
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
798
|
+
orchestrator.config = config;
|
|
799
|
+
orchestrator.buffer = {
|
|
800
|
+
clearAfterExtraction: async () => {
|
|
801
|
+
clearCalls += 1;
|
|
802
|
+
},
|
|
803
|
+
};
|
|
804
|
+
orchestrator.storageRouter = {
|
|
805
|
+
storageFor: async () => ({
|
|
806
|
+
listEntityNames: async () => [],
|
|
807
|
+
loadMeta: async () => meta,
|
|
808
|
+
saveMeta: async (nextMeta: typeof meta) => {
|
|
809
|
+
saveMetaCalls += 1;
|
|
810
|
+
savedMeta = structuredClone(nextMeta);
|
|
811
|
+
},
|
|
812
|
+
}),
|
|
813
|
+
};
|
|
814
|
+
orchestrator.extraction = {
|
|
815
|
+
extract: async () => ({
|
|
816
|
+
facts: [],
|
|
817
|
+
entities: [],
|
|
818
|
+
questions: [],
|
|
819
|
+
profileUpdates: [],
|
|
820
|
+
}),
|
|
821
|
+
};
|
|
822
|
+
orchestrator.persistExtraction = async () => {
|
|
823
|
+
persistCalls += 1;
|
|
824
|
+
return [];
|
|
825
|
+
};
|
|
826
|
+
|
|
827
|
+
const turns = [
|
|
828
|
+
{
|
|
829
|
+
...makeTurn("session-empty", "transient note not worth remembering"),
|
|
830
|
+
logicalSessionKey: "logical-thread:empty",
|
|
831
|
+
turnFingerprint: "fp-empty",
|
|
832
|
+
persistProcessedFingerprint: true,
|
|
833
|
+
},
|
|
834
|
+
];
|
|
835
|
+
const result = await orchestrator.runExtraction(turns, {
|
|
836
|
+
bufferKey: "logical-thread:empty",
|
|
837
|
+
failOnExtractionFailure: true,
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
assert.equal(result.status, "skipped");
|
|
841
|
+
assert.equal(result.reason, "empty_extraction_result");
|
|
842
|
+
assert.equal(persistCalls, 0);
|
|
843
|
+
assert.equal(saveMetaCalls, 1);
|
|
844
|
+
assert.equal(clearCalls, 1);
|
|
845
|
+
assert.equal(savedMeta?.extractionCount, 1);
|
|
846
|
+
assert.equal(savedMeta?.totalMemories, 0);
|
|
847
|
+
assert.equal(savedMeta?.totalEntities, 0);
|
|
848
|
+
assert.equal(savedMeta?.processedExtractionFingerprints.length, 1);
|
|
849
|
+
assert.equal(
|
|
850
|
+
savedMeta?.processedExtractionFingerprints[0]?.fingerprint,
|
|
851
|
+
orchestrator.buildExtractionFingerprint(turns, "logical-thread:empty"),
|
|
852
|
+
);
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
test("runExtraction does not persist processed fingerprints for failed empty extraction results", async () => {
|
|
856
|
+
const config = parseConfig({});
|
|
857
|
+
config.extractionMinChars = 0;
|
|
858
|
+
config.extractionMinUserTurns = 1;
|
|
859
|
+
|
|
860
|
+
let clearCalls = 0;
|
|
861
|
+
let persistCalls = 0;
|
|
862
|
+
let saveMetaCalls = 0;
|
|
863
|
+
|
|
864
|
+
const meta = {
|
|
865
|
+
extractionCount: 0,
|
|
866
|
+
lastExtractionAt: null,
|
|
867
|
+
lastConsolidationAt: null,
|
|
868
|
+
totalMemories: 0,
|
|
869
|
+
totalEntities: 0,
|
|
870
|
+
processedExtractionFingerprints: [] as Array<{
|
|
871
|
+
fingerprint: string;
|
|
872
|
+
observedAt: string;
|
|
873
|
+
}>,
|
|
874
|
+
};
|
|
875
|
+
|
|
876
|
+
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
877
|
+
orchestrator.config = config;
|
|
878
|
+
orchestrator.buffer = {
|
|
879
|
+
clearAfterExtraction: async () => {
|
|
880
|
+
clearCalls += 1;
|
|
881
|
+
},
|
|
882
|
+
};
|
|
883
|
+
orchestrator.storageRouter = {
|
|
884
|
+
storageFor: async () => ({
|
|
885
|
+
listEntityNames: async () => [],
|
|
886
|
+
loadMeta: async () => meta,
|
|
887
|
+
saveMeta: async () => {
|
|
888
|
+
saveMetaCalls += 1;
|
|
889
|
+
},
|
|
890
|
+
}),
|
|
891
|
+
};
|
|
892
|
+
orchestrator.extraction = {
|
|
893
|
+
extract: async () => ({
|
|
894
|
+
facts: [],
|
|
895
|
+
entities: [],
|
|
896
|
+
questions: [],
|
|
897
|
+
profileUpdates: [],
|
|
898
|
+
extractionFailure: "gateway_unavailable",
|
|
899
|
+
}),
|
|
900
|
+
};
|
|
901
|
+
orchestrator.persistExtraction = async () => {
|
|
902
|
+
persistCalls += 1;
|
|
903
|
+
return [];
|
|
904
|
+
};
|
|
905
|
+
|
|
906
|
+
const turns = [
|
|
907
|
+
{
|
|
908
|
+
...makeTurn("session-empty-failed", "remember failed gateway output"),
|
|
909
|
+
logicalSessionKey: "logical-thread:empty-failed",
|
|
910
|
+
turnFingerprint: "fp-empty-failed",
|
|
911
|
+
persistProcessedFingerprint: true,
|
|
912
|
+
},
|
|
913
|
+
];
|
|
914
|
+
const result = await orchestrator.runExtraction(turns, {
|
|
915
|
+
bufferKey: "logical-thread:empty-failed",
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
assert.equal(result.status, "skipped");
|
|
919
|
+
assert.equal(result.reason, "empty_extraction_result");
|
|
920
|
+
assert.equal(persistCalls, 0);
|
|
921
|
+
assert.equal(saveMetaCalls, 0);
|
|
922
|
+
assert.equal(clearCalls, 1);
|
|
923
|
+
assert.deepEqual(meta.processedExtractionFingerprints, []);
|
|
924
|
+
});
|
|
925
|
+
|
|
926
|
+
test("runExtraction preserves empty-result buffers when fingerprint persistence fails", async () => {
|
|
927
|
+
const config = parseConfig({});
|
|
928
|
+
config.extractionMinChars = 0;
|
|
929
|
+
config.extractionMinUserTurns = 1;
|
|
930
|
+
|
|
931
|
+
let clearCalls = 0;
|
|
932
|
+
let saveMetaCalls = 0;
|
|
933
|
+
const meta = {
|
|
934
|
+
extractionCount: 0,
|
|
935
|
+
lastExtractionAt: null,
|
|
936
|
+
lastConsolidationAt: null,
|
|
937
|
+
totalMemories: 0,
|
|
938
|
+
totalEntities: 0,
|
|
939
|
+
processedExtractionFingerprints: [] as Array<{
|
|
940
|
+
fingerprint: string;
|
|
941
|
+
observedAt: string;
|
|
942
|
+
}>,
|
|
943
|
+
};
|
|
944
|
+
|
|
945
|
+
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
946
|
+
orchestrator.config = config;
|
|
947
|
+
orchestrator.buffer = {
|
|
948
|
+
clearAfterExtraction: async () => {
|
|
949
|
+
clearCalls += 1;
|
|
950
|
+
},
|
|
951
|
+
};
|
|
952
|
+
orchestrator.storageRouter = {
|
|
953
|
+
storageFor: async () => ({
|
|
954
|
+
listEntityNames: async () => [],
|
|
955
|
+
loadMeta: async () => meta,
|
|
956
|
+
saveMeta: async () => {
|
|
957
|
+
saveMetaCalls += 1;
|
|
958
|
+
throw new Error("meta save failed");
|
|
959
|
+
},
|
|
960
|
+
}),
|
|
961
|
+
};
|
|
962
|
+
orchestrator.extraction = {
|
|
963
|
+
extract: async () => ({
|
|
964
|
+
facts: [],
|
|
965
|
+
entities: [],
|
|
966
|
+
questions: [],
|
|
967
|
+
profileUpdates: [],
|
|
968
|
+
}),
|
|
969
|
+
};
|
|
970
|
+
|
|
971
|
+
await assert.rejects(
|
|
972
|
+
orchestrator.runExtraction(
|
|
973
|
+
[
|
|
974
|
+
{
|
|
975
|
+
...makeTurn("session-empty-fail", "transient note not worth remembering"),
|
|
976
|
+
logicalSessionKey: "logical-thread:empty-fail",
|
|
977
|
+
turnFingerprint: "fp-empty-fail",
|
|
978
|
+
persistProcessedFingerprint: true,
|
|
979
|
+
},
|
|
980
|
+
],
|
|
981
|
+
{
|
|
982
|
+
bufferKey: "logical-thread:empty-fail",
|
|
983
|
+
failOnExtractionFailure: true,
|
|
984
|
+
},
|
|
985
|
+
),
|
|
986
|
+
/meta save failed/,
|
|
987
|
+
);
|
|
988
|
+
|
|
989
|
+
assert.equal(saveMetaCalls, 1);
|
|
990
|
+
assert.equal(clearCalls, 0);
|
|
991
|
+
});
|
|
992
|
+
|
|
399
993
|
test("runExtraction preserves deduped buffers when the caller aborts before clearing", async () => {
|
|
400
994
|
const config = parseConfig({});
|
|
401
995
|
config.extractionMinChars = 0;
|
|
@@ -734,7 +1328,7 @@ test("runExtraction loads meta before updating extraction counters when fingerpr
|
|
|
734
1328
|
assert.equal(savedMeta?.processedExtractionFingerprints.length, 0);
|
|
735
1329
|
});
|
|
736
1330
|
|
|
737
|
-
test("runExtraction saves the processed fingerprint
|
|
1331
|
+
test("runExtraction completes after late threading failures and saves the processed fingerprint", async () => {
|
|
738
1332
|
const config = parseConfig({});
|
|
739
1333
|
config.extractionMinChars = 0;
|
|
740
1334
|
config.extractionMinUserTurns = 1;
|
|
@@ -807,26 +1401,29 @@ test("runExtraction saves the processed fingerprint before late threading failur
|
|
|
807
1401
|
throw new Error("thread title failed");
|
|
808
1402
|
},
|
|
809
1403
|
};
|
|
1404
|
+
orchestrator.maybeScheduleConsolidation = () => undefined;
|
|
810
1405
|
orchestrator.requestQmdMaintenance = () => undefined;
|
|
811
1406
|
orchestrator.runTierMigrationCycle = async () => undefined;
|
|
1407
|
+
orchestrator.nonZeroExtractionsSinceConsolidation = 0;
|
|
812
1408
|
|
|
813
|
-
await
|
|
814
|
-
|
|
815
|
-
[
|
|
816
|
-
{
|
|
817
|
-
...makeTurn("session-f", "remember eta"),
|
|
818
|
-
logicalSessionKey: "logical-thread:thread-15",
|
|
819
|
-
turnFingerprint: "fp-thread-15",
|
|
820
|
-
persistProcessedFingerprint: true,
|
|
821
|
-
},
|
|
822
|
-
],
|
|
1409
|
+
const result = await orchestrator.runExtraction(
|
|
1410
|
+
[
|
|
823
1411
|
{
|
|
824
|
-
|
|
1412
|
+
...makeTurn("session-f", "remember eta"),
|
|
1413
|
+
logicalSessionKey: "logical-thread:thread-15",
|
|
1414
|
+
turnFingerprint: "fp-thread-15",
|
|
1415
|
+
persistProcessedFingerprint: true,
|
|
825
1416
|
},
|
|
826
|
-
|
|
827
|
-
|
|
1417
|
+
],
|
|
1418
|
+
{
|
|
1419
|
+
bufferKey: "logical-thread:thread-15",
|
|
1420
|
+
},
|
|
828
1421
|
);
|
|
829
1422
|
|
|
1423
|
+
assert.equal(result.status, "completed");
|
|
1424
|
+
assert.equal(result.persistedCount, 1);
|
|
1425
|
+
assert.equal(result.durableOutputCount, 1);
|
|
1426
|
+
assert.equal(result.postPersistMetadataFailed, false);
|
|
830
1427
|
assert.equal(saveMetaCalls, 1);
|
|
831
1428
|
assert.equal(clearCalls, 1);
|
|
832
1429
|
assert.equal(savedMeta?.processedExtractionFingerprints.length, 1);
|
|
@@ -846,7 +1443,7 @@ test("runExtraction saves the processed fingerprint before late threading failur
|
|
|
846
1443
|
);
|
|
847
1444
|
});
|
|
848
1445
|
|
|
849
|
-
test("runExtraction clears the buffer
|
|
1446
|
+
test("runExtraction completes and clears the buffer when the post-persist meta save fails", async () => {
|
|
850
1447
|
const config = parseConfig({});
|
|
851
1448
|
config.extractionMinChars = 0;
|
|
852
1449
|
config.extractionMinUserTurns = 1;
|
|
@@ -902,23 +1499,23 @@ test("runExtraction clears the buffer even when the post-persist meta save fails
|
|
|
902
1499
|
orchestrator.requestQmdMaintenance = () => undefined;
|
|
903
1500
|
orchestrator.runTierMigrationCycle = async () => undefined;
|
|
904
1501
|
|
|
905
|
-
await
|
|
906
|
-
|
|
907
|
-
[
|
|
908
|
-
{
|
|
909
|
-
...makeTurn("session-g", "remember theta"),
|
|
910
|
-
logicalSessionKey: "logical-thread:thread-16",
|
|
911
|
-
turnFingerprint: "fp-thread-16",
|
|
912
|
-
persistProcessedFingerprint: true,
|
|
913
|
-
},
|
|
914
|
-
],
|
|
1502
|
+
const result = await orchestrator.runExtraction(
|
|
1503
|
+
[
|
|
915
1504
|
{
|
|
916
|
-
|
|
1505
|
+
...makeTurn("session-g", "remember theta"),
|
|
1506
|
+
logicalSessionKey: "logical-thread:thread-16",
|
|
1507
|
+
turnFingerprint: "fp-thread-16",
|
|
1508
|
+
persistProcessedFingerprint: true,
|
|
917
1509
|
},
|
|
918
|
-
|
|
919
|
-
|
|
1510
|
+
],
|
|
1511
|
+
{
|
|
1512
|
+
bufferKey: "logical-thread:thread-16",
|
|
1513
|
+
},
|
|
920
1514
|
);
|
|
921
1515
|
|
|
1516
|
+
assert.equal(result.status, "completed");
|
|
1517
|
+
assert.equal(result.persistedCount, 1);
|
|
1518
|
+
assert.equal(result.postPersistMetadataFailed, true);
|
|
922
1519
|
assert.equal(saveMetaCalls, 1);
|
|
923
1520
|
assert.equal(clearCalls, 1);
|
|
924
1521
|
});
|
|
@@ -1004,26 +1601,27 @@ test("runExtraction still runs follow-on extraction helpers when the post-persis
|
|
|
1004
1601
|
};
|
|
1005
1602
|
orchestrator.runTierMigrationCycle = async () => {
|
|
1006
1603
|
tierCalls += 1;
|
|
1604
|
+
throw new Error("tier migration failed");
|
|
1007
1605
|
};
|
|
1008
1606
|
orchestrator.nonZeroExtractionsSinceConsolidation = 0;
|
|
1009
1607
|
|
|
1010
|
-
await
|
|
1011
|
-
|
|
1012
|
-
[
|
|
1013
|
-
{
|
|
1014
|
-
...makeTurn("session-h", "remember iota"),
|
|
1015
|
-
logicalSessionKey: "logical-thread:thread-17",
|
|
1016
|
-
turnFingerprint: "fp-thread-17",
|
|
1017
|
-
persistProcessedFingerprint: true,
|
|
1018
|
-
},
|
|
1019
|
-
],
|
|
1608
|
+
const result = await orchestrator.runExtraction(
|
|
1609
|
+
[
|
|
1020
1610
|
{
|
|
1021
|
-
|
|
1611
|
+
...makeTurn("session-h", "remember iota"),
|
|
1612
|
+
logicalSessionKey: "logical-thread:thread-17",
|
|
1613
|
+
turnFingerprint: "fp-thread-17",
|
|
1614
|
+
persistProcessedFingerprint: true,
|
|
1022
1615
|
},
|
|
1023
|
-
|
|
1024
|
-
|
|
1616
|
+
],
|
|
1617
|
+
{
|
|
1618
|
+
bufferKey: "logical-thread:thread-17",
|
|
1619
|
+
},
|
|
1025
1620
|
);
|
|
1026
1621
|
|
|
1622
|
+
assert.equal(result.status, "completed");
|
|
1623
|
+
assert.equal(result.persistedCount, 1);
|
|
1624
|
+
assert.equal(result.postPersistMetadataFailed, true);
|
|
1027
1625
|
assert.equal(clearCalls, 1);
|
|
1028
1626
|
assert.equal(boxCalls, 1);
|
|
1029
1627
|
assert.equal(appendCalls, 1);
|