@remnic/core 9.3.653 → 9.3.654
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 +17 -17
- package/dist/access-http.d.ts +4 -4
- package/dist/access-http.js +10 -10
- package/dist/access-mcp.d.ts +4 -4
- package/dist/access-mcp.js +9 -9
- package/dist/access-schema.d.ts +12 -12
- package/dist/{access-service-CdJFd3_b.d.ts → access-service-C8A5hoXJ.d.ts} +11 -2
- package/dist/access-service.d.ts +4 -4
- package/dist/access-service.js +8 -8
- 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/active-recall.js +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 +3 -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 +4 -4
- package/dist/{chunk-KJDKZVF3.js → chunk-2DSTAWNZ.js} +3 -3
- package/dist/chunk-3RACUBII.js +212 -0
- package/dist/chunk-3RACUBII.js.map +1 -0
- package/dist/{chunk-Y7NWBBHV.js → chunk-6CVI6BP6.js} +2 -2
- package/dist/{chunk-R3PQUPQ4.js → chunk-6IMKOIZ6.js} +85 -3
- package/dist/chunk-6IMKOIZ6.js.map +1 -0
- package/dist/{chunk-WTI35CVJ.js → chunk-BJA6DQOC.js} +5 -5
- package/dist/{chunk-GI45G4BK.js → chunk-BP2EV6W5.js} +3 -3
- package/dist/{chunk-WLGE6KEO.js → chunk-DBM2BD22.js} +3 -3
- package/dist/{chunk-IENGGY2C.js → chunk-ENV6RDTD.js} +2 -2
- package/dist/{chunk-BEMWL2FZ.js → chunk-FVRBLJP6.js} +2 -2
- package/dist/{chunk-H3PHZLMF.js → chunk-GKKAXVAJ.js} +20 -11
- package/dist/chunk-GKKAXVAJ.js.map +1 -0
- package/dist/{chunk-NOBL7OUP.js → chunk-GPW2E4LN.js} +12 -5
- package/dist/{chunk-NOBL7OUP.js.map → chunk-GPW2E4LN.js.map} +1 -1
- package/dist/{chunk-KWM33SPU.js → chunk-JMQSYGXS.js} +2 -2
- package/dist/{chunk-QQHIQ7JD.js → chunk-JYN7QNTA.js} +87 -18
- package/dist/chunk-JYN7QNTA.js.map +1 -0
- package/dist/{chunk-AJE7FJVE.js → chunk-K6X553JB.js} +2 -2
- package/dist/{chunk-E3J6O6N7.js → chunk-LJCEWTG3.js} +19 -8
- package/dist/{chunk-E3J6O6N7.js.map → chunk-LJCEWTG3.js.map} +1 -1
- package/dist/{chunk-EW52H5EM.js → chunk-NAZWHTYV.js} +12 -5
- package/dist/chunk-NAZWHTYV.js.map +1 -0
- package/dist/{chunk-XMN6MMTU.js → chunk-NCGWXCSW.js} +2 -2
- package/dist/{chunk-C43KEWEV.js → chunk-NE2JBMLN.js} +1 -1
- package/dist/chunk-NE2JBMLN.js.map +1 -0
- package/dist/{chunk-SPMZZUEJ.js → chunk-OL2364SB.js} +2020 -368
- package/dist/chunk-OL2364SB.js.map +1 -0
- package/dist/{chunk-JF7SFXTG.js → chunk-QKK64Z6M.js} +2 -2
- package/dist/{chunk-IVYSVAC6.js → chunk-QW6JZO5P.js} +2 -2
- package/dist/{chunk-EHQLDFSH.js → chunk-RGPUQ66K.js} +2 -2
- package/dist/{chunk-CFOCZPIQ.js → chunk-T2C6QJG2.js} +2 -2
- package/dist/{chunk-V4UDXYGG.js → chunk-XWQ6ERUG.js} +2 -2
- package/dist/{chunk-BNFRL6QW.js → chunk-Y2RIIF6H.js} +2 -2
- package/dist/{chunk-C63WC454.js → chunk-YLZLPVKK.js} +22 -1
- package/dist/chunk-YLZLPVKK.js.map +1 -0
- package/dist/{chunk-RZOBQ23O.js → chunk-Z5MQI7K2.js} +2 -2
- package/dist/{chunk-PRQXUSQV.js → chunk-ZCORQM74.js} +2 -2
- package/dist/{cli-DDo7Qgs-.d.ts → cli-uQgvDFNE.d.ts} +3 -3
- package/dist/cli.d.ts +5 -5
- package/dist/cli.js +22 -22
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +3 -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/config.js +1 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +3 -3
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +3 -3
- package/dist/consolidation-provenance-check.d.ts +1 -1
- package/dist/consolidation-undo.d.ts +1 -1
- package/dist/contradiction/index.d.ts +19 -1
- package/dist/contradiction/index.js +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 +3 -3
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +3 -3
- package/dist/explicit-capture.js +1 -1
- 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 -28
- package/dist/index.js.map +1 -1
- 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 +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 +4 -4
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -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 +4 -4
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/storage.d.ts +52 -3
- package/dist/namespaces/storage.js +9 -5
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +7 -7
- package/dist/{orchestrator-8fTZsa0y.d.ts → orchestrator-B4Y4sWQH.d.ts} +503 -3
- package/dist/orchestrator.d.ts +3 -3
- package/dist/orchestrator.js +13 -13
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +1 -1
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +2 -2
- package/dist/{resolution-3SAP4SH2.js → resolution-IDTEBJFS.js} +2 -2
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +2 -2
- 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/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/index.d.ts +1 -1
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-DKdYzQOg.d.ts → semantic-consolidation-BKd0Pype.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +4 -4
- package/dist/semantic-rule-promotion.js +3 -3
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +3 -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 +2 -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-D8yUmSik.d.ts → types-BgChEr0M.d.ts} +11 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +3 -3
- package/package.json +1 -1
- package/src/access-http.ts +7 -0
- package/src/access-mcp.ts +7 -0
- package/src/access-service.ts +12 -0
- package/src/cli.ts +104 -0
- package/src/config.test.ts +40 -0
- package/src/config.ts +29 -0
- package/src/contradiction/contradiction.test.ts +284 -0
- package/src/contradiction/resolution.ts +151 -4
- package/src/explicit-capture.ts +31 -10
- package/src/index.ts +10 -0
- package/src/namespaces/catalog.test.ts +3356 -0
- package/src/namespaces/catalog.ts +2123 -0
- package/src/namespaces/storage.ts +210 -30
- package/src/orchestrator-flush.test.ts +300 -0
- package/src/orchestrator.ts +851 -240
- package/src/types.ts +11 -0
- package/dist/chunk-C43KEWEV.js.map +0 -1
- package/dist/chunk-C63WC454.js.map +0 -1
- package/dist/chunk-EW52H5EM.js.map +0 -1
- package/dist/chunk-H3PHZLMF.js.map +0 -1
- package/dist/chunk-ORGWWNJG.js +0 -131
- package/dist/chunk-ORGWWNJG.js.map +0 -1
- package/dist/chunk-QQHIQ7JD.js.map +0 -1
- package/dist/chunk-R3PQUPQ4.js.map +0 -1
- package/dist/chunk-SPMZZUEJ.js.map +0 -1
- /package/dist/{chunk-KJDKZVF3.js.map → chunk-2DSTAWNZ.js.map} +0 -0
- /package/dist/{chunk-Y7NWBBHV.js.map → chunk-6CVI6BP6.js.map} +0 -0
- /package/dist/{chunk-WTI35CVJ.js.map → chunk-BJA6DQOC.js.map} +0 -0
- /package/dist/{chunk-GI45G4BK.js.map → chunk-BP2EV6W5.js.map} +0 -0
- /package/dist/{chunk-WLGE6KEO.js.map → chunk-DBM2BD22.js.map} +0 -0
- /package/dist/{chunk-IENGGY2C.js.map → chunk-ENV6RDTD.js.map} +0 -0
- /package/dist/{chunk-BEMWL2FZ.js.map → chunk-FVRBLJP6.js.map} +0 -0
- /package/dist/{chunk-KWM33SPU.js.map → chunk-JMQSYGXS.js.map} +0 -0
- /package/dist/{chunk-AJE7FJVE.js.map → chunk-K6X553JB.js.map} +0 -0
- /package/dist/{chunk-XMN6MMTU.js.map → chunk-NCGWXCSW.js.map} +0 -0
- /package/dist/{chunk-JF7SFXTG.js.map → chunk-QKK64Z6M.js.map} +0 -0
- /package/dist/{chunk-IVYSVAC6.js.map → chunk-QW6JZO5P.js.map} +0 -0
- /package/dist/{chunk-EHQLDFSH.js.map → chunk-RGPUQ66K.js.map} +0 -0
- /package/dist/{chunk-CFOCZPIQ.js.map → chunk-T2C6QJG2.js.map} +0 -0
- /package/dist/{chunk-V4UDXYGG.js.map → chunk-XWQ6ERUG.js.map} +0 -0
- /package/dist/{chunk-BNFRL6QW.js.map → chunk-Y2RIIF6H.js.map} +0 -0
- /package/dist/{chunk-RZOBQ23O.js.map → chunk-Z5MQI7K2.js.map} +0 -0
- /package/dist/{chunk-PRQXUSQV.js.map → chunk-ZCORQM74.js.map} +0 -0
- /package/dist/{resolution-3SAP4SH2.js.map → resolution-IDTEBJFS.js.map} +0 -0
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
* reimplement supersession logic here (rule 22: deduplicate resolution).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { log } from "../logger.js";
|
|
8
9
|
import type { StorageManager } from "../storage.js";
|
|
9
10
|
import type { MemoryCategory, MemoryFile } from "../types.js";
|
|
10
11
|
import type { ResolutionVerb } from "./contradiction-review.js";
|
|
11
|
-
import {
|
|
12
|
-
import { log } from "../logger.js";
|
|
12
|
+
import { readPair, resolvePair } from "./contradiction-review.js";
|
|
13
13
|
|
|
14
14
|
export interface ResolutionResult {
|
|
15
15
|
pairId: string;
|
|
@@ -29,6 +29,24 @@ export interface ExecuteResolutionOptions {
|
|
|
29
29
|
mergedCategory?: MemoryCategory;
|
|
30
30
|
/** Resolve storage for the pair namespace, or the default namespace for legacy unscoped pairs. */
|
|
31
31
|
storageForNamespace?: (namespace: string | undefined) => StorageManager | Promise<StorageManager>;
|
|
32
|
+
/**
|
|
33
|
+
* Best-effort hook invoked after a contradiction resolution leaves a durable
|
|
34
|
+
* mutation in the namespace's memory files (issue #1499 sweep, NH1dX / NH3X3).
|
|
35
|
+
* Every mutating verb — `merge` (creates a new memory and supersedes both
|
|
36
|
+
* sources), `keep-a`, and `keep-b` (supersede the losing source + rewrite
|
|
37
|
+
* frontmatter) — writes directly to the pair's (possibly DYNAMIC) namespace
|
|
38
|
+
* storage, bypassing the extraction write path that records catalog writes. So
|
|
39
|
+
* without this the namespace's `lastWriteAt` stays stale and QMD maintenance /
|
|
40
|
+
* `writtenSince` can skip a namespace whose only post-write mutation is
|
|
41
|
+
* resolving a contradiction. It fires after the resolution commits, or after a
|
|
42
|
+
* failed resolution/rollback path when durable memory changes are still left on
|
|
43
|
+
* disk. If a failure rolls back cleanly, this is never called, so the catalog
|
|
44
|
+
* never records a write that did not survive (rule #25). Non-mutating verbs
|
|
45
|
+
* (`both-valid`, `needs-more-context`) never trigger it. Callers wire this to
|
|
46
|
+
* `Orchestrator.recordCatalogWrite(namespace, storageDir)`. Must be
|
|
47
|
+
* failure-tolerant: it is fire-and-forget and must never affect resolution.
|
|
48
|
+
*/
|
|
49
|
+
onMergedMemoryWritten?: (namespace: string | undefined, storageDir: string) => void;
|
|
32
50
|
}
|
|
33
51
|
|
|
34
52
|
const VALID_VERBS: ResolutionVerb[] = ["keep-a", "keep-b", "merge", "both-valid", "needs-more-context"];
|
|
@@ -81,6 +99,48 @@ export async function executeResolution(
|
|
|
81
99
|
let message = "";
|
|
82
100
|
let supersedeFailed = false;
|
|
83
101
|
let rollbackAfterResolveFailure: (() => Promise<boolean>) | null = null;
|
|
102
|
+
// Deferred catalog-write touch for any resolution that leaves durable namespace
|
|
103
|
+
// memory mutations (issue #1499 sweep, NH1dX / NH3X3). Rule #25: never record a
|
|
104
|
+
// catalog touch for a write that is fully rolled back. Successful mutating
|
|
105
|
+
// resolutions invoke it after `resolvePair` persists. Failed paths invoke it
|
|
106
|
+
// only when rollback inspection shows the namespace still differs from the
|
|
107
|
+
// pre-mutation snapshot.
|
|
108
|
+
let recordCatalogWriteTouch: (() => void) | null = null;
|
|
109
|
+
// Returns the deferred touch fn for a mutating verb (or null when the caller
|
|
110
|
+
// wired no catalog hook), so each branch assigns `recordCatalogWriteTouch`
|
|
111
|
+
// directly in the function body — keeping TS control-flow narrowing intact at
|
|
112
|
+
// the post-commit invocation below.
|
|
113
|
+
const buildCatalogTouch = (): (() => void) | null => {
|
|
114
|
+
if (!options.onMergedMemoryWritten) return null;
|
|
115
|
+
const onMergedMemoryWritten = options.onMergedMemoryWritten;
|
|
116
|
+
const namespace = pair.namespace;
|
|
117
|
+
const storageDir = resolutionStorage.dir;
|
|
118
|
+
return () => onMergedMemoryWritten(namespace, storageDir);
|
|
119
|
+
};
|
|
120
|
+
const catalogWriteTouch = buildCatalogTouch();
|
|
121
|
+
const recordCatalogWriteTouchSafely = (context: string, touch = catalogWriteTouch): void => {
|
|
122
|
+
if (!touch) return;
|
|
123
|
+
try {
|
|
124
|
+
touch();
|
|
125
|
+
} catch (err) {
|
|
126
|
+
log.warn(
|
|
127
|
+
"[contradiction-resolution] catalog write touch failed for pair=%s context=%s: %s",
|
|
128
|
+
pairId,
|
|
129
|
+
context,
|
|
130
|
+
err instanceof Error ? err.message : err,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
const touchCatalogIfRollbackLeftChange = async (
|
|
135
|
+
context: string,
|
|
136
|
+
snapshots: MemoryFile[],
|
|
137
|
+
replacement?: Extract<MergeReplacement, { ok: true }>,
|
|
138
|
+
): Promise<void> => {
|
|
139
|
+
if (!catalogWriteTouch) return;
|
|
140
|
+
if (await rollbackLeftDurableMutation(resolutionStorage, snapshots, replacement)) {
|
|
141
|
+
recordCatalogWriteTouchSafely(context);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
84
144
|
|
|
85
145
|
switch (verb) {
|
|
86
146
|
case "keep-a": {
|
|
@@ -98,6 +158,9 @@ export async function executeResolution(
|
|
|
98
158
|
affectedIds.push(idB);
|
|
99
159
|
rollbackAfterResolveFailure = async () =>
|
|
100
160
|
restoreMemorySnapshot(resolutionStorage, sourceB!, "contradiction-resolution:keep-a-rollback");
|
|
161
|
+
// keep-a superseded idB in this namespace — record a catalog touch once
|
|
162
|
+
// the resolution durably commits (NH3X3).
|
|
163
|
+
recordCatalogWriteTouch = catalogWriteTouch;
|
|
101
164
|
message = `Kept ${idA}, superseded ${idB}`;
|
|
102
165
|
}
|
|
103
166
|
else {
|
|
@@ -105,6 +168,9 @@ export async function executeResolution(
|
|
|
105
168
|
const rolledBack = sourceB
|
|
106
169
|
? await restoreMemorySnapshot(resolutionStorage, sourceB, "contradiction-resolution:keep-a-rollback")
|
|
107
170
|
: false;
|
|
171
|
+
if (sourceB && !rolledBack) {
|
|
172
|
+
await touchCatalogIfRollbackLeftChange("keep-a-rollback-incomplete", [sourceB]);
|
|
173
|
+
}
|
|
108
174
|
message = rolledBack
|
|
109
175
|
? `Supersede failed for ${idB}; restored ${idB} and did not resolve`
|
|
110
176
|
: `Supersede failed for ${idB}; rollback incomplete for ${idB} and pair is not resolved`;
|
|
@@ -126,6 +192,9 @@ export async function executeResolution(
|
|
|
126
192
|
affectedIds.push(idA);
|
|
127
193
|
rollbackAfterResolveFailure = async () =>
|
|
128
194
|
restoreMemorySnapshot(resolutionStorage, sourceA!, "contradiction-resolution:keep-b-rollback");
|
|
195
|
+
// keep-b superseded idA in this namespace — record a catalog touch once
|
|
196
|
+
// the resolution durably commits (NH3X3).
|
|
197
|
+
recordCatalogWriteTouch = catalogWriteTouch;
|
|
129
198
|
message = `Kept ${idB}, superseded ${idA}`;
|
|
130
199
|
}
|
|
131
200
|
else {
|
|
@@ -133,6 +202,9 @@ export async function executeResolution(
|
|
|
133
202
|
const rolledBack = sourceA
|
|
134
203
|
? await restoreMemorySnapshot(resolutionStorage, sourceA, "contradiction-resolution:keep-b-rollback")
|
|
135
204
|
: false;
|
|
205
|
+
if (sourceA && !rolledBack) {
|
|
206
|
+
await touchCatalogIfRollbackLeftChange("keep-b-rollback-incomplete", [sourceA]);
|
|
207
|
+
}
|
|
136
208
|
message = rolledBack
|
|
137
209
|
? `Supersede failed for ${idA}; restored ${idA} and did not resolve`
|
|
138
210
|
: `Supersede failed for ${idA}; rollback incomplete for ${idA} and pair is not resolved`;
|
|
@@ -157,6 +229,9 @@ export async function executeResolution(
|
|
|
157
229
|
if (rolledBackA) {
|
|
158
230
|
await cleanupCreatedReplacement(resolutionStorage, replacement);
|
|
159
231
|
}
|
|
232
|
+
else {
|
|
233
|
+
await touchCatalogIfRollbackLeftChange("merge-first-rollback-incomplete", [replacement.sourceA], replacement);
|
|
234
|
+
}
|
|
160
235
|
break;
|
|
161
236
|
}
|
|
162
237
|
|
|
@@ -174,6 +249,13 @@ export async function executeResolution(
|
|
|
174
249
|
if (rolledBackA && rolledBackB) {
|
|
175
250
|
await cleanupCreatedReplacement(resolutionStorage, replacement);
|
|
176
251
|
}
|
|
252
|
+
else {
|
|
253
|
+
await touchCatalogIfRollbackLeftChange(
|
|
254
|
+
"merge-second-rollback-incomplete",
|
|
255
|
+
[replacement.sourceA, replacement.sourceB],
|
|
256
|
+
replacement,
|
|
257
|
+
);
|
|
258
|
+
}
|
|
177
259
|
break;
|
|
178
260
|
}
|
|
179
261
|
|
|
@@ -186,6 +268,17 @@ export async function executeResolution(
|
|
|
186
268
|
}
|
|
187
269
|
return rolledBackA && rolledBackB;
|
|
188
270
|
};
|
|
271
|
+
// Catalog write touch (issue #1499 sweep): the merge supersedes BOTH sources
|
|
272
|
+
// (and, when created, writes a fresh merged memory) in the pair's (possibly
|
|
273
|
+
// dynamic) namespace storage — but the resolution is not durable yet
|
|
274
|
+
// (resolvePair persists below, and a failure rolls the merge back). Defer
|
|
275
|
+
// the touch so it fires ONLY after the resolution commits past the rollback
|
|
276
|
+
// point (NH1dX, rule #25). Arm it for EVERY successful merge — even reusing
|
|
277
|
+
// an existing merged-id still supersedes both sources, a namespace mutation
|
|
278
|
+
// that must refresh `lastWriteAt` (NH3X3). Otherwise a dynamic namespace
|
|
279
|
+
// whose only durable mutation is a contradiction merge stays invisible to
|
|
280
|
+
// QMD maintenance / `writtenSince`. Best-effort on the caller side.
|
|
281
|
+
recordCatalogWriteTouch = catalogWriteTouch;
|
|
189
282
|
message = `Both memories superseded by merged ${replacement.mergedId}`;
|
|
190
283
|
break;
|
|
191
284
|
}
|
|
@@ -217,10 +310,20 @@ export async function executeResolution(
|
|
|
217
310
|
affectedIds.length = 0;
|
|
218
311
|
message = rolledBack
|
|
219
312
|
? `Resolution persistence failed; rolled back memory changes and did not resolve ${pairId}`
|
|
220
|
-
:
|
|
313
|
+
: "Resolution persistence failed; rollback incomplete and pair is not resolved";
|
|
314
|
+
if (!rolledBack && recordCatalogWriteTouch) {
|
|
315
|
+
recordCatalogWriteTouchSafely("resolve-persistence-rollback-incomplete", recordCatalogWriteTouch);
|
|
316
|
+
}
|
|
221
317
|
} else {
|
|
222
|
-
message =
|
|
318
|
+
message = "Resolution persistence failed; pair is not resolved";
|
|
223
319
|
}
|
|
320
|
+
} else if (recordCatalogWriteTouch) {
|
|
321
|
+
// The resolution durably committed (memory mutated AND the resolution
|
|
322
|
+
// persisted past the rollback point). Only now is it safe to record the
|
|
323
|
+
// catalog write for the namespace mutation (NH1dX / NH3X3, rule #25).
|
|
324
|
+
// Best-effort: the caller's callback swallows errors; guard here so a
|
|
325
|
+
// throwing callback never derails a successful resolution.
|
|
326
|
+
recordCatalogWriteTouchSafely("resolved", recordCatalogWriteTouch);
|
|
224
327
|
}
|
|
225
328
|
}
|
|
226
329
|
log.info("[contradiction-resolution] pair=%s verb=%s affected=%d", pairId, verb, affectedIds.length);
|
|
@@ -376,6 +479,50 @@ async function restoreMemorySnapshot(
|
|
|
376
479
|
}
|
|
377
480
|
}
|
|
378
481
|
|
|
482
|
+
async function rollbackLeftDurableMutation(
|
|
483
|
+
storage: StorageManager,
|
|
484
|
+
snapshots: MemoryFile[],
|
|
485
|
+
replacement?: Extract<MergeReplacement, { ok: true }>,
|
|
486
|
+
): Promise<boolean> {
|
|
487
|
+
for (const snapshot of snapshots) {
|
|
488
|
+
try {
|
|
489
|
+
const current = await storage.getMemoryById(snapshot.frontmatter.id);
|
|
490
|
+
if (!current) return true;
|
|
491
|
+
if (supersessionStateChanged(current, snapshot)) return true;
|
|
492
|
+
} catch (err) {
|
|
493
|
+
log.warn(
|
|
494
|
+
"[contradiction-resolution] rollback inspection failed for %s: %s",
|
|
495
|
+
snapshot.frontmatter.id,
|
|
496
|
+
err instanceof Error ? err.message : err,
|
|
497
|
+
);
|
|
498
|
+
return true;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if (replacement?.created) {
|
|
503
|
+
try {
|
|
504
|
+
return (await storage.getMemoryById(replacement.mergedId)) !== null;
|
|
505
|
+
} catch (err) {
|
|
506
|
+
log.warn(
|
|
507
|
+
"[contradiction-resolution] rollback replacement inspection failed for %s: %s",
|
|
508
|
+
replacement.mergedId,
|
|
509
|
+
err instanceof Error ? err.message : err,
|
|
510
|
+
);
|
|
511
|
+
return true;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function supersessionStateChanged(current: MemoryFile, snapshot: MemoryFile): boolean {
|
|
519
|
+
return (
|
|
520
|
+
current.frontmatter.status !== snapshot.frontmatter.status ||
|
|
521
|
+
current.frontmatter.supersededBy !== snapshot.frontmatter.supersededBy ||
|
|
522
|
+
current.frontmatter.supersededAt !== snapshot.frontmatter.supersededAt
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
|
|
379
526
|
async function cleanupCreatedReplacement(storage: StorageManager, replacement: Extract<MergeReplacement, { ok: true }>): Promise<void> {
|
|
380
527
|
if (!replacement.created) return;
|
|
381
528
|
await cleanupMemoryId(storage, replacement.mergedId);
|
package/src/explicit-capture.ts
CHANGED
|
@@ -435,6 +435,15 @@ export async function persistExplicitCapture(
|
|
|
435
435
|
expiresAt: candidate.expiresAt,
|
|
436
436
|
source: source === "inline" ? "explicit-inline" : "explicit",
|
|
437
437
|
});
|
|
438
|
+
// Record the catalog write touch (issue #1499, round 5 codex P2). Explicit
|
|
439
|
+
// captures bypass the extraction write path, so without this their namespace
|
|
440
|
+
// never updates `lastWriteAt`. An undefined namespace means the DEFAULT root
|
|
441
|
+
// (round 6, codex P2), which recordCatalogWrite resolves. The method is an
|
|
442
|
+
// optional best-effort hook — guard so Orchestrator-like callers without it
|
|
443
|
+
// don't break (rule #33). Best-effort and failure-tolerant.
|
|
444
|
+
if (typeof orchestrator.recordCatalogWrite === "function") {
|
|
445
|
+
orchestrator.recordCatalogWrite(resolvedNamespace, storage.dir);
|
|
446
|
+
}
|
|
438
447
|
|
|
439
448
|
const created = new Date().toISOString();
|
|
440
449
|
const event: MemoryLifecycleEvent = {
|
|
@@ -531,16 +540,28 @@ export async function queueExplicitCaptureForReview(
|
|
|
531
540
|
entityRef: sanitizeReviewMetadata(input.entityRef),
|
|
532
541
|
source: source === "inline" ? "explicit-inline-review" : "explicit-review",
|
|
533
542
|
});
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
543
|
+
try {
|
|
544
|
+
const created = await storage.getMemoryById(id);
|
|
545
|
+
if (created) {
|
|
546
|
+
await storage.writeMemoryFrontmatter(created, {
|
|
547
|
+
status: "pending_review",
|
|
548
|
+
updated: new Date().toISOString(),
|
|
549
|
+
}, {
|
|
550
|
+
actor: explicitCaptureActor(source),
|
|
551
|
+
reasonCode: reason,
|
|
552
|
+
ruleVersion: "explicit-capture.v1",
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
} finally {
|
|
556
|
+
// Record the catalog write touch (issue #1499, round 5/6 codex P2; NIhUg).
|
|
557
|
+
// A queued review capture writes memory to the namespace's root (the DEFAULT
|
|
558
|
+
// root when undefined), so its `lastWriteAt` must reflect the write once
|
|
559
|
+
// `writeMemory` returns an id. If the later pending-review frontmatter update
|
|
560
|
+
// fails, the memory file is still durable and must not disappear from
|
|
561
|
+
// writtenSince/maintenance scheduling. Guarded optional hook (rule #33).
|
|
562
|
+
if (typeof orchestrator.recordCatalogWrite === "function") {
|
|
563
|
+
orchestrator.recordCatalogWrite(queueNamespace, storage.dir);
|
|
564
|
+
}
|
|
544
565
|
}
|
|
545
566
|
const event: MemoryLifecycleEvent = {
|
|
546
567
|
eventId: `mle-${randomUUID()}`,
|
package/src/index.ts
CHANGED
|
@@ -323,6 +323,16 @@ export { MeilisearchBackend } from "./search/meilisearch-backend.js";
|
|
|
323
323
|
|
|
324
324
|
export { buildEntityRecallSection } from "./entity-retrieval.js";
|
|
325
325
|
export { resolvePrincipal } from "./namespaces/principal.js";
|
|
326
|
+
export {
|
|
327
|
+
NamespaceCatalog,
|
|
328
|
+
type NamespaceRecord,
|
|
329
|
+
type NamespaceKind,
|
|
330
|
+
type NamespaceDiscoverySource,
|
|
331
|
+
type NamespaceCatalogFilter,
|
|
332
|
+
type NamespaceTouchMetadata,
|
|
333
|
+
type NamespaceCatalogRebuildResult,
|
|
334
|
+
type NamespaceCatalogSkippedRoot,
|
|
335
|
+
} from "./namespaces/catalog.js";
|
|
326
336
|
|
|
327
337
|
// ---------------------------------------------------------------------------
|
|
328
338
|
// Session identity / transcript pathing (issue #1496)
|