@remnic/core 9.3.749 → 9.3.751
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-boundary.d.ts +8 -8
- package/dist/access-boundary.js +15 -15
- package/dist/access-cli.js +39 -39
- package/dist/access-http.d.ts +8 -8
- package/dist/access-http.js +20 -20
- package/dist/access-mcp.d.ts +8 -8
- package/dist/access-mcp.js +19 -19
- package/dist/access-operations-batch.js +16 -16
- package/dist/access-operations.d.ts +8 -8
- package/dist/access-operations.js +18 -18
- package/dist/access-schema.d.ts +2 -2
- package/dist/access-schema.js +5 -5
- package/dist/{access-service-5-EVpt3v.d.ts → access-service-DmAEOJdJ.d.ts} +11 -3
- package/dist/access-service.d.ts +5 -5
- package/dist/access-service.js +14 -14
- package/dist/access-surface-catalog.d.ts +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 +8 -1
- package/dist/active-recall.js.map +1 -1
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +7 -7
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +7 -7
- 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/capabilities.d.ts +1 -1
- package/dist/{catalog-D_ZDxu8Y.d.ts → catalog-BpOcwHdE.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 +8 -8
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-C5WN74HH.js → chunk-2HAG7JOI.js} +8 -2
- package/dist/chunk-2HAG7JOI.js.map +1 -0
- package/dist/{chunk-GKRLPHPU.js → chunk-2KK5WNQB.js} +2 -2
- package/dist/{chunk-JRDPT6DA.js → chunk-4NWIGAIC.js} +2 -2
- package/dist/{chunk-OBJUKYDO.js → chunk-4XL3CVII.js} +2 -2
- package/dist/{chunk-WWVXXSLF.js → chunk-5PZCDUJ6.js} +4 -4
- package/dist/{chunk-URQLO7BX.js → chunk-6FNZ3NMS.js} +2 -2
- package/dist/{chunk-DRUDC2UX.js → chunk-6MMONPHM.js} +2 -2
- package/dist/{chunk-RB6A7OUN.js → chunk-6SXNQFHB.js} +2 -2
- package/dist/{chunk-ZGZNLRKF.js → chunk-7TWA7DKP.js} +5 -5
- package/dist/{chunk-KMNYXLPL.js → chunk-B35L45HZ.js} +2 -2
- package/dist/{chunk-SIV3JZPP.js → chunk-CGZWGVQF.js} +5 -5
- package/dist/{chunk-CKFLLAIS.js → chunk-DANV2HTH.js} +3 -3
- package/dist/{chunk-NVYF5OB6.js → chunk-DBRVU5PR.js} +3 -3
- package/dist/{chunk-LMFS64GY.js → chunk-E4PFDD3N.js} +4 -4
- package/dist/{chunk-DIHCFJVV.js → chunk-HYI2GP3F.js} +4 -4
- package/dist/{chunk-UIKZZ242.js → chunk-IKHKHADN.js} +2 -2
- package/dist/{chunk-PUR2CKX2.js → chunk-JS2HKKOK.js} +1 -1
- package/dist/{chunk-PUR2CKX2.js.map → chunk-JS2HKKOK.js.map} +1 -1
- package/dist/{chunk-R2OAN6IP.js → chunk-K2JM4DZZ.js} +2 -2
- package/dist/{chunk-3IJGF6K2.js → chunk-K2R3DEEH.js} +2 -2
- package/dist/{chunk-HIHCB2S6.js → chunk-QRRBXD24.js} +2 -2
- package/dist/{chunk-PNUOQYRJ.js → chunk-RWZB6MAY.js} +1953 -2172
- package/dist/chunk-RWZB6MAY.js.map +1 -0
- package/dist/{chunk-ZPZ4VVLT.js → chunk-SD34EK4Z.js} +2 -2
- package/dist/{chunk-WK3NK6SP.js → chunk-SNEXVX3U.js} +6 -6
- package/dist/{chunk-FVZ2ISIX.js → chunk-TPLE6SNY.js} +7 -7
- package/dist/{chunk-24YDB5QP.js → chunk-UETBEHBA.js} +44 -24
- package/dist/chunk-UETBEHBA.js.map +1 -0
- package/dist/{chunk-5IN26V6W.js → chunk-UU32EAIY.js} +5 -5
- package/dist/{chunk-ZGAJB74V.js → chunk-W3BFT5OJ.js} +2 -2
- package/dist/{chunk-L4N7SVNN.js → chunk-WA7FECSG.js} +11 -11
- package/dist/{chunk-Q3X7GUAQ.js → chunk-WOX2UW4K.js} +2 -2
- package/dist/{chunk-XTEWVF3C.js → chunk-WWSXVOGY.js} +2 -2
- package/dist/{chunk-PWWWLD7D.js → chunk-X64YLC24.js} +153 -27
- package/dist/chunk-X64YLC24.js.map +1 -0
- package/dist/{chunk-P4FW3ZU5.js → chunk-YGGXUNS4.js} +20 -20
- package/dist/{chunk-JHQUIH3W.js → chunk-ZBVQ4ZM4.js} +19 -19
- package/dist/{cli-DuWWjaKT.d.ts → cli-lmf5RGyQ.d.ts} +3 -3
- package/dist/cli.d.ts +9 -9
- package/dist/cli.js +33 -33
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +7 -7
- 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 +8 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +7 -7
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +7 -7
- 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 +7 -7
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +7 -7
- package/dist/extraction-faithfulness.d.ts +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/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/index.d.ts +10 -10
- package/dist/index.js +76 -72
- 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/local-model-endpoint.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +7 -7
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +7 -7
- package/dist/maintenance/rebuild-memory-projection.js +8 -8
- 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 +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 +2 -2
- package/dist/namespaces/migrate.js +8 -8
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +1 -1
- package/dist/namespaces/storage.d.ts +2 -2
- package/dist/namespaces/storage.js +7 -7
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync.d.ts +108 -1
- package/dist/offline-sync.js +7 -1
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +15 -12
- package/dist/orchestration/compression-guideline-coordinator.d.ts +1 -1
- package/dist/orchestration/maintenance.d.ts +2 -2
- package/dist/orchestration/maintenance.js +9 -9
- package/dist/{orchestrator-CZI6hO_R.d.ts → orchestrator-D7bb8ZGp.d.ts} +193 -201
- package/dist/orchestrator.d.ts +5 -5
- package/dist/orchestrator.js +51 -41
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/provenance.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/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +9 -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/schemas.d.ts +22 -22
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +1 -1
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +1 -1
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/lancedb-backend.js +1 -1
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.js +1 -1
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/orama-backend.js +1 -1
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-D3aSiu9t.d.ts → semantic-consolidation-CTfbVwCA.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +8 -8
- package/dist/semantic-rule-promotion.js +7 -7
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +7 -7
- 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 +6 -6
- 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/transfer/types.d.ts +12 -12
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-CZm6rx9-.d.ts → types-D4NYDtXI.d.ts} +7 -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 +7 -7
- package/package.json +2 -2
- package/src/access-service.ts +22 -0
- package/src/config.ts +5 -0
- package/src/index.ts +3 -1
- package/src/offline-sync.test.ts +354 -52
- package/src/offline-sync.ts +332 -24
- package/src/orchestration/extraction-run.ts +4 -34
- package/src/orchestration/namespace-read-fanout.ts +484 -0
- package/src/orchestration/orchestrator-helpers.ts +1130 -0
- package/src/orchestration/self-deps.ts +50 -0
- package/src/orchestration/workspace-ops.ts +789 -0
- package/src/orchestrator.ts +177 -2296
- package/src/types.ts +9 -0
- package/dist/chunk-24YDB5QP.js.map +0 -1
- package/dist/chunk-C5WN74HH.js.map +0 -1
- package/dist/chunk-PNUOQYRJ.js.map +0 -1
- package/dist/chunk-PWWWLD7D.js.map +0 -1
- /package/dist/{chunk-GKRLPHPU.js.map → chunk-2KK5WNQB.js.map} +0 -0
- /package/dist/{chunk-JRDPT6DA.js.map → chunk-4NWIGAIC.js.map} +0 -0
- /package/dist/{chunk-OBJUKYDO.js.map → chunk-4XL3CVII.js.map} +0 -0
- /package/dist/{chunk-WWVXXSLF.js.map → chunk-5PZCDUJ6.js.map} +0 -0
- /package/dist/{chunk-URQLO7BX.js.map → chunk-6FNZ3NMS.js.map} +0 -0
- /package/dist/{chunk-DRUDC2UX.js.map → chunk-6MMONPHM.js.map} +0 -0
- /package/dist/{chunk-RB6A7OUN.js.map → chunk-6SXNQFHB.js.map} +0 -0
- /package/dist/{chunk-ZGZNLRKF.js.map → chunk-7TWA7DKP.js.map} +0 -0
- /package/dist/{chunk-KMNYXLPL.js.map → chunk-B35L45HZ.js.map} +0 -0
- /package/dist/{chunk-SIV3JZPP.js.map → chunk-CGZWGVQF.js.map} +0 -0
- /package/dist/{chunk-CKFLLAIS.js.map → chunk-DANV2HTH.js.map} +0 -0
- /package/dist/{chunk-NVYF5OB6.js.map → chunk-DBRVU5PR.js.map} +0 -0
- /package/dist/{chunk-LMFS64GY.js.map → chunk-E4PFDD3N.js.map} +0 -0
- /package/dist/{chunk-DIHCFJVV.js.map → chunk-HYI2GP3F.js.map} +0 -0
- /package/dist/{chunk-UIKZZ242.js.map → chunk-IKHKHADN.js.map} +0 -0
- /package/dist/{chunk-R2OAN6IP.js.map → chunk-K2JM4DZZ.js.map} +0 -0
- /package/dist/{chunk-3IJGF6K2.js.map → chunk-K2R3DEEH.js.map} +0 -0
- /package/dist/{chunk-HIHCB2S6.js.map → chunk-QRRBXD24.js.map} +0 -0
- /package/dist/{chunk-ZPZ4VVLT.js.map → chunk-SD34EK4Z.js.map} +0 -0
- /package/dist/{chunk-WK3NK6SP.js.map → chunk-SNEXVX3U.js.map} +0 -0
- /package/dist/{chunk-FVZ2ISIX.js.map → chunk-TPLE6SNY.js.map} +0 -0
- /package/dist/{chunk-5IN26V6W.js.map → chunk-UU32EAIY.js.map} +0 -0
- /package/dist/{chunk-ZGAJB74V.js.map → chunk-W3BFT5OJ.js.map} +0 -0
- /package/dist/{chunk-L4N7SVNN.js.map → chunk-WA7FECSG.js.map} +0 -0
- /package/dist/{chunk-Q3X7GUAQ.js.map → chunk-WOX2UW4K.js.map} +0 -0
- /package/dist/{chunk-XTEWVF3C.js.map → chunk-WWSXVOGY.js.map} +0 -0
- /package/dist/{chunk-P4FW3ZU5.js.map → chunk-YGGXUNS4.js.map} +0 -0
- /package/dist/{chunk-JHQUIH3W.js.map → chunk-ZBVQ4ZM4.js.map} +0 -0
package/src/offline-sync.test.ts
CHANGED
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
buildOfflineSyncSnapshot,
|
|
15
15
|
buildOfflineSyncSnapshotFromBase,
|
|
16
16
|
buildOfflineSyncSnapshotForPaths,
|
|
17
|
+
compileOfflineSyncExcludeGlobs,
|
|
18
|
+
globToRegExp,
|
|
17
19
|
OFFLINE_SYNC_MAX_MTIME_MS,
|
|
18
20
|
readOfflineSyncFileContentChunk,
|
|
19
21
|
shouldPreferIncomingOfflineRuntimeFile,
|
|
@@ -74,9 +76,6 @@ test("offline snapshot captures source-of-truth files and excludes private/inter
|
|
|
74
76
|
"state/last_intent.json",
|
|
75
77
|
"state/last_qmd_recall.json",
|
|
76
78
|
"state/last_recall.json",
|
|
77
|
-
"state/lcm.sqlite",
|
|
78
|
-
"state/lcm.sqlite-shm",
|
|
79
|
-
"state/lcm.sqlite-wal",
|
|
80
79
|
"transcripts/session.jsonl",
|
|
81
80
|
],
|
|
82
81
|
);
|
|
@@ -101,9 +100,6 @@ test("offline snapshot captures source-of-truth files and excludes private/inter
|
|
|
101
100
|
"state/last_intent.json",
|
|
102
101
|
"state/last_qmd_recall.json",
|
|
103
102
|
"state/last_recall.json",
|
|
104
|
-
"state/lcm.sqlite",
|
|
105
|
-
"state/lcm.sqlite-shm",
|
|
106
|
-
"state/lcm.sqlite-wal",
|
|
107
103
|
],
|
|
108
104
|
);
|
|
109
105
|
} finally {
|
|
@@ -191,7 +187,7 @@ test("offline sync includes retrieval debug snapshots for full-fidelity offline
|
|
|
191
187
|
}
|
|
192
188
|
});
|
|
193
189
|
|
|
194
|
-
test("offline sync
|
|
190
|
+
test("offline sync push-side default excludes live LCM sqlite but apply-side still accepts it (#1786)", async () => {
|
|
195
191
|
const root = await tempDir("remnic-offline-lcm-sqlite");
|
|
196
192
|
try {
|
|
197
193
|
await write(root, "facts/a.md", "alpha");
|
|
@@ -199,33 +195,53 @@ test("offline sync includes live LCM sqlite artifacts for full-fidelity offline
|
|
|
199
195
|
await write(root, "state/lcm.sqlite-shm", "live shm");
|
|
200
196
|
await write(root, "state/lcm.sqlite-wal", "live wal");
|
|
201
197
|
|
|
198
|
+
// Push-side enumeration: node-local sqlite state is excluded by default
|
|
199
|
+
// so the local node does not broadcast its live sqlite WAL/SHM to peers.
|
|
202
200
|
const snapshot = await buildOfflineSyncSnapshot({
|
|
203
201
|
root,
|
|
204
202
|
sourceId: "remote",
|
|
205
203
|
includeContent: true,
|
|
206
204
|
});
|
|
207
205
|
|
|
208
|
-
assert.deepEqual(snapshot.files.map((file) => file.path), [
|
|
209
|
-
"facts/a.md",
|
|
210
|
-
"state/lcm.sqlite",
|
|
211
|
-
"state/lcm.sqlite-shm",
|
|
212
|
-
"state/lcm.sqlite-wal",
|
|
213
|
-
]);
|
|
206
|
+
assert.deepEqual(snapshot.files.map((file) => file.path), ["facts/a.md"]);
|
|
214
207
|
assert.equal(shouldPreferIncomingOfflineRuntimeFile("state/lcm.sqlite-shm"), true);
|
|
215
208
|
assert.equal(shouldPreferIncomingOfflineRuntimeFile("state/lcm.sqlite-wal"), true);
|
|
216
209
|
assert.equal(shouldPreferIncomingOfflineRuntimeFile("state/last_qmd_recall.json"), true);
|
|
217
|
-
const focused = await buildOfflineSyncSnapshotForPaths({
|
|
218
|
-
root,
|
|
219
|
-
sourceId: "remote",
|
|
220
|
-
paths: ["state/lcm.sqlite"],
|
|
221
|
-
includeContent: true,
|
|
222
|
-
});
|
|
223
|
-
assert.deepEqual(focused.files.map((file) => file.path), ["state/lcm.sqlite"]);
|
|
224
210
|
|
|
211
|
+
// buildOfflineSyncSnapshotForPaths still rejects explicitly excluded
|
|
212
|
+
// paths so callers cannot bypass the default exclude via path lists.
|
|
213
|
+
await assert.rejects(
|
|
214
|
+
() =>
|
|
215
|
+
buildOfflineSyncSnapshotForPaths({
|
|
216
|
+
root,
|
|
217
|
+
sourceId: "remote",
|
|
218
|
+
paths: ["state/lcm.sqlite"],
|
|
219
|
+
includeContent: true,
|
|
220
|
+
}),
|
|
221
|
+
/offline sync snapshot path is excluded/,
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
// Apply-side: the remote is authoritative for lcm.sqlite and we must
|
|
225
|
+
// still accept the file content when an upstream snapshot includes it
|
|
226
|
+
// (e.g. first sync bootstrapping the LCM DB).
|
|
227
|
+
const remoteSnapshot = {
|
|
228
|
+
format: "remnic.offline-sync.snapshot.v1" as const,
|
|
229
|
+
schemaVersion: 1 as const,
|
|
230
|
+
createdAt: new Date().toISOString(),
|
|
231
|
+
sourceId: "remote",
|
|
232
|
+
includeTranscripts: true,
|
|
233
|
+
files: [{
|
|
234
|
+
path: "state/lcm.sqlite",
|
|
235
|
+
sha256: createHash("sha256").update("live db").digest("hex"),
|
|
236
|
+
bytes: Buffer.byteLength("live db"),
|
|
237
|
+
mtimeMs: 0,
|
|
238
|
+
contentBase64: Buffer.from("live db").toString("base64"),
|
|
239
|
+
}],
|
|
240
|
+
};
|
|
225
241
|
const oldDb = Buffer.from("old live db");
|
|
226
242
|
const pull = await applyOfflineSyncSnapshot({
|
|
227
243
|
root,
|
|
228
|
-
snapshot,
|
|
244
|
+
snapshot: remoteSnapshot,
|
|
229
245
|
baseFiles: [{
|
|
230
246
|
path: "state/lcm.sqlite",
|
|
231
247
|
sha256: createHash("sha256").update(oldDb).digest("hex"),
|
|
@@ -234,14 +250,27 @@ test("offline sync includes live LCM sqlite artifacts for full-fidelity offline
|
|
|
234
250
|
}],
|
|
235
251
|
});
|
|
236
252
|
|
|
237
|
-
|
|
253
|
+
// Corrected apply contract (#1793 review): the apply-side view SEES the
|
|
254
|
+
// local sqlite (identical content -> no write, no conflict), the local
|
|
255
|
+
// sidecars survive (node-local guard: absence from a push-filtered
|
|
256
|
+
// snapshot is not a delete instruction), and nothing is deleted.
|
|
257
|
+
assert.equal(pull.deleted, 0);
|
|
258
|
+
assert.deepEqual(pull.conflicts, []);
|
|
238
259
|
assert.equal(await readUtf8(root, "state/lcm.sqlite"), "live db");
|
|
260
|
+
assert.equal(await readUtf8(root, "state/lcm.sqlite-shm"), "live shm");
|
|
261
|
+
assert.equal(await readUtf8(root, "state/lcm.sqlite-wal"), "live wal");
|
|
239
262
|
|
|
240
|
-
|
|
263
|
+
// The push-side changeset excludes lcm.sqlite and friends so the
|
|
264
|
+
// local node never pushes the live sqlite state up.
|
|
241
265
|
const changeset = await buildOfflineSyncChangeset({
|
|
242
266
|
root,
|
|
243
267
|
sourceId: "laptop",
|
|
244
|
-
baseFiles:
|
|
268
|
+
baseFiles: [{
|
|
269
|
+
path: "facts/a.md",
|
|
270
|
+
sha256: createHash("sha256").update("alpha").digest("hex"),
|
|
271
|
+
bytes: Buffer.byteLength("alpha"),
|
|
272
|
+
mtimeMs: 0,
|
|
273
|
+
}],
|
|
245
274
|
});
|
|
246
275
|
assert.deepEqual(changeset.changes.map((change) => change.path), []);
|
|
247
276
|
} finally {
|
|
@@ -278,24 +307,23 @@ test("offline sync includes durable runtime state and excludes only transient sy
|
|
|
278
307
|
includeContent: true,
|
|
279
308
|
});
|
|
280
309
|
|
|
310
|
+
// Issue #1786: default excludes drop state/*.sqlite*, state/index_tags.json,
|
|
311
|
+
// state/entity-mention-index.json, and state/memory-governance/runs/** from
|
|
312
|
+
// push-side enumeration. Durableremote-authoritative runtime files
|
|
313
|
+
// (last_intent.json, .artifact-write-version.log, etc.) and namespace
|
|
314
|
+
// state still ship so the remote can merge.
|
|
281
315
|
assert.deepEqual(snapshot.files.map((file) => file.path), [
|
|
282
316
|
"assets/state/fact-hashes.txt",
|
|
283
317
|
"facts/a.md",
|
|
284
318
|
"namespaces/generalist-project-origin-6ebeaa54/state/.memory-status-version.log",
|
|
285
|
-
"namespaces/generalist-project-origin-6ebeaa54/state/entity-mention-index.json",
|
|
286
319
|
"namespaces/generalist-project-origin-6ebeaa54/state/last_intent.json",
|
|
287
320
|
"state/.artifact-write-version.log",
|
|
288
321
|
"state/.memory-status-version.log",
|
|
289
322
|
"state/buffer-surprise-ledger.jsonl",
|
|
290
323
|
"state/buffer.json",
|
|
291
324
|
"state/embeddings.json",
|
|
292
|
-
"state/entity-mention-index.json",
|
|
293
|
-
"state/index_tags.json",
|
|
294
325
|
"state/index_time.json",
|
|
295
326
|
"state/memory-lifecycle-ledger.jsonl",
|
|
296
|
-
"state/memory-projection.sqlite",
|
|
297
|
-
"state/memory-projection.sqlite-shm",
|
|
298
|
-
"state/memory-projection.sqlite-wal",
|
|
299
327
|
"state/recall_impressions.jsonl",
|
|
300
328
|
]);
|
|
301
329
|
const focused = await buildOfflineSyncSnapshotForPaths({
|
|
@@ -335,6 +363,10 @@ test("offline sync includes durable runtime state and excludes only transient sy
|
|
|
335
363
|
}],
|
|
336
364
|
});
|
|
337
365
|
|
|
366
|
+
// 18 = the 13 durable local-only files plus the 5 node-local excluded
|
|
367
|
+
// artifacts, which the corrected apply-side view (#1793 review) now
|
|
368
|
+
// SEES and deliberately leaves untouched (counted as skipped) instead
|
|
369
|
+
// of hiding from enumeration entirely.
|
|
338
370
|
assert.equal(pull.skipped, 18);
|
|
339
371
|
assert.equal(await readUtf8(root, "state/memory-lifecycle-ledger.jsonl"), "ledger");
|
|
340
372
|
} finally {
|
|
@@ -885,7 +917,7 @@ test("offline sync reads bounded file content chunks with metadata", async () =>
|
|
|
885
917
|
const root = await tempDir("remnic-offline-file-content");
|
|
886
918
|
try {
|
|
887
919
|
await write(root, "artifacts/large.txt", "alpha\nbeta\ngamma\n");
|
|
888
|
-
await write(root, "
|
|
920
|
+
await write(root, "facts/small.md", "live db");
|
|
889
921
|
|
|
890
922
|
const chunk = await readOfflineSyncFileContentChunk({
|
|
891
923
|
root,
|
|
@@ -900,11 +932,22 @@ test("offline sync reads bounded file content chunks with metadata", async () =>
|
|
|
900
932
|
assert.equal(chunk.content.toString("utf-8"), "beta\n");
|
|
901
933
|
assert.equal(chunk.bytes, Buffer.byteLength("alpha\nbeta\ngamma\n"));
|
|
902
934
|
|
|
903
|
-
const
|
|
935
|
+
const factsFile = await readOfflineSyncFileContentChunk({
|
|
904
936
|
root,
|
|
905
|
-
path: "
|
|
937
|
+
path: "facts/small.md",
|
|
906
938
|
});
|
|
907
|
-
assert.equal(
|
|
939
|
+
assert.equal(factsFile.content.toString("utf-8"), "live db");
|
|
940
|
+
|
|
941
|
+
// Issue #1786: push-side default excludes state/lcm.sqlite; reading the
|
|
942
|
+
// chunk for an excluded path is rejected loudly.
|
|
943
|
+
await assert.rejects(
|
|
944
|
+
() =>
|
|
945
|
+
readOfflineSyncFileContentChunk({
|
|
946
|
+
root,
|
|
947
|
+
path: "state/lcm.sqlite",
|
|
948
|
+
}),
|
|
949
|
+
/offline sync file content path is excluded: state\/lcm\.sqlite/,
|
|
950
|
+
);
|
|
908
951
|
} finally {
|
|
909
952
|
await rm(root, { recursive: true, force: true });
|
|
910
953
|
}
|
|
@@ -1280,7 +1323,13 @@ test("offline snapshot apply preserves new local runtime files without a base",
|
|
|
1280
1323
|
}
|
|
1281
1324
|
});
|
|
1282
1325
|
|
|
1283
|
-
test("offline snapshot apply
|
|
1326
|
+
test("offline snapshot apply leaves stale sqlite sidecars in place under push-side default exclude (#1786)", async () => {
|
|
1327
|
+
// Issue #1786: lcm.sqlite-wal/shm are excluded from push-side enumeration
|
|
1328
|
+
// because each node rebuilds its own live sqlite state. The remote never
|
|
1329
|
+
// ships those sidecars, so apply cannot enumerate them as "current-only"
|
|
1330
|
+
// and never issues a delete for them. The local WAL/SHM is naturally
|
|
1331
|
+
// overwritten when the next lcm.sqlite reconciliation lands a fresh
|
|
1332
|
+
// page and the WAL is checkpointed.
|
|
1284
1333
|
const root = await tempDir("remnic-offline-runtime-sidecar-local-create");
|
|
1285
1334
|
try {
|
|
1286
1335
|
await write(root, "state/lcm.sqlite-wal", "stale local wal");
|
|
@@ -1299,19 +1348,18 @@ test("offline snapshot apply removes current-only sqlite sidecars when absent re
|
|
|
1299
1348
|
baseFiles: [],
|
|
1300
1349
|
});
|
|
1301
1350
|
|
|
1302
|
-
assert.equal(pull.deleted,
|
|
1351
|
+
assert.equal(pull.deleted, 0);
|
|
1303
1352
|
assert.equal(pull.pendingLocal, 0);
|
|
1304
|
-
|
|
1353
|
+
// The corrected apply-side view (#1793 review) sees both sidecars and
|
|
1354
|
+
// skips them via the node-local guard — visible-but-untouched, rather
|
|
1355
|
+
// than invisible. deleted stays 0 either way.
|
|
1356
|
+
assert.equal(pull.skipped, 2);
|
|
1305
1357
|
assert.equal(pull.conflicts.length, 0);
|
|
1306
1358
|
assert.deepEqual(pull.nextBaseFiles, []);
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
);
|
|
1311
|
-
await assert.rejects(
|
|
1312
|
-
() => readFile(path.join(root, "state/lcm.sqlite-shm")),
|
|
1313
|
-
/ENOENT/,
|
|
1314
|
-
);
|
|
1359
|
+
// Local sidecars are still on disk because the remote never asserted
|
|
1360
|
+
// them and the push-side default keeps them out of enumeration.
|
|
1361
|
+
assert.equal(await readUtf8(root, "state/lcm.sqlite-wal"), "stale local wal");
|
|
1362
|
+
assert.equal(await readUtf8(root, "state/lcm.sqlite-shm"), "stale local shm");
|
|
1315
1363
|
} finally {
|
|
1316
1364
|
await rm(root, { recursive: true, force: true });
|
|
1317
1365
|
}
|
|
@@ -1788,19 +1836,22 @@ test("offline pull can record conflicts without hydrating oversized incoming con
|
|
|
1788
1836
|
const remote = await tempDir("remnic-offline-metadata-conflict-remote");
|
|
1789
1837
|
const local = await tempDir("remnic-offline-metadata-conflict-local");
|
|
1790
1838
|
try {
|
|
1791
|
-
|
|
1839
|
+
// Issue #1786: live sqlite is excluded by push-side defaults, so use a
|
|
1840
|
+
// regular facts path to exercise the metadata-only conflict path.
|
|
1841
|
+
await write(remote, "facts/big.bin", "base");
|
|
1792
1842
|
const initial = await buildOfflineSyncSnapshot({
|
|
1793
1843
|
root: remote,
|
|
1794
1844
|
sourceId: "remote",
|
|
1795
1845
|
includeContent: true,
|
|
1796
1846
|
});
|
|
1797
|
-
|
|
1847
|
+
await applyOfflineSyncSnapshot({
|
|
1798
1848
|
root: local,
|
|
1799
1849
|
snapshot: initial,
|
|
1850
|
+
baseFiles: initial.files,
|
|
1800
1851
|
});
|
|
1801
1852
|
|
|
1802
|
-
await write(local, "
|
|
1803
|
-
await write(remote, "
|
|
1853
|
+
await write(local, "facts/big.bin", "local edit");
|
|
1854
|
+
await write(remote, "facts/big.bin", "remote edit");
|
|
1804
1855
|
const metadataOnly = await buildOfflineSyncSnapshot({
|
|
1805
1856
|
root: remote,
|
|
1806
1857
|
sourceId: "remote",
|
|
@@ -1810,20 +1861,23 @@ test("offline pull can record conflicts without hydrating oversized incoming con
|
|
|
1810
1861
|
const secondPull = await applyOfflineSyncSnapshot({
|
|
1811
1862
|
root: local,
|
|
1812
1863
|
snapshot: metadataOnly,
|
|
1813
|
-
|
|
1864
|
+
// Use the initial base so the apply path can detect both_modified
|
|
1865
|
+
// (local edited from "base" -> "local edit", remote edited from
|
|
1866
|
+
// "base" -> "remote edit").
|
|
1867
|
+
baseFiles: initial.files,
|
|
1814
1868
|
allowMissingConflictContent: true,
|
|
1869
|
+
writeConflictCopies: false,
|
|
1815
1870
|
});
|
|
1816
1871
|
|
|
1817
1872
|
assert.equal(secondPull.conflicts.length, 1);
|
|
1818
1873
|
assert.equal(secondPull.conflicts[0]?.reason, "both_modified");
|
|
1819
1874
|
assert.equal(secondPull.conflicts[0]?.conflictPath, undefined);
|
|
1820
|
-
assert.equal(await readUtf8(local, "
|
|
1875
|
+
assert.equal(await readUtf8(local, "facts/big.bin"), "local edit");
|
|
1821
1876
|
} finally {
|
|
1822
1877
|
await rm(remote, { recursive: true, force: true });
|
|
1823
1878
|
await rm(local, { recursive: true, force: true });
|
|
1824
1879
|
}
|
|
1825
1880
|
});
|
|
1826
|
-
|
|
1827
1881
|
test("offline push preserves remote edits when both sides changed since the base", async () => {
|
|
1828
1882
|
const remote = await tempDir("remnic-offline-push-conflict-remote");
|
|
1829
1883
|
const local = await tempDir("remnic-offline-push-conflict-local");
|
|
@@ -2647,3 +2701,251 @@ test("offline changeset validation reports client input errors with an offline s
|
|
|
2647
2701
|
await rm(root, { recursive: true, force: true });
|
|
2648
2702
|
}
|
|
2649
2703
|
});
|
|
2704
|
+
|
|
2705
|
+
test("globToRegExp matches single-segment globs and rejects cross-segment or suffix matches (#1786)", () => {
|
|
2706
|
+
const re = globToRegExp("state/*.sqlite");
|
|
2707
|
+
assert.equal(re.test("state/lcm.sqlite"), true);
|
|
2708
|
+
// Single `*` must stay in-segment: a nested file is NOT matched.
|
|
2709
|
+
assert.equal(re.test("state/nested/lcm.sqlite"), false);
|
|
2710
|
+
// Anchored to full path: trailing variants like -wal are NOT matched.
|
|
2711
|
+
assert.equal(re.test("state/lcm.sqlite-wal"), false);
|
|
2712
|
+
});
|
|
2713
|
+
|
|
2714
|
+
test("globToRegExp matches sqlite sidecars via the state/*.sqlite-* pattern (#1786)", () => {
|
|
2715
|
+
const re = globToRegExp("state/*.sqlite-*");
|
|
2716
|
+
assert.equal(re.test("state/lcm.sqlite-wal"), true);
|
|
2717
|
+
assert.equal(re.test("state/lcm.sqlite-shm"), true);
|
|
2718
|
+
// The bare database file is NOT matched by the sidecar pattern.
|
|
2719
|
+
assert.equal(re.test("state/lcm.sqlite"), false);
|
|
2720
|
+
});
|
|
2721
|
+
|
|
2722
|
+
test("globToRegExp leading **/ matches zero or more directory segments (#1786)", () => {
|
|
2723
|
+
const re = globToRegExp("**/foo.md");
|
|
2724
|
+
// Leading ** matches zero segments (file at the root).
|
|
2725
|
+
assert.equal(re.test("foo.md"), true);
|
|
2726
|
+
// And any number of intermediate segments.
|
|
2727
|
+
assert.equal(re.test("a/b/foo.md"), true);
|
|
2728
|
+
assert.equal(re.test("a/b/c/d/foo.md"), true);
|
|
2729
|
+
// No match for an unrelated path.
|
|
2730
|
+
assert.equal(re.test("bar.md"), false);
|
|
2731
|
+
assert.equal(re.test("a/b/bar.md"), false);
|
|
2732
|
+
});
|
|
2733
|
+
|
|
2734
|
+
test("globToRegExp escapes regex metacharacters in literal glob segments (#1786)", () => {
|
|
2735
|
+
// The literal `+` in the glob must NOT act as a regex quantifier.
|
|
2736
|
+
const re = globToRegExp("a+b.md");
|
|
2737
|
+
assert.equal(re.test("aab.md"), false);
|
|
2738
|
+
assert.equal(re.test("a+b.md"), true);
|
|
2739
|
+
});
|
|
2740
|
+
|
|
2741
|
+
test("globToRegExp rejects empty input and strings containing NUL (#1786)", () => {
|
|
2742
|
+
assert.throws(() => globToRegExp(""), /non-empty string/);
|
|
2743
|
+
assert.throws(() => globToRegExp("state/with\0nul"), /NUL bytes/);
|
|
2744
|
+
});
|
|
2745
|
+
|
|
2746
|
+
test("globToRegExp treats ** as cross-segment in trailing and embedded positions (#1786 review)", () => {
|
|
2747
|
+
// Trailing `dir/**` must exclude the whole subtree, matching the
|
|
2748
|
+
// offline-mode guide's `scratch/**` example (Cursor review, PR #1793).
|
|
2749
|
+
const trailing = globToRegExp("scratch/**");
|
|
2750
|
+
assert.equal(trailing.test("scratch/a.md"), true);
|
|
2751
|
+
assert.equal(trailing.test("scratch/a/b.md"), true);
|
|
2752
|
+
assert.equal(trailing.test("scratch"), false);
|
|
2753
|
+
assert.equal(trailing.test("other/scratch/a.md"), false);
|
|
2754
|
+
// Embedded `a/**/b` spans any depth (including zero extra segments).
|
|
2755
|
+
const embedded = globToRegExp("state/**/runs.json");
|
|
2756
|
+
assert.equal(embedded.test("state/runs.json"), true);
|
|
2757
|
+
assert.equal(embedded.test("state/a/runs.json"), true);
|
|
2758
|
+
assert.equal(embedded.test("state/a/b/runs.json"), true);
|
|
2759
|
+
assert.equal(embedded.test("state/a/b/other.json"), false);
|
|
2760
|
+
});
|
|
2761
|
+
|
|
2762
|
+
test("default excludes cover per-namespace state dirs (#1793 review)", () => {
|
|
2763
|
+
const sqlite = globToRegExp("**/state/*.sqlite");
|
|
2764
|
+
assert.equal(sqlite.test("state/lcm.sqlite"), true);
|
|
2765
|
+
assert.equal(sqlite.test("namespaces/team-a/state/lcm.sqlite"), true);
|
|
2766
|
+
assert.equal(sqlite.test("facts/lcm.sqlite"), false);
|
|
2767
|
+
});
|
|
2768
|
+
|
|
2769
|
+
test("apply-side local enumeration sees node-local state via excludeNodeLocalState=false (#1793 review)", async () => {
|
|
2770
|
+
const root = await tempDir("remnic-offline-apply-view");
|
|
2771
|
+
try {
|
|
2772
|
+
await write(root, "facts/a.md", "alpha");
|
|
2773
|
+
await write(root, "state/lcm.sqlite", "live db");
|
|
2774
|
+
const pushView = await buildOfflineSyncSnapshot({ root, sourceId: "local", includeContent: false });
|
|
2775
|
+
assert.deepEqual(pushView.files.map((f) => f.path), ["facts/a.md"]);
|
|
2776
|
+
const applyView = await buildOfflineSyncSnapshot({
|
|
2777
|
+
root,
|
|
2778
|
+
sourceId: "local",
|
|
2779
|
+
includeContent: false,
|
|
2780
|
+
excludeNodeLocalState: false,
|
|
2781
|
+
});
|
|
2782
|
+
assert.deepEqual(applyView.files.map((f) => f.path), ["facts/a.md", "state/lcm.sqlite"]);
|
|
2783
|
+
} finally {
|
|
2784
|
+
await rm(root, { recursive: true, force: true });
|
|
2785
|
+
}
|
|
2786
|
+
});
|
|
2787
|
+
|
|
2788
|
+
test("applyOfflineSyncChangeset accepts an incoming node-local state upsert without currentFiles (#1793 review)", async () => {
|
|
2789
|
+
// Forces the internal buildOfflineSyncSnapshotForPaths local enumeration:
|
|
2790
|
+
// with push-side excludes it would THROW "path is excluded" and fail the
|
|
2791
|
+
// whole apply. Apply-side must stay permissive.
|
|
2792
|
+
const root = await tempDir("remnic-offline-apply-state-upsert");
|
|
2793
|
+
try {
|
|
2794
|
+
const content = Buffer.from("incoming durable sqlite", "utf-8");
|
|
2795
|
+
const result = await applyOfflineSyncChangeset({
|
|
2796
|
+
root,
|
|
2797
|
+
changeset: {
|
|
2798
|
+
format: "remnic.offline-sync.changeset.v1",
|
|
2799
|
+
schemaVersion: 1,
|
|
2800
|
+
createdAt: new Date().toISOString(),
|
|
2801
|
+
sourceId: "laptop",
|
|
2802
|
+
includeTranscripts: true,
|
|
2803
|
+
changes: [
|
|
2804
|
+
{
|
|
2805
|
+
type: "upsert",
|
|
2806
|
+
path: "state/lcm.sqlite",
|
|
2807
|
+
file: {
|
|
2808
|
+
path: "state/lcm.sqlite",
|
|
2809
|
+
sha256: createHash("sha256").update(content).digest("hex"),
|
|
2810
|
+
bytes: content.length,
|
|
2811
|
+
mtimeMs: Date.now(),
|
|
2812
|
+
contentBase64: content.toString("base64"),
|
|
2813
|
+
},
|
|
2814
|
+
},
|
|
2815
|
+
],
|
|
2816
|
+
},
|
|
2817
|
+
});
|
|
2818
|
+
assert.equal(result.appliedUpserts, 1);
|
|
2819
|
+
assert.equal(await readUtf8(root, "state/lcm.sqlite"), "incoming durable sqlite");
|
|
2820
|
+
} finally {
|
|
2821
|
+
await rm(root, { recursive: true, force: true });
|
|
2822
|
+
}
|
|
2823
|
+
});
|
|
2824
|
+
|
|
2825
|
+
test("buildOfflineSyncChangesetFromSnapshot honors excludePaths for 3-strikes skipped files (#1793 review)", async () => {
|
|
2826
|
+
const root = await tempDir("remnic-offline-changeset-skip");
|
|
2827
|
+
try {
|
|
2828
|
+
await write(root, "facts/a.md", "alpha");
|
|
2829
|
+
await write(root, "facts/huge.md", "huge changed content");
|
|
2830
|
+
const current = await buildOfflineSyncSnapshot({ root, sourceId: "local", includeContent: false });
|
|
2831
|
+
const changeset = await buildOfflineSyncChangesetFromSnapshot({
|
|
2832
|
+
root,
|
|
2833
|
+
sourceId: "local",
|
|
2834
|
+
currentFiles: current.files,
|
|
2835
|
+
baseFiles: [],
|
|
2836
|
+
excludePaths: ["facts/huge.md"],
|
|
2837
|
+
});
|
|
2838
|
+
// The skipped file must not ride the inline changeset path.
|
|
2839
|
+
assert.deepEqual(changeset.changes.map((c) => c.path), ["facts/a.md"]);
|
|
2840
|
+
} finally {
|
|
2841
|
+
await rm(root, { recursive: true, force: true });
|
|
2842
|
+
}
|
|
2843
|
+
});
|
|
2844
|
+
|
|
2845
|
+
test("buildOfflineSyncSnapshotForPaths deduplicates repeated request paths (#1786 review)", async () => {
|
|
2846
|
+
const root = await tempDir("remnic-offline-forpaths-dedup");
|
|
2847
|
+
try {
|
|
2848
|
+
await write(root, "facts/a.md", "alpha");
|
|
2849
|
+
const snapshot = await buildOfflineSyncSnapshotForPaths({
|
|
2850
|
+
root,
|
|
2851
|
+
sourceId: "remote",
|
|
2852
|
+
paths: ["facts/a.md", "facts/a.md", "facts/a.md"],
|
|
2853
|
+
includeContent: false,
|
|
2854
|
+
});
|
|
2855
|
+
// Duplicate entries in paths[] must yield exactly one record —
|
|
2856
|
+
// the seen-set add was dropped in an earlier refactor (Cursor
|
|
2857
|
+
// review, PR #1793) and produced duplicate file records.
|
|
2858
|
+
assert.deepEqual(snapshot.files.map((file) => file.path), ["facts/a.md"]);
|
|
2859
|
+
} finally {
|
|
2860
|
+
await rm(root, { recursive: true, force: true });
|
|
2861
|
+
}
|
|
2862
|
+
});
|
|
2863
|
+
|
|
2864
|
+
test("compileOfflineSyncExcludeGlobs throws on non-string / empty entries and accepts empty input (#1786)", () => {
|
|
2865
|
+
assert.throws(() => compileOfflineSyncExcludeGlobs([42]), /non-empty strings/);
|
|
2866
|
+
assert.throws(() => compileOfflineSyncExcludeGlobs([""]), /non-empty strings/);
|
|
2867
|
+
assert.deepEqual(compileOfflineSyncExcludeGlobs([]), []);
|
|
2868
|
+
});
|
|
2869
|
+
|
|
2870
|
+
test("buildOfflineSyncSnapshot excludes all default #1786 push-side runtime state artifacts", async () => {
|
|
2871
|
+
const root = await tempDir("remnic-offline-default-exclude-1786");
|
|
2872
|
+
try {
|
|
2873
|
+
await write(root, "facts/a.md", "alpha");
|
|
2874
|
+
await write(root, "state/lcm.sqlite", "live db");
|
|
2875
|
+
await write(root, "state/lcm.sqlite-wal", "live wal");
|
|
2876
|
+
await write(root, "state/index_tags.json", "tags");
|
|
2877
|
+
await write(root, "state/entity-mention-index.json", "entities");
|
|
2878
|
+
await mkdir(path.join(root, "state/memory-governance/runs"), { recursive: true });
|
|
2879
|
+
await writeFile(path.join(root, "state/memory-governance/runs/r1.json"), "run");
|
|
2880
|
+
|
|
2881
|
+
const snapshot = await buildOfflineSyncSnapshot({
|
|
2882
|
+
root,
|
|
2883
|
+
sourceId: "remote",
|
|
2884
|
+
includeContent: true,
|
|
2885
|
+
});
|
|
2886
|
+
|
|
2887
|
+
const paths = snapshot.files.map((file) => file.path);
|
|
2888
|
+
// Only the source-of-truth fact ships; every default-excluded state
|
|
2889
|
+
// artifact is gone from push-side enumeration.
|
|
2890
|
+
assert.deepEqual(paths, ["facts/a.md"]);
|
|
2891
|
+
// And the excluded paths are individually absent — guard against
|
|
2892
|
+
// accidentally re-introducing one of them via a future code path.
|
|
2893
|
+
for (const excluded of [
|
|
2894
|
+
"state/lcm.sqlite",
|
|
2895
|
+
"state/lcm.sqlite-wal",
|
|
2896
|
+
"state/index_tags.json",
|
|
2897
|
+
"state/entity-mention-index.json",
|
|
2898
|
+
"state/memory-governance/runs/r1.json",
|
|
2899
|
+
]) {
|
|
2900
|
+
assert.equal(paths.includes(excluded), false, `unexpectedly included: ${excluded}`);
|
|
2901
|
+
}
|
|
2902
|
+
} finally {
|
|
2903
|
+
await rm(root, { recursive: true, force: true });
|
|
2904
|
+
}
|
|
2905
|
+
});
|
|
2906
|
+
|
|
2907
|
+
test("buildOfflineSyncSnapshot honors userExcludeRegexps additively over default excludes (#1786)", async () => {
|
|
2908
|
+
const root = await tempDir("remnic-offline-user-exclude-1786");
|
|
2909
|
+
try {
|
|
2910
|
+
await write(root, "facts/a.md", "alpha");
|
|
2911
|
+
await write(root, "notes/secret.md", "do not sync");
|
|
2912
|
+
|
|
2913
|
+
const snapshot = await buildOfflineSyncSnapshot({
|
|
2914
|
+
root,
|
|
2915
|
+
sourceId: "remote",
|
|
2916
|
+
includeContent: true,
|
|
2917
|
+
userExcludeRegexps: compileOfflineSyncExcludeGlobs(["notes/**"]),
|
|
2918
|
+
});
|
|
2919
|
+
|
|
2920
|
+
assert.deepEqual(snapshot.files.map((file) => file.path), ["facts/a.md"]);
|
|
2921
|
+
} finally {
|
|
2922
|
+
await rm(root, { recursive: true, force: true });
|
|
2923
|
+
}
|
|
2924
|
+
});
|
|
2925
|
+
|
|
2926
|
+
test("buildOfflineSyncSnapshotFromBase honors userExcludeRegexps on the fast-base path (#1786)", async () => {
|
|
2927
|
+
const root = await tempDir("remnic-offline-user-exclude-fast-base-1786");
|
|
2928
|
+
try {
|
|
2929
|
+
await write(root, "facts/a.md", "alpha");
|
|
2930
|
+
await write(root, "notes/secret.md", "do not sync");
|
|
2931
|
+
|
|
2932
|
+
const baseSnapshot = await buildOfflineSyncSnapshot({
|
|
2933
|
+
root,
|
|
2934
|
+
sourceId: "remote",
|
|
2935
|
+
includeContent: false,
|
|
2936
|
+
});
|
|
2937
|
+
|
|
2938
|
+
const next = await buildOfflineSyncSnapshotFromBase({
|
|
2939
|
+
root,
|
|
2940
|
+
sourceId: "remote",
|
|
2941
|
+
baseFiles: baseSnapshot.files,
|
|
2942
|
+
baseCapturedAt: new Date(),
|
|
2943
|
+
includeContent: false,
|
|
2944
|
+
userExcludeRegexps: compileOfflineSyncExcludeGlobs(["notes/**"]),
|
|
2945
|
+
});
|
|
2946
|
+
|
|
2947
|
+
assert.deepEqual(next.files.map((file) => file.path), ["facts/a.md"]);
|
|
2948
|
+
} finally {
|
|
2949
|
+
await rm(root, { recursive: true, force: true });
|
|
2950
|
+
}
|
|
2951
|
+
});
|