@remnic/core 9.3.659 → 9.3.661
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 +22 -22
- package/dist/access-http.d.ts +5 -5
- package/dist/access-http.js +15 -14
- package/dist/access-mcp.d.ts +5 -5
- package/dist/access-mcp.js +14 -13
- package/dist/{access-service-D_nbpexW.d.ts → access-service-D0SLB4MH.d.ts} +2 -2
- package/dist/access-service.d.ts +5 -5
- package/dist/access-service.js +13 -12
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +3 -3
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +4 -3
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +1 -1
- package/dist/calibration.d.ts +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +5 -4
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/{chunk-QRSKPI62.js → chunk-34NSUPWS.js} +110 -25
- package/dist/chunk-34NSUPWS.js.map +1 -0
- package/dist/{chunk-SCPFRKIT.js → chunk-42JKGUFJ.js} +6 -2
- package/dist/{chunk-SCPFRKIT.js.map → chunk-42JKGUFJ.js.map} +1 -1
- package/dist/{chunk-FWIROLS6.js → chunk-44VFF3BB.js} +18 -16
- package/dist/chunk-44VFF3BB.js.map +1 -0
- package/dist/{chunk-5PFIMBJJ.js → chunk-4SYURHI6.js} +12 -12
- package/dist/{chunk-2VCTTEJM.js → chunk-5G2DNO54.js} +3 -3
- package/dist/{chunk-3R6OP33G.js → chunk-7F7LC6HW.js} +3 -3
- package/dist/{chunk-ZCMO46YY.js → chunk-A7EF2XRO.js} +2 -2
- package/dist/{chunk-BNUAOLDK.js → chunk-ANJOULTP.js} +2 -2
- package/dist/{chunk-CPPS65WS.js → chunk-AWJ2FHCF.js} +84 -17
- package/dist/chunk-AWJ2FHCF.js.map +1 -0
- package/dist/{chunk-4PLOQDBB.js → chunk-AX5O25EF.js} +7 -5
- package/dist/chunk-AX5O25EF.js.map +1 -0
- package/dist/{chunk-QFKRE7AU.js → chunk-BP5O3GYD.js} +4 -4
- package/dist/{chunk-BKRIAXTU.js → chunk-D2EFNQMY.js} +2 -2
- package/dist/{chunk-6M4LYWA2.js → chunk-D44FQVCU.js} +5 -5
- package/dist/{chunk-SSSXWIBP.js → chunk-EMSC4P66.js} +5 -5
- package/dist/{chunk-MBZAESQ3.js → chunk-F6O7IOS3.js} +2 -2
- package/dist/{chunk-6G5JEN55.js → chunk-FZC2WSDB.js} +2 -2
- package/dist/{chunk-KI6QM5AV.js → chunk-GY3V3SUI.js} +2 -2
- package/dist/{chunk-EKQMQQ3U.js → chunk-LFZUFZQR.js} +10 -2
- package/dist/chunk-LFZUFZQR.js.map +1 -0
- package/dist/{chunk-7VWDC7AD.js → chunk-MHYRRV43.js} +122 -29
- package/dist/chunk-MHYRRV43.js.map +1 -0
- package/dist/{chunk-VJYFXDCZ.js → chunk-NMPEJV5M.js} +3 -3
- package/dist/{chunk-7KSPKZIQ.js → chunk-PXVFMQLD.js} +3 -3
- package/dist/{chunk-FIS5RT6K.js → chunk-QXHBWFR3.js} +2 -2
- package/dist/{chunk-G2VVBWFU.js → chunk-RQGR3ETH.js} +2 -2
- package/dist/{chunk-GGL7R2L2.js → chunk-RQRKQJYM.js} +4 -4
- package/dist/{chunk-JI3LQFJH.js → chunk-TBLGI2LT.js} +2 -2
- package/dist/{chunk-RVT6U6PV.js → chunk-TWAJICBN.js} +2 -2
- package/dist/{chunk-46RXRASB.js → chunk-TYIXG4VR.js} +3 -3
- package/dist/{chunk-5PLUC5OB.js → chunk-WSQG37DV.js} +2 -2
- package/dist/{chunk-M3VYPE2H.js → chunk-YNQ6DFSV.js} +1 -1
- package/dist/chunk-YNQ6DFSV.js.map +1 -0
- package/dist/{chunk-GRYAECRV.js → chunk-ZJH723NM.js} +2 -2
- package/dist/{cli-aYxSuPvP.d.ts → cli-C6twwe84.d.ts} +3 -3
- package/dist/cli.d.ts +5 -5
- package/dist/cli.js +24 -23
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +4 -3
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +4 -3
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +4 -3
- package/dist/consolidation-provenance-check.d.ts +1 -1
- package/dist/consolidation-undo.d.ts +1 -1
- package/dist/contradiction/index.d.ts +1 -1
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +1 -1
- package/dist/entity-retrieval.js +4 -3
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +3 -3
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction.d.ts +1 -1
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.js +30 -30
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +4 -3
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +4 -3
- package/dist/maintenance/rebuild-memory-projection.js +5 -4
- package/dist/mcp-memory-inspector-app.d.ts +5 -5
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +44 -2
- package/dist/memory-cache.js +6 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +1 -1
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +1 -1
- package/dist/namespaces/migrate.js +12 -11
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +9 -8
- package/dist/namespaces/storage.d.ts +1 -1
- package/dist/namespaces/storage.js +4 -3
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +14 -13
- package/dist/{orchestrator-D1wcmPNj.d.ts → orchestrator-Cg1UkvmO.d.ts} +2 -2
- package/dist/orchestrator.d.ts +3 -3
- package/dist/orchestrator.js +20 -20
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/qmd-recall-cache.d.ts +5 -2
- package/dist/qmd-recall-cache.js +3 -1
- package/dist/qmd.d.ts +17 -1
- package/dist/qmd.js +4 -3
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +2 -1
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-state.d.ts +17 -1
- package/dist/recall-state.js +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/retrieval-agents.d.ts +1 -1
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/schemas.d.ts +24 -24
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +8 -7
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +8 -7
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/lancedb-backend.js +2 -2
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.js +2 -2
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/orama-backend.js +2 -2
- package/dist/search/port.d.ts +21 -2
- package/dist/search/port.js +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-MWOdNtSE.d.ts → semantic-consolidation-BICZvQ3C.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +5 -4
- package/dist/semantic-rule-promotion.js +4 -3
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +4 -3
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/storage.d.ts +1 -1
- package/dist/storage.js +3 -2
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +1 -1
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/{types-CgcCpUrf.d.ts → types-D96bCB3C.d.ts} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +4 -3
- package/package.json +1 -1
- package/src/memory-cache.test.ts +30 -0
- package/src/memory-cache.ts +129 -16
- package/src/namespaces/search.ts +16 -0
- package/src/orchestrator.ts +144 -3
- package/src/qmd-recall-cache.ts +6 -0
- package/src/qmd.ts +131 -18
- package/src/recall-state.ts +47 -21
- package/src/search/port.ts +25 -0
- package/src/storage.ts +27 -3
- package/dist/chunk-4PLOQDBB.js.map +0 -1
- package/dist/chunk-7VWDC7AD.js.map +0 -1
- package/dist/chunk-CPPS65WS.js.map +0 -1
- package/dist/chunk-EKQMQQ3U.js.map +0 -1
- package/dist/chunk-FWIROLS6.js.map +0 -1
- package/dist/chunk-M3VYPE2H.js.map +0 -1
- package/dist/chunk-QRSKPI62.js.map +0 -1
- /package/dist/{chunk-5PFIMBJJ.js.map → chunk-4SYURHI6.js.map} +0 -0
- /package/dist/{chunk-2VCTTEJM.js.map → chunk-5G2DNO54.js.map} +0 -0
- /package/dist/{chunk-3R6OP33G.js.map → chunk-7F7LC6HW.js.map} +0 -0
- /package/dist/{chunk-ZCMO46YY.js.map → chunk-A7EF2XRO.js.map} +0 -0
- /package/dist/{chunk-BNUAOLDK.js.map → chunk-ANJOULTP.js.map} +0 -0
- /package/dist/{chunk-QFKRE7AU.js.map → chunk-BP5O3GYD.js.map} +0 -0
- /package/dist/{chunk-BKRIAXTU.js.map → chunk-D2EFNQMY.js.map} +0 -0
- /package/dist/{chunk-6M4LYWA2.js.map → chunk-D44FQVCU.js.map} +0 -0
- /package/dist/{chunk-SSSXWIBP.js.map → chunk-EMSC4P66.js.map} +0 -0
- /package/dist/{chunk-MBZAESQ3.js.map → chunk-F6O7IOS3.js.map} +0 -0
- /package/dist/{chunk-6G5JEN55.js.map → chunk-FZC2WSDB.js.map} +0 -0
- /package/dist/{chunk-KI6QM5AV.js.map → chunk-GY3V3SUI.js.map} +0 -0
- /package/dist/{chunk-VJYFXDCZ.js.map → chunk-NMPEJV5M.js.map} +0 -0
- /package/dist/{chunk-7KSPKZIQ.js.map → chunk-PXVFMQLD.js.map} +0 -0
- /package/dist/{chunk-FIS5RT6K.js.map → chunk-QXHBWFR3.js.map} +0 -0
- /package/dist/{chunk-G2VVBWFU.js.map → chunk-RQGR3ETH.js.map} +0 -0
- /package/dist/{chunk-GGL7R2L2.js.map → chunk-RQRKQJYM.js.map} +0 -0
- /package/dist/{chunk-JI3LQFJH.js.map → chunk-TBLGI2LT.js.map} +0 -0
- /package/dist/{chunk-RVT6U6PV.js.map → chunk-TWAJICBN.js.map} +0 -0
- /package/dist/{chunk-46RXRASB.js.map → chunk-TYIXG4VR.js.map} +0 -0
- /package/dist/{chunk-5PLUC5OB.js.map → chunk-WSQG37DV.js.map} +0 -0
- /package/dist/{chunk-GRYAECRV.js.map → chunk-ZJH723NM.js.map} +0 -0
package/src/recall-state.ts
CHANGED
|
@@ -3,6 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
import { log } from "./logger.js";
|
|
5
5
|
import { writeFileAtomically } from "./maintenance/atomic-file.js";
|
|
6
|
+
import type { SearchDegradation } from "./search/port.js";
|
|
6
7
|
import type {
|
|
7
8
|
IdentityInjectionMode,
|
|
8
9
|
RecallPlanMode,
|
|
@@ -59,6 +60,14 @@ export interface LastRecallSnapshot {
|
|
|
59
60
|
* graph-path `recallExplain` operation.
|
|
60
61
|
*/
|
|
61
62
|
tierExplain?: RecallTierExplain;
|
|
63
|
+
/**
|
|
64
|
+
* Backend degradations observed while serving this recall (issue #1536).
|
|
65
|
+
* Present only when a search backend reported unavailable/loading/timeout
|
|
66
|
+
* during the recall — distinguishing "no matches" from "backend could not
|
|
67
|
+
* answer" (CLAUDE.md rule 34). Deliberately independent of `tierExplain`,
|
|
68
|
+
* which is gated behind `recallDirectAnswerEnabled`.
|
|
69
|
+
*/
|
|
70
|
+
backendDegradations?: SearchDegradation[];
|
|
62
71
|
}
|
|
63
72
|
|
|
64
73
|
export interface GraphRecallExpandedEntry {
|
|
@@ -136,6 +145,31 @@ type StateFileWriter = (filePath: string, content: string) => Promise<void>;
|
|
|
136
145
|
* qmd-recall-cache.ts). The payload is pure JSON-shaped data, so
|
|
137
146
|
* structuredClone is both safe and complete here.
|
|
138
147
|
*/
|
|
148
|
+
/**
|
|
149
|
+
* Stale-snapshot identity guard shared by the annotate methods (issue #518):
|
|
150
|
+
* a writeNonce match wins outright; otherwise traceId (when expected) and
|
|
151
|
+
* recordedAt are compared. Extracted so every annotation surface applies
|
|
152
|
+
* identical guards (CLAUDE.md rule 22).
|
|
153
|
+
*/
|
|
154
|
+
function snapshotMatchesExpectedIdentity(
|
|
155
|
+
current: LastRecallSnapshot,
|
|
156
|
+
expected?: { writeNonce?: string; traceId?: string; recordedAt?: string },
|
|
157
|
+
): boolean {
|
|
158
|
+
if (!expected) return true;
|
|
159
|
+
if (typeof expected.writeNonce === "string" && expected.writeNonce.length > 0) {
|
|
160
|
+
return current.writeNonce === expected.writeNonce;
|
|
161
|
+
}
|
|
162
|
+
const hasExpectedTraceId =
|
|
163
|
+
typeof expected.traceId === "string" && expected.traceId.length > 0;
|
|
164
|
+
if (hasExpectedTraceId) {
|
|
165
|
+
return current.traceId === expected.traceId;
|
|
166
|
+
}
|
|
167
|
+
if (expected.recordedAt !== undefined) {
|
|
168
|
+
return current.recordedAt === expected.recordedAt;
|
|
169
|
+
}
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
|
|
139
173
|
function cloneTierExplain(
|
|
140
174
|
tierExplain: RecallTierExplain | undefined,
|
|
141
175
|
): RecallTierExplain | undefined {
|
|
@@ -271,6 +305,13 @@ export class LastRecallStore {
|
|
|
271
305
|
* can render which retrieval tier served the query.
|
|
272
306
|
*/
|
|
273
307
|
tierExplain?: RecallTierExplain;
|
|
308
|
+
/**
|
|
309
|
+
* Backend degradations observed while serving this recall (issue #1536).
|
|
310
|
+
* Passed at record time so the published snapshot is born annotated —
|
|
311
|
+
* a post-record annotation would leave a window where readers see the
|
|
312
|
+
* snapshot without them (codex review on #1544).
|
|
313
|
+
*/
|
|
314
|
+
backendDegradations?: SearchDegradation[];
|
|
274
315
|
}): Promise<void> {
|
|
275
316
|
const now = new Date().toISOString();
|
|
276
317
|
const queryHash = createHash("sha256").update(opts.query).digest("hex");
|
|
@@ -302,6 +343,10 @@ export class LastRecallStore {
|
|
|
302
343
|
identityInjectedChars: opts.identityInjection?.injectedChars,
|
|
303
344
|
identityInjectionTruncated: opts.identityInjection?.truncated,
|
|
304
345
|
tierExplain: opts.tierExplain,
|
|
346
|
+
backendDegradations:
|
|
347
|
+
opts.backendDegradations && opts.backendDegradations.length > 0
|
|
348
|
+
? opts.backendDegradations
|
|
349
|
+
: undefined,
|
|
305
350
|
};
|
|
306
351
|
// `cloneLastRecallSnapshot` handles `null` but that never applies
|
|
307
352
|
// at this call site — the non-null assertion keeps the type
|
|
@@ -355,27 +400,7 @@ export class LastRecallStore {
|
|
|
355
400
|
): Promise<void> {
|
|
356
401
|
const current = this.state[sessionKey];
|
|
357
402
|
if (!current) return;
|
|
358
|
-
if (expected)
|
|
359
|
-
if (
|
|
360
|
-
typeof expected.writeNonce === "string" &&
|
|
361
|
-
expected.writeNonce.length > 0
|
|
362
|
-
) {
|
|
363
|
-
if (current.writeNonce !== expected.writeNonce) return;
|
|
364
|
-
} else {
|
|
365
|
-
const hasExpectedTraceId =
|
|
366
|
-
typeof expected.traceId === "string" && expected.traceId.length > 0;
|
|
367
|
-
const traceIdMatches =
|
|
368
|
-
hasExpectedTraceId && current.traceId === expected.traceId;
|
|
369
|
-
const recordedAtMatches =
|
|
370
|
-
expected.recordedAt !== undefined &&
|
|
371
|
-
current.recordedAt === expected.recordedAt;
|
|
372
|
-
if (hasExpectedTraceId) {
|
|
373
|
-
if (!traceIdMatches) return;
|
|
374
|
-
} else if (expected.recordedAt !== undefined && !recordedAtMatches) {
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
}
|
|
403
|
+
if (!snapshotMatchesExpectedIdentity(current, expected)) return;
|
|
379
404
|
this.state[sessionKey] = {
|
|
380
405
|
...current,
|
|
381
406
|
tierExplain: cloneTierExplain(tierExplain),
|
|
@@ -387,6 +412,7 @@ export class LastRecallStore {
|
|
|
387
412
|
}
|
|
388
413
|
}
|
|
389
414
|
|
|
415
|
+
|
|
390
416
|
private flushState(): Promise<void> {
|
|
391
417
|
const run = this.stateWriteChain.catch(() => undefined).then(async () => {
|
|
392
418
|
await mkdir(path.dirname(this.statePath), { recursive: true });
|
package/src/search/port.ts
CHANGED
|
@@ -12,8 +12,33 @@ export interface SearchQueryOptions {
|
|
|
12
12
|
structuredSearches?: Array<{ type: "lex" | "vec" | "hyde"; query: string }>;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* A search that returned empty (or partial) results because the backend was
|
|
17
|
+
* unavailable, still loading, or timed out — cases that are otherwise
|
|
18
|
+
* indistinguishable from a genuine "no matches" (issue #1536, CLAUDE.md
|
|
19
|
+
* rule 34).
|
|
20
|
+
*/
|
|
21
|
+
export interface SearchDegradation {
|
|
22
|
+
backend: "qmd";
|
|
23
|
+
code:
|
|
24
|
+
| "backend_unavailable"
|
|
25
|
+
| "daemon_timeout"
|
|
26
|
+
| "daemon_loading"
|
|
27
|
+
| "subprocess_error"
|
|
28
|
+
| "deadline_exceeded";
|
|
29
|
+
detail?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
15
32
|
export interface SearchExecutionOptions {
|
|
16
33
|
signal?: AbortSignal;
|
|
34
|
+
/**
|
|
35
|
+
* Observer invoked when the backend degrades during this call (#1536).
|
|
36
|
+
* Callers that need to distinguish empty-because-degraded from
|
|
37
|
+
* empty-because-no-matches (recall x-ray, fallback decisions) pass a
|
|
38
|
+
* collector here. Observer failures are swallowed by the notifier —
|
|
39
|
+
* observability must never break search.
|
|
40
|
+
*/
|
|
41
|
+
onDegradation?: (degradation: SearchDegradation) => void;
|
|
17
42
|
}
|
|
18
43
|
|
|
19
44
|
export function resolveEnsureCollectionArgs(
|
package/src/storage.ts
CHANGED
|
@@ -5,7 +5,7 @@ import path from "node:path";
|
|
|
5
5
|
import { log } from "./logger.js";
|
|
6
6
|
import { isErrnoCode } from "./utils/errno.js";
|
|
7
7
|
import { RECALL_FALLBACK_DIRS } from "./utils/category-dir.js";
|
|
8
|
-
import { getCachedEntities,
|
|
8
|
+
import { getCachedEntities, invalidateAllForDir, setCachedEntities } from "./memory-cache.js";
|
|
9
9
|
import { rotateMarkdownFileToArchive } from "./hygiene.js";
|
|
10
10
|
import { sanitizeMemoryContent } from "./sanitize.js";
|
|
11
11
|
import { createVersion as createPageVersion, type VersioningConfig, type VersionTrigger } from "./page-versioning.js";
|
|
@@ -2335,7 +2335,10 @@ export class StorageManager {
|
|
|
2335
2335
|
setSecureStoreKey(key: Buffer | null, encryptOnWrite = true): void {
|
|
2336
2336
|
this._secureStoreKey = key;
|
|
2337
2337
|
this._secureStoreEncryptOnWrite = encryptOnWrite;
|
|
2338
|
-
|
|
2338
|
+
// Route through the invalidation chokepoint (issue #1535): a key change
|
|
2339
|
+
// (or store lock) must evict every cache layer that may hold content
|
|
2340
|
+
// decrypted under the previous key, not just the entity layer.
|
|
2341
|
+
invalidateAllForDir(this.baseDir);
|
|
2339
2342
|
this.invalidateKnowledgeIndexCache();
|
|
2340
2343
|
}
|
|
2341
2344
|
|
|
@@ -2511,6 +2514,13 @@ export class StorageManager {
|
|
|
2511
2514
|
|
|
2512
2515
|
private bumpMemoryStatusVersion(): void {
|
|
2513
2516
|
this.bumpSharedVersion("memory-status", StorageManager.memoryStatusVersionByDir);
|
|
2517
|
+
// Invalidation chokepoint (issue #1535): status/entity mutations funnel
|
|
2518
|
+
// through this bump — several of them (supersedeMemory, archiveMemories,
|
|
2519
|
+
// writeEntity, cleanExpiredCommitments) do NOT call
|
|
2520
|
+
// invalidateAllMemoriesCache(), so the chokepoint must fire here as well.
|
|
2521
|
+
// Version bumps only invalidate the version-checked layers lazily; the
|
|
2522
|
+
// TTL-based QMD caches need this eager clear.
|
|
2523
|
+
invalidateAllForDir(this.baseDir);
|
|
2514
2524
|
}
|
|
2515
2525
|
|
|
2516
2526
|
getMemoryStatusVersion(): number {
|
|
@@ -2943,8 +2953,9 @@ export class StorageManager {
|
|
|
2943
2953
|
}
|
|
2944
2954
|
|
|
2945
2955
|
private async invalidateAfterOfflineSyncMutation(filePath: string): Promise<void> {
|
|
2956
|
+
// invalidateAllMemoriesCache() routes through the invalidateAllForDir()
|
|
2957
|
+
// chokepoint, which also covers the entity cache (issue #1535).
|
|
2946
2958
|
this.invalidateAllMemoriesCache();
|
|
2947
|
-
invalidateCachedEntities(this.baseDir);
|
|
2948
2959
|
this.invalidateKnowledgeIndexCache();
|
|
2949
2960
|
this.factHashIndexAuthoritative = false;
|
|
2950
2961
|
await unlink(this.factHashIndexReadyPath).catch((error: unknown) => {
|
|
@@ -4002,6 +4013,13 @@ export class StorageManager {
|
|
|
4002
4013
|
* inside cold/, archiveMemory, etc.). */
|
|
4003
4014
|
private invalidateAllMemoriesCache(): void {
|
|
4004
4015
|
StorageManager.allMemoriesInFlight.delete(this.baseDir);
|
|
4016
|
+
// Invalidation chokepoint (issue #1535): eagerly evict EVERY process-level
|
|
4017
|
+
// cache layer that can serve memory-derived content for this dir — hot,
|
|
4018
|
+
// archive, entity, derived episode/rule views, and both QMD result caches
|
|
4019
|
+
// (qmdSearchCache and qmdRecallCache). Before this call was added, the QMD
|
|
4020
|
+
// recall cache was never invalidated on mutations, so recall served
|
|
4021
|
+
// pre-edit bundles for the remainder of its fresh/stale TTL window.
|
|
4022
|
+
invalidateAllForDir(this.baseDir);
|
|
4005
4023
|
}
|
|
4006
4024
|
|
|
4007
4025
|
/**
|
|
@@ -4020,6 +4038,12 @@ export class StorageManager {
|
|
|
4020
4038
|
const coldRoot = path.join(this.baseDir, "cold");
|
|
4021
4039
|
StorageManager.coldMemoriesCache.delete(coldRoot);
|
|
4022
4040
|
this.bumpColdWriteVersion();
|
|
4041
|
+
// Invalidation chokepoint (issue #1535): cold-tier mutations can reach
|
|
4042
|
+
// this funnel without invalidateAllMemoriesCache() (e.g. the cold-only
|
|
4043
|
+
// branch of invalidateMemoryCachesForTiers used by maintenance/purge), so
|
|
4044
|
+
// the chokepoint must fire here too — cold memories are recallable and a
|
|
4045
|
+
// cold delete must not leave stale QMD recall bundles behind.
|
|
4046
|
+
invalidateAllForDir(this.baseDir);
|
|
4023
4047
|
}
|
|
4024
4048
|
|
|
4025
4049
|
/** Return the current cold-write version counter for this storage root.
|