@remnic/core 9.69.45 → 9.69.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-admin-ops-surface.d.ts +6 -6
- package/dist/access-admin-ops-surface.js +25 -25
- package/dist/access-audit.js +2 -2
- package/dist/access-authorization-probe.d.ts +6 -6
- package/dist/access-authorization-probe.js +26 -26
- package/dist/access-boundary.d.ts +6 -6
- package/dist/access-boundary.js +25 -25
- package/dist/access-cli.js +70 -69
- package/dist/access-cli.js.map +1 -1
- package/dist/access-extraction-force-flush.d.ts +6 -6
- package/dist/access-extraction-force-flush.js +25 -25
- package/dist/access-http-briefing.d.ts +6 -6
- package/dist/access-http-briefing.js +26 -26
- package/dist/access-http-lcm-compaction.d.ts +6 -6
- package/dist/access-http-lifecycle-flush.d.ts +6 -6
- package/dist/access-http-offline-stream.d.ts +6 -6
- package/dist/access-http-query.js +27 -27
- package/dist/access-http.d.ts +7 -7
- package/dist/access-http.js +41 -41
- package/dist/access-identity-continuity-surface.d.ts +4 -4
- package/dist/access-identity-continuity-surface.js +25 -25
- package/dist/access-lcm-surface.d.ts +6 -6
- package/dist/access-lcm-surface.js +25 -25
- package/dist/access-mcp-shared-context-tools.d.ts +6 -6
- package/dist/access-mcp.d.ts +6 -6
- package/dist/access-mcp.js +33 -33
- package/dist/access-namespace-preflight.js +25 -25
- package/dist/access-observe-transcript.d.ts +4 -4
- package/dist/access-observe-write-category.d.ts +2 -2
- package/dist/access-observe-write-category.js +4 -4
- package/dist/access-observe-write-surface.d.ts +6 -6
- package/dist/access-observe-write-surface.js +25 -25
- package/dist/access-offline-manifest.d.ts +4 -4
- package/dist/access-operations-batch.js +26 -26
- package/dist/access-operations.d.ts +6 -6
- package/dist/access-operations.js +32 -32
- package/dist/access-recall-concurrency.d.ts +6 -6
- package/dist/access-recall-concurrency.js +25 -25
- package/dist/access-recall-response.d.ts +6 -6
- package/dist/access-recall-response.js +25 -25
- package/dist/access-recall-surface.d.ts +6 -6
- package/dist/access-recall-surface.js +25 -25
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-CDteK_Gt.d.ts → access-service-DGynYlbE.d.ts} +4 -4
- package/dist/access-service-helpers.d.ts +6 -6
- package/dist/access-service.d.ts +6 -6
- package/dist/access-service.js +25 -25
- package/dist/access-surface-catalog.d.ts +6 -6
- package/dist/access-surface-catalog.js +27 -27
- package/dist/access-wearables-meetings-surface.d.ts +2 -2
- package/dist/bootstrap.d.ts +4 -4
- package/dist/briefing-window.d.ts +1 -1
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +8 -8
- package/dist/buffer.d.ts +1 -1
- package/dist/causal-consolidation.js +9 -9
- package/dist/{chunk-R4HFCATE.js → chunk-3NYM6JA4.js} +2 -2
- package/dist/{chunk-WNMVUWI7.js → chunk-3UQIS2GC.js} +2 -2
- package/dist/{chunk-46A4RGBC.js → chunk-47C6DF43.js} +3 -3
- package/dist/{chunk-NRFUYVW7.js → chunk-4ECS33D4.js} +2 -2
- package/dist/{chunk-C3MCMFNV.js → chunk-5EHPB6RL.js} +2 -2
- package/dist/{chunk-SPN5YFXN.js → chunk-62NYUFNH.js} +7 -7
- package/dist/{chunk-TFS5FAAC.js → chunk-66QOUYNS.js} +66 -47
- package/dist/chunk-66QOUYNS.js.map +1 -0
- package/dist/{chunk-OOBUWMPY.js → chunk-7BNB7KHB.js} +4 -4
- package/dist/{chunk-Z5YNVWRD.js → chunk-AN2O4KZY.js} +2 -2
- package/dist/{chunk-JUOJGZPV.js → chunk-AYP2FZFA.js} +2 -2
- package/dist/{chunk-T2ZNUWUY.js → chunk-AYZHHMTK.js} +2 -2
- package/dist/{chunk-LQVXSRAJ.js → chunk-B4EGAHHS.js} +2 -2
- package/dist/{chunk-JPNUKKSL.js → chunk-CFIHVWYZ.js} +2 -2
- package/dist/{chunk-D3DJYGU2.js → chunk-CWMMKFO7.js} +3 -3
- package/dist/{chunk-OREGA7GY.js → chunk-D5DTZBTV.js} +6 -6
- package/dist/{chunk-BOWAP64Q.js → chunk-DDBLZJRV.js} +2 -2
- package/dist/{chunk-SHEHQBCG.js → chunk-DUJYRVUD.js} +638 -45
- package/dist/chunk-DUJYRVUD.js.map +1 -0
- package/dist/{chunk-DSHSQGSN.js → chunk-EAZ3YZKN.js} +2 -2
- package/dist/{chunk-WCQ7L54B.js → chunk-EEHQY7ER.js} +2 -2
- package/dist/{chunk-2WMXZYMS.js → chunk-F5V7KRQS.js} +2 -2
- package/dist/{chunk-HVGCQMTQ.js → chunk-FMEP2RB5.js} +2 -2
- package/dist/{chunk-NGAFYIHQ.js → chunk-FSH6KRKO.js} +2 -2
- package/dist/{chunk-E5VOKZQV.js → chunk-GGY7WKMB.js} +1 -1
- package/dist/{chunk-UJXNBZYJ.js → chunk-GYYLZOSO.js} +4 -4
- package/dist/{chunk-NKDQXDFL.js → chunk-HXDXR2FH.js} +17 -17
- package/dist/{chunk-OFBJD3V5.js → chunk-HYWPUYLO.js} +2 -2
- package/dist/{chunk-KQAFEZQX.js → chunk-IPLYGWQF.js} +5 -5
- package/dist/{chunk-ZQ533JDE.js → chunk-IXMWTNNY.js} +2 -2
- package/dist/{chunk-WMTYEQO5.js → chunk-J5OXWG66.js} +2 -2
- package/dist/{chunk-C3YEB7CR.js → chunk-JOP7W24J.js} +4 -4
- package/dist/{chunk-5TKHJ5RW.js → chunk-KF57IQFX.js} +12 -12
- package/dist/chunk-M4DB6ZNN.js +87 -0
- package/dist/chunk-M4DB6ZNN.js.map +1 -0
- package/dist/{chunk-5XTILJRI.js → chunk-N46SNYMC.js} +2 -2
- package/dist/{chunk-HBGSZ62Z.js → chunk-ORQ7AZMU.js} +5 -5
- package/dist/{chunk-ZLVURGAJ.js → chunk-OV7GJ4KW.js} +2 -2
- package/dist/{chunk-6II55MXS.js → chunk-P7S7BVBJ.js} +2 -2
- package/dist/{chunk-IAT5WFEU.js → chunk-QUFJ3NJE.js} +258 -794
- package/dist/chunk-QUFJ3NJE.js.map +1 -0
- package/dist/{chunk-LZAJONO5.js → chunk-S3SND7SO.js} +2 -2
- package/dist/{chunk-K75H7FSO.js → chunk-S7KDHFF2.js} +2 -2
- package/dist/{chunk-Q4MI2KCG.js → chunk-SRRZZHA5.js} +2 -2
- package/dist/{chunk-AOMUIF3R.js → chunk-SW42MMAH.js} +2 -2
- package/dist/{chunk-B55FYF6F.js → chunk-T5NI5K46.js} +2 -2
- package/dist/{chunk-J6NEOCQC.js → chunk-TE6B2UU6.js} +2 -2
- package/dist/{chunk-7K5Q6COX.js → chunk-TVVEYCNW.js} +4 -4
- package/dist/{chunk-T3F2HWIG.js → chunk-TZ44E732.js} +2 -2
- package/dist/{chunk-A2YN3FR4.js → chunk-U4ESGVZ2.js} +3 -3
- package/dist/{chunk-F6AYWLRN.js → chunk-UEJY6ZTQ.js} +2 -2
- package/dist/{chunk-7LKXP4QG.js → chunk-UGGOQKO6.js} +25 -25
- package/dist/{chunk-M7ECD3KN.js → chunk-UOREPQ3C.js} +2 -2
- package/dist/{chunk-IFULZYYQ.js → chunk-VGRVJ6Z6.js} +2 -2
- package/dist/{chunk-N6I257OM.js → chunk-WOBMSMUK.js} +3 -3
- package/dist/{chunk-VA5QRTZX.js → chunk-XLZZLUWL.js} +2 -2
- package/dist/{chunk-MDYZCPNQ.js → chunk-Y3XHIWVP.js} +2 -2
- package/dist/{chunk-2LRO73MN.js → chunk-YFVIA5A6.js} +2 -2
- package/dist/{chunk-YHSEUZV5.js → chunk-YUQPUK6H.js} +2 -2
- package/dist/{cli-CZDhIcxu.d.ts → cli-Cik3H7q1.d.ts} +3 -3
- package/dist/cli.d.ts +7 -7
- package/dist/cli.js +56 -56
- package/dist/coding/export-okf-codegraph.js +7 -7
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +8 -8
- package/dist/connectors/codex-materialize-runner.js +8 -8
- package/dist/connectors/index.js +8 -8
- package/dist/consolidation-provenance-check.d.ts +1 -1
- package/dist/consolidation-undo.d.ts +1 -1
- package/dist/contradiction/index.d.ts +2 -2
- package/dist/convergence-refresh.d.ts +1 -1
- package/dist/convergence-refresh.js +10 -10
- package/dist/corpus-watermark.js +10 -10
- package/dist/deep-recall-http-glue.d.ts +6 -6
- package/dist/deep-recall-http-glue.js +4 -4
- package/dist/deep-recall-mcp-tools.d.ts +6 -6
- package/dist/entity-retrieval.d.ts +1 -1
- package/dist/entity-retrieval.js +8 -8
- package/dist/explicit-capture.d.ts +4 -4
- package/dist/explicit-capture.js +2 -2
- package/dist/external-wiki-access.d.ts +6 -6
- package/dist/external-wiki-access.js +26 -26
- package/dist/external-wiki-mcp-tools.d.ts +6 -6
- package/dist/extraction.js +8 -8
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/{index-TBPe6NvB.d.ts → index-Zb6SUV4R.d.ts} +1 -1
- package/dist/index.d.ts +14 -14
- package/dist/index.js +70 -69
- package/dist/lcm/index.js +3 -3
- package/dist/location/index.d.ts +3 -3
- package/dist/location/index.js +10 -10
- package/dist/maintenance/memory-governance.js +8 -8
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +1 -1
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +8 -8
- package/dist/maintenance/rebuild-memory-projection.d.ts +1 -1
- package/dist/maintenance/rebuild-memory-projection.js +9 -9
- package/dist/{maintenance-B813YFWJ.d.ts → maintenance-Du_VRvVg.d.ts} +1 -1
- package/dist/mcp-memory-inspector-app.d.ts +6 -6
- package/dist/memory-age.d.ts +100 -0
- package/dist/memory-age.js +22 -0
- package/dist/memory-age.js.map +1 -0
- package/dist/memory-subject-cli.d.ts +6 -6
- package/dist/memory-subject.d.ts +6 -6
- package/dist/memory-worth-outcomes.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +1 -1
- package/dist/namespaces/migrate.js +9 -9
- package/dist/namespaces/search.js +3 -3
- package/dist/namespaces/storage.d.ts +1 -1
- package/dist/namespaces/storage.js +8 -8
- package/dist/operator-doctor-corpus.js +11 -11
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +16 -16
- package/dist/orchestration/compression-guideline-coordinator.d.ts +1 -1
- package/dist/orchestration/maintenance.d.ts +2 -2
- package/dist/orchestration/maintenance.js +16 -16
- package/dist/{orchestrator-BCvQavIS.d.ts → orchestrator-D9U3Zgjp.d.ts} +3 -3
- package/dist/orchestrator.d.ts +4 -4
- package/dist/orchestrator.js +70 -69
- package/dist/{public-http-NH72a6aa.d.ts → public-http-AabioFLh.d.ts} +1 -1
- package/dist/recall-navigation-http-glue.d.ts +6 -6
- package/dist/recall-navigation-mcp-tools.d.ts +6 -6
- package/dist/recall-navigation-operations.js +26 -26
- package/dist/recall-navigation.js +25 -25
- package/dist/search/factory.js +2 -2
- package/dist/search/index.js +4 -4
- package/dist/semantic-consolidation.js +9 -9
- package/dist/semantic-rule-promotion.js +8 -8
- package/dist/semantic-rule-verifier.js +8 -8
- package/dist/{service-Echc5Xso.d.ts → service-ByjbCuj7.d.ts} +1 -1
- package/dist/source-agent-qualifier.js +8 -8
- package/dist/standup/build.js +8 -8
- package/dist/{storage-ClkHCQSE.d.ts → storage-CbdF077z.d.ts} +16 -2
- package/dist/storage.d.ts +1 -1
- package/dist/storage.js +7 -7
- package/dist/support-passport/index.d.ts +9 -9
- package/dist/support-passport/index.js +28 -28
- package/dist/{tagging-ksktJ60G.d.ts → tagging-CF7DNV7_.d.ts} +1 -1
- package/dist/tier-migration.d.ts +1 -1
- package/dist/transfer/autodetect.js +1 -1
- package/dist/transfer/backup.js +1 -1
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/export-okf.js +8 -8
- package/dist/verified-recall.js +8 -8
- package/dist/who-knows-cli.d.ts +6 -6
- package/dist/who-knows-cli.js +9 -9
- package/dist/who-knows.d.ts +6 -6
- package/dist/who-knows.js +8 -8
- package/package.json +2 -2
- package/src/dedup/merge.test.ts +185 -8
- package/src/memory-age.test.ts +106 -0
- package/src/memory-age.ts +196 -0
- package/src/orchestration/recall-search-pipeline.test.ts +207 -1
- package/src/orchestration/recall-search-pipeline.ts +13 -9
- package/src/orchestration/semantic-merge-commit-effects.ts +31 -9
- package/src/orchestration/semantic-merge-persist.ts +16 -5
- package/src/storage/cas-revision-host.ts +22 -0
- package/src/storage/cas-revision-store.test.ts +44 -1
- package/src/storage/cas-revision-store.ts +23 -0
- package/src/storage/tombstone-blocked-capture-sync.ts +26 -1
- package/src/storage-frontmatter-cas.test.ts +49 -1
- package/src/storage.ts +1 -1
- package/dist/chunk-IAT5WFEU.js.map +0 -1
- package/dist/chunk-SHEHQBCG.js.map +0 -1
- package/dist/chunk-TFS5FAAC.js.map +0 -1
- /package/dist/{chunk-R4HFCATE.js.map → chunk-3NYM6JA4.js.map} +0 -0
- /package/dist/{chunk-WNMVUWI7.js.map → chunk-3UQIS2GC.js.map} +0 -0
- /package/dist/{chunk-46A4RGBC.js.map → chunk-47C6DF43.js.map} +0 -0
- /package/dist/{chunk-NRFUYVW7.js.map → chunk-4ECS33D4.js.map} +0 -0
- /package/dist/{chunk-C3MCMFNV.js.map → chunk-5EHPB6RL.js.map} +0 -0
- /package/dist/{chunk-SPN5YFXN.js.map → chunk-62NYUFNH.js.map} +0 -0
- /package/dist/{chunk-OOBUWMPY.js.map → chunk-7BNB7KHB.js.map} +0 -0
- /package/dist/{chunk-Z5YNVWRD.js.map → chunk-AN2O4KZY.js.map} +0 -0
- /package/dist/{chunk-JUOJGZPV.js.map → chunk-AYP2FZFA.js.map} +0 -0
- /package/dist/{chunk-T2ZNUWUY.js.map → chunk-AYZHHMTK.js.map} +0 -0
- /package/dist/{chunk-LQVXSRAJ.js.map → chunk-B4EGAHHS.js.map} +0 -0
- /package/dist/{chunk-JPNUKKSL.js.map → chunk-CFIHVWYZ.js.map} +0 -0
- /package/dist/{chunk-D3DJYGU2.js.map → chunk-CWMMKFO7.js.map} +0 -0
- /package/dist/{chunk-OREGA7GY.js.map → chunk-D5DTZBTV.js.map} +0 -0
- /package/dist/{chunk-BOWAP64Q.js.map → chunk-DDBLZJRV.js.map} +0 -0
- /package/dist/{chunk-DSHSQGSN.js.map → chunk-EAZ3YZKN.js.map} +0 -0
- /package/dist/{chunk-WCQ7L54B.js.map → chunk-EEHQY7ER.js.map} +0 -0
- /package/dist/{chunk-2WMXZYMS.js.map → chunk-F5V7KRQS.js.map} +0 -0
- /package/dist/{chunk-HVGCQMTQ.js.map → chunk-FMEP2RB5.js.map} +0 -0
- /package/dist/{chunk-NGAFYIHQ.js.map → chunk-FSH6KRKO.js.map} +0 -0
- /package/dist/{chunk-E5VOKZQV.js.map → chunk-GGY7WKMB.js.map} +0 -0
- /package/dist/{chunk-UJXNBZYJ.js.map → chunk-GYYLZOSO.js.map} +0 -0
- /package/dist/{chunk-NKDQXDFL.js.map → chunk-HXDXR2FH.js.map} +0 -0
- /package/dist/{chunk-OFBJD3V5.js.map → chunk-HYWPUYLO.js.map} +0 -0
- /package/dist/{chunk-KQAFEZQX.js.map → chunk-IPLYGWQF.js.map} +0 -0
- /package/dist/{chunk-ZQ533JDE.js.map → chunk-IXMWTNNY.js.map} +0 -0
- /package/dist/{chunk-WMTYEQO5.js.map → chunk-J5OXWG66.js.map} +0 -0
- /package/dist/{chunk-C3YEB7CR.js.map → chunk-JOP7W24J.js.map} +0 -0
- /package/dist/{chunk-5TKHJ5RW.js.map → chunk-KF57IQFX.js.map} +0 -0
- /package/dist/{chunk-5XTILJRI.js.map → chunk-N46SNYMC.js.map} +0 -0
- /package/dist/{chunk-HBGSZ62Z.js.map → chunk-ORQ7AZMU.js.map} +0 -0
- /package/dist/{chunk-ZLVURGAJ.js.map → chunk-OV7GJ4KW.js.map} +0 -0
- /package/dist/{chunk-6II55MXS.js.map → chunk-P7S7BVBJ.js.map} +0 -0
- /package/dist/{chunk-LZAJONO5.js.map → chunk-S3SND7SO.js.map} +0 -0
- /package/dist/{chunk-K75H7FSO.js.map → chunk-S7KDHFF2.js.map} +0 -0
- /package/dist/{chunk-Q4MI2KCG.js.map → chunk-SRRZZHA5.js.map} +0 -0
- /package/dist/{chunk-AOMUIF3R.js.map → chunk-SW42MMAH.js.map} +0 -0
- /package/dist/{chunk-B55FYF6F.js.map → chunk-T5NI5K46.js.map} +0 -0
- /package/dist/{chunk-J6NEOCQC.js.map → chunk-TE6B2UU6.js.map} +0 -0
- /package/dist/{chunk-7K5Q6COX.js.map → chunk-TVVEYCNW.js.map} +0 -0
- /package/dist/{chunk-T3F2HWIG.js.map → chunk-TZ44E732.js.map} +0 -0
- /package/dist/{chunk-A2YN3FR4.js.map → chunk-U4ESGVZ2.js.map} +0 -0
- /package/dist/{chunk-F6AYWLRN.js.map → chunk-UEJY6ZTQ.js.map} +0 -0
- /package/dist/{chunk-7LKXP4QG.js.map → chunk-UGGOQKO6.js.map} +0 -0
- /package/dist/{chunk-M7ECD3KN.js.map → chunk-UOREPQ3C.js.map} +0 -0
- /package/dist/{chunk-IFULZYYQ.js.map → chunk-VGRVJ6Z6.js.map} +0 -0
- /package/dist/{chunk-N6I257OM.js.map → chunk-WOBMSMUK.js.map} +0 -0
- /package/dist/{chunk-VA5QRTZX.js.map → chunk-XLZZLUWL.js.map} +0 -0
- /package/dist/{chunk-MDYZCPNQ.js.map → chunk-Y3XHIWVP.js.map} +0 -0
- /package/dist/{chunk-2LRO73MN.js.map → chunk-YFVIA5A6.js.map} +0 -0
- /package/dist/{chunk-YHSEUZV5.js.map → chunk-YUQPUK6H.js.map} +0 -0
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
TombstoneStore,
|
|
3
|
-
applyTombstoneResurrectionGate,
|
|
4
|
-
buildRetiredFactTombstoneInputs,
|
|
5
|
-
collectRetiredMemoriesForRebuild
|
|
6
|
-
} from "./chunk-46WM6JX2.js";
|
|
7
1
|
import {
|
|
8
2
|
WEARABLES_DIR_NAME,
|
|
9
3
|
escapeSegmentText,
|
|
@@ -15,9 +9,15 @@ import {
|
|
|
15
9
|
unescapeSegmentText,
|
|
16
10
|
unescapeSpeakerLabel
|
|
17
11
|
} from "./chunk-UHVXE24Y.js";
|
|
12
|
+
import {
|
|
13
|
+
TombstoneStore,
|
|
14
|
+
applyTombstoneResurrectionGate,
|
|
15
|
+
buildRetiredFactTombstoneInputs,
|
|
16
|
+
collectRetiredMemoriesForRebuild
|
|
17
|
+
} from "./chunk-46WM6JX2.js";
|
|
18
18
|
import {
|
|
19
19
|
normalizeQmdResultPath
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-TZ44E732.js";
|
|
21
21
|
import {
|
|
22
22
|
withholdToolScopedFromSharedNamespace
|
|
23
23
|
} from "./chunk-S2V7RUCL.js";
|
|
@@ -31,6 +31,9 @@ import {
|
|
|
31
31
|
SPECULATIVE_TTL_DAYS,
|
|
32
32
|
confidenceTier
|
|
33
33
|
} from "./chunk-66YUOI53.js";
|
|
34
|
+
import {
|
|
35
|
+
createMemorySnapshot
|
|
36
|
+
} from "./chunk-S2HLHAZ4.js";
|
|
34
37
|
import {
|
|
35
38
|
normalizeProjectionPreview,
|
|
36
39
|
normalizeProjectionTags
|
|
@@ -39,9 +42,6 @@ import {
|
|
|
39
42
|
rewriteProjectedEntityReferences,
|
|
40
43
|
rewriteProjectedMemoryEntityReference
|
|
41
44
|
} from "./chunk-MRPOZBJC.js";
|
|
42
|
-
import {
|
|
43
|
-
createMemorySnapshot
|
|
44
|
-
} from "./chunk-S2HLHAZ4.js";
|
|
45
45
|
import {
|
|
46
46
|
rotateMarkdownFileToArchive
|
|
47
47
|
} from "./chunk-DM2T26WE.js";
|
|
@@ -59,18 +59,17 @@ import {
|
|
|
59
59
|
serializeFaithfulnessFields
|
|
60
60
|
} from "./chunk-J3IWHCMF.js";
|
|
61
61
|
import {
|
|
62
|
+
CasRevisionHost,
|
|
62
63
|
DeletionRevisionStore,
|
|
63
64
|
TombstoneBlockedCaptureIndexHost,
|
|
64
65
|
buildCapturePathLockIdentity,
|
|
65
66
|
buildExplicitCaptureDedupKey,
|
|
66
67
|
invalidationCommitFingerprint,
|
|
67
68
|
isQueuedReviewMemory,
|
|
68
|
-
isValidManagedStoragePath,
|
|
69
|
-
nextCasRevisionIso,
|
|
70
69
|
parseTombstoneBlockedOfflineSyncMemory,
|
|
71
70
|
tombstoneBlocked,
|
|
72
71
|
withCasCommitReceipt
|
|
73
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-DUJYRVUD.js";
|
|
74
73
|
import {
|
|
75
74
|
normalizeEntityName
|
|
76
75
|
} from "./chunk-YXZG5TVN.js";
|
|
@@ -261,17 +260,17 @@ import {
|
|
|
261
260
|
import {
|
|
262
261
|
lstat as lstat3,
|
|
263
262
|
readdir as readdir6,
|
|
264
|
-
readFile as
|
|
263
|
+
readFile as readFile2,
|
|
265
264
|
realpath as realpath3,
|
|
266
265
|
stat as stat3,
|
|
267
266
|
writeFile as writeFile2,
|
|
268
|
-
mkdir
|
|
269
|
-
unlink as
|
|
267
|
+
mkdir,
|
|
268
|
+
unlink as unlink4,
|
|
270
269
|
appendFile,
|
|
271
|
-
open
|
|
270
|
+
open
|
|
272
271
|
} from "fs/promises";
|
|
273
272
|
import { appendFileSync, createReadStream, mkdirSync, statSync as statSync2 } from "fs";
|
|
274
|
-
import { createHash as
|
|
273
|
+
import { createHash as createHash6 } from "crypto";
|
|
275
274
|
|
|
276
275
|
// src/preferences/drift-types.ts
|
|
277
276
|
function serializeDriftProvenance(fm) {
|
|
@@ -295,7 +294,7 @@ function parseDriftProvenance(fm) {
|
|
|
295
294
|
}
|
|
296
295
|
|
|
297
296
|
// src/storage.ts
|
|
298
|
-
import
|
|
297
|
+
import path13 from "path";
|
|
299
298
|
|
|
300
299
|
// src/storage/tombstone-migration-sources.ts
|
|
301
300
|
import { statSync } from "fs";
|
|
@@ -2349,543 +2348,9 @@ ${rows.join("\n")}
|
|
|
2349
2348
|
}
|
|
2350
2349
|
};
|
|
2351
2350
|
|
|
2352
|
-
// src/storage/cas-revision-store.ts
|
|
2353
|
-
import { createHash, randomUUID } from "crypto";
|
|
2354
|
-
import { mkdir, open, readFile, rename, unlink as unlink2 } from "fs/promises";
|
|
2355
|
-
import path6 from "path";
|
|
2356
|
-
var ACCESS_TELEMETRY_FRONTMATTER_KEY = /^(?:accessCount|lastAccessed):/;
|
|
2357
|
-
function canonicalSemanticFileText(raw) {
|
|
2358
|
-
const lines = raw.split("\n");
|
|
2359
|
-
if (lines[0] !== "---") return raw;
|
|
2360
|
-
const closing = lines.indexOf("---", 1);
|
|
2361
|
-
if (closing === -1) return raw;
|
|
2362
|
-
const frontmatter = lines.slice(1, closing).filter((line) => !ACCESS_TELEMETRY_FRONTMATTER_KEY.test(line));
|
|
2363
|
-
return [...lines.slice(0, 1), ...frontmatter, ...lines.slice(closing)].join("\n");
|
|
2364
|
-
}
|
|
2365
|
-
var CAS_REVISION_LOCK_STALE_MS = 6e4;
|
|
2366
|
-
var CAS_REVISION_LOCK_MAX_WAIT_MS = 12e4;
|
|
2367
|
-
var SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
2368
|
-
function isDigestOrNullOrUndefined(value) {
|
|
2369
|
-
if (value === void 0 || value === null) return true;
|
|
2370
|
-
return typeof value === "string" && SHA256_HEX.test(value);
|
|
2371
|
-
}
|
|
2372
|
-
function pendingWriteHasLanded(view) {
|
|
2373
|
-
if (view.writeLanded === true) return true;
|
|
2374
|
-
if (view.writeLanded === false) return false;
|
|
2375
|
-
return view.expectedDigest !== void 0;
|
|
2376
|
-
}
|
|
2377
|
-
var CasRevisionStore = class {
|
|
2378
|
-
baseDir;
|
|
2379
|
-
legacyMetadataPath;
|
|
2380
|
-
constructor(baseDir) {
|
|
2381
|
-
this.baseDir = baseDir;
|
|
2382
|
-
this.legacyMetadataPath = path6.join(baseDir, ".offline-sync", "cas-revisions.v1.json");
|
|
2383
|
-
}
|
|
2384
|
-
parseCasRevisionMetadata(raw) {
|
|
2385
|
-
let parsed;
|
|
2386
|
-
try {
|
|
2387
|
-
parsed = JSON.parse(raw);
|
|
2388
|
-
} catch {
|
|
2389
|
-
throw new Error("CAS revision metadata is invalid.");
|
|
2390
|
-
}
|
|
2391
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2392
|
-
throw new Error("CAS revision metadata is invalid.");
|
|
2393
|
-
}
|
|
2394
|
-
const root = parsed;
|
|
2395
|
-
if (Object.keys(root).sort().join(",") !== "revisions,version" || root.version !== 1 || !Array.isArray(root.revisions)) {
|
|
2396
|
-
throw new Error("CAS revision metadata is invalid.");
|
|
2397
|
-
}
|
|
2398
|
-
const revisions = /* @__PURE__ */ new Map();
|
|
2399
|
-
for (const rawEntry of root.revisions) {
|
|
2400
|
-
if (!rawEntry || typeof rawEntry !== "object" || Array.isArray(rawEntry)) {
|
|
2401
|
-
throw new Error("CAS revision metadata is invalid.");
|
|
2402
|
-
}
|
|
2403
|
-
const entry = rawEntry;
|
|
2404
|
-
if (Object.keys(entry).sort().join(",") !== "path,revision" || typeof entry.path !== "string" || !isValidManagedStoragePath(entry.path) || typeof entry.revision !== "string" || entry.revision.length === 0 || revisions.has(entry.path)) {
|
|
2405
|
-
throw new Error("CAS revision metadata is invalid.");
|
|
2406
|
-
}
|
|
2407
|
-
revisions.set(entry.path, entry.revision);
|
|
2408
|
-
}
|
|
2409
|
-
return revisions;
|
|
2410
|
-
}
|
|
2411
|
-
async readLegacyMetadata() {
|
|
2412
|
-
const baseDir = path6.resolve(this.baseDir);
|
|
2413
|
-
const legacyPath = await resolveSafeStoragePath(baseDir, path6.relative(baseDir, this.legacyMetadataPath));
|
|
2414
|
-
let raw;
|
|
2415
|
-
try {
|
|
2416
|
-
raw = await readFile(legacyPath, "utf8");
|
|
2417
|
-
} catch (error) {
|
|
2418
|
-
if (isErrnoCode(error, "ENOENT")) return /* @__PURE__ */ new Map();
|
|
2419
|
-
throw new Error("CAS revision metadata is unavailable.");
|
|
2420
|
-
}
|
|
2421
|
-
return this.parseCasRevisionMetadata(raw);
|
|
2422
|
-
}
|
|
2423
|
-
/** #2813 (P1 A): every sidecar path is resolved (and symlink-audited)
|
|
2424
|
-
* inside the memory root before it is used for a lock, read, rename, or
|
|
2425
|
-
* unlink. A symlinked `.offline-sync`, `.offline-sync/cas-revisions`, or
|
|
2426
|
-
* shard file makes this throw, so no sidecar traffic can follow a link
|
|
2427
|
-
* out of the root. */
|
|
2428
|
-
async getSafeShardInfo(relativePath) {
|
|
2429
|
-
const baseDir = path6.resolve(this.baseDir);
|
|
2430
|
-
const hash = createHash("sha256").update(relativePath).digest("hex");
|
|
2431
|
-
const shardPath = await resolveSafeStoragePath(baseDir, ".offline-sync", "cas-revisions", `${hash}.json`);
|
|
2432
|
-
const lockPath2 = await resolveSafeStoragePath(baseDir, ".offline-sync", "cas-revisions", `${hash}.json.lock`);
|
|
2433
|
-
return { shardPath, lockPath: lockPath2 };
|
|
2434
|
-
}
|
|
2435
|
-
async readShardView(shardPath, relativePath) {
|
|
2436
|
-
let raw;
|
|
2437
|
-
try {
|
|
2438
|
-
raw = await readFile(shardPath, "utf8");
|
|
2439
|
-
} catch (error) {
|
|
2440
|
-
if (isErrnoCode(error, "ENOENT")) return { kind: "missing" };
|
|
2441
|
-
throw error;
|
|
2442
|
-
}
|
|
2443
|
-
let parsed;
|
|
2444
|
-
try {
|
|
2445
|
-
parsed = JSON.parse(raw);
|
|
2446
|
-
} catch {
|
|
2447
|
-
throw new Error("CAS revision shard is unreadable.");
|
|
2448
|
-
}
|
|
2449
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2450
|
-
return { kind: "foreign" };
|
|
2451
|
-
}
|
|
2452
|
-
const root = parsed;
|
|
2453
|
-
if (root.state !== void 0) {
|
|
2454
|
-
if (root.version !== 1 || typeof root.path !== "string" || typeof root.revision !== "string" || root.revision.length === 0 || root.state !== "pending" && root.state !== "committed" || root.previous !== void 0 && (typeof root.previous !== "string" || root.previous.length === 0) || !isDigestOrNullOrUndefined(root.baselineDigest) || !isDigestOrNullOrUndefined(root.expectedDigest) || root.writeLanded !== void 0 && typeof root.writeLanded !== "boolean") {
|
|
2455
|
-
throw new Error("CAS revision shard is unreadable.");
|
|
2456
|
-
}
|
|
2457
|
-
const foreign = root.path !== relativePath;
|
|
2458
|
-
const committedDigest = typeof root.committedDigest === "string" ? root.committedDigest : void 0;
|
|
2459
|
-
const committedSemanticFingerprint = typeof root.committedSemanticFingerprint === "string" ? root.committedSemanticFingerprint : void 0;
|
|
2460
|
-
return root.state === "pending" ? {
|
|
2461
|
-
kind: "pending",
|
|
2462
|
-
revision: root.revision,
|
|
2463
|
-
previous: root.previous,
|
|
2464
|
-
foreign,
|
|
2465
|
-
baselineDigest: root.baselineDigest,
|
|
2466
|
-
expectedDigest: root.expectedDigest,
|
|
2467
|
-
...typeof root.writeLanded === "boolean" ? { writeLanded: root.writeLanded } : {}
|
|
2468
|
-
} : {
|
|
2469
|
-
kind: "committed",
|
|
2470
|
-
revision: root.revision,
|
|
2471
|
-
committedDigest,
|
|
2472
|
-
committedSemanticFingerprint,
|
|
2473
|
-
foreign
|
|
2474
|
-
};
|
|
2475
|
-
}
|
|
2476
|
-
if (root.version === 1 && root.path === relativePath && typeof root.revision === "string" && root.revision.length > 0) {
|
|
2477
|
-
const committedDigest = typeof root.committedDigest === "string" ? root.committedDigest : void 0;
|
|
2478
|
-
const committedSemanticFingerprint = typeof root.committedSemanticFingerprint === "string" ? root.committedSemanticFingerprint : void 0;
|
|
2479
|
-
return {
|
|
2480
|
-
kind: "committed",
|
|
2481
|
-
revision: root.revision,
|
|
2482
|
-
committedDigest,
|
|
2483
|
-
committedSemanticFingerprint,
|
|
2484
|
-
foreign: false
|
|
2485
|
-
};
|
|
2486
|
-
}
|
|
2487
|
-
return { kind: "foreign" };
|
|
2488
|
-
}
|
|
2489
|
-
async readStandingRevision(relativePath) {
|
|
2490
|
-
try {
|
|
2491
|
-
return await this.readStandingRevisionStrict(relativePath);
|
|
2492
|
-
} catch (error) {
|
|
2493
|
-
log.warn(`CasRevisionStore failed to read standing revision for ${relativePath}: ${error}`);
|
|
2494
|
-
return void 0;
|
|
2495
|
-
}
|
|
2496
|
-
}
|
|
2497
|
-
/** #2813 (P1 A): strict core of {@link readStandingRevision}. Returns
|
|
2498
|
-
* undefined ONLY when no receipt exists for the target — the shard is
|
|
2499
|
-
* absent (or names another target) and the legacy metadata holds no
|
|
2500
|
-
* entry. THROWS when the standing receipt cannot be determined: an
|
|
2501
|
-
* unreadable or corrupt shard, a PENDING reservation awaiting
|
|
2502
|
-
* finalization or recovery, or unreadable or invalid legacy metadata.
|
|
2503
|
-
* Fail-open callers collapse the throw into undefined; truthful callers
|
|
2504
|
-
* ({@link readRevisionStatus}) report `unavailable` so a transaction can
|
|
2505
|
-
* refuse instead of mistaking the failure for absence. */
|
|
2506
|
-
async readStandingRevisionStrict(relativePath) {
|
|
2507
|
-
const { shardPath } = await this.getSafeShardInfo(relativePath);
|
|
2508
|
-
const view = await this.readShardView(shardPath, relativePath);
|
|
2509
|
-
if (view.kind === "pending") {
|
|
2510
|
-
throw new Error(
|
|
2511
|
-
`CAS revision receipt for ${relativePath} is pending finalization (recovery needed).`
|
|
2512
|
-
);
|
|
2513
|
-
}
|
|
2514
|
-
if (view.kind === "committed" && !view.foreign) {
|
|
2515
|
-
return {
|
|
2516
|
-
revision: view.revision,
|
|
2517
|
-
committedDigest: view.committedDigest,
|
|
2518
|
-
committedSemanticFingerprint: view.committedSemanticFingerprint
|
|
2519
|
-
};
|
|
2520
|
-
}
|
|
2521
|
-
const legacyMap = await this.readLegacyMetadata();
|
|
2522
|
-
const legacyRev = legacyMap.get(relativePath);
|
|
2523
|
-
return legacyRev === void 0 ? void 0 : { revision: legacyRev };
|
|
2524
|
-
}
|
|
2525
|
-
async writeShard(shardPath, relativePath, revision, lock, state, previous, evidence) {
|
|
2526
|
-
let payload;
|
|
2527
|
-
if (state === "committed") {
|
|
2528
|
-
payload = { version: 1, path: relativePath, revision, state };
|
|
2529
|
-
if (evidence?.committedDigest !== void 0) {
|
|
2530
|
-
payload.committedDigest = evidence.committedDigest;
|
|
2531
|
-
}
|
|
2532
|
-
if (evidence?.committedSemanticFingerprint !== void 0) {
|
|
2533
|
-
payload.committedSemanticFingerprint = evidence.committedSemanticFingerprint;
|
|
2534
|
-
}
|
|
2535
|
-
} else {
|
|
2536
|
-
payload = { version: 1, path: relativePath, revision, state };
|
|
2537
|
-
if (previous !== void 0) payload.previous = previous;
|
|
2538
|
-
if (evidence !== void 0) {
|
|
2539
|
-
if (evidence.baselineDigest !== void 0) payload.baselineDigest = evidence.baselineDigest;
|
|
2540
|
-
if (evidence.expectedDigest !== void 0) payload.expectedDigest = evidence.expectedDigest;
|
|
2541
|
-
if (evidence.writeLanded !== void 0) payload.writeLanded = evidence.writeLanded;
|
|
2542
|
-
}
|
|
2543
|
-
}
|
|
2544
|
-
const baseDir = path6.resolve(this.baseDir);
|
|
2545
|
-
const temporaryPath = await resolveSafeStoragePath(
|
|
2546
|
-
baseDir,
|
|
2547
|
-
path6.relative(baseDir, `${shardPath}.${process.pid}.${randomUUID()}.tmp`)
|
|
2548
|
-
);
|
|
2549
|
-
let handle = null;
|
|
2550
|
-
try {
|
|
2551
|
-
await mkdir(path6.dirname(shardPath), { recursive: true });
|
|
2552
|
-
handle = await open(temporaryPath, "wx", 384);
|
|
2553
|
-
await handle.writeFile(`${JSON.stringify(payload)}
|
|
2554
|
-
`, "utf8");
|
|
2555
|
-
await handle.sync();
|
|
2556
|
-
await handle.close();
|
|
2557
|
-
handle = null;
|
|
2558
|
-
if (!await lock.refresh()) {
|
|
2559
|
-
throw new Error("CAS revision shard lock was lost.");
|
|
2560
|
-
}
|
|
2561
|
-
await rename(temporaryPath, shardPath);
|
|
2562
|
-
} finally {
|
|
2563
|
-
if (handle !== null) {
|
|
2564
|
-
await handle.close().catch((error) => {
|
|
2565
|
-
log.warn("failed to close CAS revision shard temporary file", error);
|
|
2566
|
-
});
|
|
2567
|
-
}
|
|
2568
|
-
await unlink2(temporaryPath).catch((error) => {
|
|
2569
|
-
if (!isErrnoCode(error, "ENOENT")) {
|
|
2570
|
-
log.warn("failed to clean up CAS revision shard temporary file", error);
|
|
2571
|
-
}
|
|
2572
|
-
});
|
|
2573
|
-
}
|
|
2574
|
-
}
|
|
2575
|
-
async resolveRelativePath(filePath) {
|
|
2576
|
-
const baseDir = path6.resolve(this.baseDir);
|
|
2577
|
-
const target = await resolveSafeStoragePath(baseDir, path6.relative(baseDir, path6.resolve(baseDir, filePath)));
|
|
2578
|
-
return path6.relative(baseDir, target).split(path6.sep).join("/");
|
|
2579
|
-
}
|
|
2580
|
-
async withShardLock(relativePath, task) {
|
|
2581
|
-
const { lockPath: lockPath2 } = await this.getSafeShardInfo(relativePath);
|
|
2582
|
-
return await withHeldFileLock(
|
|
2583
|
-
lockPath2,
|
|
2584
|
-
{ staleMs: CAS_REVISION_LOCK_STALE_MS, maxWaitMs: CAS_REVISION_LOCK_MAX_WAIT_MS },
|
|
2585
|
-
async (acquired, lock) => {
|
|
2586
|
-
if (!acquired) throw new Error("CAS revision lock is unavailable.");
|
|
2587
|
-
return await task(lock);
|
|
2588
|
-
}
|
|
2589
|
-
);
|
|
2590
|
-
}
|
|
2591
|
-
/** The target's standing revision token, or undefined when no receipt was
|
|
2592
|
-
* ever minted for it. Fail-open on storage/read errors. */
|
|
2593
|
-
async readRevision(filePath) {
|
|
2594
|
-
try {
|
|
2595
|
-
const relativePath = await this.resolveRelativePath(filePath);
|
|
2596
|
-
return (await this.readStandingRevision(relativePath))?.revision;
|
|
2597
|
-
} catch (error) {
|
|
2598
|
-
log.warn(`CasRevisionStore.readRevision failed for ${filePath}: ${error}`);
|
|
2599
|
-
return void 0;
|
|
2600
|
-
}
|
|
2601
|
-
}
|
|
2602
|
-
/** #2813 (P1 A): the truthful standing-receipt read. `absent` means no
|
|
2603
|
-
* receipt was ever minted for the target — undefined semantics stay
|
|
2604
|
-
* correct for those records. `unavailable` means the sidecar could not
|
|
2605
|
-
* be read OR a reservation is pending finalization; callers that
|
|
2606
|
-
* transact on receipt identity MUST refuse rather than treat the
|
|
2607
|
-
* failure as absence. */
|
|
2608
|
-
async readRevisionStatus(filePath) {
|
|
2609
|
-
try {
|
|
2610
|
-
const relativePath = await this.resolveRelativePath(filePath);
|
|
2611
|
-
const standing = await this.readStandingRevisionStrict(relativePath);
|
|
2612
|
-
return standing === void 0 ? { status: "absent" } : {
|
|
2613
|
-
status: "present",
|
|
2614
|
-
revision: standing.revision,
|
|
2615
|
-
...standing.committedDigest !== void 0 ? { committedDigest: standing.committedDigest } : {},
|
|
2616
|
-
...standing.committedSemanticFingerprint !== void 0 ? { committedSemanticFingerprint: standing.committedSemanticFingerprint } : {}
|
|
2617
|
-
};
|
|
2618
|
-
} catch (error) {
|
|
2619
|
-
return { status: "unavailable", reason: error instanceof Error ? error.message : String(error) };
|
|
2620
|
-
}
|
|
2621
|
-
}
|
|
2622
|
-
/** #2813 (P1 C): reserve the NEXT revision token as a durable PENDING
|
|
2623
|
-
* marker under the target's shard lock — the write-ahead record of a
|
|
2624
|
-
* receipt transaction. The token is strictly greater than the standing
|
|
2625
|
-
* token (max(clock, prev + 1ms)), so two commits can never share a
|
|
2626
|
-
* receipt — not within one millisecond, not across a backward clock
|
|
2627
|
-
* step. Refuses while another reservation is pending (recovery needed)
|
|
2628
|
-
* or the standing receipt cannot be determined. The caller MUST either
|
|
2629
|
-
* `commit` after its durable write lands or `abort` when it fails.
|
|
2630
|
-
* #2807: the marker records the reserve-time fingerprint (`baselineDigest`)
|
|
2631
|
-
* and optional expected post-write fingerprint (`expectedDigest`) with
|
|
2632
|
-
* `writeLanded: false`. {@link recoverPendingRevision} decides crashes
|
|
2633
|
-
* before or after the memory write from that evidence. */
|
|
2634
|
-
async beginRevisionTransaction(filePath, expectedContent) {
|
|
2635
|
-
const relativePath = await this.resolveRelativePath(filePath);
|
|
2636
|
-
return await this.withShardLock(relativePath, async (lock) => {
|
|
2637
|
-
const standingInfo = await this.readStandingRevisionStrict(relativePath);
|
|
2638
|
-
const standing = standingInfo?.revision;
|
|
2639
|
-
const next = nextCasRevisionIso(standing);
|
|
2640
|
-
const baselineDigest = await this.digestDurableFile(relativePath);
|
|
2641
|
-
const expectedDigest = expectedContent != null ? this.digestContent(expectedContent) : void 0;
|
|
2642
|
-
const { shardPath } = await this.getSafeShardInfo(relativePath);
|
|
2643
|
-
await this.writeShard(shardPath, relativePath, next, lock, "pending", standing, {
|
|
2644
|
-
baselineDigest,
|
|
2645
|
-
...expectedDigest !== void 0 ? { expectedDigest } : {},
|
|
2646
|
-
writeLanded: false
|
|
2647
|
-
});
|
|
2648
|
-
return {
|
|
2649
|
-
pendingRevision: next,
|
|
2650
|
-
writeLanded: () => this.recordPendingWriteLanded(relativePath, next),
|
|
2651
|
-
commit: () => this.finalizeRevisionTransaction(relativePath, next, "commit"),
|
|
2652
|
-
abort: () => this.finalizeRevisionTransaction(relativePath, next, "abort")
|
|
2653
|
-
};
|
|
2654
|
-
});
|
|
2655
|
-
}
|
|
2656
|
-
digestContent(content) {
|
|
2657
|
-
const bytes = typeof content === "string" ? Buffer.from(content, "utf8") : content;
|
|
2658
|
-
return createHash("sha256").update(bytes).digest("hex");
|
|
2659
|
-
}
|
|
2660
|
-
async digestDurableFile(filePath) {
|
|
2661
|
-
const bytes = await this.readDurableBytes(filePath);
|
|
2662
|
-
return bytes === null ? null : createHash("sha256").update(bytes).digest("hex");
|
|
2663
|
-
}
|
|
2664
|
-
/** #2870: canonical SEMANTIC fingerprint of the durable file — its text
|
|
2665
|
-
* with the access-telemetry frontmatter keys stripped
|
|
2666
|
-
* ({@link canonicalSemanticFileText}), hashed. Null means the file was
|
|
2667
|
-
* absent. A fingerprint, never memory content. */
|
|
2668
|
-
async semanticFingerprintDurableFile(filePath) {
|
|
2669
|
-
const bytes = await this.readDurableBytes(filePath);
|
|
2670
|
-
return bytes === null ? null : createHash("sha256").update(canonicalSemanticFileText(bytes.toString("utf8"))).digest("hex");
|
|
2671
|
-
}
|
|
2672
|
-
async readDurableBytes(filePath) {
|
|
2673
|
-
const relativePath = await this.resolveRelativePath(filePath);
|
|
2674
|
-
const baseDir = path6.resolve(this.baseDir);
|
|
2675
|
-
const target = await resolveSafeStoragePath(baseDir, relativePath);
|
|
2676
|
-
try {
|
|
2677
|
-
return await readFile(target);
|
|
2678
|
-
} catch (error) {
|
|
2679
|
-
if (isErrnoCode(error, "ENOENT")) return null;
|
|
2680
|
-
throw new Error(
|
|
2681
|
-
`CAS revision evidence: durable memory file ${relativePath} is unreadable: ${error instanceof Error ? error.message : String(error)}`
|
|
2682
|
-
);
|
|
2683
|
-
}
|
|
2684
|
-
}
|
|
2685
|
-
/** #2807: mark the reserved write as landed. Verifies a pre-recorded
|
|
2686
|
-
* expected digest against the durable file, or records the file digest
|
|
2687
|
-
* when none was reserved. Sets `writeLanded: true`. Idempotent. */
|
|
2688
|
-
async recordPendingWriteLanded(relativePath, token) {
|
|
2689
|
-
await this.withShardLock(relativePath, async (lock) => {
|
|
2690
|
-
const { shardPath } = await this.getSafeShardInfo(relativePath);
|
|
2691
|
-
const view = await this.readShardView(shardPath, relativePath);
|
|
2692
|
-
if (view.kind !== "pending" || view.foreign || view.revision !== token) {
|
|
2693
|
-
throw new Error(
|
|
2694
|
-
`CAS revision evidence recording refused for ${relativePath}: the pending marker does not match the reserved token.`
|
|
2695
|
-
);
|
|
2696
|
-
}
|
|
2697
|
-
if (pendingWriteHasLanded(view) && view.writeLanded !== false) return;
|
|
2698
|
-
const currentDigest = await this.digestDurableFile(relativePath);
|
|
2699
|
-
if (view.expectedDigest !== void 0 && currentDigest !== view.expectedDigest) {
|
|
2700
|
-
throw new Error(
|
|
2701
|
-
`CAS revision evidence recording refused for ${relativePath}: the durable file does not match the reserved expected digest.`
|
|
2702
|
-
);
|
|
2703
|
-
}
|
|
2704
|
-
await this.writeShard(shardPath, relativePath, token, lock, "pending", view.previous, {
|
|
2705
|
-
baselineDigest: view.baselineDigest === void 0 ? null : view.baselineDigest,
|
|
2706
|
-
expectedDigest: view.expectedDigest !== void 0 ? view.expectedDigest : currentDigest,
|
|
2707
|
-
writeLanded: true
|
|
2708
|
-
});
|
|
2709
|
-
});
|
|
2710
|
-
}
|
|
2711
|
-
async finalizeRevisionTransaction(relativePath, token, outcome) {
|
|
2712
|
-
await this.withShardLock(relativePath, async (lock) => {
|
|
2713
|
-
const { shardPath } = await this.getSafeShardInfo(relativePath);
|
|
2714
|
-
const view = await this.readShardView(shardPath, relativePath);
|
|
2715
|
-
if (view.kind === "pending" && !view.foreign && view.revision === token) {
|
|
2716
|
-
if (outcome === "commit") {
|
|
2717
|
-
const committedDigest = view.expectedDigest ?? await this.digestDurableFile(relativePath);
|
|
2718
|
-
const committedSemanticFingerprint = await this.semanticFingerprintDurableFile(relativePath);
|
|
2719
|
-
await this.writeShard(shardPath, relativePath, token, lock, "committed", void 0, {
|
|
2720
|
-
committedDigest,
|
|
2721
|
-
committedSemanticFingerprint
|
|
2722
|
-
});
|
|
2723
|
-
} else if (view.previous !== void 0) {
|
|
2724
|
-
await this.writeShard(shardPath, relativePath, view.previous, lock, "committed");
|
|
2725
|
-
} else {
|
|
2726
|
-
await unlink2(shardPath);
|
|
2727
|
-
}
|
|
2728
|
-
return;
|
|
2729
|
-
}
|
|
2730
|
-
if (outcome === "commit" && view.kind === "committed" && !view.foreign && view.revision === token) {
|
|
2731
|
-
return;
|
|
2732
|
-
}
|
|
2733
|
-
if (outcome === "abort" && view.kind === "missing") {
|
|
2734
|
-
return;
|
|
2735
|
-
}
|
|
2736
|
-
throw new Error(
|
|
2737
|
-
`CAS revision transaction ${outcome} refused for ${relativePath}: the pending marker does not match the reserved token.`
|
|
2738
|
-
);
|
|
2739
|
-
});
|
|
2740
|
-
}
|
|
2741
|
-
/** #2813 (P1 C): recovery for a reservation left PENDING by a crash or a
|
|
2742
|
-
* failed publication. The caller supplies the KNOWN outcome of the
|
|
2743
|
-
* durable memory write: `fileWriteLanded` publishes the pending token as
|
|
2744
|
-
* the COMMITTED receipt; otherwise the reservation is discarded and the
|
|
2745
|
-
* previous standing token restored. Idempotent — an already-committed or
|
|
2746
|
-
* absent shard is reported, not mutated. */
|
|
2747
|
-
async reconcilePendingRevision(filePath, fileWriteLanded) {
|
|
2748
|
-
const relativePath = await this.resolveRelativePath(filePath);
|
|
2749
|
-
return await this.withShardLock(relativePath, async (lock) => {
|
|
2750
|
-
const { shardPath } = await this.getSafeShardInfo(relativePath);
|
|
2751
|
-
const view = await this.readShardView(shardPath, relativePath);
|
|
2752
|
-
if (view.kind === "committed") return "committed";
|
|
2753
|
-
if (view.kind !== "pending") return "absent";
|
|
2754
|
-
if (view.foreign) {
|
|
2755
|
-
throw new Error(
|
|
2756
|
-
`CAS revision pending marker for ${relativePath} names another target; manual recovery is required.`
|
|
2757
|
-
);
|
|
2758
|
-
}
|
|
2759
|
-
if (fileWriteLanded) {
|
|
2760
|
-
const committedDigest = view.expectedDigest ?? await this.digestDurableFile(relativePath);
|
|
2761
|
-
const committedSemanticFingerprint = await this.semanticFingerprintDurableFile(relativePath);
|
|
2762
|
-
await this.writeShard(shardPath, relativePath, view.revision, lock, "committed", void 0, {
|
|
2763
|
-
committedDigest,
|
|
2764
|
-
committedSemanticFingerprint
|
|
2765
|
-
});
|
|
2766
|
-
} else if (view.previous !== void 0) {
|
|
2767
|
-
await this.writeShard(shardPath, relativePath, view.previous, lock, "committed");
|
|
2768
|
-
} else {
|
|
2769
|
-
await unlink2(shardPath);
|
|
2770
|
-
}
|
|
2771
|
-
return "reconciled";
|
|
2772
|
-
});
|
|
2773
|
-
}
|
|
2774
|
-
/** #2807 (P1): evidence-based recovery for a reservation left PENDING by
|
|
2775
|
-
* a crash between reserve and finalize. Under the shard lock, the durable
|
|
2776
|
-
* file's current fingerprint is compared against the marker:
|
|
2777
|
-
* - matches `expectedDigest` → publish the pending token as COMMITTED;
|
|
2778
|
-
* - matches `baselineDigest` (including both absent) → abort and restore;
|
|
2779
|
-
* - anything else THROWS. Recovery never guesses.
|
|
2780
|
-
*
|
|
2781
|
-
* `onlyWithWriteEvidence` is the unlocked read path. A marker whose write
|
|
2782
|
-
* has not landed (`writeLanded` false, or legacy with no expectedDigest)
|
|
2783
|
-
* may belong to a live writer, so it returns `reserved`. A landed marker
|
|
2784
|
-
* whose file no longer matches `expectedDigest` throws ambiguity. */
|
|
2785
|
-
async recoverPendingRevision(filePath, options) {
|
|
2786
|
-
const relativePath = await this.resolveRelativePath(filePath);
|
|
2787
|
-
return await this.withShardLock(relativePath, async (lock) => {
|
|
2788
|
-
const { shardPath } = await this.getSafeShardInfo(relativePath);
|
|
2789
|
-
const view = await this.readShardView(shardPath, relativePath);
|
|
2790
|
-
if (view.kind === "missing" || view.kind === "foreign") return "absent";
|
|
2791
|
-
if (view.kind === "committed") return view.foreign ? "absent" : "committed";
|
|
2792
|
-
if (view.foreign) {
|
|
2793
|
-
throw new Error(
|
|
2794
|
-
`CAS revision pending marker for ${relativePath} names another target; manual recovery is required.`
|
|
2795
|
-
);
|
|
2796
|
-
}
|
|
2797
|
-
const currentDigest = await this.digestDurableFile(relativePath);
|
|
2798
|
-
const landed = pendingWriteHasLanded(view);
|
|
2799
|
-
if (options?.onlyWithWriteEvidence && !landed) {
|
|
2800
|
-
return "reserved";
|
|
2801
|
-
}
|
|
2802
|
-
if (view.expectedDigest !== void 0 && currentDigest === view.expectedDigest) {
|
|
2803
|
-
await this.writeShard(shardPath, relativePath, view.revision, lock, "committed", void 0, {
|
|
2804
|
-
committedDigest: view.expectedDigest,
|
|
2805
|
-
committedSemanticFingerprint: await this.semanticFingerprintDurableFile(relativePath)
|
|
2806
|
-
});
|
|
2807
|
-
return "committed";
|
|
2808
|
-
}
|
|
2809
|
-
if (options?.onlyWithWriteEvidence) {
|
|
2810
|
-
const evidence2 = view.expectedDigest === void 0 ? "the pending marker carries no crash evidence (written by an older version)" : "the durable file matches neither the expected commit fingerprint nor the reserve-time baseline";
|
|
2811
|
-
throw new Error(
|
|
2812
|
-
`CAS revision recovery for ${relativePath} is ambiguous: ${evidence2} (pending token ${view.revision}${view.previous !== void 0 ? `, previous ${view.previous}` : ""}). Inspect the durable file and the shard at ${shardPath}, then run reconcilePendingRevision with the known write outcome. Refusing to guess.`
|
|
2813
|
-
);
|
|
2814
|
-
}
|
|
2815
|
-
if (view.baselineDigest !== void 0 && currentDigest === view.baselineDigest) {
|
|
2816
|
-
if (view.previous !== void 0) {
|
|
2817
|
-
await this.writeShard(shardPath, relativePath, view.previous, lock, "committed");
|
|
2818
|
-
} else {
|
|
2819
|
-
await unlink2(shardPath);
|
|
2820
|
-
}
|
|
2821
|
-
return "aborted";
|
|
2822
|
-
}
|
|
2823
|
-
const evidence = view.baselineDigest === void 0 && view.expectedDigest === void 0 ? "the pending marker carries no crash evidence (written by an older version)" : "the durable file matches neither the expected commit fingerprint nor the reserve-time baseline";
|
|
2824
|
-
throw new Error(
|
|
2825
|
-
`CAS revision recovery for ${relativePath} is ambiguous: ${evidence} (pending token ${view.revision}${view.previous !== void 0 ? `, previous ${view.previous}` : ""}). Inspect the durable file and the shard at ${shardPath}, then run reconcilePendingRevision with the known write outcome. Refusing to guess.`
|
|
2826
|
-
);
|
|
2827
|
-
});
|
|
2828
|
-
}
|
|
2829
|
-
};
|
|
2830
|
-
|
|
2831
|
-
// src/storage/cas-revision-host.ts
|
|
2832
|
-
var CasRevisionHost = class extends CasRevisionStore {
|
|
2833
|
-
/** Standing CAS revision token — receipt identity, never public `updated` (#2807). Fail-open on read errors. */
|
|
2834
|
-
async readCasRevision(filePath) {
|
|
2835
|
-
try {
|
|
2836
|
-
return await this.readRevision(filePath);
|
|
2837
|
-
} catch (err) {
|
|
2838
|
-
log.warn(`storage.readCasRevision failed for ${filePath}: ${err}`);
|
|
2839
|
-
return void 0;
|
|
2840
|
-
}
|
|
2841
|
-
}
|
|
2842
|
-
/** #2813 (P1 A): truthful CAS receipt probe — distinguishes a genuinely
|
|
2843
|
-
* ABSENT receipt (target predates the sidecar; `undefined` semantics stay
|
|
2844
|
-
* correct) from an UNAVAILABLE one (unreadable shard: receipt identity is
|
|
2845
|
-
* unknown). Transactional callers that must refuse on unknown identity
|
|
2846
|
-
* route here; `readCasRevision` stays fail-open for advisory reads.
|
|
2847
|
-
* #2807 (P1): a PENDING marker left by a crash is lazily recovered on
|
|
2848
|
-
* this first read when its evidence is decisive; a reserve-only marker
|
|
2849
|
-
* may belong to a live writer and defers to the path-locked write path,
|
|
2850
|
-
* and ambiguity stays `unavailable` with an actionable reason. */
|
|
2851
|
-
async readCasRevisionStatus(filePath) {
|
|
2852
|
-
const status = await this.readRevisionStatus(filePath);
|
|
2853
|
-
if (status.status !== "unavailable" || !/pending/i.test(status.reason)) return status;
|
|
2854
|
-
try {
|
|
2855
|
-
const outcome = await this.recoverPendingRevision(filePath, {
|
|
2856
|
-
onlyWithWriteEvidence: true
|
|
2857
|
-
});
|
|
2858
|
-
if (outcome === "reserved") return status;
|
|
2859
|
-
} catch (error) {
|
|
2860
|
-
return {
|
|
2861
|
-
status: "unavailable",
|
|
2862
|
-
reason: error instanceof Error ? error.message : String(error)
|
|
2863
|
-
};
|
|
2864
|
-
}
|
|
2865
|
-
return await this.readRevisionStatus(filePath);
|
|
2866
|
-
}
|
|
2867
|
-
async readDurableFileDigest(filePath) {
|
|
2868
|
-
try {
|
|
2869
|
-
return await this.digestDurableFile(filePath);
|
|
2870
|
-
} catch (err) {
|
|
2871
|
-
log.warn(`storage.readDurableFileDigest failed for ${filePath}: ${err}`);
|
|
2872
|
-
return null;
|
|
2873
|
-
}
|
|
2874
|
-
}
|
|
2875
|
-
/** #2807 (P1): reconcile any crash-orphaned PENDING marker for this
|
|
2876
|
-
* target BEFORE the next semantic mutation reserves a new token —
|
|
2877
|
-
* otherwise a crashed reservation would make the memory permanently
|
|
2878
|
-
* unwritable. Decisive evidence heals; ambiguity throws so the
|
|
2879
|
-
* mutation fails closed with the actionable recovery error. */
|
|
2880
|
-
async beginRevision(pathname, expectedContent) {
|
|
2881
|
-
await this.recoverPendingRevision(pathname);
|
|
2882
|
-
return await this.beginRevisionTransaction(pathname, expectedContent);
|
|
2883
|
-
}
|
|
2884
|
-
};
|
|
2885
|
-
|
|
2886
2351
|
// src/storage/identity-continuity-store.ts
|
|
2887
2352
|
import { readdir as readdir3 } from "fs/promises";
|
|
2888
|
-
import
|
|
2353
|
+
import path6 from "path";
|
|
2889
2354
|
var IdentityContinuityStore = class {
|
|
2890
2355
|
constructor(deps) {
|
|
2891
2356
|
this.deps = deps;
|
|
@@ -2910,7 +2375,7 @@ var IdentityContinuityStore = class {
|
|
|
2910
2375
|
const date = nowIso.slice(0, 10);
|
|
2911
2376
|
const id = this.deps.generateId("incident");
|
|
2912
2377
|
const incident = createContinuityIncidentRecord(id, input, nowIso);
|
|
2913
|
-
const filePath =
|
|
2378
|
+
const filePath = path6.join(this.deps.identityIncidentsDir, `${date}-${id}.md`);
|
|
2914
2379
|
await this.deps.writeStorageSecureFile(filePath, serializeContinuityIncident(incident));
|
|
2915
2380
|
return { ...incident, filePath };
|
|
2916
2381
|
}
|
|
@@ -2923,7 +2388,7 @@ var IdentityContinuityStore = class {
|
|
|
2923
2388
|
const incidents = [];
|
|
2924
2389
|
for (const file of candidates) {
|
|
2925
2390
|
if (incidents.length >= cappedLimit) break;
|
|
2926
|
-
const filePath =
|
|
2391
|
+
const filePath = path6.join(this.deps.identityIncidentsDir, file);
|
|
2927
2392
|
try {
|
|
2928
2393
|
const raw = await this.deps.readStorageSecureFile(filePath);
|
|
2929
2394
|
const parsed = parseContinuityIncident(raw);
|
|
@@ -2954,7 +2419,7 @@ var IdentityContinuityStore = class {
|
|
|
2954
2419
|
await this.deps.ensureDirectories();
|
|
2955
2420
|
const safeKey = this.sanitizeIdentityAuditKey(key);
|
|
2956
2421
|
const dir = period === "weekly" ? this.deps.identityAuditsWeeklyDir : this.deps.identityAuditsMonthlyDir;
|
|
2957
|
-
const filePath =
|
|
2422
|
+
const filePath = path6.join(dir, `${safeKey}.md`);
|
|
2958
2423
|
await this.deps.writeStorageSecureFile(filePath, content);
|
|
2959
2424
|
return filePath;
|
|
2960
2425
|
}
|
|
@@ -2962,7 +2427,7 @@ var IdentityContinuityStore = class {
|
|
|
2962
2427
|
try {
|
|
2963
2428
|
const safeKey = this.sanitizeIdentityAuditKey(key);
|
|
2964
2429
|
const dir = period === "weekly" ? this.deps.identityAuditsWeeklyDir : this.deps.identityAuditsMonthlyDir;
|
|
2965
|
-
return await this.deps.readStorageSecureFile(
|
|
2430
|
+
return await this.deps.readStorageSecureFile(path6.join(dir, `${safeKey}.md`));
|
|
2966
2431
|
} catch (err) {
|
|
2967
2432
|
if (err instanceof Error && err.message === "Invalid identity audit key") return null;
|
|
2968
2433
|
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
@@ -3018,12 +2483,12 @@ var IdentityContinuityStore = class {
|
|
|
3018
2483
|
const fileNames = await this.readContinuityIncidentFileNames();
|
|
3019
2484
|
const directMatch = fileNames.find((name) => name.endsWith(`-${id}.md`));
|
|
3020
2485
|
if (directMatch) {
|
|
3021
|
-
const directPath =
|
|
2486
|
+
const directPath = path6.join(this.deps.identityIncidentsDir, directMatch);
|
|
3022
2487
|
const parsed = await this.readContinuityIncidentFile(directPath);
|
|
3023
2488
|
if (parsed?.id === id) return directPath;
|
|
3024
2489
|
}
|
|
3025
2490
|
for (const fileName of fileNames) {
|
|
3026
|
-
const filePath =
|
|
2491
|
+
const filePath = path6.join(this.deps.identityIncidentsDir, fileName);
|
|
3027
2492
|
const parsed = await this.readContinuityIncidentFile(filePath);
|
|
3028
2493
|
if (parsed?.id === id) return filePath;
|
|
3029
2494
|
}
|
|
@@ -3039,19 +2504,19 @@ var IdentityContinuityStore = class {
|
|
|
3039
2504
|
};
|
|
3040
2505
|
|
|
3041
2506
|
// src/storage/entity-canonical-id-migration.ts
|
|
3042
|
-
import { createHash
|
|
2507
|
+
import { createHash, randomUUID } from "crypto";
|
|
3043
2508
|
import {
|
|
3044
2509
|
access,
|
|
3045
2510
|
lstat as lstat2,
|
|
3046
|
-
readFile
|
|
2511
|
+
readFile,
|
|
3047
2512
|
readdir as readdir4,
|
|
3048
2513
|
realpath as realpath2,
|
|
3049
|
-
rename
|
|
2514
|
+
rename,
|
|
3050
2515
|
stat as statFile,
|
|
3051
|
-
unlink as
|
|
2516
|
+
unlink as unlink2,
|
|
3052
2517
|
writeFile
|
|
3053
2518
|
} from "fs/promises";
|
|
3054
|
-
import
|
|
2519
|
+
import path7 from "path";
|
|
3055
2520
|
import { z } from "zod";
|
|
3056
2521
|
var ENTITY_CANONICAL_ID_MIGRATION_VERSION = 1;
|
|
3057
2522
|
var ENTITY_CANONICAL_ID_MIGRATION_LOCK_STALE_MS = 6e4;
|
|
@@ -3085,16 +2550,16 @@ async function fingerprintPath(filePath) {
|
|
|
3085
2550
|
async function fingerprintEntityEntries(entitiesDir) {
|
|
3086
2551
|
let names;
|
|
3087
2552
|
try {
|
|
3088
|
-
names = (await readdir4(entitiesDir)).filter((name) =>
|
|
2553
|
+
names = (await readdir4(entitiesDir)).filter((name) => path7.extname(name) === ".md").sort();
|
|
3089
2554
|
} catch (error) {
|
|
3090
2555
|
if (isErrnoCode(error, "ENOENT")) return "missing";
|
|
3091
2556
|
throw error;
|
|
3092
2557
|
}
|
|
3093
|
-
const hash =
|
|
2558
|
+
const hash = createHash("sha256");
|
|
3094
2559
|
for (const name of names) {
|
|
3095
2560
|
hash.update(`${name.length}:${name}:`);
|
|
3096
2561
|
try {
|
|
3097
|
-
const entry = await lstat2(
|
|
2562
|
+
const entry = await lstat2(path7.join(entitiesDir, name), { bigint: true });
|
|
3098
2563
|
hash.update(`${entry.dev}:${entry.ino}:${entry.size}:${entry.mtimeNs}:${entry.ctimeNs}`);
|
|
3099
2564
|
} catch (error) {
|
|
3100
2565
|
if (!isErrnoCode(error, "ENOENT")) throw error;
|
|
@@ -3114,9 +2579,9 @@ async function getFingerprint(baseDir, entitiesDir, getEntityMutationVersion) {
|
|
|
3114
2579
|
] = await Promise.all([
|
|
3115
2580
|
fingerprintPath(entitiesDir),
|
|
3116
2581
|
fingerprintEntityEntries(entitiesDir),
|
|
3117
|
-
fingerprintPath(
|
|
3118
|
-
fingerprintPath(
|
|
3119
|
-
fingerprintPath(
|
|
2582
|
+
fingerprintPath(path7.join(baseDir, "config", "aliases.json")),
|
|
2583
|
+
fingerprintPath(path7.join(baseDir, "state", ENTITY_CANONICAL_ID_RECONCILE_MARKER)),
|
|
2584
|
+
fingerprintPath(path7.join(baseDir, "state", ENTITY_CANONICAL_ID_RECONCILE_CONSUMING_MARKER))
|
|
3120
2585
|
]);
|
|
3121
2586
|
return [
|
|
3122
2587
|
entityDirectoryFingerprint,
|
|
@@ -3142,8 +2607,8 @@ async function validateRoots(baseDir, entitiesDir, stateDir, archiveDir) {
|
|
|
3142
2607
|
const roots = [
|
|
3143
2608
|
entitiesDir,
|
|
3144
2609
|
stateDir,
|
|
3145
|
-
...RECALL_FALLBACK_DIRS.map((directory) =>
|
|
3146
|
-
|
|
2610
|
+
...RECALL_FALLBACK_DIRS.map((directory) => path7.join(baseDir, directory)),
|
|
2611
|
+
path7.join(baseDir, "cold"),
|
|
3147
2612
|
archiveDir
|
|
3148
2613
|
];
|
|
3149
2614
|
for (const root of roots) {
|
|
@@ -3161,7 +2626,7 @@ async function validateRoots(baseDir, entitiesDir, stateDir, archiveDir) {
|
|
|
3161
2626
|
}
|
|
3162
2627
|
}
|
|
3163
2628
|
function statePath(deps) {
|
|
3164
|
-
return
|
|
2629
|
+
return path7.join(deps.stateDir, ENTITY_CANONICAL_ID_MIGRATION_FILE);
|
|
3165
2630
|
}
|
|
3166
2631
|
async function assertSafeMigrationStateFile(deps) {
|
|
3167
2632
|
const migrationStatePath = statePath(deps);
|
|
@@ -3204,13 +2669,13 @@ async function assertSafeTombstoneFile(deps, tombstonePath) {
|
|
|
3204
2669
|
return true;
|
|
3205
2670
|
}
|
|
3206
2671
|
function lockPath(deps) {
|
|
3207
|
-
return
|
|
2672
|
+
return path7.join(deps.stateDir, "entity-canonical-id-migration.lock");
|
|
3208
2673
|
}
|
|
3209
2674
|
async function readState(deps) {
|
|
3210
2675
|
await assertSafeMigrationStateFile(deps);
|
|
3211
2676
|
let raw;
|
|
3212
2677
|
try {
|
|
3213
|
-
raw = await
|
|
2678
|
+
raw = await readFile(statePath(deps), "utf-8");
|
|
3214
2679
|
} catch (error) {
|
|
3215
2680
|
if (isErrnoCode(error, "ENOENT")) return null;
|
|
3216
2681
|
throw error;
|
|
@@ -3233,13 +2698,13 @@ async function readState(deps) {
|
|
|
3233
2698
|
}
|
|
3234
2699
|
async function writeState(deps, state) {
|
|
3235
2700
|
const destination = statePath(deps);
|
|
3236
|
-
const temporary = `${destination}.${process.pid}.${
|
|
2701
|
+
const temporary = `${destination}.${process.pid}.${randomUUID()}.tmp`;
|
|
3237
2702
|
try {
|
|
3238
2703
|
await writeFile(temporary, `${JSON.stringify(state, null, 2)}
|
|
3239
2704
|
`, "utf-8");
|
|
3240
|
-
await
|
|
2705
|
+
await rename(temporary, destination);
|
|
3241
2706
|
} finally {
|
|
3242
|
-
await
|
|
2707
|
+
await unlink2(temporary).catch(() => void 0);
|
|
3243
2708
|
}
|
|
3244
2709
|
}
|
|
3245
2710
|
async function publishState(deps, state) {
|
|
@@ -3302,7 +2767,7 @@ async function readSafeEntityEntries(entitiesDir) {
|
|
|
3302
2767
|
}
|
|
3303
2768
|
const entries = await readdir4(entitiesDir);
|
|
3304
2769
|
for (const entry of entries) {
|
|
3305
|
-
await assertNotSymlink(
|
|
2770
|
+
await assertNotSymlink(path7.join(entitiesDir, entry), entry);
|
|
3306
2771
|
}
|
|
3307
2772
|
return entries;
|
|
3308
2773
|
}
|
|
@@ -3364,7 +2829,7 @@ async function rewriteReferences(deps, mappings, refreshLock) {
|
|
|
3364
2829
|
const seenPaths = /* @__PURE__ */ new Set();
|
|
3365
2830
|
let rewroteAny = false;
|
|
3366
2831
|
for (const memory of [...hotMemories, ...coldMemories, ...archivedMemories]) {
|
|
3367
|
-
const memoryPath =
|
|
2832
|
+
const memoryPath = path7.resolve(memory.path);
|
|
3368
2833
|
if (seenPaths.has(memoryPath)) continue;
|
|
3369
2834
|
seenPaths.add(memoryPath);
|
|
3370
2835
|
const previousEntityRef = memory.frontmatter.entityRef;
|
|
@@ -3396,7 +2861,7 @@ async function rewriteReferences(deps, mappings, refreshLock) {
|
|
|
3396
2861
|
latestCanonicalId
|
|
3397
2862
|
);
|
|
3398
2863
|
rewroteAny = true;
|
|
3399
|
-
rewroteColdMemory ||= latestMemory.path.includes(`${
|
|
2864
|
+
rewroteColdMemory ||= latestMemory.path.includes(`${path7.sep}cold${path7.sep}`);
|
|
3400
2865
|
}
|
|
3401
2866
|
if (!rewroteAny) return { rewroteColdMemory };
|
|
3402
2867
|
}
|
|
@@ -3435,7 +2900,7 @@ function rewriteTombstoneLine(line, mappings, normalizedMappings) {
|
|
|
3435
2900
|
return changed ? JSON.stringify(tombstone) : line;
|
|
3436
2901
|
}
|
|
3437
2902
|
async function rewriteTombstoneReferences(deps, mappings, refreshLock) {
|
|
3438
|
-
const tombstonePath =
|
|
2903
|
+
const tombstonePath = path7.join(deps.stateDir, "tombstones.jsonl");
|
|
3439
2904
|
const normalizedMappings = /* @__PURE__ */ new Map();
|
|
3440
2905
|
for (const [legacyId, canonicalId] of Object.entries(mappings)) {
|
|
3441
2906
|
const normalizedLegacyId = normalizeSupersessionKey(legacyId);
|
|
@@ -3444,7 +2909,7 @@ async function rewriteTombstoneReferences(deps, mappings, refreshLock) {
|
|
|
3444
2909
|
}
|
|
3445
2910
|
}
|
|
3446
2911
|
await withHeldFileLock(
|
|
3447
|
-
|
|
2912
|
+
path7.join(deps.stateDir, "tombstones.lock"),
|
|
3448
2913
|
{
|
|
3449
2914
|
staleMs: TOMBSTONE_LOCK_STALE_MS,
|
|
3450
2915
|
maxWaitMs: TOMBSTONE_LOCK_MAX_WAIT_MS
|
|
@@ -3632,7 +3097,7 @@ async function migrateEntityFilePair(deps, legacyId, canonicalId, refreshMigrati
|
|
|
3632
3097
|
await deps.snapshotBeforeWrite(legacyPath, "write");
|
|
3633
3098
|
await assertNotSymlink(legacyPath, legacyId);
|
|
3634
3099
|
await assertEntityContentUnchanged(deps, legacyPath, legacyContent, legacyId);
|
|
3635
|
-
await
|
|
3100
|
+
await unlink2(legacyPath);
|
|
3636
3101
|
return { deferred: false, progressed: true };
|
|
3637
3102
|
}
|
|
3638
3103
|
if (!legacyExists) return { deferred: false, progressed: false };
|
|
@@ -3656,13 +3121,13 @@ async function migrateEntityFilePair(deps, legacyId, canonicalId, refreshMigrati
|
|
|
3656
3121
|
await deps.snapshotBeforeWrite(legacyPath, "write");
|
|
3657
3122
|
await assertNotSymlink(legacyPath, legacyId);
|
|
3658
3123
|
await assertEntityContentUnchanged(deps, legacyPath, entityContent, legacyId);
|
|
3659
|
-
await
|
|
3124
|
+
await unlink2(legacyPath);
|
|
3660
3125
|
} catch (error) {
|
|
3661
3126
|
if (!canonicalExists && canonicalFingerprintAfterWrite !== null) {
|
|
3662
3127
|
try {
|
|
3663
3128
|
if (await fingerprintPath(canonicalPath) === canonicalFingerprintAfterWrite) {
|
|
3664
3129
|
await assertNotSymlink(canonicalPath, canonicalId);
|
|
3665
|
-
await
|
|
3130
|
+
await unlink2(canonicalPath);
|
|
3666
3131
|
}
|
|
3667
3132
|
} catch {
|
|
3668
3133
|
}
|
|
@@ -3683,12 +3148,12 @@ async function migrateLegacyEntityCanonicalIds(deps) {
|
|
|
3683
3148
|
if (!acquired) throw new Error("Timed out waiting for active entity canonical-id migration.");
|
|
3684
3149
|
await deps.validateMemoryScanRoots?.();
|
|
3685
3150
|
await readSafeEntityEntries(deps.entitiesDir);
|
|
3686
|
-
const reconcileMarkerPath =
|
|
3687
|
-
const consumingMarkerPath =
|
|
3151
|
+
const reconcileMarkerPath = path7.join(deps.stateDir, ENTITY_CANONICAL_ID_RECONCILE_MARKER);
|
|
3152
|
+
const consumingMarkerPath = path7.join(deps.stateDir, ENTITY_CANONICAL_ID_RECONCILE_CONSUMING_MARKER);
|
|
3688
3153
|
let reconcileRequested = await fileExists(consumingMarkerPath);
|
|
3689
3154
|
if (await fileExists(reconcileMarkerPath)) {
|
|
3690
3155
|
try {
|
|
3691
|
-
await
|
|
3156
|
+
await rename(reconcileMarkerPath, consumingMarkerPath);
|
|
3692
3157
|
reconcileRequested = true;
|
|
3693
3158
|
} catch (error) {
|
|
3694
3159
|
if (!isErrnoCode(error, "ENOENT")) throw error;
|
|
@@ -3711,7 +3176,7 @@ async function migrateLegacyEntityCanonicalIds(deps) {
|
|
|
3711
3176
|
);
|
|
3712
3177
|
}
|
|
3713
3178
|
if (reconcileRequested) {
|
|
3714
|
-
await
|
|
3179
|
+
await unlink2(consumingMarkerPath).catch((error) => {
|
|
3715
3180
|
if (!isErrnoCode(error, "ENOENT")) throw error;
|
|
3716
3181
|
});
|
|
3717
3182
|
}
|
|
@@ -3857,7 +3322,7 @@ async function migrateLegacyEntityCanonicalIds(deps) {
|
|
|
3857
3322
|
}
|
|
3858
3323
|
|
|
3859
3324
|
// src/storage/entity-ref-repair.ts
|
|
3860
|
-
import { unlink as
|
|
3325
|
+
import { unlink as unlink3 } from "fs/promises";
|
|
3861
3326
|
var EntityRefRepair = class {
|
|
3862
3327
|
constructor(deps) {
|
|
3863
3328
|
this.deps = deps;
|
|
@@ -3919,7 +3384,7 @@ ${body}
|
|
|
3919
3384
|
if (opts.onFailRestore) {
|
|
3920
3385
|
await this.restore(opts.onFailRestore);
|
|
3921
3386
|
} else if (opts.onFailRemove) {
|
|
3922
|
-
await
|
|
3387
|
+
await unlink3(opts.onFailRemove).catch(() => void 0);
|
|
3923
3388
|
this.deps.invalidateAllMemoriesCache();
|
|
3924
3389
|
}
|
|
3925
3390
|
throw err;
|
|
@@ -4080,7 +3545,7 @@ import {
|
|
|
4080
3545
|
readFileSync,
|
|
4081
3546
|
realpathSync
|
|
4082
3547
|
} from "fs";
|
|
4083
|
-
import
|
|
3548
|
+
import path8 from "path";
|
|
4084
3549
|
function resolveSafeAliasRoots(baseDir, configDir) {
|
|
4085
3550
|
let baseStat;
|
|
4086
3551
|
try {
|
|
@@ -4107,8 +3572,8 @@ function resolveSafeAliasRoots(baseDir, configDir) {
|
|
|
4107
3572
|
return memoryRoot;
|
|
4108
3573
|
}
|
|
4109
3574
|
function readEntityAliasConfigSync(baseDir) {
|
|
4110
|
-
const configDir =
|
|
4111
|
-
const aliasPath =
|
|
3575
|
+
const configDir = path8.join(baseDir, "config");
|
|
3576
|
+
const aliasPath = path8.join(configDir, "aliases.json");
|
|
4112
3577
|
let memoryRoot = resolveSafeAliasRoots(baseDir, configDir);
|
|
4113
3578
|
if (memoryRoot === void 0) return void 0;
|
|
4114
3579
|
let aliasPreflight;
|
|
@@ -4169,7 +3634,7 @@ function decodeYamlScalar(value) {
|
|
|
4169
3634
|
}
|
|
4170
3635
|
|
|
4171
3636
|
// src/orchestration/qmd-result-resolver.ts
|
|
4172
|
-
import
|
|
3637
|
+
import path9 from "path";
|
|
4173
3638
|
var INTERNAL_QMD_ROOTS = /* @__PURE__ */ new Set([
|
|
4174
3639
|
...ALL_CATEGORY_DIRS,
|
|
4175
3640
|
"activity",
|
|
@@ -4191,7 +3656,7 @@ var INTERNAL_QMD_ROOTS = /* @__PURE__ */ new Set([
|
|
|
4191
3656
|
]);
|
|
4192
3657
|
var PRIVATE_RESULT_RESOLUTION_CONCURRENCY = 16;
|
|
4193
3658
|
function qmdCollectionPathParts(resultPath) {
|
|
4194
|
-
if (!resultPath ||
|
|
3659
|
+
if (!resultPath || path9.isAbsolute(resultPath)) return null;
|
|
4195
3660
|
if (resultPath.trim().startsWith("qmd://")) {
|
|
4196
3661
|
try {
|
|
4197
3662
|
const parsed = new URL(resultPath.trim());
|
|
@@ -4216,9 +3681,9 @@ function qmdCollectionPathParts(resultPath) {
|
|
|
4216
3681
|
}
|
|
4217
3682
|
function qmdResultPathCandidates(storageDir, resultPath) {
|
|
4218
3683
|
const candidates = /* @__PURE__ */ new Set();
|
|
4219
|
-
const storageRoot =
|
|
3684
|
+
const storageRoot = path9.resolve(storageDir);
|
|
4220
3685
|
const addCandidate = (candidate) => {
|
|
4221
|
-
const resolved =
|
|
3686
|
+
const resolved = path9.resolve(candidate);
|
|
4222
3687
|
if (isPathInsideStorageRoot(storageRoot, resolved)) {
|
|
4223
3688
|
candidates.add(resolved);
|
|
4224
3689
|
}
|
|
@@ -4226,18 +3691,18 @@ function qmdResultPathCandidates(storageDir, resultPath) {
|
|
|
4226
3691
|
const addRelativeCandidates = (relativePath) => {
|
|
4227
3692
|
const normalized = relativePath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
4228
3693
|
if (!normalized) return;
|
|
4229
|
-
addCandidate(
|
|
3694
|
+
addCandidate(path9.join(storageRoot, normalized));
|
|
4230
3695
|
if (/^\d{4}-\d{2}-\d{2}\//.test(normalized)) {
|
|
4231
|
-
addCandidate(
|
|
3696
|
+
addCandidate(path9.join(storageRoot, "facts", normalized));
|
|
4232
3697
|
}
|
|
4233
3698
|
};
|
|
4234
|
-
if (
|
|
3699
|
+
if (path9.isAbsolute(resultPath)) {
|
|
4235
3700
|
addCandidate(resultPath);
|
|
4236
|
-
const relative2 =
|
|
4237
|
-
if (relative2 && !relative2.startsWith("..") && !
|
|
4238
|
-
const normalized = relative2.split(
|
|
3701
|
+
const relative2 = path9.relative(storageRoot, path9.resolve(resultPath));
|
|
3702
|
+
if (relative2 && !relative2.startsWith("..") && !path9.isAbsolute(relative2)) {
|
|
3703
|
+
const normalized = relative2.split(path9.sep).join("/");
|
|
4239
3704
|
if (/^\d{4}-\d{2}-\d{2}\//.test(normalized)) {
|
|
4240
|
-
addCandidate(
|
|
3705
|
+
addCandidate(path9.join(storageRoot, "facts", normalized));
|
|
4241
3706
|
}
|
|
4242
3707
|
}
|
|
4243
3708
|
} else {
|
|
@@ -4273,7 +3738,7 @@ var QmdResultResolver = class {
|
|
|
4273
3738
|
const seenStorageDirs = /* @__PURE__ */ new Set();
|
|
4274
3739
|
const addStorage = (storage) => {
|
|
4275
3740
|
const storageDir = storageDirOrNull(storage);
|
|
4276
|
-
const storageKey = storageDir ?
|
|
3741
|
+
const storageKey = storageDir ? path9.resolve(storageDir) : `storage-without-dir-${storages.length}`;
|
|
4277
3742
|
if (seenStorageDirs.has(storageKey)) return;
|
|
4278
3743
|
seenStorageDirs.add(storageKey);
|
|
4279
3744
|
storages.push(storage);
|
|
@@ -4303,7 +3768,7 @@ var QmdResultResolver = class {
|
|
|
4303
3768
|
continue;
|
|
4304
3769
|
}
|
|
4305
3770
|
try {
|
|
4306
|
-
const coldRoot =
|
|
3771
|
+
const coldRoot = path9.join(storageDir, "cold");
|
|
4307
3772
|
for (const candidate of qmdResultPathCandidates(coldRoot, parts.relativePath)) {
|
|
4308
3773
|
const memory = await storage.readMemoryByPath(candidate);
|
|
4309
3774
|
if (memory) return memory;
|
|
@@ -4353,9 +3818,9 @@ var QmdResultResolver = class {
|
|
|
4353
3818
|
error: err.message
|
|
4354
3819
|
});
|
|
4355
3820
|
}
|
|
4356
|
-
if (!
|
|
3821
|
+
if (!path9.isAbsolute(fallbackResultPath)) return null;
|
|
4357
3822
|
}
|
|
4358
|
-
if (
|
|
3823
|
+
if (path9.isAbsolute(fallbackResultPath)) {
|
|
4359
3824
|
if (!fallbackStorageDir) {
|
|
4360
3825
|
return await fallbackStorage.readMemoryByPath(fallbackResultPath);
|
|
4361
3826
|
}
|
|
@@ -4412,17 +3877,17 @@ var QmdResultResolver = class {
|
|
|
4412
3877
|
if (!memory && parts && !collectionIsKnownInternal && !collectionIsConfigured) {
|
|
4413
3878
|
memory = await this.readQmdResultMemory(parts.relativePath, fallbackStorage, namespaces, result.namespace);
|
|
4414
3879
|
}
|
|
4415
|
-
const absoluteInsideMemoryRoot =
|
|
3880
|
+
const absoluteInsideMemoryRoot = path9.isAbsolute(result.path) && isPathInsideStorageRoot(path9.resolve(config.memoryDir), path9.resolve(result.path));
|
|
4416
3881
|
const hasExplicitQmdCollection = result.path.trim().startsWith("qmd://");
|
|
4417
3882
|
const unresolvedInternalPath = !memory && (absoluteInsideMemoryRoot || collectionIsKnownInternal && (!preserveUnresolved || hasExplicitQmdCollection) || hasExplicitQmdCollection && collectionIsConfigured);
|
|
4418
|
-
const unresolvedExternalCollection = !memory && (parts !== null && !collectionIsKnownInternal && !collectionIsConfigured ||
|
|
3883
|
+
const unresolvedExternalCollection = !memory && (parts !== null && !collectionIsKnownInternal && !collectionIsConfigured || path9.isAbsolute(result.path) && !isPathInsideStorageRoot(path9.resolve(config.memoryDir), path9.resolve(result.path)));
|
|
4419
3884
|
return memory !== null && !isSupportPassportPrivateMemory(memory) || !memory && !unresolvedInternalPath && (preserveUnresolved || unresolvedExternalCollection);
|
|
4420
3885
|
}
|
|
4421
3886
|
async resolveColdQmdResultForRecall(result, fallbackStorage, recallNamespaces = []) {
|
|
4422
3887
|
const memory = await this.readQmdResultMemory(result.path, fallbackStorage, recallNamespaces, result.namespace);
|
|
4423
3888
|
if (!memory) return null;
|
|
4424
3889
|
let ownerNamespace = null;
|
|
4425
|
-
if (
|
|
3890
|
+
if (path9.isAbsolute(memory.path)) {
|
|
4426
3891
|
const ownerStorage = await this.storageForAbsoluteQmdResultPath(memory.path, fallbackStorage, recallNamespaces);
|
|
4427
3892
|
ownerNamespace = ownerStorage?.namespace ?? null;
|
|
4428
3893
|
if (!ownerNamespace && resolveNamespaceCapabilities(this.getConfig()).namespaces) return null;
|
|
@@ -4443,17 +3908,17 @@ var QmdResultResolver = class {
|
|
|
4443
3908
|
};
|
|
4444
3909
|
}
|
|
4445
3910
|
async storageForAbsoluteQmdResultPath(resultPath, fallbackStorage, recallNamespaces = []) {
|
|
4446
|
-
const resolvedPath =
|
|
3911
|
+
const resolvedPath = path9.resolve(resultPath);
|
|
4447
3912
|
const config = this.getConfig();
|
|
4448
|
-
const memoryRoot =
|
|
4449
|
-
const namespacesRoot =
|
|
3913
|
+
const memoryRoot = path9.resolve(config.memoryDir);
|
|
3914
|
+
const namespacesRoot = path9.join(memoryRoot, "namespaces");
|
|
4450
3915
|
const fallbackStorageDir = storageDirOrNull(fallbackStorage);
|
|
4451
3916
|
const matches = [];
|
|
4452
3917
|
const seenDirs = /* @__PURE__ */ new Set();
|
|
4453
3918
|
const maybeAddStorage = (storage, namespace) => {
|
|
4454
3919
|
const storageDir = storageDirOrNull(storage);
|
|
4455
3920
|
if (!storageDir) return;
|
|
4456
|
-
const candidateRoot =
|
|
3921
|
+
const candidateRoot = path9.resolve(storageDir);
|
|
4457
3922
|
if (seenDirs.has(candidateRoot)) return;
|
|
4458
3923
|
if (!isPathInsideStorageRoot(candidateRoot, resolvedPath)) return;
|
|
4459
3924
|
if (candidateRoot === memoryRoot && isPathInsideStorageRoot(namespacesRoot, resolvedPath)) {
|
|
@@ -4471,7 +3936,7 @@ var QmdResultResolver = class {
|
|
|
4471
3936
|
candidateNamespaces.add(ns);
|
|
4472
3937
|
}
|
|
4473
3938
|
if (isPathInsideStorageRoot(namespacesRoot, resolvedPath)) {
|
|
4474
|
-
const relativeToNamespaces =
|
|
3939
|
+
const relativeToNamespaces = path9.relative(namespacesRoot, resolvedPath);
|
|
4475
3940
|
const [namespaceSegment] = relativeToNamespaces.split(/[\\/]/);
|
|
4476
3941
|
if (namespaceSegment) {
|
|
4477
3942
|
candidateNamespaces.add(namespaceIdentityFromToken(namespaceSegment) ?? namespaceSegment);
|
|
@@ -5012,7 +4477,7 @@ function collectSpeakers(tagged) {
|
|
|
5012
4477
|
}
|
|
5013
4478
|
|
|
5014
4479
|
// src/wearables/fusion/fuse.ts
|
|
5015
|
-
import { createHash as
|
|
4480
|
+
import { createHash as createHash2 } from "crypto";
|
|
5016
4481
|
var FUSION_ID_PREFIX = "fusion";
|
|
5017
4482
|
var FUSION_ALGO_VERSION = "2026-07-12-1";
|
|
5018
4483
|
function canonicalInputsKey(date, inputs) {
|
|
@@ -5041,7 +4506,7 @@ function canonicalInputsKey(date, inputs) {
|
|
|
5041
4506
|
])
|
|
5042
4507
|
])
|
|
5043
4508
|
];
|
|
5044
|
-
return
|
|
4509
|
+
return createHash2("sha256").update(JSON.stringify(record), "utf-8").digest("hex");
|
|
5045
4510
|
}
|
|
5046
4511
|
function configFingerprint(options, sources) {
|
|
5047
4512
|
const proximityGapMs = options.proximityGapMs ?? DEFAULT_PROXIMITY_GAP_MS;
|
|
@@ -5056,7 +4521,7 @@ function canonicalDayKey(date, inputs, options, algoVersion = FUSION_ALGO_VERSIO
|
|
|
5056
4521
|
options,
|
|
5057
4522
|
inputs.map((conv) => conv.source)
|
|
5058
4523
|
);
|
|
5059
|
-
return
|
|
4524
|
+
return createHash2("sha256").update(inputsKey).update("\n").update(fingerprint).update("\n").update(algoVersion).digest("hex");
|
|
5060
4525
|
}
|
|
5061
4526
|
function buildProvenance(cluster, proximityGapMs, windowToleranceMs) {
|
|
5062
4527
|
return {
|
|
@@ -5116,11 +4581,11 @@ function fuseDay(date, inputs, options = {}) {
|
|
|
5116
4581
|
}
|
|
5117
4582
|
|
|
5118
4583
|
// src/wearables/fusion/store.ts
|
|
5119
|
-
import { createHash as
|
|
5120
|
-
import * as
|
|
4584
|
+
import { createHash as createHash3 } from "crypto";
|
|
4585
|
+
import * as path10 from "path";
|
|
5121
4586
|
var FUSION_KIND = "wearable-fusion";
|
|
5122
4587
|
function hashFusionBody(conversations) {
|
|
5123
|
-
return
|
|
4588
|
+
return createHash3("sha256").update(JSON.stringify(conversations), "utf-8").digest("hex");
|
|
5124
4589
|
}
|
|
5125
4590
|
function composeFusionDayMeta(date, conversations, sources, contentHash, fusedAt) {
|
|
5126
4591
|
return {
|
|
@@ -5304,7 +4769,7 @@ var FusionArtifactStore = class {
|
|
|
5304
4769
|
`invalid wearable fusion date '${String(date)}' \u2014 expected YYYY-MM-DD`
|
|
5305
4770
|
);
|
|
5306
4771
|
}
|
|
5307
|
-
return
|
|
4772
|
+
return path10.join(this.wearablesDir, FUSION_DIR_NAME, `${date}.md`);
|
|
5308
4773
|
}
|
|
5309
4774
|
async writeFusedDay(date, serialized) {
|
|
5310
4775
|
const filePath = this.fusedDayPath(date);
|
|
@@ -5341,7 +4806,7 @@ var FusionArtifactStore = class {
|
|
|
5341
4806
|
}
|
|
5342
4807
|
/** List dates with stored fused artifacts, newest first. */
|
|
5343
4808
|
async listFusedDays() {
|
|
5344
|
-
const fusionDir =
|
|
4809
|
+
const fusionDir = path10.join(this.wearablesDir, FUSION_DIR_NAME);
|
|
5345
4810
|
await this.assertPathContained(fusionDir);
|
|
5346
4811
|
let entries;
|
|
5347
4812
|
try {
|
|
@@ -5385,10 +4850,10 @@ var FusionArtifactStore = class {
|
|
|
5385
4850
|
try {
|
|
5386
4851
|
rootReal = await this.io.realpath(this.memoryDir);
|
|
5387
4852
|
} catch {
|
|
5388
|
-
rootReal =
|
|
4853
|
+
rootReal = path10.resolve(this.memoryDir);
|
|
5389
4854
|
}
|
|
5390
4855
|
await this.assertNoEscape(rootReal, this.wearablesDir);
|
|
5391
|
-
await this.assertNoEscape(rootReal,
|
|
4856
|
+
await this.assertNoEscape(rootReal, path10.dirname(targetPath));
|
|
5392
4857
|
await this.assertNoEscape(rootReal, targetPath);
|
|
5393
4858
|
await this.assertFusionDirNotSymlinked();
|
|
5394
4859
|
}
|
|
@@ -5402,7 +4867,7 @@ var FusionArtifactStore = class {
|
|
|
5402
4867
|
* (a fresh write creates a real directory lexically under the root).
|
|
5403
4868
|
*/
|
|
5404
4869
|
async assertFusionDirNotSymlinked() {
|
|
5405
|
-
const fusionDir =
|
|
4870
|
+
const fusionDir = path10.join(this.wearablesDir, FUSION_DIR_NAME);
|
|
5406
4871
|
let stat4;
|
|
5407
4872
|
try {
|
|
5408
4873
|
stat4 = await this.io.lstat(fusionDir);
|
|
@@ -5537,8 +5002,8 @@ function reconstructFusionInputs(date, dayTranscripts) {
|
|
|
5537
5002
|
}
|
|
5538
5003
|
|
|
5539
5004
|
// src/meetings/store.ts
|
|
5540
|
-
import { createHash as
|
|
5541
|
-
import * as
|
|
5005
|
+
import { createHash as createHash4 } from "crypto";
|
|
5006
|
+
import * as path11 from "path";
|
|
5542
5007
|
var MEETINGS_DIR_NAME = "meetings";
|
|
5543
5008
|
var MEETING_KIND = "meeting";
|
|
5544
5009
|
var MEETING_FORMAT_VERSION = 1;
|
|
@@ -5568,7 +5033,7 @@ function canonicalContent(base, fused) {
|
|
|
5568
5033
|
});
|
|
5569
5034
|
}
|
|
5570
5035
|
function composeMeetingRecord(base, fused) {
|
|
5571
|
-
const contentHash =
|
|
5036
|
+
const contentHash = createHash4("sha256").update(canonicalContent(base, fused), "utf-8").digest("hex");
|
|
5572
5037
|
return {
|
|
5573
5038
|
...base,
|
|
5574
5039
|
...fused,
|
|
@@ -5707,7 +5172,7 @@ var MeetingRecordStore = class {
|
|
|
5707
5172
|
constructor(memoryDir, io) {
|
|
5708
5173
|
this.memoryDir = memoryDir;
|
|
5709
5174
|
this.io = io;
|
|
5710
|
-
this.meetingsDir =
|
|
5175
|
+
this.meetingsDir = path11.join(memoryDir, MEETINGS_DIR_NAME);
|
|
5711
5176
|
}
|
|
5712
5177
|
memoryDir;
|
|
5713
5178
|
io;
|
|
@@ -5723,7 +5188,7 @@ var MeetingRecordStore = class {
|
|
|
5723
5188
|
if (idDate !== date) {
|
|
5724
5189
|
throw new Error(`meeting id '${id}' does not belong to date '${date}'`);
|
|
5725
5190
|
}
|
|
5726
|
-
return
|
|
5191
|
+
return path11.join(this.meetingsDir, date, `${id}.md`);
|
|
5727
5192
|
}
|
|
5728
5193
|
/** Idempotently persist a record: skip the write when contentHash is unchanged. */
|
|
5729
5194
|
async saveMeetingRecord(record) {
|
|
@@ -5754,7 +5219,7 @@ var MeetingRecordStore = class {
|
|
|
5754
5219
|
if (!isValidTranscriptDate(date)) {
|
|
5755
5220
|
throw new Error(`invalid meeting date '${String(date)}' \u2014 expected YYYY-MM-DD`);
|
|
5756
5221
|
}
|
|
5757
|
-
const dayDir =
|
|
5222
|
+
const dayDir = path11.join(this.meetingsDir, date);
|
|
5758
5223
|
await this.assertPathContained(dayDir);
|
|
5759
5224
|
let entries;
|
|
5760
5225
|
try {
|
|
@@ -5822,10 +5287,10 @@ var MeetingRecordStore = class {
|
|
|
5822
5287
|
try {
|
|
5823
5288
|
rootReal = await this.io.realpath(this.memoryDir);
|
|
5824
5289
|
} catch {
|
|
5825
|
-
rootReal =
|
|
5290
|
+
rootReal = path11.resolve(this.memoryDir);
|
|
5826
5291
|
}
|
|
5827
5292
|
await this.assertNoEscape(rootReal, this.meetingsDir);
|
|
5828
|
-
await this.assertNoEscape(rootReal,
|
|
5293
|
+
await this.assertNoEscape(rootReal, path11.dirname(targetPath));
|
|
5829
5294
|
await this.assertNoEscape(rootReal, targetPath);
|
|
5830
5295
|
await this.assertNotSymlink(this.meetingsDir, "directory");
|
|
5831
5296
|
const dayDir = this.dayDirWithin(targetPath);
|
|
@@ -5835,11 +5300,11 @@ var MeetingRecordStore = class {
|
|
|
5835
5300
|
/** The `<meetings>/<date>` directory on the path to `targetPath`, or null when
|
|
5836
5301
|
* `targetPath` is the meetings root itself. */
|
|
5837
5302
|
dayDirWithin(targetPath) {
|
|
5838
|
-
const rel =
|
|
5839
|
-
if (rel === "" || rel.startsWith("..") ||
|
|
5840
|
-
const first = rel.split(
|
|
5303
|
+
const rel = path11.relative(this.meetingsDir, targetPath);
|
|
5304
|
+
if (rel === "" || rel.startsWith("..") || path11.isAbsolute(rel)) return null;
|
|
5305
|
+
const first = rel.split(path11.sep)[0];
|
|
5841
5306
|
if (first === void 0 || first.length === 0) return null;
|
|
5842
|
-
return
|
|
5307
|
+
return path11.join(this.meetingsDir, first);
|
|
5843
5308
|
}
|
|
5844
5309
|
async assertNotSymlink(target, kind) {
|
|
5845
5310
|
let stat4;
|
|
@@ -5850,7 +5315,7 @@ var MeetingRecordStore = class {
|
|
|
5850
5315
|
throw err;
|
|
5851
5316
|
}
|
|
5852
5317
|
if (stat4.isSymbolicLink) {
|
|
5853
|
-
const label =
|
|
5318
|
+
const label = path11.relative(this.memoryDir, target) || MEETINGS_DIR_NAME;
|
|
5854
5319
|
throw new Error(
|
|
5855
5320
|
`meetings ${kind} '${label}' is a symbolic link \u2014 refusing to follow it even when it resolves inside the memory dir (AGENTS.md pattern #3)`
|
|
5856
5321
|
);
|
|
@@ -5874,7 +5339,7 @@ var SORT_STR = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
|
5874
5339
|
|
|
5875
5340
|
// src/storage/supersession-audit.ts
|
|
5876
5341
|
import { readdir as readdir5 } from "fs/promises";
|
|
5877
|
-
import
|
|
5342
|
+
import path12 from "path";
|
|
5878
5343
|
async function hasSupersessionAudit(lookup, oldMemoryId, newMemoryId, auditBody, structuredAttributes) {
|
|
5879
5344
|
let entries;
|
|
5880
5345
|
try {
|
|
@@ -5885,7 +5350,7 @@ async function hasSupersessionAudit(lookup, oldMemoryId, newMemoryId, auditBody,
|
|
|
5885
5350
|
}
|
|
5886
5351
|
for (const entry of entries) {
|
|
5887
5352
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
5888
|
-
const memory = await lookup.readMemoryByPath(
|
|
5353
|
+
const memory = await lookup.readMemoryByPath(path12.join(lookup.correctionsDir, entry.name));
|
|
5889
5354
|
if (memory?.frontmatter.category === "correction" && (memory.frontmatter.sourceMemoryId === oldMemoryId || memory.frontmatter.sourceMemoryId === void 0 && memory.frontmatter.lineage?.includes(oldMemoryId)) && memory.frontmatter.lineage?.includes(newMemoryId) && stripAttributesSuffix(memory.content) === auditBody && (structuredAttributes === void 0 || Object.entries(structuredAttributes).every(
|
|
5890
5355
|
([key, value]) => memory.frontmatter.structuredAttributes?.[key] === value
|
|
5891
5356
|
))) {
|
|
@@ -5975,7 +5440,7 @@ Reason: ${reason}`;
|
|
|
5975
5440
|
}
|
|
5976
5441
|
|
|
5977
5442
|
// src/storage/entity-timeline.ts
|
|
5978
|
-
import { createHash as
|
|
5443
|
+
import { createHash as createHash5 } from "crypto";
|
|
5979
5444
|
var ENTITY_TIMELINE_METADATA_MARKER = "remnic-meta-v1";
|
|
5980
5445
|
function parseEntityTimelineBullet(bullet, fallbackTimestamp) {
|
|
5981
5446
|
const trimmed = bullet.trim();
|
|
@@ -6417,7 +5882,7 @@ function fingerprintEntityStructuredFacts(entity) {
|
|
|
6417
5882
|
(left, right) => left.key.localeCompare(right.key) || left.title.localeCompare(right.title) || left.facts.join("\n").localeCompare(right.facts.join("\n"))
|
|
6418
5883
|
);
|
|
6419
5884
|
if (normalizedSections.length === 0) return void 0;
|
|
6420
|
-
return
|
|
5885
|
+
return createHash5("sha256").update(JSON.stringify(normalizedSections)).digest("hex");
|
|
6421
5886
|
}
|
|
6422
5887
|
function isEntitySynthesisStale(entity) {
|
|
6423
5888
|
const structuredFactCount = countEntityStructuredFacts(entity);
|
|
@@ -6974,17 +6439,17 @@ function inferEntityTypeFromContent(content) {
|
|
|
6974
6439
|
}
|
|
6975
6440
|
var KNOWN_ENTITY_FILENAME_PREFIXES = /* @__PURE__ */ new Set(["company", "other", "person", "place", "project", "tool", "topic"]);
|
|
6976
6441
|
function inferEntityTypeFromFilename(pathRel) {
|
|
6977
|
-
const basename =
|
|
6442
|
+
const basename = path13.basename(pathRel, ".md").toLowerCase();
|
|
6978
6443
|
const separator = basename.indexOf("-");
|
|
6979
6444
|
if (separator <= 0) return void 0;
|
|
6980
6445
|
const candidate = basename.slice(0, separator);
|
|
6981
6446
|
return KNOWN_ENTITY_FILENAME_PREFIXES.has(candidate) ? candidate : void 0;
|
|
6982
6447
|
}
|
|
6983
6448
|
function normalizeFrontmatterForPath(frontmatter, pathRel, content = "") {
|
|
6984
|
-
const normalizedPath = pathRel.split(
|
|
6449
|
+
const normalizedPath = pathRel.split(path13.sep).join("/");
|
|
6985
6450
|
let normalizedFrontmatter = frontmatter;
|
|
6986
6451
|
if (normalizedPath === "entities" || normalizedPath.startsWith("entities/") || normalizedPath.includes("/entities/")) {
|
|
6987
|
-
const basename =
|
|
6452
|
+
const basename = path13.basename(pathRel, ".md");
|
|
6988
6453
|
const inferredType = inferEntityTypeFromContent(content) || inferEntityTypeFromFilename(pathRel) || "entity";
|
|
6989
6454
|
const existingTags = Array.isArray(frontmatter.tags) ? frontmatter.tags : [];
|
|
6990
6455
|
normalizedFrontmatter = {
|
|
@@ -7457,9 +6922,9 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
7457
6922
|
this.entitySchemas = entitySchemas;
|
|
7458
6923
|
this.deletionRevisionStore = new DeletionRevisionStore({
|
|
7459
6924
|
baseDir,
|
|
7460
|
-
deletionRevisionMetadataPath:
|
|
7461
|
-
invalidationCommitMetadataPath:
|
|
7462
|
-
deletionRevisionLockPath:
|
|
6925
|
+
deletionRevisionMetadataPath: path13.join(baseDir, ".offline-sync", "deletion-revisions.v1.json"),
|
|
6926
|
+
invalidationCommitMetadataPath: path13.join(baseDir, ".offline-sync", "invalidation-commits.v1.json"),
|
|
6927
|
+
deletionRevisionLockPath: path13.join(baseDir, ".offline-sync", "deletion-revisions.v1.json.lock"),
|
|
7463
6928
|
assertManagedStoragePath: (filePath, method) => this.assertManagedStoragePath(filePath, method)
|
|
7464
6929
|
});
|
|
7465
6930
|
this.casRevisions = new CasRevisionHost(baseDir);
|
|
@@ -7645,8 +7110,8 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
7645
7110
|
}
|
|
7646
7111
|
/** Whether `filePath` resolves to `stateDir` itself or a path contained in it. */
|
|
7647
7112
|
isStateFilePath(filePath) {
|
|
7648
|
-
const rel =
|
|
7649
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
7113
|
+
const rel = path13.relative(this.stateDir, path13.resolve(filePath));
|
|
7114
|
+
return rel === "" || !rel.startsWith("..") && !path13.isAbsolute(rel);
|
|
7650
7115
|
}
|
|
7651
7116
|
// ── Tombstone store (issue #1579) ───────────────────────────────────────
|
|
7652
7117
|
// Instance-scoped (rule 11) + namespace-scoped (rule 42) tombstone index.
|
|
@@ -7761,8 +7226,8 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
7761
7226
|
* /srv/cold/remnic), skipping the patch and stranding a stale hot entry.
|
|
7762
7227
|
*/
|
|
7763
7228
|
isColdOrArchiveTierPath(p) {
|
|
7764
|
-
const rel =
|
|
7765
|
-
return rel === "cold" || rel === "archive" || rel.startsWith(`cold${
|
|
7229
|
+
const rel = path13.relative(this.baseDir, p);
|
|
7230
|
+
return rel === "cold" || rel === "archive" || rel.startsWith(`cold${path13.sep}`) || rel.startsWith(`archive${path13.sep}`);
|
|
7766
7231
|
}
|
|
7767
7232
|
/**
|
|
7768
7233
|
* Mark the secure-store as required for this storage instance.
|
|
@@ -7849,7 +7314,7 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
7849
7314
|
void 0,
|
|
7850
7315
|
this.baseDir
|
|
7851
7316
|
);
|
|
7852
|
-
const rel =
|
|
7317
|
+
const rel = path13.relative(this.baseDir, filePath).split(path13.sep).join("/");
|
|
7853
7318
|
return `${rel}:${version.versionId}`;
|
|
7854
7319
|
} catch (err) {
|
|
7855
7320
|
log.warn(
|
|
@@ -7894,10 +7359,10 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
7894
7359
|
_StorageManager.okfConformanceByDir.set(memoryDir, enabled !== false);
|
|
7895
7360
|
}
|
|
7896
7361
|
okfConformanceEnabled() {
|
|
7897
|
-
for (let dir = this.baseDir; ; dir =
|
|
7362
|
+
for (let dir = this.baseDir; ; dir = path13.dirname(dir)) {
|
|
7898
7363
|
const registered = _StorageManager.okfConformanceByDir.get(dir);
|
|
7899
7364
|
if (registered !== void 0) return registered;
|
|
7900
|
-
if (dir ===
|
|
7365
|
+
if (dir === path13.dirname(dir)) return true;
|
|
7901
7366
|
}
|
|
7902
7367
|
}
|
|
7903
7368
|
/**
|
|
@@ -7944,12 +7409,12 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
7944
7409
|
}
|
|
7945
7410
|
identityFilePath(workspaceDir, namespace) {
|
|
7946
7411
|
const rawNamespace = typeof namespace === "string" ? namespace.trim() : "";
|
|
7947
|
-
if (!rawNamespace) return
|
|
7412
|
+
if (!rawNamespace) return path13.join(workspaceDir, "IDENTITY.md");
|
|
7948
7413
|
const safeNamespace = rawNamespace.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
7949
|
-
return
|
|
7414
|
+
return path13.join(workspaceDir, `IDENTITY.${safeNamespace}.md`);
|
|
7950
7415
|
}
|
|
7951
7416
|
versionFilePath(kind) {
|
|
7952
|
-
return
|
|
7417
|
+
return path13.join(this.stateDir, `.${kind}-version.log`);
|
|
7953
7418
|
}
|
|
7954
7419
|
bumpSharedVersion(kind, fallbackMap) {
|
|
7955
7420
|
const filePath = this.versionFilePath(kind);
|
|
@@ -8053,7 +7518,7 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8053
7518
|
// memories.
|
|
8054
7519
|
// -------------------------------------------------------------------------
|
|
8055
7520
|
get wearablesDir() {
|
|
8056
|
-
return
|
|
7521
|
+
return path13.join(this.baseDir, WEARABLES_DIR_NAME);
|
|
8057
7522
|
}
|
|
8058
7523
|
/**
|
|
8059
7524
|
* Resolve the on-disk path for a source/day transcript. Throws on
|
|
@@ -8069,7 +7534,7 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8069
7534
|
if (!isValidTranscriptDate(date)) {
|
|
8070
7535
|
throw new Error(`invalid wearable transcript date '${String(date)}' \u2014 expected YYYY-MM-DD`);
|
|
8071
7536
|
}
|
|
8072
|
-
return
|
|
7537
|
+
return path13.join(this.wearablesDir, sourceId, `${date}.md`);
|
|
8073
7538
|
}
|
|
8074
7539
|
async writeWearableDayTranscript(sourceId, date, serialized) {
|
|
8075
7540
|
const targetPath = this.wearableTranscriptPath(sourceId, date);
|
|
@@ -8096,7 +7561,7 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8096
7561
|
writeFile: (p, c) => this.writeStorageSecureFile(p, c),
|
|
8097
7562
|
readFile: (p) => readMaybeEncryptedFile(p, this._secureStoreKey, this.baseDir),
|
|
8098
7563
|
readDir: (d) => readdir6(d),
|
|
8099
|
-
deleteFile: (p) =>
|
|
7564
|
+
deleteFile: (p) => unlink4(p),
|
|
8100
7565
|
realpath: (p) => realpath3(p),
|
|
8101
7566
|
lstat: (p) => lstat3(p).then((st) => ({ isSymbolicLink: st.isSymbolicLink() }))
|
|
8102
7567
|
});
|
|
@@ -8114,7 +7579,7 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8114
7579
|
writeFile: (p, c) => this.writeStorageSecureFile(p, c),
|
|
8115
7580
|
readFile: (p) => readMaybeEncryptedFile(p, this._secureStoreKey, this.baseDir),
|
|
8116
7581
|
readDir: (d) => readdir6(d),
|
|
8117
|
-
deleteFile: (p) =>
|
|
7582
|
+
deleteFile: (p) => unlink4(p),
|
|
8118
7583
|
realpath: (p) => realpath3(p),
|
|
8119
7584
|
lstat: (p) => lstat3(p).then((st) => ({ isSymbolicLink: st.isSymbolicLink() }))
|
|
8120
7585
|
});
|
|
@@ -8185,25 +7650,25 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8185
7650
|
});
|
|
8186
7651
|
}
|
|
8187
7652
|
get factsDir() {
|
|
8188
|
-
return
|
|
7653
|
+
return path13.join(this.baseDir, "facts");
|
|
8189
7654
|
}
|
|
8190
7655
|
get correctionsDir() {
|
|
8191
|
-
return
|
|
7656
|
+
return path13.join(this.baseDir, "corrections");
|
|
8192
7657
|
}
|
|
8193
7658
|
get proceduresDir() {
|
|
8194
|
-
return
|
|
7659
|
+
return path13.join(this.baseDir, "procedures");
|
|
8195
7660
|
}
|
|
8196
7661
|
get reasoningTracesDir() {
|
|
8197
|
-
return
|
|
7662
|
+
return path13.join(this.baseDir, "reasoning-traces");
|
|
8198
7663
|
}
|
|
8199
7664
|
get entitiesDir() {
|
|
8200
|
-
return
|
|
7665
|
+
return path13.join(this.baseDir, "entities");
|
|
8201
7666
|
}
|
|
8202
7667
|
resolveEntityFilePath(name) {
|
|
8203
7668
|
if (typeof name !== "string") return null;
|
|
8204
|
-
const filePath =
|
|
8205
|
-
const relative2 =
|
|
8206
|
-
if (relative2 === "" || relative2.startsWith("..") ||
|
|
7669
|
+
const filePath = path13.resolve(this.entitiesDir, `${name}.md`);
|
|
7670
|
+
const relative2 = path13.relative(this.entitiesDir, filePath);
|
|
7671
|
+
if (relative2 === "" || relative2.startsWith("..") || path13.isAbsolute(relative2)) {
|
|
8207
7672
|
return null;
|
|
8208
7673
|
}
|
|
8209
7674
|
return filePath;
|
|
@@ -8214,7 +7679,7 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8214
7679
|
async readDependencyPropagationQueueFile(filePath) {
|
|
8215
7680
|
const target = await resolveSafeStoragePath(
|
|
8216
7681
|
this.baseDir,
|
|
8217
|
-
|
|
7682
|
+
path13.relative(
|
|
8218
7683
|
this.baseDir,
|
|
8219
7684
|
this.assertManagedStoragePath(filePath, "storage.readDependencyPropagationQueueFile")
|
|
8220
7685
|
)
|
|
@@ -8224,7 +7689,7 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8224
7689
|
async writeDependencyPropagationQueueFile(filePath, content) {
|
|
8225
7690
|
const target = await resolveSafeStoragePath(
|
|
8226
7691
|
this.baseDir,
|
|
8227
|
-
|
|
7692
|
+
path13.relative(
|
|
8228
7693
|
this.baseDir,
|
|
8229
7694
|
this.assertManagedStoragePath(filePath, "storage.writeDependencyPropagationQueueFile")
|
|
8230
7695
|
)
|
|
@@ -8239,10 +7704,10 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8239
7704
|
});
|
|
8240
7705
|
}
|
|
8241
7706
|
assertManagedStoragePath(filePath, method) {
|
|
8242
|
-
const resolved =
|
|
8243
|
-
const base =
|
|
8244
|
-
const rel =
|
|
8245
|
-
if (rel === "" || rel === ".." || rel.startsWith(`..${
|
|
7707
|
+
const resolved = path13.resolve(filePath);
|
|
7708
|
+
const base = path13.resolve(this.baseDir);
|
|
7709
|
+
const rel = path13.relative(base, resolved);
|
|
7710
|
+
if (rel === "" || rel === ".." || rel.startsWith(`..${path13.sep}`) || path13.isAbsolute(rel)) {
|
|
8246
7711
|
throw new Error(`${method}: file path escapes memory dir`);
|
|
8247
7712
|
}
|
|
8248
7713
|
return resolved;
|
|
@@ -8269,7 +7734,7 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8269
7734
|
async writeManagedStorageFile(filePath, write) {
|
|
8270
7735
|
return this.deletionRevisionStore.writeManagedStorageFile(filePath, write);
|
|
8271
7736
|
}
|
|
8272
|
-
async
|
|
7737
|
+
async deleteManagedStorageFileDurable(filePath, deletionMtimeMs) {
|
|
8273
7738
|
return this.deletionRevisionStore.deleteManagedStorageFile(filePath, deletionMtimeMs);
|
|
8274
7739
|
}
|
|
8275
7740
|
async readOfflineSyncFile(filePath) {
|
|
@@ -8279,7 +7744,7 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8279
7744
|
async digestOfflineSyncFile(filePath) {
|
|
8280
7745
|
const target = this.assertManagedStoragePath(filePath, "storage.digestOfflineSyncFile");
|
|
8281
7746
|
const st = await stat3(target);
|
|
8282
|
-
const relPath =
|
|
7747
|
+
const relPath = path13.relative(this.baseDir, target).split(path13.sep).join("/");
|
|
8283
7748
|
const cache = await this.loadOfflineSyncDigestCache();
|
|
8284
7749
|
const cached = cache.get(relPath);
|
|
8285
7750
|
if (cached && cached.statBytes === st.size && cached.mtimeMs === st.mtimeMs && cached.ctimeMs === st.ctimeMs && !cached.encrypted) {
|
|
@@ -8293,11 +7758,11 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8293
7758
|
if (encrypted) {
|
|
8294
7759
|
const content = await readMaybeEncryptedFileBuffer(target, this._secureStoreKey, this.baseDir);
|
|
8295
7760
|
digest = {
|
|
8296
|
-
sha256:
|
|
7761
|
+
sha256: createHash6("sha256").update(content).digest("hex"),
|
|
8297
7762
|
bytes: content.byteLength
|
|
8298
7763
|
};
|
|
8299
7764
|
} else {
|
|
8300
|
-
const hash =
|
|
7765
|
+
const hash = createHash6("sha256");
|
|
8301
7766
|
let bytes = 0;
|
|
8302
7767
|
for await (const rawChunk of createReadStream(target)) {
|
|
8303
7768
|
const chunk = Buffer.isBuffer(rawChunk) ? rawChunk : Buffer.from(rawChunk);
|
|
@@ -8325,7 +7790,7 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8325
7790
|
async readOfflineSyncDigestCache() {
|
|
8326
7791
|
const cache = /* @__PURE__ */ new Map();
|
|
8327
7792
|
try {
|
|
8328
|
-
const raw = await
|
|
7793
|
+
const raw = await readFile2(this.offlineSyncDigestCachePath, "utf-8");
|
|
8329
7794
|
const parsed = JSON.parse(raw);
|
|
8330
7795
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return cache;
|
|
8331
7796
|
const entries = parsed.entries;
|
|
@@ -8340,7 +7805,7 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8340
7805
|
const bytes = typeof record.bytes === "number" ? record.bytes : NaN;
|
|
8341
7806
|
const sha256 = typeof record.sha256 === "string" ? record.sha256 : "";
|
|
8342
7807
|
const encrypted = record.encrypted === true;
|
|
8343
|
-
if (cachePath.length === 0 || cachePath === ".." || cachePath.startsWith("../") ||
|
|
7808
|
+
if (cachePath.length === 0 || cachePath === ".." || cachePath.startsWith("../") || path13.isAbsolute(cachePath) || !Number.isFinite(statBytes) || !Number.isFinite(mtimeMs) || !Number.isFinite(ctimeMs) || !Number.isFinite(bytes) || !/^[a-f0-9]{64}$/i.test(sha256)) {
|
|
8344
7809
|
continue;
|
|
8345
7810
|
}
|
|
8346
7811
|
cache.set(cachePath, { statBytes, mtimeMs, ctimeMs, encrypted, sha256, bytes });
|
|
@@ -8382,7 +7847,7 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8382
7847
|
const cache = this.offlineSyncDigestCache;
|
|
8383
7848
|
if (!cache) return;
|
|
8384
7849
|
const entries = [...cache.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([entryPath, entry]) => ({ path: entryPath, ...entry }));
|
|
8385
|
-
await
|
|
7850
|
+
await mkdir(path13.dirname(this.offlineSyncDigestCachePath), { recursive: true });
|
|
8386
7851
|
await writeFile2(this.offlineSyncDigestCachePath, `${JSON.stringify({ version: 1, entries })}
|
|
8387
7852
|
`, "utf-8");
|
|
8388
7853
|
}).catch((err) => {
|
|
@@ -8392,7 +7857,7 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8392
7857
|
});
|
|
8393
7858
|
}
|
|
8394
7859
|
async offlineSyncFileIsEncrypted(filePath) {
|
|
8395
|
-
const handle = await
|
|
7860
|
+
const handle = await open(filePath, "r");
|
|
8396
7861
|
try {
|
|
8397
7862
|
const header = Buffer.alloc(MAGIC_HEADER_SIZE);
|
|
8398
7863
|
const { bytesRead } = await handle.read(header, 0, header.length, 0);
|
|
@@ -8451,7 +7916,7 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8451
7916
|
}
|
|
8452
7917
|
let handle;
|
|
8453
7918
|
try {
|
|
8454
|
-
handle = await
|
|
7919
|
+
handle = await open(filePath, "r");
|
|
8455
7920
|
} catch (err) {
|
|
8456
7921
|
if (isErrnoCode(err, "ENOENT")) return false;
|
|
8457
7922
|
throw err;
|
|
@@ -8471,7 +7936,7 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8471
7936
|
}
|
|
8472
7937
|
async appendStorageSecureFileUnlocked(filePath, content) {
|
|
8473
7938
|
const writeKey = this.resolveWriteKey();
|
|
8474
|
-
await
|
|
7939
|
+
await mkdir(path13.dirname(filePath), { recursive: true });
|
|
8475
7940
|
if (writeKey === null) {
|
|
8476
7941
|
try {
|
|
8477
7942
|
if (await this.isEncryptedFileHeader(filePath)) {
|
|
@@ -8497,13 +7962,13 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8497
7962
|
this.notifyCatalogWriteForPath(filePath);
|
|
8498
7963
|
}
|
|
8499
7964
|
get stateDir() {
|
|
8500
|
-
return
|
|
7965
|
+
return path13.join(this.baseDir, "state");
|
|
8501
7966
|
}
|
|
8502
7967
|
get offlineSyncDigestCachePath() {
|
|
8503
|
-
return
|
|
7968
|
+
return path13.join(this.baseDir, ".offline-sync", "digest-cache.v1.json");
|
|
8504
7969
|
}
|
|
8505
7970
|
get entitySynthesisQueuePath() {
|
|
8506
|
-
return
|
|
7971
|
+
return path13.join(this.stateDir, "entity-synthesis-queue.json");
|
|
8507
7972
|
}
|
|
8508
7973
|
// ── Tombstone store access (issue #1579) ─────────────────────────────────
|
|
8509
7974
|
/**
|
|
@@ -8513,7 +7978,7 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8513
7978
|
* helpers are injected into the store).
|
|
8514
7979
|
*/
|
|
8515
7980
|
get tombstonesPath() {
|
|
8516
|
-
return
|
|
7981
|
+
return path13.join(this.stateDir, "tombstones.jsonl");
|
|
8517
7982
|
}
|
|
8518
7983
|
/**
|
|
8519
7984
|
* Configure the tombstone invariant for this storage instance. Installed
|
|
@@ -8815,55 +8280,55 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8815
8280
|
return legacyMatches.length > 0 ? [...new Set(legacyMatches.map((content) => ContentHashIndex.computeHash(content)))] : [persistedHash];
|
|
8816
8281
|
}
|
|
8817
8282
|
get questionsDir() {
|
|
8818
|
-
return
|
|
8283
|
+
return path13.join(this.baseDir, "questions");
|
|
8819
8284
|
}
|
|
8820
8285
|
get artifactsDir() {
|
|
8821
|
-
return
|
|
8286
|
+
return path13.join(this.baseDir, "artifacts");
|
|
8822
8287
|
}
|
|
8823
8288
|
get identityDir() {
|
|
8824
|
-
return
|
|
8289
|
+
return path13.join(this.baseDir, "identity");
|
|
8825
8290
|
}
|
|
8826
8291
|
get identityAnchorPath() {
|
|
8827
|
-
return
|
|
8292
|
+
return path13.join(this.identityDir, "identity-anchor.md");
|
|
8828
8293
|
}
|
|
8829
8294
|
get identityIncidentsDir() {
|
|
8830
|
-
return
|
|
8295
|
+
return path13.join(this.identityDir, "incidents");
|
|
8831
8296
|
}
|
|
8832
8297
|
get identityAuditsWeeklyDir() {
|
|
8833
|
-
return
|
|
8298
|
+
return path13.join(this.identityDir, "audits", "weekly");
|
|
8834
8299
|
}
|
|
8835
8300
|
get identityAuditsMonthlyDir() {
|
|
8836
|
-
return
|
|
8301
|
+
return path13.join(this.identityDir, "audits", "monthly");
|
|
8837
8302
|
}
|
|
8838
8303
|
get identityImprovementLoopsPath() {
|
|
8839
|
-
return
|
|
8304
|
+
return path13.join(this.identityDir, "improvement-loops.md");
|
|
8840
8305
|
}
|
|
8841
8306
|
get identityReflectionsPath() {
|
|
8842
|
-
return
|
|
8307
|
+
return path13.join(this.identityDir, "reflections.md");
|
|
8843
8308
|
}
|
|
8844
8309
|
get profilePath() {
|
|
8845
|
-
return
|
|
8310
|
+
return path13.join(this.baseDir, "profile.md");
|
|
8846
8311
|
}
|
|
8847
8312
|
get memoryActionsPath() {
|
|
8848
|
-
return
|
|
8313
|
+
return path13.join(this.stateDir, "memory-actions.jsonl");
|
|
8849
8314
|
}
|
|
8850
8315
|
get memoryLifecycleLedgerPath() {
|
|
8851
|
-
return
|
|
8316
|
+
return path13.join(this.stateDir, "memory-lifecycle-ledger.jsonl");
|
|
8852
8317
|
}
|
|
8853
8318
|
get compressionGuidelinesPath() {
|
|
8854
|
-
return
|
|
8319
|
+
return path13.join(this.stateDir, "compression-guidelines.md");
|
|
8855
8320
|
}
|
|
8856
8321
|
get compressionGuidelineDraftPath() {
|
|
8857
|
-
return
|
|
8322
|
+
return path13.join(this.stateDir, "compression-guidelines.draft.md");
|
|
8858
8323
|
}
|
|
8859
8324
|
get compressionGuidelineStatePath() {
|
|
8860
|
-
return
|
|
8325
|
+
return path13.join(this.stateDir, "compression-guideline-state.json");
|
|
8861
8326
|
}
|
|
8862
8327
|
get compressionGuidelineDraftStatePath() {
|
|
8863
|
-
return
|
|
8328
|
+
return path13.join(this.stateDir, "compression-guideline-draft-state.json");
|
|
8864
8329
|
}
|
|
8865
8330
|
get behaviorSignalsPath() {
|
|
8866
|
-
return
|
|
8331
|
+
return path13.join(this.stateDir, "behavior-signals.jsonl");
|
|
8867
8332
|
}
|
|
8868
8333
|
/**
|
|
8869
8334
|
* In-memory dedup key set for behavior-signals appends (issue #1909),
|
|
@@ -8895,7 +8360,7 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8895
8360
|
* governance sweeps can treat it uniformly.
|
|
8896
8361
|
*/
|
|
8897
8362
|
get bufferSurpriseLedgerPath() {
|
|
8898
|
-
return
|
|
8363
|
+
return path13.join(this.stateDir, "buffer-surprise-ledger.jsonl");
|
|
8899
8364
|
}
|
|
8900
8365
|
/**
|
|
8901
8366
|
* Entity alias table loaded from THIS store's config/aliases.json.
|
|
@@ -8956,7 +8421,7 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8956
8421
|
this.loadAliasesSync();
|
|
8957
8422
|
}
|
|
8958
8423
|
loadAliasesSync() {
|
|
8959
|
-
const aliasPath =
|
|
8424
|
+
const aliasPath = path13.join(this.baseDir, "config", "aliases.json");
|
|
8960
8425
|
this.userAliases = {};
|
|
8961
8426
|
const raw = readEntityAliasConfigSync(this.baseDir);
|
|
8962
8427
|
if (raw === void 0) return;
|
|
@@ -8991,22 +8456,22 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
8991
8456
|
return completionFingerprint;
|
|
8992
8457
|
}
|
|
8993
8458
|
async ensureDirectories() {
|
|
8994
|
-
await
|
|
8459
|
+
await mkdir(this.baseDir, { recursive: true });
|
|
8995
8460
|
await validateRoots(this.baseDir, this.entitiesDir, this.stateDir, this.archiveDir);
|
|
8996
8461
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
8997
|
-
await
|
|
8998
|
-
await
|
|
8999
|
-
await
|
|
9000
|
-
await
|
|
9001
|
-
await
|
|
9002
|
-
await
|
|
9003
|
-
await
|
|
9004
|
-
await
|
|
9005
|
-
await
|
|
9006
|
-
await
|
|
9007
|
-
await
|
|
9008
|
-
await
|
|
9009
|
-
await
|
|
8462
|
+
await mkdir(path13.join(this.factsDir, today), { recursive: true });
|
|
8463
|
+
await mkdir(path13.join(this.proceduresDir, today), { recursive: true });
|
|
8464
|
+
await mkdir(path13.join(this.reasoningTracesDir, today), { recursive: true });
|
|
8465
|
+
await mkdir(this.correctionsDir, { recursive: true });
|
|
8466
|
+
await mkdir(this.entitiesDir, { recursive: true });
|
|
8467
|
+
await mkdir(this.stateDir, { recursive: true });
|
|
8468
|
+
await mkdir(this.questionsDir, { recursive: true });
|
|
8469
|
+
await mkdir(this.artifactsDir, { recursive: true });
|
|
8470
|
+
await mkdir(this.identityDir, { recursive: true });
|
|
8471
|
+
await mkdir(this.identityIncidentsDir, { recursive: true });
|
|
8472
|
+
await mkdir(this.identityAuditsWeeklyDir, { recursive: true });
|
|
8473
|
+
await mkdir(this.identityAuditsMonthlyDir, { recursive: true });
|
|
8474
|
+
await mkdir(path13.join(this.baseDir, "config"), { recursive: true });
|
|
9010
8475
|
await this.entityCanonicalIdMigration.ensure();
|
|
9011
8476
|
if (this.hotMemoriesCacheEnabled && this.getMemoryCorpusVersion() === 0) {
|
|
9012
8477
|
this.bumpMemoryCorpusVersion();
|
|
@@ -9027,12 +8492,12 @@ var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndex
|
|
|
9027
8492
|
*/
|
|
9028
8493
|
async resolveCategoryWritePath(category, id, today) {
|
|
9029
8494
|
if (category === "correction") {
|
|
9030
|
-
await
|
|
9031
|
-
return
|
|
8495
|
+
await mkdir(this.correctionsDir, { recursive: true });
|
|
8496
|
+
return path13.join(this.correctionsDir, `${id}.md`);
|
|
9032
8497
|
}
|
|
9033
|
-
const datedDir =
|
|
9034
|
-
await
|
|
9035
|
-
return
|
|
8498
|
+
const datedDir = path13.join(getCategoryDir(this.baseDir, category), today);
|
|
8499
|
+
await mkdir(datedDir, { recursive: true });
|
|
8500
|
+
return path13.join(datedDir, `${id}.md`);
|
|
9036
8501
|
}
|
|
9037
8502
|
async findExistingTombstoneBlockedMemory(content, category, sourceConnector) {
|
|
9038
8503
|
const identity = buildExplicitCaptureDedupKey(content, category, sourceConnector);
|
|
@@ -9203,7 +8668,7 @@ ${sanitized.text}
|
|
|
9203
8668
|
}
|
|
9204
8669
|
});
|
|
9205
8670
|
} catch (err) {
|
|
9206
|
-
await
|
|
8671
|
+
await unlink4(filePath).catch(() => void 0);
|
|
9207
8672
|
this.invalidateAllMemoriesCache();
|
|
9208
8673
|
await this.rebuildTombstoneBlockedCaptureAfterInvalidationForPath(filePath);
|
|
9209
8674
|
throw err;
|
|
@@ -9419,8 +8884,8 @@ ${sanitized.text}
|
|
|
9419
8884
|
await this.ensureDirectories();
|
|
9420
8885
|
const now = /* @__PURE__ */ new Date();
|
|
9421
8886
|
const day = now.toISOString().slice(0, 10);
|
|
9422
|
-
const dir =
|
|
9423
|
-
await
|
|
8887
|
+
const dir = path13.join(this.artifactsDir, day);
|
|
8888
|
+
await mkdir(dir, { recursive: true });
|
|
9424
8889
|
const id = `artifact-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
9425
8890
|
const fm = {
|
|
9426
8891
|
id,
|
|
@@ -9446,7 +8911,7 @@ ${sanitized.text}
|
|
|
9446
8911
|
log.warn(`artifact content rejected for ${id}; violations=${sanitized.violations.join(", ")}`);
|
|
9447
8912
|
return "";
|
|
9448
8913
|
}
|
|
9449
|
-
const filePath =
|
|
8914
|
+
const filePath = path13.join(dir, `${id}.md`);
|
|
9450
8915
|
await this.writeStorageSecureFile(filePath, `${serializeFrontmatter(this.withOkfType(fm))}
|
|
9451
8916
|
|
|
9452
8917
|
${sanitized.text}
|
|
@@ -9739,7 +9204,7 @@ ${sanitized.text}
|
|
|
9739
9204
|
* per-process in-memory cache safe across process boundaries.
|
|
9740
9205
|
*/
|
|
9741
9206
|
invalidateColdMemoriesCache() {
|
|
9742
|
-
const coldRoot =
|
|
9207
|
+
const coldRoot = path13.join(this.baseDir, "cold");
|
|
9743
9208
|
_StorageManager.coldMemoriesCache.delete(coldRoot);
|
|
9744
9209
|
this.bumpColdWriteVersion();
|
|
9745
9210
|
invalidateAllForDir(this.baseDir);
|
|
@@ -9859,7 +9324,7 @@ ${sanitized.text}
|
|
|
9859
9324
|
const correctionPaths = [];
|
|
9860
9325
|
const factPaths = [];
|
|
9861
9326
|
for (const filePath of filePaths) {
|
|
9862
|
-
if (filePath === this.correctionsDir || filePath.startsWith(`${this.correctionsDir}${
|
|
9327
|
+
if (filePath === this.correctionsDir || filePath.startsWith(`${this.correctionsDir}${path13.sep}`)) {
|
|
9863
9328
|
correctionPaths.push(filePath);
|
|
9864
9329
|
} else {
|
|
9865
9330
|
factPaths.push(filePath);
|
|
@@ -9922,7 +9387,7 @@ ${sanitized.text}
|
|
|
9922
9387
|
try {
|
|
9923
9388
|
const entries = await readdir6(dir, { withFileTypes: true });
|
|
9924
9389
|
for (const entry of entries) {
|
|
9925
|
-
const fullPath =
|
|
9390
|
+
const fullPath = path13.join(dir, entry.name);
|
|
9926
9391
|
if (entry.isDirectory()) {
|
|
9927
9392
|
await readDir(fullPath);
|
|
9928
9393
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -9965,7 +9430,7 @@ ${sanitized.text}
|
|
|
9965
9430
|
return this.rememberMemorySnapshot(await this.memoryReadStore.readMemoryByPath(filePath));
|
|
9966
9431
|
}
|
|
9967
9432
|
resolveTierRootDir(tier) {
|
|
9968
|
-
return tier === "cold" ?
|
|
9433
|
+
return tier === "cold" ? path13.join(this.baseDir, "cold") : this.baseDir;
|
|
9969
9434
|
}
|
|
9970
9435
|
resolveMemoryDateDir(memory) {
|
|
9971
9436
|
const preferred = memory.frontmatter.created || memory.frontmatter.updated;
|
|
@@ -9980,13 +9445,13 @@ ${sanitized.text}
|
|
|
9980
9445
|
buildTierMemoryPath(memory, tier) {
|
|
9981
9446
|
const root = this.resolveTierRootDir(tier);
|
|
9982
9447
|
if (this.isArtifactMemory(memory)) {
|
|
9983
|
-
return
|
|
9448
|
+
return path13.join(root, "artifacts", this.resolveMemoryDateDir(memory), `${memory.frontmatter.id}.md`);
|
|
9984
9449
|
}
|
|
9985
9450
|
if (memory.frontmatter.category === "correction") {
|
|
9986
|
-
return
|
|
9451
|
+
return path13.join(root, "corrections", `${memory.frontmatter.id}.md`);
|
|
9987
9452
|
}
|
|
9988
9453
|
const dir = categoryDirName(memory.frontmatter.category);
|
|
9989
|
-
return
|
|
9454
|
+
return path13.join(root, dir, this.resolveMemoryDateDir(memory), `${memory.frontmatter.id}.md`);
|
|
9990
9455
|
}
|
|
9991
9456
|
async writeMemoryFileAtomic(targetPath, memory) {
|
|
9992
9457
|
assertNotOkfReservedBasename(targetPath);
|
|
@@ -10003,7 +9468,7 @@ ${memory.content}
|
|
|
10003
9468
|
{},
|
|
10004
9469
|
this.baseDir
|
|
10005
9470
|
);
|
|
10006
|
-
const priorDest = await
|
|
9471
|
+
const priorDest = await readFile2(targetPath).catch(() => null);
|
|
10007
9472
|
await persist();
|
|
10008
9473
|
if (typeof rawRef === "string") {
|
|
10009
9474
|
const refBeforeRepair = frontmatter.entityRef;
|
|
@@ -10018,7 +9483,7 @@ ${memory.content}
|
|
|
10018
9483
|
});
|
|
10019
9484
|
} catch (err) {
|
|
10020
9485
|
if (priorDest === null) {
|
|
10021
|
-
await
|
|
9486
|
+
await unlink4(targetPath).catch(() => void 0);
|
|
10022
9487
|
} else {
|
|
10023
9488
|
await writeFile2(targetPath, priorDest).catch(() => void 0);
|
|
10024
9489
|
}
|
|
@@ -10065,8 +9530,8 @@ ${memory.content}
|
|
|
10065
9530
|
memory.frontmatter.entityRef = current.frontmatter.entityRef;
|
|
10066
9531
|
}
|
|
10067
9532
|
durable = true;
|
|
10068
|
-
const sourcePath =
|
|
10069
|
-
const destPath =
|
|
9533
|
+
const sourcePath = path13.resolve(current.path);
|
|
9534
|
+
const destPath = path13.resolve(targetPath);
|
|
10070
9535
|
if (sourcePath !== destPath) {
|
|
10071
9536
|
await recordArchiveDelete2(this.baseDir, current.path, () => this.deleteManagedStorageFile(current.path));
|
|
10072
9537
|
this.invalidateAllMemoriesCache();
|
|
@@ -10090,8 +9555,8 @@ ${memory.content}
|
|
|
10090
9555
|
}
|
|
10091
9556
|
async migrateMemoryToTier(memory, targetTier) {
|
|
10092
9557
|
const targetPath = this.buildTierMemoryPath(memory, targetTier);
|
|
10093
|
-
const sourcePath =
|
|
10094
|
-
const destPath =
|
|
9558
|
+
const sourcePath = path13.resolve(memory.path);
|
|
9559
|
+
const destPath = path13.resolve(targetPath);
|
|
10095
9560
|
if (sourcePath === destPath) {
|
|
10096
9561
|
return { changed: false, targetPath };
|
|
10097
9562
|
}
|
|
@@ -10101,7 +9566,7 @@ ${memory.content}
|
|
|
10101
9566
|
return { changed, targetPath };
|
|
10102
9567
|
}
|
|
10103
9568
|
get archiveDir() {
|
|
10104
|
-
return
|
|
9569
|
+
return path13.join(this.baseDir, "archive");
|
|
10105
9570
|
}
|
|
10106
9571
|
/**
|
|
10107
9572
|
* Archive a memory by moving it from facts/ to archive/YYYY-MM-DD/.
|
|
@@ -10114,8 +9579,8 @@ ${memory.content}
|
|
|
10114
9579
|
try {
|
|
10115
9580
|
const now = lifecycle?.at ?? /* @__PURE__ */ new Date();
|
|
10116
9581
|
const today = now.toISOString().slice(0, 10);
|
|
10117
|
-
const destDir =
|
|
10118
|
-
await
|
|
9582
|
+
const destDir = path13.join(this.archiveDir, today);
|
|
9583
|
+
await mkdir(destDir, { recursive: true });
|
|
10119
9584
|
const refIdsAtWrite = this.currentHistoricalIds();
|
|
10120
9585
|
const updatedFm = canonicalizeEntityRefOption(
|
|
10121
9586
|
{
|
|
@@ -10130,7 +9595,7 @@ ${memory.content}
|
|
|
10130
9595
|
|
|
10131
9596
|
${current.content}
|
|
10132
9597
|
`;
|
|
10133
|
-
const destPath =
|
|
9598
|
+
const destPath = path13.join(destDir, path13.basename(current.path));
|
|
10134
9599
|
const priorDest = await this.readStorageSecureFile(destPath).catch(() => null);
|
|
10135
9600
|
await recordArchiveWrite(this.baseDir, destPath, () => this.writeStorageSecureFile(destPath, fileContent));
|
|
10136
9601
|
if (typeof current.frontmatter.entityRef === "string") {
|
|
@@ -10147,7 +9612,7 @@ ${current.content}
|
|
|
10147
9612
|
await recordArchiveDelete2(
|
|
10148
9613
|
this.baseDir,
|
|
10149
9614
|
destPath,
|
|
10150
|
-
() =>
|
|
9615
|
+
() => unlink4(destPath).then(
|
|
10151
9616
|
() => true,
|
|
10152
9617
|
() => false
|
|
10153
9618
|
)
|
|
@@ -10361,7 +9826,7 @@ ${memory.content}
|
|
|
10361
9826
|
`;
|
|
10362
9827
|
await this.writeTombstoneBlockedFrontmatter(memory, fileContent, updated, async () => {
|
|
10363
9828
|
this.invalidateAllMemoriesCache();
|
|
10364
|
-
if (memory.path.includes(`${
|
|
9829
|
+
if (memory.path.includes(`${path13.sep}cold${path13.sep}`)) {
|
|
10365
9830
|
this.invalidateColdMemoriesCache();
|
|
10366
9831
|
}
|
|
10367
9832
|
});
|
|
@@ -10372,7 +9837,7 @@ ${memory.content}
|
|
|
10372
9837
|
});
|
|
10373
9838
|
}
|
|
10374
9839
|
await this.patchHotMemoriesCache({ addedPath: memory.path });
|
|
10375
|
-
if (memory.path.includes(`${
|
|
9840
|
+
if (memory.path.includes(`${path13.sep}cold${path13.sep}`)) {
|
|
10376
9841
|
this.invalidateColdMemoriesCache();
|
|
10377
9842
|
}
|
|
10378
9843
|
try {
|
|
@@ -10453,7 +9918,7 @@ ${memory.content}
|
|
|
10453
9918
|
return deleted;
|
|
10454
9919
|
}
|
|
10455
9920
|
async loadBuffer() {
|
|
10456
|
-
const bufferPath =
|
|
9921
|
+
const bufferPath = path13.join(this.stateDir, "buffer.json");
|
|
10457
9922
|
try {
|
|
10458
9923
|
const raw = await this.readStorageSecureFile(bufferPath);
|
|
10459
9924
|
return JSON.parse(raw);
|
|
@@ -10465,11 +9930,11 @@ ${memory.content}
|
|
|
10465
9930
|
}
|
|
10466
9931
|
async saveBuffer(state) {
|
|
10467
9932
|
await this.ensureDirectories();
|
|
10468
|
-
const bufferPath =
|
|
9933
|
+
const bufferPath = path13.join(this.stateDir, "buffer.json");
|
|
10469
9934
|
await this.writeStorageSecureFile(bufferPath, JSON.stringify(state, null, 2));
|
|
10470
9935
|
}
|
|
10471
9936
|
async loadMeta() {
|
|
10472
|
-
const metaPath =
|
|
9937
|
+
const metaPath = path13.join(this.stateDir, "meta.json");
|
|
10473
9938
|
try {
|
|
10474
9939
|
const raw = await this.readStorageSecureFile(metaPath);
|
|
10475
9940
|
const parsed = JSON.parse(raw);
|
|
@@ -10502,7 +9967,7 @@ ${memory.content}
|
|
|
10502
9967
|
}
|
|
10503
9968
|
async saveMeta(state) {
|
|
10504
9969
|
await this.ensureDirectories();
|
|
10505
|
-
const metaPath =
|
|
9970
|
+
const metaPath = path13.join(this.stateDir, "meta.json");
|
|
10506
9971
|
await this.writeStorageSecureFile(metaPath, JSON.stringify(state, null, 2));
|
|
10507
9972
|
}
|
|
10508
9973
|
async appendMemoryActionEvents(events) {
|
|
@@ -10572,7 +10037,7 @@ ${memory.content}
|
|
|
10572
10037
|
},
|
|
10573
10038
|
(payload) => this.appendStorageSecureFile(target, payload),
|
|
10574
10039
|
async () => {
|
|
10575
|
-
await
|
|
10040
|
+
await mkdir(path13.dirname(target), { recursive: true });
|
|
10576
10041
|
}
|
|
10577
10042
|
);
|
|
10578
10043
|
}
|
|
@@ -10670,7 +10135,7 @@ ${memory.content}
|
|
|
10670
10135
|
async appendReextractJobs(events) {
|
|
10671
10136
|
if (events.length === 0) return 0;
|
|
10672
10137
|
await this.ensureDirectories();
|
|
10673
|
-
const filePath =
|
|
10138
|
+
const filePath = path13.join(this.stateDir, "reextract-jobs.jsonl");
|
|
10674
10139
|
const lines = events.map((event) => JSON.stringify(event)).join("\n") + "\n";
|
|
10675
10140
|
try {
|
|
10676
10141
|
await this.appendStorageSecureFile(filePath, lines);
|
|
@@ -10683,7 +10148,7 @@ ${memory.content}
|
|
|
10683
10148
|
}
|
|
10684
10149
|
async readReextractJobs(limit = 200) {
|
|
10685
10150
|
const safeLimit = Number.isFinite(limit) ? Math.max(1, Math.min(1e3, Math.floor(limit))) : 200;
|
|
10686
|
-
const filePath =
|
|
10151
|
+
const filePath = path13.join(this.stateDir, "reextract-jobs.jsonl");
|
|
10687
10152
|
try {
|
|
10688
10153
|
const raw = await this.readStorageSecureFile(filePath);
|
|
10689
10154
|
const lines = raw.split("\n").filter((line) => line.trim().length > 0);
|
|
@@ -10832,7 +10297,7 @@ ${memory.content}
|
|
|
10832
10297
|
return false;
|
|
10833
10298
|
}
|
|
10834
10299
|
if (draftState.contentHash) {
|
|
10835
|
-
const contentHash =
|
|
10300
|
+
const contentHash = createHash6("sha256").update(draftContent).digest("hex");
|
|
10836
10301
|
if (contentHash !== draftState.contentHash) return false;
|
|
10837
10302
|
}
|
|
10838
10303
|
await this.writeCompressionGuidelines(draftContent);
|
|
@@ -10841,8 +10306,8 @@ ${memory.content}
|
|
|
10841
10306
|
activationState: "active"
|
|
10842
10307
|
});
|
|
10843
10308
|
await Promise.all([
|
|
10844
|
-
|
|
10845
|
-
|
|
10309
|
+
unlink4(this.compressionGuidelineDraftPath).catch(() => void 0),
|
|
10310
|
+
unlink4(this.compressionGuidelineDraftStatePath).catch(() => void 0)
|
|
10846
10311
|
]);
|
|
10847
10312
|
return true;
|
|
10848
10313
|
}
|
|
@@ -10894,7 +10359,7 @@ ${memory.content}
|
|
|
10894
10359
|
return `${prefix}-${ts}-${rand}`;
|
|
10895
10360
|
}
|
|
10896
10361
|
async writeQuestion(question, context, priority) {
|
|
10897
|
-
await
|
|
10362
|
+
await mkdir(this.questionsDir, { recursive: true });
|
|
10898
10363
|
const id = this.generateId("q");
|
|
10899
10364
|
const frontmatter = {
|
|
10900
10365
|
id,
|
|
@@ -10911,7 +10376,7 @@ ${question}
|
|
|
10911
10376
|
|
|
10912
10377
|
**Context:** ${context}
|
|
10913
10378
|
`;
|
|
10914
|
-
const filePath =
|
|
10379
|
+
const filePath = path13.join(this.questionsDir, `${id}.md`);
|
|
10915
10380
|
await this.writeStorageSecureFile(filePath, content);
|
|
10916
10381
|
log.debug(`wrote question ${id} to ${filePath}`);
|
|
10917
10382
|
this.invalidateQuestionsCache();
|
|
@@ -10948,7 +10413,7 @@ ${question}
|
|
|
10948
10413
|
async readIdentity(workspaceDir, namespace) {
|
|
10949
10414
|
const identityPath = this.identityFilePath(workspaceDir, namespace);
|
|
10950
10415
|
try {
|
|
10951
|
-
return await
|
|
10416
|
+
return await readFile2(identityPath, "utf-8");
|
|
10952
10417
|
} catch {
|
|
10953
10418
|
return "";
|
|
10954
10419
|
}
|
|
@@ -10966,15 +10431,15 @@ ${question}
|
|
|
10966
10431
|
const identityPath = this.identityFilePath(workspaceDir, opts?.namespace);
|
|
10967
10432
|
let existing = "";
|
|
10968
10433
|
try {
|
|
10969
|
-
existing = await
|
|
10434
|
+
existing = await readFile2(identityPath, "utf-8");
|
|
10970
10435
|
} catch {
|
|
10971
10436
|
}
|
|
10972
10437
|
const hygiene = opts?.hygiene;
|
|
10973
|
-
const rotateEnabled = hygiene?.enabled === true && hygiene.rotateEnabled === true && Array.isArray(hygiene.rotatePaths) && hygiene.rotatePaths.includes(
|
|
10438
|
+
const rotateEnabled = hygiene?.enabled === true && hygiene.rotateEnabled === true && Array.isArray(hygiene.rotatePaths) && hygiene.rotatePaths.includes(path13.basename(identityPath));
|
|
10974
10439
|
if (rotateEnabled) {
|
|
10975
10440
|
const maxBytes = hygiene.rotateMaxBytes;
|
|
10976
10441
|
if (existing.length > maxBytes) {
|
|
10977
|
-
const archiveDir =
|
|
10442
|
+
const archiveDir = path13.join(workspaceDir, hygiene.archiveDir);
|
|
10978
10443
|
const { newContent } = await rotateMarkdownFileToArchive({
|
|
10979
10444
|
filePath: identityPath,
|
|
10980
10445
|
archiveDir,
|
|
@@ -11026,7 +10491,7 @@ ${reflection}
|
|
|
11026
10491
|
}
|
|
11027
10492
|
}
|
|
11028
10493
|
async writeIdentityReflections(content) {
|
|
11029
|
-
await
|
|
10494
|
+
await mkdir(this.identityDir, { recursive: true });
|
|
11030
10495
|
await this.writeStorageSecureFile(this.identityReflectionsPath, content);
|
|
11031
10496
|
}
|
|
11032
10497
|
async appendIdentityReflection(reflection) {
|
|
@@ -11058,7 +10523,7 @@ ${reflection}
|
|
|
11058
10523
|
|
|
11059
10524
|
${reflection}
|
|
11060
10525
|
`;
|
|
11061
|
-
await
|
|
10526
|
+
await mkdir(this.identityDir, { recursive: true });
|
|
11062
10527
|
await this.writeStorageSecureFile(this.identityReflectionsPath, `${existing.trimEnd()}${section}`);
|
|
11063
10528
|
log.debug(`appended namespace-local reflection to ${this.identityReflectionsPath}`);
|
|
11064
10529
|
}
|
|
@@ -11114,7 +10579,7 @@ ${reflection}
|
|
|
11114
10579
|
const rightTs = latestEntityTimelineTimestamp(right.entity) ?? "";
|
|
11115
10580
|
return compareEntityTimestamps(rightTs, leftTs);
|
|
11116
10581
|
}).map(({ entityName }) => entityName);
|
|
11117
|
-
await
|
|
10582
|
+
await mkdir(this.stateDir, { recursive: true });
|
|
11118
10583
|
await this.writeStorageSecureFile(
|
|
11119
10584
|
this.entitySynthesisQueuePath,
|
|
11120
10585
|
JSON.stringify(
|
|
@@ -11134,7 +10599,7 @@ ${reflection}
|
|
|
11134
10599
|
if (queue.length === 0) return;
|
|
11135
10600
|
const removals = new Set(entityNames);
|
|
11136
10601
|
const nextQueue = queue.filter((name) => !removals.has(name));
|
|
11137
|
-
await
|
|
10602
|
+
await mkdir(this.stateDir, { recursive: true });
|
|
11138
10603
|
await this.writeStorageSecureFile(
|
|
11139
10604
|
this.entitySynthesisQueuePath,
|
|
11140
10605
|
JSON.stringify(
|
|
@@ -11234,14 +10699,14 @@ ${reflection}
|
|
|
11234
10699
|
if (entries.length === 0) return 0;
|
|
11235
10700
|
const memories = await this.readAllMemories();
|
|
11236
10701
|
const memoryMap = new Map(memories.map((m) => [m.frontmatter.id, m]));
|
|
11237
|
-
const memoryPathMap = new Map(memories.map((m) => [
|
|
10702
|
+
const memoryPathMap = new Map(memories.map((m) => [path13.resolve(m.path), m]));
|
|
11238
10703
|
let updated = 0;
|
|
11239
10704
|
const prevVersion = this.getMemoryCorpusVersion();
|
|
11240
10705
|
const keyId = this.hotCacheKeyId();
|
|
11241
10706
|
const warm = this.hotMemoriesCacheEnabled && getCachedMemories(this.baseDir, prevVersion, keyId, this.hotCacheTtlMs()) !== null;
|
|
11242
10707
|
const appliedPatches = /* @__PURE__ */ new Map();
|
|
11243
10708
|
for (const entry of entries) {
|
|
11244
|
-
const memory = entry.memoryPath ? memoryPathMap.get(
|
|
10709
|
+
const memory = entry.memoryPath ? memoryPathMap.get(path13.resolve(entry.memoryPath)) : memoryMap.get(entry.memoryId);
|
|
11245
10710
|
if (!memory) continue;
|
|
11246
10711
|
try {
|
|
11247
10712
|
const applied = await this.withTombstoneBlockedMemoryPathLock(memory.path, async (current) => {
|
|
@@ -11272,7 +10737,7 @@ ${current.content}
|
|
|
11272
10737
|
if (warm) {
|
|
11273
10738
|
updateCacheOnWrite(this.baseDir, { ...applied.current, frontmatter: applied.newFm }, keyId);
|
|
11274
10739
|
}
|
|
11275
|
-
appliedPatches.set(
|
|
10740
|
+
appliedPatches.set(path13.resolve(memory.path), {
|
|
11276
10741
|
accessCount: entry.newCount,
|
|
11277
10742
|
lastAccessed: entry.lastAccessed
|
|
11278
10743
|
});
|
|
@@ -11288,7 +10753,7 @@ ${current.content}
|
|
|
11288
10753
|
const cur = getCachedMemories(this.baseDir, prevVersion, keyId);
|
|
11289
10754
|
if (cur) {
|
|
11290
10755
|
const reapplied = cur.map((m) => {
|
|
11291
|
-
const patch = appliedPatches.get(
|
|
10756
|
+
const patch = appliedPatches.get(path13.resolve(m.path));
|
|
11292
10757
|
return patch ? {
|
|
11293
10758
|
...m,
|
|
11294
10759
|
frontmatter: { ...m.frontmatter, accessCount: patch.accessCount, lastAccessed: patch.lastAccessed }
|
|
@@ -11318,7 +10783,7 @@ ${current.content}
|
|
|
11318
10783
|
const pathsById = /* @__PURE__ */ new Map();
|
|
11319
10784
|
const filePathsById = /* @__PURE__ */ new Map();
|
|
11320
10785
|
for (const filePath of filePaths) {
|
|
11321
|
-
const memoryId =
|
|
10786
|
+
const memoryId = path13.basename(filePath, ".md");
|
|
11322
10787
|
if (!wantedIds.has(memoryId)) continue;
|
|
11323
10788
|
const paths = filePathsById.get(memoryId) ?? [];
|
|
11324
10789
|
paths.push(filePath);
|
|
@@ -11330,7 +10795,7 @@ ${current.content}
|
|
|
11330
10795
|
const preferredMatches = [];
|
|
11331
10796
|
for (const preferredPath of preferred) {
|
|
11332
10797
|
const directCandidates = new Set(qmdResultPathCandidates(this.baseDir, preferredPath));
|
|
11333
|
-
const directMatch = existingPaths.find((filePath) => directCandidates.has(
|
|
10798
|
+
const directMatch = existingPaths.find((filePath) => directCandidates.has(path13.resolve(filePath)));
|
|
11334
10799
|
if (directMatch) {
|
|
11335
10800
|
preferredMatches.push(directMatch);
|
|
11336
10801
|
continue;
|
|
@@ -11342,7 +10807,7 @@ ${current.content}
|
|
|
11342
10807
|
candidates.add(candidate);
|
|
11343
10808
|
}
|
|
11344
10809
|
}
|
|
11345
|
-
const match = existingPaths.find((filePath) => candidates.has(
|
|
10810
|
+
const match = existingPaths.find((filePath) => candidates.has(path13.resolve(filePath)));
|
|
11346
10811
|
if (match) preferredMatches.push(match);
|
|
11347
10812
|
}
|
|
11348
10813
|
if (preferredMatches.length > 0) {
|
|
@@ -11533,7 +10998,7 @@ ${sanitized.text}
|
|
|
11533
10998
|
}
|
|
11534
10999
|
if (!oldMemory) return false;
|
|
11535
11000
|
const requireActiveAtWrite = options.requireActive === true || inferMemoryStatus(oldMemory.frontmatter, toMemoryPathRel(this.baseDir, oldMemory.path)) === "active";
|
|
11536
|
-
const operationId =
|
|
11001
|
+
const operationId = createHash6("sha256").update(
|
|
11537
11002
|
JSON.stringify({
|
|
11538
11003
|
oldMemoryId,
|
|
11539
11004
|
newMemoryId,
|
|
@@ -11662,11 +11127,11 @@ ${current.content}
|
|
|
11662
11127
|
}
|
|
11663
11128
|
}
|
|
11664
11129
|
get summariesDir() {
|
|
11665
|
-
return
|
|
11130
|
+
return path13.join(this.baseDir, "summaries");
|
|
11666
11131
|
}
|
|
11667
11132
|
async writeSummary(summary) {
|
|
11668
|
-
await
|
|
11669
|
-
const filePath =
|
|
11133
|
+
await mkdir(this.summariesDir, { recursive: true });
|
|
11134
|
+
const filePath = path13.join(this.summariesDir, `${summary.id}.json`);
|
|
11670
11135
|
await this.writeStorageSecureFile(filePath, JSON.stringify(summary, null, 2));
|
|
11671
11136
|
log.debug(`wrote summary ${summary.id}`);
|
|
11672
11137
|
}
|
|
@@ -11676,7 +11141,7 @@ ${current.content}
|
|
|
11676
11141
|
const summaries = [];
|
|
11677
11142
|
for (const file of files) {
|
|
11678
11143
|
if (!file.endsWith(".json")) continue;
|
|
11679
|
-
const filePath =
|
|
11144
|
+
const filePath = path13.join(this.summariesDir, file);
|
|
11680
11145
|
const raw = await this.readStorageSecureFile(filePath);
|
|
11681
11146
|
summaries.push(JSON.parse(raw));
|
|
11682
11147
|
}
|
|
@@ -11746,8 +11211,8 @@ ${current.content}
|
|
|
11746
11211
|
return archived;
|
|
11747
11212
|
}
|
|
11748
11213
|
async saveTopics(topics) {
|
|
11749
|
-
const metaPath =
|
|
11750
|
-
await
|
|
11214
|
+
const metaPath = path13.join(this.stateDir, "topics.json");
|
|
11215
|
+
await mkdir(this.stateDir, { recursive: true });
|
|
11751
11216
|
await this.writeStorageSecureFile(
|
|
11752
11217
|
metaPath,
|
|
11753
11218
|
JSON.stringify({ topics, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
|
|
@@ -11755,7 +11220,7 @@ ${current.content}
|
|
|
11755
11220
|
log.debug(`saved ${topics.length} topic scores`);
|
|
11756
11221
|
}
|
|
11757
11222
|
async loadTopics() {
|
|
11758
|
-
const metaPath =
|
|
11223
|
+
const metaPath = path13.join(this.stateDir, "topics.json");
|
|
11759
11224
|
try {
|
|
11760
11225
|
const raw = await this.readStorageSecureFile(metaPath);
|
|
11761
11226
|
return JSON.parse(raw);
|
|
@@ -11833,7 +11298,6 @@ export {
|
|
|
11833
11298
|
serializeDriftProvenance,
|
|
11834
11299
|
parseDriftProvenance,
|
|
11835
11300
|
selfDeps,
|
|
11836
|
-
canonicalSemanticFileText,
|
|
11837
11301
|
qmdCollectionPathParts,
|
|
11838
11302
|
qmdResultPathCandidates,
|
|
11839
11303
|
QmdResultResolver,
|
|
@@ -11874,4 +11338,4 @@ export {
|
|
|
11874
11338
|
isValidBufferSurpriseEvent,
|
|
11875
11339
|
StorageManager3 as StorageManager
|
|
11876
11340
|
};
|
|
11877
|
-
//# sourceMappingURL=chunk-
|
|
11341
|
+
//# sourceMappingURL=chunk-QUFJ3NJE.js.map
|