@remnic/core 10.0.35 → 10.0.37
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.js +81 -81
- package/dist/access-authorization-probe.js +82 -82
- package/dist/access-boundary.js +81 -81
- package/dist/access-citation-namespace.js +2 -2
- package/dist/access-cli.js +172 -170
- package/dist/access-cli.js.map +1 -1
- package/dist/access-extraction-force-flush.js +81 -81
- package/dist/access-http-briefing.js +82 -82
- package/dist/access-http-query.js +83 -83
- package/dist/access-http.js +104 -104
- package/dist/access-identity-continuity-surface.js +81 -81
- package/dist/access-lcm-surface.js +81 -81
- package/dist/access-mcp.js +93 -93
- package/dist/access-namespace-preflight.js +81 -81
- package/dist/access-observe-write-category.js +13 -13
- package/dist/access-observe-write-surface.js +81 -81
- package/dist/access-offline-manifest.js +12 -12
- package/dist/access-operations-batch.js +85 -85
- package/dist/access-operations.js +92 -92
- package/dist/access-recall-concurrency.js +81 -81
- package/dist/access-recall-response.js +81 -81
- package/dist/access-recall-surface.js +81 -81
- package/dist/access-schema.js +8 -8
- package/dist/access-service.js +81 -81
- package/dist/access-surface-catalog.js +83 -83
- package/dist/active-recall.js +40 -40
- package/dist/adapters/index.js +7 -7
- package/dist/adapters/registry.js +3 -3
- package/dist/{auto-sync-VPHR3FBI.js → auto-sync-R3XGJL5I.js} +7 -7
- package/dist/briefing.js +29 -29
- package/dist/buffer-eviction.d.ts +16 -0
- package/dist/buffer-eviction.d.ts.map +1 -0
- package/dist/buffer-eviction.js +8 -0
- package/dist/buffer-persistence.d.ts +45 -0
- package/dist/buffer-persistence.d.ts.map +1 -0
- package/dist/buffer-persistence.js +8 -0
- package/dist/buffer.d.ts +5 -2
- package/dist/buffer.d.ts.map +1 -1
- package/dist/buffer.js +13 -11
- package/dist/calibration.js +3 -3
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.js +32 -32
- package/dist/causal-retrieval.js +2 -2
- package/dist/causal-trajectory-graph.js +2 -2
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-HT6IEMQ2.js → chunk-2RJO7VS4.js} +2 -2
- package/dist/{chunk-YLF42JQ7.js → chunk-3BPMYMPX.js} +2 -2
- package/dist/{chunk-GAARPWMN.js → chunk-3L5WH6CV.js} +2 -2
- package/dist/{chunk-XVZA6VXC.js → chunk-3SL52JKD.js} +4 -4
- package/dist/chunk-4OFXLQJD.js +58 -0
- package/dist/chunk-4OFXLQJD.js.map +1 -0
- package/dist/{chunk-S2PDFSXS.js → chunk-4P4VLPGB.js} +1 -1
- package/dist/{chunk-2ZYIAUYU.js → chunk-5CWH2LKL.js} +4 -4
- package/dist/{chunk-G2PC6IZB.js → chunk-5U3NUHMA.js} +86 -86
- package/dist/{chunk-4LOMATQA.js → chunk-5UWP5X3T.js} +2 -2
- package/dist/{chunk-JUXOTY6J.js → chunk-5YQXWVCP.js} +9 -2
- package/dist/chunk-5YQXWVCP.js.map +1 -0
- package/dist/{chunk-G3T6YMBO.js → chunk-64S6X4IU.js} +7 -7
- package/dist/{chunk-NHB4RC3G.js → chunk-6QOL4G76.js} +2 -2
- package/dist/{chunk-GRBQO56D.js → chunk-6R6CU3MR.js} +51 -51
- package/dist/{chunk-SQ554O3K.js → chunk-74AY3S7U.js} +3 -3
- package/dist/{chunk-4ISHIPOC.js → chunk-7PG7QBWD.js} +2 -2
- package/dist/{chunk-A4B7WL5N.js → chunk-7ZBANI3M.js} +2 -2
- package/dist/{chunk-DG5UKN2Y.js → chunk-A23DPFFI.js} +2 -2
- package/dist/{chunk-77ZK533I.js → chunk-ARFT6ZBE.js} +2 -2
- package/dist/{chunk-V44DWTFA.js → chunk-AYXRK264.js} +6 -6
- package/dist/{chunk-GRZX3RHY.js → chunk-BPBUUGAO.js} +2 -2
- package/dist/{chunk-DFP37GUN.js → chunk-CCYXCT4B.js} +2 -2
- package/dist/{chunk-JJWOKRZA.js → chunk-CNQQNYJ2.js} +2 -2
- package/dist/{chunk-QZP3RMYP.js → chunk-CU7WDDPV.js} +4 -4
- package/dist/{chunk-GY4R4P2A.js → chunk-CUWOSRCS.js} +2 -2
- package/dist/{chunk-CJKHLACF.js → chunk-D2AKUKAQ.js} +2 -2
- package/dist/{chunk-BRZRP5WD.js → chunk-DK5VMJL3.js} +2 -2
- package/dist/{chunk-77IXBNV6.js → chunk-E24RUQEN.js} +2 -2
- package/dist/{chunk-PTQZPVRQ.js → chunk-E5PVMB3I.js} +2 -2
- package/dist/{chunk-AHS3QEEA.js → chunk-EEOYF3IB.js} +2 -2
- package/dist/{chunk-PH33R7GN.js → chunk-EHIKY2IA.js} +2 -2
- package/dist/{chunk-NLQBTHBV.js → chunk-EVVCE7S2.js} +2 -2
- package/dist/{chunk-FQ52AJXX.js → chunk-FBAHBFT5.js} +2 -2
- package/dist/{chunk-D75ET36O.js → chunk-FL6SLVAJ.js} +2 -2
- package/dist/{chunk-6NAN4WFY.js → chunk-FO5AFCRI.js} +5 -5
- package/dist/{chunk-5LWODP5R.js → chunk-FRAL3EU4.js} +2 -2
- package/dist/{chunk-AAANTXCQ.js → chunk-FZVZYU5C.js} +2 -2
- package/dist/{chunk-GQVTALUJ.js → chunk-G2SRB62M.js} +2 -2
- package/dist/{chunk-ORWDCX5Q.js → chunk-G6PPA4TH.js} +11 -11
- package/dist/{chunk-VNAIUNXI.js → chunk-GCVUBVEN.js} +10 -10
- package/dist/{chunk-TGAXRT7N.js → chunk-GFYFKXWZ.js} +2 -2
- package/dist/{chunk-ZABEDRGQ.js → chunk-HIMXDRSQ.js} +2 -2
- package/dist/{chunk-GDYLZ6FR.js → chunk-HJ3UTGIW.js} +3 -3
- package/dist/{chunk-MGXLLL5L.js → chunk-HKEK27M4.js} +2 -2
- package/dist/{chunk-X3IFRQKS.js → chunk-I73ZSORJ.js} +4 -4
- package/dist/{chunk-XUB4I2N6.js → chunk-JFI3ATK4.js} +2 -2
- package/dist/{chunk-R4SATHCV.js → chunk-JH2ALMPR.js} +2 -2
- package/dist/{chunk-TKBW4YLS.js → chunk-JH4FCVWJ.js} +2 -2
- package/dist/{chunk-UAYA4NJN.js → chunk-JWTUJVIF.js} +28 -36
- package/dist/chunk-JWTUJVIF.js.map +1 -0
- package/dist/chunk-KASJJGRG.js +29 -0
- package/dist/chunk-KASJJGRG.js.map +1 -0
- package/dist/{chunk-X73SCHKR.js → chunk-KC5SVCUJ.js} +2 -2
- package/dist/{chunk-6NXHX6MI.js → chunk-KOY25HUB.js} +2 -2
- package/dist/{chunk-TRW7N4FS.js → chunk-L2JQSG5M.js} +2 -2
- package/dist/{chunk-SIIWHIBK.js → chunk-L46Y5FPE.js} +2 -2
- package/dist/{chunk-CIJRM6SQ.js → chunk-LAQS5HYU.js} +2 -2
- package/dist/{chunk-TYT4TEQV.js → chunk-LDWE7OZS.js} +6 -2
- package/dist/chunk-LDWE7OZS.js.map +1 -0
- package/dist/{chunk-23VGZ65V.js → chunk-LHBOFROT.js} +2 -2
- package/dist/{chunk-LO775P3S.js → chunk-LRJKHXU4.js} +2 -2
- package/dist/{chunk-QISYYK4L.js → chunk-LSSCMZQ2.js} +3 -3
- package/dist/{chunk-PCIIKQXW.js → chunk-MBMT5FUQ.js} +2 -2
- package/dist/{chunk-XFDHL252.js → chunk-MGXFJOUR.js} +3 -3
- package/dist/{chunk-7WTTE5FW.js → chunk-MH3LC7KB.js} +2 -2
- package/dist/{chunk-ERMT5WQM.js → chunk-MKE5DP3S.js} +2 -2
- package/dist/{chunk-DJMC4W6N.js → chunk-MNDHKZZQ.js} +2 -2
- package/dist/{chunk-7FG2JSU6.js → chunk-NMKETOAX.js} +2 -2
- package/dist/{chunk-TVM47JQB.js → chunk-NQOM6PHO.js} +6 -6
- package/dist/{chunk-AI2VXOSO.js → chunk-NSQSOLCE.js} +2 -2
- package/dist/{chunk-UR25IQVT.js → chunk-P5CUEB2Q.js} +8 -8
- package/dist/{chunk-FXO43NBW.js → chunk-PSQ2P2T3.js} +3 -3
- package/dist/{chunk-KVNOHMVE.js → chunk-PTRPOJ7U.js} +2 -2
- package/dist/{chunk-SXRX6K35.js → chunk-QEGE5QCL.js} +47 -47
- package/dist/{chunk-CM6JDFCN.js → chunk-QIC7YZ3D.js} +16 -16
- package/dist/{chunk-KRAMCBIW.js → chunk-QKXPOLQD.js} +4 -4
- package/dist/{chunk-O3VKQMWC.js → chunk-R6TCM47S.js} +364 -166
- package/dist/chunk-R6TCM47S.js.map +1 -0
- package/dist/{chunk-G5UR4DXQ.js → chunk-RBC4GIZU.js} +2 -2
- package/dist/{chunk-GK56GNNS.js → chunk-RVYXSS3Q.js} +8 -8
- package/dist/{chunk-G7XAZU2L.js → chunk-RWWYBE7E.js} +3 -3
- package/dist/{chunk-HJM23E65.js → chunk-S53WZWGL.js} +2 -2
- package/dist/{chunk-MLED254J.js → chunk-T6OZW2WR.js} +8 -2
- package/dist/chunk-T6OZW2WR.js.map +1 -0
- package/dist/{chunk-3ODEGTVV.js → chunk-TDCR6PQL.js} +2 -2
- package/dist/{chunk-QCP75JGO.js → chunk-TFA6HPOA.js} +5 -5
- package/dist/{chunk-SE3JAD22.js → chunk-TPKB7T6Z.js} +2 -2
- package/dist/{chunk-6VYY2XFK.js → chunk-TVIGN3XZ.js} +19 -19
- package/dist/{chunk-ZG3HZZ6K.js → chunk-TYGLV6ZG.js} +5 -1
- package/dist/{chunk-ZG3HZZ6K.js.map → chunk-TYGLV6ZG.js.map} +1 -1
- package/dist/{chunk-K567EIW3.js → chunk-U4JXQCJ3.js} +2 -2
- package/dist/{chunk-WI6MIJYK.js → chunk-U7HJBODU.js} +5 -5
- package/dist/{chunk-IS4HKNWP.js → chunk-UBN5AOLT.js} +2 -2
- package/dist/{chunk-7VJEZQYF.js → chunk-UFBIOF7C.js} +3 -3
- package/dist/{chunk-JNELS2TW.js → chunk-UJDTXWMT.js} +2 -2
- package/dist/{chunk-M3GKSCFN.js → chunk-ULUX6Q7R.js} +4 -4
- package/dist/{chunk-ZRU7636R.js → chunk-UMFBHYPW.js} +12 -12
- package/dist/{chunk-GKNX4QO3.js → chunk-UQWA5QDT.js} +3 -3
- package/dist/{chunk-4Z7O2QVJ.js → chunk-USOXPN5H.js} +2 -2
- package/dist/{chunk-XA32GZ6B.js → chunk-UXXVOA4D.js} +3 -3
- package/dist/{chunk-OLAQWYNF.js → chunk-V5RJCXLG.js} +6 -6
- package/dist/{chunk-A2D3WICT.js → chunk-VGFNTY3Y.js} +2 -2
- package/dist/{chunk-EH6KLYIE.js → chunk-VKTP3TZB.js} +4 -4
- package/dist/{chunk-CSGFGQBI.js → chunk-VPO53QMG.js} +2 -2
- package/dist/{chunk-P2RCFQA6.js → chunk-WADPRAZE.js} +2 -2
- package/dist/{chunk-IVYAPAHG.js → chunk-WRDFYSUC.js} +2 -2
- package/dist/{chunk-FW7KWZBI.js → chunk-WSLNC5AL.js} +8 -8
- package/dist/{chunk-C6S2Y7HE.js → chunk-XKDQTDXN.js} +4 -4
- package/dist/{chunk-4AHKNZW7.js → chunk-XLEP7SS3.js} +2 -2
- package/dist/{chunk-B6L7BKA7.js → chunk-XRFLR3XF.js} +4 -4
- package/dist/{chunk-H7QC7WCU.js → chunk-XTBEXZFN.js} +4 -4
- package/dist/{chunk-S2S4U4G3.js → chunk-Y6Y3TRBN.js} +4 -4
- package/dist/{chunk-H6SKBCNI.js → chunk-YC2C67F7.js} +2 -2
- package/dist/{chunk-6FIFEXFF.js → chunk-YGYT7S3G.js} +7 -7
- package/dist/{chunk-EZ25VE3G.js → chunk-YNDLCWXS.js} +4 -4
- package/dist/{chunk-YOL56HY5.js → chunk-YQ6SLFNZ.js} +3 -3
- package/dist/{chunk-G22A6WJQ.js → chunk-Z2T5BPOZ.js} +2 -2
- package/dist/{chunk-IHTKBQBS.js → chunk-Z5AQZRHF.js} +2 -2
- package/dist/{chunk-QQ4T622E.js → chunk-Z5QWUR3Y.js} +2 -2
- package/dist/cli.js +131 -131
- package/dist/coding/export-okf-codegraph.js +30 -30
- package/dist/coding/pre-action-gate.js +2 -2
- package/dist/compounding/engine.js +31 -31
- package/dist/config.js +40 -40
- package/dist/connectors/codex-materialize-runner.js +29 -29
- package/dist/connectors/index.js +29 -29
- package/dist/contradiction/index.js +5 -5
- package/dist/convergence-refresh.js +31 -31
- package/dist/conversation-index/backend.js +2 -2
- package/dist/corpus-watermark.js +31 -31
- package/dist/dashboard-runtime.js +2 -2
- package/dist/deep-recall-http-glue.js +9 -9
- package/dist/entity-retrieval.js +31 -31
- package/dist/explicit-capture.js +7 -7
- package/dist/external-wiki-access.js +82 -82
- package/dist/extraction-span-materialize.js +2 -2
- package/dist/extraction.js +44 -44
- package/dist/{graph-edge-decay-IU75NV42.js → graph-edge-decay-2BZTY2IM.js} +5 -5
- package/dist/graph-edge-decay-2BZTY2IM.js.map +1 -0
- package/dist/graph-jsonl.js +3 -3
- package/dist/graph-snapshot.js +3 -3
- package/dist/graph.js +2 -2
- package/dist/harmonic-retrieval.js +30 -30
- package/dist/harmonic-source-lookup.js +2 -2
- package/dist/index.js +226 -224
- package/dist/lcm/index.js +3 -3
- package/dist/local-llm-helpers.d.ts.map +1 -1
- package/dist/local-llm-helpers.js +2 -1
- package/dist/local-llm-transport.d.ts +1 -0
- package/dist/local-llm-transport.d.ts.map +1 -1
- package/dist/local-llm-transport.js +5 -3
- package/dist/local-llm.js +3 -3
- package/dist/location/index.js +32 -32
- package/dist/maintenance/memory-governance.js +29 -29
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +29 -29
- package/dist/maintenance/rebuild-memory-projection-runner.js +31 -31
- package/dist/maintenance/rebuild-memory-projection.js +30 -30
- package/dist/memory-browse-operations.js +82 -82
- package/dist/memory-browse.js +5 -5
- package/dist/memory-subject.js +3 -3
- package/dist/namespaces/migrate.js +30 -30
- package/dist/namespaces/search.js +11 -11
- package/dist/namespaces/storage.js +29 -29
- package/dist/offline-sync-changeset-generations.js +3 -3
- package/dist/offline-sync-embedding-generation.js +2 -2
- package/dist/offline-sync-file-io.js +2 -2
- package/dist/offline-sync-impression-drain.js +3 -3
- package/dist/offline-sync-runtime-state.d.ts.map +1 -1
- package/dist/offline-sync-runtime-state.js +1 -1
- package/dist/offline-sync-snapshot-stream.js +8 -8
- package/dist/offline-sync-upload-staging.js +3 -3
- package/dist/offline-sync.js +8 -8
- package/dist/operator-doctor-corpus.js +32 -32
- package/dist/operator-toolkit.js +51 -51
- package/dist/orchestration/maintenance.js +44 -44
- package/dist/orchestrator.js +174 -172
- package/dist/qmd.js +4 -4
- package/dist/recall-navigation-operations.js +82 -82
- package/dist/recall-navigation.js +81 -81
- package/dist/recall-state-view-admission.js +5 -5
- package/dist/recall-why-service.js +5 -5
- package/dist/reconcile/manifest.js +3 -3
- package/dist/{resolution-V6JBMW7U.js → resolution-4ZEQPXUZ.js} +6 -6
- package/dist/resolution-4ZEQPXUZ.js.map +1 -0
- package/dist/resume-bundles.js +41 -41
- package/dist/retrieval-agents.js +2 -2
- package/dist/salvage-envelope.js +5 -5
- package/dist/search/factory.js +10 -10
- package/dist/search/index.js +14 -14
- package/dist/search/lancedb-backend.js +2 -2
- package/dist/search/meilisearch-backend.js +2 -2
- package/dist/search/orama-backend.js +2 -2
- package/dist/secure-store/index.js +2 -2
- package/dist/semantic-consolidation.js +30 -30
- package/dist/semantic-rule-promotion.js +29 -29
- package/dist/semantic-rule-verifier.js +29 -29
- package/dist/source-agent-qualifier.js +34 -34
- package/dist/standup/build.js +29 -29
- package/dist/storage/buffer-session-files.d.ts +69 -0
- package/dist/storage/buffer-session-files.d.ts.map +1 -0
- package/dist/storage.d.ts +5 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +28 -28
- package/dist/subject-config.js +4 -4
- package/dist/summarizer.js +4 -4
- package/dist/support-passport/index.js +84 -84
- package/dist/transfer/export-okf.js +30 -30
- package/dist/verified-recall.js +29 -29
- package/dist/who-knows-cli.js +30 -30
- package/dist/who-knows.js +29 -29
- package/dist/write-envelope.js +4 -4
- package/package.json +2 -2
- package/src/buffer-eviction.ts +51 -0
- package/src/buffer-incremental-persistence.test.ts +256 -0
- package/src/buffer-persistence.ts +89 -0
- package/src/buffer.ts +32 -61
- package/src/local-llm-headers-timeout.test.ts +64 -0
- package/src/local-llm-helpers.ts +6 -1
- package/src/local-llm-transport.ts +14 -1
- package/src/offline-sync-runtime-state.ts +24 -2
- package/src/offline-sync.test.ts +9 -0
- package/src/secure-store/secure-fs.ts +11 -0
- package/src/secure-store/secure-store.test.ts +22 -0
- package/src/storage/buffer-session-files.ts +249 -0
- package/src/storage-buffer-sessions.test.ts +369 -0
- package/src/storage.ts +11 -16
- package/dist/chunk-JUXOTY6J.js.map +0 -1
- package/dist/chunk-MLED254J.js.map +0 -1
- package/dist/chunk-O3VKQMWC.js.map +0 -1
- package/dist/chunk-TYT4TEQV.js.map +0 -1
- package/dist/chunk-UAYA4NJN.js.map +0 -1
- /package/dist/{auto-sync-VPHR3FBI.js.map → auto-sync-R3XGJL5I.js.map} +0 -0
- /package/dist/{graph-edge-decay-IU75NV42.js.map → buffer-eviction.js.map} +0 -0
- /package/dist/{resolution-V6JBMW7U.js.map → buffer-persistence.js.map} +0 -0
- /package/dist/{chunk-HT6IEMQ2.js.map → chunk-2RJO7VS4.js.map} +0 -0
- /package/dist/{chunk-YLF42JQ7.js.map → chunk-3BPMYMPX.js.map} +0 -0
- /package/dist/{chunk-GAARPWMN.js.map → chunk-3L5WH6CV.js.map} +0 -0
- /package/dist/{chunk-XVZA6VXC.js.map → chunk-3SL52JKD.js.map} +0 -0
- /package/dist/{chunk-S2PDFSXS.js.map → chunk-4P4VLPGB.js.map} +0 -0
- /package/dist/{chunk-2ZYIAUYU.js.map → chunk-5CWH2LKL.js.map} +0 -0
- /package/dist/{chunk-G2PC6IZB.js.map → chunk-5U3NUHMA.js.map} +0 -0
- /package/dist/{chunk-4LOMATQA.js.map → chunk-5UWP5X3T.js.map} +0 -0
- /package/dist/{chunk-G3T6YMBO.js.map → chunk-64S6X4IU.js.map} +0 -0
- /package/dist/{chunk-NHB4RC3G.js.map → chunk-6QOL4G76.js.map} +0 -0
- /package/dist/{chunk-GRBQO56D.js.map → chunk-6R6CU3MR.js.map} +0 -0
- /package/dist/{chunk-SQ554O3K.js.map → chunk-74AY3S7U.js.map} +0 -0
- /package/dist/{chunk-4ISHIPOC.js.map → chunk-7PG7QBWD.js.map} +0 -0
- /package/dist/{chunk-A4B7WL5N.js.map → chunk-7ZBANI3M.js.map} +0 -0
- /package/dist/{chunk-DG5UKN2Y.js.map → chunk-A23DPFFI.js.map} +0 -0
- /package/dist/{chunk-77ZK533I.js.map → chunk-ARFT6ZBE.js.map} +0 -0
- /package/dist/{chunk-V44DWTFA.js.map → chunk-AYXRK264.js.map} +0 -0
- /package/dist/{chunk-GRZX3RHY.js.map → chunk-BPBUUGAO.js.map} +0 -0
- /package/dist/{chunk-DFP37GUN.js.map → chunk-CCYXCT4B.js.map} +0 -0
- /package/dist/{chunk-JJWOKRZA.js.map → chunk-CNQQNYJ2.js.map} +0 -0
- /package/dist/{chunk-QZP3RMYP.js.map → chunk-CU7WDDPV.js.map} +0 -0
- /package/dist/{chunk-GY4R4P2A.js.map → chunk-CUWOSRCS.js.map} +0 -0
- /package/dist/{chunk-CJKHLACF.js.map → chunk-D2AKUKAQ.js.map} +0 -0
- /package/dist/{chunk-BRZRP5WD.js.map → chunk-DK5VMJL3.js.map} +0 -0
- /package/dist/{chunk-77IXBNV6.js.map → chunk-E24RUQEN.js.map} +0 -0
- /package/dist/{chunk-PTQZPVRQ.js.map → chunk-E5PVMB3I.js.map} +0 -0
- /package/dist/{chunk-AHS3QEEA.js.map → chunk-EEOYF3IB.js.map} +0 -0
- /package/dist/{chunk-PH33R7GN.js.map → chunk-EHIKY2IA.js.map} +0 -0
- /package/dist/{chunk-NLQBTHBV.js.map → chunk-EVVCE7S2.js.map} +0 -0
- /package/dist/{chunk-FQ52AJXX.js.map → chunk-FBAHBFT5.js.map} +0 -0
- /package/dist/{chunk-D75ET36O.js.map → chunk-FL6SLVAJ.js.map} +0 -0
- /package/dist/{chunk-6NAN4WFY.js.map → chunk-FO5AFCRI.js.map} +0 -0
- /package/dist/{chunk-5LWODP5R.js.map → chunk-FRAL3EU4.js.map} +0 -0
- /package/dist/{chunk-AAANTXCQ.js.map → chunk-FZVZYU5C.js.map} +0 -0
- /package/dist/{chunk-GQVTALUJ.js.map → chunk-G2SRB62M.js.map} +0 -0
- /package/dist/{chunk-ORWDCX5Q.js.map → chunk-G6PPA4TH.js.map} +0 -0
- /package/dist/{chunk-VNAIUNXI.js.map → chunk-GCVUBVEN.js.map} +0 -0
- /package/dist/{chunk-TGAXRT7N.js.map → chunk-GFYFKXWZ.js.map} +0 -0
- /package/dist/{chunk-ZABEDRGQ.js.map → chunk-HIMXDRSQ.js.map} +0 -0
- /package/dist/{chunk-GDYLZ6FR.js.map → chunk-HJ3UTGIW.js.map} +0 -0
- /package/dist/{chunk-MGXLLL5L.js.map → chunk-HKEK27M4.js.map} +0 -0
- /package/dist/{chunk-X3IFRQKS.js.map → chunk-I73ZSORJ.js.map} +0 -0
- /package/dist/{chunk-XUB4I2N6.js.map → chunk-JFI3ATK4.js.map} +0 -0
- /package/dist/{chunk-R4SATHCV.js.map → chunk-JH2ALMPR.js.map} +0 -0
- /package/dist/{chunk-TKBW4YLS.js.map → chunk-JH4FCVWJ.js.map} +0 -0
- /package/dist/{chunk-X73SCHKR.js.map → chunk-KC5SVCUJ.js.map} +0 -0
- /package/dist/{chunk-6NXHX6MI.js.map → chunk-KOY25HUB.js.map} +0 -0
- /package/dist/{chunk-TRW7N4FS.js.map → chunk-L2JQSG5M.js.map} +0 -0
- /package/dist/{chunk-SIIWHIBK.js.map → chunk-L46Y5FPE.js.map} +0 -0
- /package/dist/{chunk-CIJRM6SQ.js.map → chunk-LAQS5HYU.js.map} +0 -0
- /package/dist/{chunk-23VGZ65V.js.map → chunk-LHBOFROT.js.map} +0 -0
- /package/dist/{chunk-LO775P3S.js.map → chunk-LRJKHXU4.js.map} +0 -0
- /package/dist/{chunk-QISYYK4L.js.map → chunk-LSSCMZQ2.js.map} +0 -0
- /package/dist/{chunk-PCIIKQXW.js.map → chunk-MBMT5FUQ.js.map} +0 -0
- /package/dist/{chunk-XFDHL252.js.map → chunk-MGXFJOUR.js.map} +0 -0
- /package/dist/{chunk-7WTTE5FW.js.map → chunk-MH3LC7KB.js.map} +0 -0
- /package/dist/{chunk-ERMT5WQM.js.map → chunk-MKE5DP3S.js.map} +0 -0
- /package/dist/{chunk-DJMC4W6N.js.map → chunk-MNDHKZZQ.js.map} +0 -0
- /package/dist/{chunk-7FG2JSU6.js.map → chunk-NMKETOAX.js.map} +0 -0
- /package/dist/{chunk-TVM47JQB.js.map → chunk-NQOM6PHO.js.map} +0 -0
- /package/dist/{chunk-AI2VXOSO.js.map → chunk-NSQSOLCE.js.map} +0 -0
- /package/dist/{chunk-UR25IQVT.js.map → chunk-P5CUEB2Q.js.map} +0 -0
- /package/dist/{chunk-FXO43NBW.js.map → chunk-PSQ2P2T3.js.map} +0 -0
- /package/dist/{chunk-KVNOHMVE.js.map → chunk-PTRPOJ7U.js.map} +0 -0
- /package/dist/{chunk-SXRX6K35.js.map → chunk-QEGE5QCL.js.map} +0 -0
- /package/dist/{chunk-CM6JDFCN.js.map → chunk-QIC7YZ3D.js.map} +0 -0
- /package/dist/{chunk-KRAMCBIW.js.map → chunk-QKXPOLQD.js.map} +0 -0
- /package/dist/{chunk-G5UR4DXQ.js.map → chunk-RBC4GIZU.js.map} +0 -0
- /package/dist/{chunk-GK56GNNS.js.map → chunk-RVYXSS3Q.js.map} +0 -0
- /package/dist/{chunk-G7XAZU2L.js.map → chunk-RWWYBE7E.js.map} +0 -0
- /package/dist/{chunk-HJM23E65.js.map → chunk-S53WZWGL.js.map} +0 -0
- /package/dist/{chunk-3ODEGTVV.js.map → chunk-TDCR6PQL.js.map} +0 -0
- /package/dist/{chunk-QCP75JGO.js.map → chunk-TFA6HPOA.js.map} +0 -0
- /package/dist/{chunk-SE3JAD22.js.map → chunk-TPKB7T6Z.js.map} +0 -0
- /package/dist/{chunk-6VYY2XFK.js.map → chunk-TVIGN3XZ.js.map} +0 -0
- /package/dist/{chunk-K567EIW3.js.map → chunk-U4JXQCJ3.js.map} +0 -0
- /package/dist/{chunk-WI6MIJYK.js.map → chunk-U7HJBODU.js.map} +0 -0
- /package/dist/{chunk-IS4HKNWP.js.map → chunk-UBN5AOLT.js.map} +0 -0
- /package/dist/{chunk-7VJEZQYF.js.map → chunk-UFBIOF7C.js.map} +0 -0
- /package/dist/{chunk-JNELS2TW.js.map → chunk-UJDTXWMT.js.map} +0 -0
- /package/dist/{chunk-M3GKSCFN.js.map → chunk-ULUX6Q7R.js.map} +0 -0
- /package/dist/{chunk-ZRU7636R.js.map → chunk-UMFBHYPW.js.map} +0 -0
- /package/dist/{chunk-GKNX4QO3.js.map → chunk-UQWA5QDT.js.map} +0 -0
- /package/dist/{chunk-4Z7O2QVJ.js.map → chunk-USOXPN5H.js.map} +0 -0
- /package/dist/{chunk-XA32GZ6B.js.map → chunk-UXXVOA4D.js.map} +0 -0
- /package/dist/{chunk-OLAQWYNF.js.map → chunk-V5RJCXLG.js.map} +0 -0
- /package/dist/{chunk-A2D3WICT.js.map → chunk-VGFNTY3Y.js.map} +0 -0
- /package/dist/{chunk-EH6KLYIE.js.map → chunk-VKTP3TZB.js.map} +0 -0
- /package/dist/{chunk-CSGFGQBI.js.map → chunk-VPO53QMG.js.map} +0 -0
- /package/dist/{chunk-P2RCFQA6.js.map → chunk-WADPRAZE.js.map} +0 -0
- /package/dist/{chunk-IVYAPAHG.js.map → chunk-WRDFYSUC.js.map} +0 -0
- /package/dist/{chunk-FW7KWZBI.js.map → chunk-WSLNC5AL.js.map} +0 -0
- /package/dist/{chunk-C6S2Y7HE.js.map → chunk-XKDQTDXN.js.map} +0 -0
- /package/dist/{chunk-4AHKNZW7.js.map → chunk-XLEP7SS3.js.map} +0 -0
- /package/dist/{chunk-B6L7BKA7.js.map → chunk-XRFLR3XF.js.map} +0 -0
- /package/dist/{chunk-H7QC7WCU.js.map → chunk-XTBEXZFN.js.map} +0 -0
- /package/dist/{chunk-S2S4U4G3.js.map → chunk-Y6Y3TRBN.js.map} +0 -0
- /package/dist/{chunk-H6SKBCNI.js.map → chunk-YC2C67F7.js.map} +0 -0
- /package/dist/{chunk-6FIFEXFF.js.map → chunk-YGYT7S3G.js.map} +0 -0
- /package/dist/{chunk-EZ25VE3G.js.map → chunk-YNDLCWXS.js.map} +0 -0
- /package/dist/{chunk-YOL56HY5.js.map → chunk-YQ6SLFNZ.js.map} +0 -0
- /package/dist/{chunk-G22A6WJQ.js.map → chunk-Z2T5BPOZ.js.map} +0 -0
- /package/dist/{chunk-IHTKBQBS.js.map → chunk-Z5AQZRHF.js.map} +0 -0
- /package/dist/{chunk-QQ4T622E.js.map → chunk-Z5QWUR3Y.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runLocationSync
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ULUX6Q7R.js";
|
|
4
4
|
import {
|
|
5
5
|
log
|
|
6
6
|
} from "./chunk-3WCASNQZ.js";
|
|
@@ -58,4 +58,4 @@ export {
|
|
|
58
58
|
LOCATION_SYNC_DEFAULT_INTERVAL_MS,
|
|
59
59
|
startLocationAutoSync
|
|
60
60
|
};
|
|
61
|
-
//# sourceMappingURL=chunk-
|
|
61
|
+
//# sourceMappingURL=chunk-RBC4GIZU.js.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
headerConnector,
|
|
4
4
|
renderExtractionConversation,
|
|
5
5
|
resolveSourceConnector
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-U4JXQCJ3.js";
|
|
7
7
|
import {
|
|
8
8
|
normalizeReasoningTrace
|
|
9
9
|
} from "./chunk-54V4BZWP.js";
|
|
@@ -29,10 +29,7 @@ import {
|
|
|
29
29
|
buildSpanMaterializeTurns,
|
|
30
30
|
stripUntrustedFactSpans,
|
|
31
31
|
truncateLocalExtractionConversation
|
|
32
|
-
} from "./chunk-
|
|
33
|
-
import {
|
|
34
|
-
attachExtractionProvenance
|
|
35
|
-
} from "./chunk-CIIHIAJ2.js";
|
|
32
|
+
} from "./chunk-3SL52JKD.js";
|
|
36
33
|
import {
|
|
37
34
|
containsExtractionPlaceholder,
|
|
38
35
|
extractionAttributes,
|
|
@@ -40,6 +37,9 @@ import {
|
|
|
40
37
|
extractionText,
|
|
41
38
|
isPlainRecord
|
|
42
39
|
} from "./chunk-BVRZSI7Q.js";
|
|
40
|
+
import {
|
|
41
|
+
attachExtractionProvenance
|
|
42
|
+
} from "./chunk-CIIHIAJ2.js";
|
|
43
43
|
import {
|
|
44
44
|
classifyExtractionThrownError,
|
|
45
45
|
classifyFallbackParseFailure,
|
|
@@ -83,7 +83,7 @@ import {
|
|
|
83
83
|
} from "./chunk-5MA7ZLPD.js";
|
|
84
84
|
import {
|
|
85
85
|
LocalLlmClient
|
|
86
|
-
} from "./chunk-
|
|
86
|
+
} from "./chunk-74AY3S7U.js";
|
|
87
87
|
import {
|
|
88
88
|
normalizeProcedureSteps
|
|
89
89
|
} from "./chunk-QDW3E4RD.js";
|
|
@@ -103,7 +103,7 @@ import {
|
|
|
103
103
|
} from "./chunk-YBA52OKG.js";
|
|
104
104
|
import {
|
|
105
105
|
isMemoryCategory
|
|
106
|
-
} from "./chunk-
|
|
106
|
+
} from "./chunk-TDCR6PQL.js";
|
|
107
107
|
import {
|
|
108
108
|
sanitizeMemoryContent
|
|
109
109
|
} from "./chunk-5GKPFL3S.js";
|
|
@@ -2439,4 +2439,4 @@ export {
|
|
|
2439
2439
|
shouldEnableLocalExtractionThinking,
|
|
2440
2440
|
ExtractionEngine
|
|
2441
2441
|
};
|
|
2442
|
-
//# sourceMappingURL=chunk-
|
|
2442
|
+
//# sourceMappingURL=chunk-RVYXSS3Q.js.map
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
rejectSymlinkPath,
|
|
6
6
|
renderFrontmatter,
|
|
7
7
|
writeBundleFile
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-L2JQSG5M.js";
|
|
9
9
|
import {
|
|
10
10
|
parseOaiMemCitation
|
|
11
11
|
} from "./chunk-D24OXEPB.js";
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from "./chunk-3UXOZBHV.js";
|
|
15
15
|
import {
|
|
16
16
|
StorageManager
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-R6TCM47S.js";
|
|
18
18
|
import {
|
|
19
19
|
OKF_PROFILE_TYPE,
|
|
20
20
|
okfTypeForMemory
|
|
@@ -272,4 +272,4 @@ export {
|
|
|
272
272
|
exportOkfBundle,
|
|
273
273
|
firstHeading
|
|
274
274
|
};
|
|
275
|
-
//# sourceMappingURL=chunk-
|
|
275
|
+
//# sourceMappingURL=chunk-RWWYBE7E.js.map
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "./chunk-FRXS6SC7.js";
|
|
7
7
|
import {
|
|
8
8
|
StorageManager
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-R6TCM47S.js";
|
|
10
10
|
import {
|
|
11
11
|
normalizeEntityName
|
|
12
12
|
} from "./chunk-YXZG5TVN.js";
|
|
@@ -962,4 +962,4 @@ export {
|
|
|
962
962
|
resolveBriefingSaveDir,
|
|
963
963
|
briefingFilename
|
|
964
964
|
};
|
|
965
|
-
//# sourceMappingURL=chunk-
|
|
965
|
+
//# sourceMappingURL=chunk-S53WZWGL.js.map
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
directProbeDispatcher
|
|
3
|
+
} from "./chunk-5YQXWVCP.js";
|
|
1
4
|
import {
|
|
2
5
|
log
|
|
3
6
|
} from "./chunk-3WCASNQZ.js";
|
|
@@ -79,7 +82,10 @@ async function probeFetch(url, options) {
|
|
|
79
82
|
const requestSignal = options.signal ? AbortSignal.any([options.signal, controller.signal]) : controller.signal;
|
|
80
83
|
const timeout = setTimeout(() => controller.abort(), options.timeoutMs);
|
|
81
84
|
try {
|
|
82
|
-
const
|
|
85
|
+
const init = { signal: requestSignal, headers: options.headers };
|
|
86
|
+
const dispatcher = directProbeDispatcher();
|
|
87
|
+
if (dispatcher) init.dispatcher = dispatcher;
|
|
88
|
+
const response = await fetch(url, init);
|
|
83
89
|
clearTimeout(timeout);
|
|
84
90
|
if (!response.ok) {
|
|
85
91
|
log.debug(`local LLM probe: ${url} returned HTTP ${response.status}`);
|
|
@@ -155,4 +161,4 @@ export {
|
|
|
155
161
|
probeFetch,
|
|
156
162
|
SingleFlightProbe
|
|
157
163
|
};
|
|
158
|
-
//# sourceMappingURL=chunk-
|
|
164
|
+
//# sourceMappingURL=chunk-T6OZW2WR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/local-llm-helpers.ts"],"sourcesContent":["import type { ResponseFormatJSONSchema } from \"openai/resources/shared\";\nimport type { Agent } from \"undici\";\nimport { log } from \"./logger.js\";\nimport { directProbeDispatcher } from \"./local-llm-transport.js\";\nimport type { LocalLlmRequestPriority } from \"./local-llm.js\";\n\nexport interface LocalLlmChatCompletionOptions {\n temperature?: number;\n maxTokens?: number;\n responseFormat?: { type: string } | ResponseFormatJSONSchema;\n timeoutMs?: number;\n operation?: string;\n forceDisableThinking?: boolean;\n disableThinking?: boolean;\n priority?: LocalLlmRequestPriority;\n signal?: AbortSignal;\n redactProviderErrors?: boolean;\n /** Per-request model; defaults to config.localLlmModel. */\n model?: string;\n /**\n * Optional out-box for the terminal failure cause (issue #2891): on a\n * failed request the client records the last provider error here so\n * callers can classify it (e.g. HTTP 429 → rate-limited) in memory\n * instead of seeing only null. Never logged.\n */\n failureDiag?: { lastError?: unknown };\n}\n\nexport interface LocalLlmChatCompletionResult {\n content: string;\n usage?: { promptTokens: number; completionTokens: number; totalTokens: number };\n}\nexport function isAbortError(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const maybe = err as { name?: string; message?: string };\n return (\n maybe.name === \"AbortError\" ||\n maybe.message === \"This operation was aborted\" ||\n maybe.message === \"The operation was aborted\"\n );\n}\n\nexport function waitForRetryBackoff(backoffMs: number, signal?: AbortSignal): Promise<boolean> {\n return new Promise((resolve) => {\n let settled = false;\n let timer: NodeJS.Timeout | undefined;\n const onAbort = (): void => {\n if (settled) return;\n settled = true;\n if (timer) clearTimeout(timer);\n signal?.removeEventListener(\"abort\", onAbort);\n resolve(false);\n };\n const onTimer = (): void => {\n if (settled) return;\n settled = true;\n signal?.removeEventListener(\"abort\", onAbort);\n resolve(true);\n };\n timer = setTimeout(onTimer, backoffMs);\n if (signal) {\n if (signal.aborted) onAbort();\n else signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n });\n}\n\nexport function normalizeBackendTripReason(reason: string): string {\n const cleaned = reason.replace(/\\s+/g, \" \").replace(/^[-:–—\\s]+/, \"\").trim();\n if (!cleaned) return \"unknown local backend failure\";\n return cleaned.length > 160 ? `${cleaned.slice(0, 157)}...` : cleaned;\n}\n\nexport function extractNonRecoverableBackendReason(reason: string): string | null {\n const match = reason.match(\n /Failed to load model|Library not loaded|different Team IDs|code signature|llm_engine_mlx_amphibian/i,\n );\n return match?.[0] ?? null;\n}\n\nexport function extractNonRecoverableBackendReasonFromErrorText(errorText: string): string | null {\n const directReason = extractNonRecoverableBackendReason(errorText);\n if (directReason) return directReason;\n try {\n const parsed = JSON.parse(errorText) as { error?: { message?: string } };\n return extractNonRecoverableBackendReason(parsed?.error?.message ?? \"\");\n } catch {\n return null;\n }\n}\n\n/**\n * Flatten a fetch rejection into one line, including the `cause` undici hides\n * the real reason behind (`fetch failed` alone says nothing).\n */\nexport function describeFetchFailure(err: unknown): string {\n const name = err instanceof Error ? err.name : \"\";\n const message = err instanceof Error ? err.message : String(err);\n const cause: unknown = err instanceof Error ? (err as Error & { cause?: unknown }).cause : undefined;\n const causeText = cause instanceof Error\n ? ` (cause: ${cause.name}: ${cause.message}${\n typeof (cause as Error & { code?: unknown }).code === \"string\"\n ? ` [${String((cause as Error & { code?: unknown }).code)}]`\n : \"\"\n })`\n : cause === undefined\n ? \"\"\n : ` (cause: ${String(cause)})`;\n return `${name ? `${name}: ` : \"\"}${message}${causeText}`;\n}\n\n/**\n * What to record when every availability probe failed.\n *\n * A probe that TIMED OUT is not evidence the backend is down: the budget is a\n * fixed 2s and a busy event loop can burn it before the socket is scheduled,\n * so the backend looks dead while it answers other callers in single-digit ms.\n * Caching that verdict took extraction offline for a whole health-check\n * interval at a time (issue #2210), so a timeout leaves availability UNKNOWN —\n * this call fails, the next one re-probes instead of reading a stale false.\n *\n * A backend that ANSWERED and said no is a real verdict and stays cached, or\n * the daemon re-probes a known-bad endpoint on every request.\n */\nexport function resolveUnavailableVerdict(args: {\n baseUrl: string;\n sawAbortedProbe: boolean;\n wasAvailable: boolean | null;\n}): { cacheVerdict: boolean; warning: string | null } {\n if (args.sawAbortedProbe) {\n return {\n cacheVerdict: false,\n warning:\n `local LLM availability probe timed out at ${args.baseUrl} (event loop busy?); treating availability as ` +\n \"unknown and re-probing on the next request rather than marking the backend down\",\n };\n }\n return {\n cacheVerdict: true,\n // Surfaced at warn, not debug: extraction stops silently when this flips,\n // and the daemon emits no debug at all unless configured for it.\n warning: args.wasAvailable !== false ? `local LLM became unavailable at ${args.baseUrl}` : null,\n };\n}\n\nexport interface ProbeFetchResult {\n ok: boolean;\n data: unknown;\n status: number | null;\n /** The probe hit its own budget rather than getting an answer. */\n aborted?: boolean;\n}\n\n/**\n * One health-probe request.\n *\n * Every failure is logged with its cause: a transport error and a 404 both\n * returned `ok: false` with nothing recorded, so an unreachable backend was\n * indistinguishable from a wrong path and an availability failure had no\n * diagnosable reason at all (§22, issue #2210).\n */\nexport async function probeFetch(\n url: string,\n options: { timeoutMs: number; headers: Record<string, string>; signal?: AbortSignal },\n): Promise<ProbeFetchResult> {\n const controller = new AbortController();\n const requestSignal = options.signal\n ? AbortSignal.any([options.signal, controller.signal])\n : controller.signal;\n const timeout = setTimeout(() => controller.abort(), options.timeoutMs);\n try {\n const init: RequestInit & { dispatcher?: Agent } = { signal: requestSignal, headers: options.headers };\n const dispatcher = directProbeDispatcher();\n if (dispatcher) init.dispatcher = dispatcher;\n const response = await fetch(url, init);\n clearTimeout(timeout);\n if (!response.ok) {\n log.debug(`local LLM probe: ${url} returned HTTP ${response.status}`);\n return { ok: false, data: null, status: response.status };\n }\n const contentType = response.headers.get(\"content-type\");\n const data = contentType?.includes(\"application/json\") ? await response.json() : await response.text();\n return { ok: true, data, status: response.status };\n } catch (err) {\n clearTimeout(timeout);\n const aborted = isAbortError(err);\n log.debug(\n `local LLM probe: ${url} ${aborted ? `timed out after ${options.timeoutMs}ms` : \"failed\"}: ${describeFetchFailure(err)}`,\n );\n return { ok: false, data: null, status: null, aborted };\n }\n}\n\ninterface PendingProbe {\n promise: Promise<boolean>;\n controller: AbortController;\n /** Callers still awaiting this sequence; at zero the sequence is aborted. */\n waiters: number;\n}\n\n/**\n * Collapses concurrent probe sequences onto one in-flight run.\n *\n * A timed-out availability probe deliberately caches nothing (issue #2210),\n * which removed the false verdict that used to absorb concurrent callers:\n * every queued request would then run its own full probe sequence, on a host\n * already slow enough to blow the probe budget.\n *\n * Cancellation is refcounted rather than shared: one caller walking away must\n * not abort a sequence the others are still waiting on, so the underlying task\n * is aborted only once every waiter has gone.\n */\nexport class SingleFlightProbe {\n private pending: PendingProbe | null = null;\n\n run(task: (signal: AbortSignal) => Promise<boolean>, signal?: AbortSignal): Promise<boolean> {\n let active = this.pending;\n if (!active) {\n const controller = new AbortController();\n const created: PendingProbe = { promise: Promise.resolve(false), controller, waiters: 0 };\n created.promise = task(controller.signal).finally(() => {\n if (this.pending === created) this.pending = null;\n });\n this.pending = created;\n active = created;\n }\n const entry = active;\n entry.waiters += 1;\n let released = false;\n const release = () => {\n if (released) return;\n released = true;\n entry.waiters -= 1;\n if (entry.waiters > 0) return;\n // Detach BEFORE aborting. `pending` would otherwise still point at this\n // doomed entry until the task's own `finally` runs a microtask later, and\n // a caller arriving in that window would join an aborted sequence and\n // read its `false` as a verdict.\n if (this.pending === entry) this.pending = null;\n entry.controller.abort();\n };\n if (!signal) return entry.promise.finally(release);\n return new Promise<boolean>((resolve, reject) => {\n const onAbort = () => {\n release();\n resolve(false);\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n entry.promise.then(\n (value) => {\n signal.removeEventListener(\"abort\", onAbort);\n release();\n resolve(value);\n },\n (err: unknown) => {\n signal.removeEventListener(\"abort\", onAbort);\n release();\n reject(err instanceof Error ? err : new Error(String(err)));\n },\n );\n });\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-BT7NVCML.js";
|
|
4
4
|
import {
|
|
5
5
|
isMemorySubject
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-E5PVMB3I.js";
|
|
7
7
|
import {
|
|
8
8
|
assemblePersistedBody
|
|
9
9
|
} from "./chunk-JDSVLVCG.js";
|
|
@@ -443,4 +443,4 @@ export {
|
|
|
443
443
|
buildAccessWriteRequestFingerprint,
|
|
444
444
|
buildObserveRequestFingerprint
|
|
445
445
|
};
|
|
446
|
-
//# sourceMappingURL=chunk-
|
|
446
|
+
//# sourceMappingURL=chunk-TDCR6PQL.js.map
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
searchConversationIndex,
|
|
3
|
-
searchConversationIndexFaissFailOpen
|
|
4
|
-
} from "./chunk-A7Q43EUZ.js";
|
|
5
1
|
import {
|
|
6
2
|
failOpenFaissHealth
|
|
7
3
|
} from "./chunk-F4TWWUMP.js";
|
|
@@ -9,6 +5,10 @@ import {
|
|
|
9
5
|
rebuildConversationChunksFailOpen,
|
|
10
6
|
upsertConversationChunksFailOpen
|
|
11
7
|
} from "./chunk-72A4L5JJ.js";
|
|
8
|
+
import {
|
|
9
|
+
searchConversationIndex,
|
|
10
|
+
searchConversationIndexFaissFailOpen
|
|
11
|
+
} from "./chunk-A7Q43EUZ.js";
|
|
12
12
|
|
|
13
13
|
// src/conversation-index/backend.ts
|
|
14
14
|
function createConversationIndexBackend(options) {
|
|
@@ -235,4 +235,4 @@ function createFaissBackend(getFaiss) {
|
|
|
235
235
|
export {
|
|
236
236
|
createConversationIndexBackend
|
|
237
237
|
};
|
|
238
|
-
//# sourceMappingURL=chunk-
|
|
238
|
+
//# sourceMappingURL=chunk-TFA6HPOA.js.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-B7RWBZXW.js";
|
|
4
4
|
import {
|
|
5
5
|
resolveCausalTrajectoryStoreDir
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-4P4VLPGB.js";
|
|
7
7
|
import {
|
|
8
8
|
normalizeRecallTokens
|
|
9
9
|
} from "./chunk-YCVB7X56.js";
|
|
@@ -339,4 +339,4 @@ export {
|
|
|
339
339
|
scoreStitchCandidate,
|
|
340
340
|
stitchCausalChain
|
|
341
341
|
};
|
|
342
|
-
//# sourceMappingURL=chunk-
|
|
342
|
+
//# sourceMappingURL=chunk-TPKB7T6Z.js.map
|
|
@@ -8,22 +8,11 @@ import {
|
|
|
8
8
|
pruneOfflineUploadStaging,
|
|
9
9
|
writeOfflineUploadChunk,
|
|
10
10
|
writeSafeFileFromUpload
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import {
|
|
13
|
-
OFFLINE_SYNC_FAST_BASE_MTIME_TOLERANCE_MS,
|
|
14
|
-
SYNC_INTERNAL_DIR,
|
|
15
|
-
assertOfflineSyncMtimeMs,
|
|
16
|
-
isEncryptedOfflineSyncFile,
|
|
17
|
-
plainFileDigest,
|
|
18
|
-
readPlainOfflineSyncFileChunk,
|
|
19
|
-
setSafeFileMtime,
|
|
20
|
-
sha256OfflineSyncFile,
|
|
21
|
-
shouldExcludeOfflineSyncFile
|
|
22
|
-
} from "./chunk-GY4R4P2A.js";
|
|
11
|
+
} from "./chunk-WRDFYSUC.js";
|
|
23
12
|
import {
|
|
24
13
|
applyChangesetEmbeddingGenerations,
|
|
25
14
|
normalizeChangesetGenerations
|
|
26
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-U7HJBODU.js";
|
|
27
16
|
import {
|
|
28
17
|
applyIncomingEmbeddingGenerations,
|
|
29
18
|
assertEmbeddingGenerationStillIncluded,
|
|
@@ -32,22 +21,33 @@ import {
|
|
|
32
21
|
embeddingGenerationMembership,
|
|
33
22
|
isEmbeddingGenerationDirPath,
|
|
34
23
|
isPathInOmittedEmbeddingGeneration
|
|
35
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-I73ZSORJ.js";
|
|
36
25
|
import {
|
|
37
26
|
isEmbeddingGenerationMarkerPath,
|
|
38
27
|
matchesOfflineSyncDefaultExclude
|
|
39
28
|
} from "./chunk-63LFM77Z.js";
|
|
40
29
|
import {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
30
|
+
OFFLINE_SYNC_FAST_BASE_MTIME_TOLERANCE_MS,
|
|
31
|
+
SYNC_INTERNAL_DIR,
|
|
32
|
+
assertOfflineSyncMtimeMs,
|
|
33
|
+
isEncryptedOfflineSyncFile,
|
|
34
|
+
plainFileDigest,
|
|
35
|
+
readPlainOfflineSyncFileChunk,
|
|
36
|
+
setSafeFileMtime,
|
|
37
|
+
sha256OfflineSyncFile,
|
|
38
|
+
shouldExcludeOfflineSyncFile
|
|
39
|
+
} from "./chunk-CUWOSRCS.js";
|
|
45
40
|
import {
|
|
46
41
|
prepareSafeArchiveRoot,
|
|
47
42
|
resolveSafeArchiveTarget,
|
|
48
43
|
sha256Bytes,
|
|
49
44
|
validateArchiveRelativePath
|
|
50
45
|
} from "./chunk-GCGJW34D.js";
|
|
46
|
+
import {
|
|
47
|
+
isCanonicalRuntimeStatePath,
|
|
48
|
+
shouldDeleteAbsentIncomingOfflineRuntimeFile,
|
|
49
|
+
shouldPreferIncomingOfflineRuntimeFile
|
|
50
|
+
} from "./chunk-LDWE7OZS.js";
|
|
51
51
|
import {
|
|
52
52
|
withEmbeddingGenerationLock,
|
|
53
53
|
withEmbeddingGenerationLockIter
|
|
@@ -1792,4 +1792,4 @@ export {
|
|
|
1792
1792
|
normalizeOfflineSyncState,
|
|
1793
1793
|
fileStatesFromSnapshot
|
|
1794
1794
|
};
|
|
1795
|
-
//# sourceMappingURL=chunk-
|
|
1795
|
+
//# sourceMappingURL=chunk-TVIGN3XZ.js.map
|
|
@@ -532,6 +532,7 @@ function isEncryptableStoragePath(filePath, rootDir) {
|
|
|
532
532
|
const normalized = normalizeStorageRelativePath(rel);
|
|
533
533
|
if (normalized === "profile.md") return true;
|
|
534
534
|
if (isEncryptableStateSidecar(normalized)) return true;
|
|
535
|
+
if (isEncryptableBufferSessionFile(normalized)) return true;
|
|
535
536
|
if (isEncryptableSummarySidecar(normalized)) return true;
|
|
536
537
|
const firstSegment = normalized.split("/", 1)[0];
|
|
537
538
|
return ENCRYPTABLE_MARKDOWN_STORAGE_ROOTS.has(firstSegment) && normalized.endsWith(".md");
|
|
@@ -578,6 +579,9 @@ var ENCRYPTABLE_STATE_SIDECARS = /* @__PURE__ */ new Set([
|
|
|
578
579
|
function isEncryptableStateSidecar(normalized) {
|
|
579
580
|
return ENCRYPTABLE_STATE_SIDECARS.has(normalized);
|
|
580
581
|
}
|
|
582
|
+
function isEncryptableBufferSessionFile(normalized) {
|
|
583
|
+
return normalized.startsWith("state/buffer/") && normalized.endsWith(".json");
|
|
584
|
+
}
|
|
581
585
|
function isEncryptableSummarySidecar(normalized) {
|
|
582
586
|
return normalized.startsWith("summaries/") && normalized.endsWith(".json");
|
|
583
587
|
}
|
|
@@ -604,4 +608,4 @@ export {
|
|
|
604
608
|
migrateMemoryDirToEncrypted,
|
|
605
609
|
decryptMemoryDirToPlaintext
|
|
606
610
|
};
|
|
607
|
-
//# sourceMappingURL=chunk-
|
|
611
|
+
//# sourceMappingURL=chunk-TYGLV6ZG.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/secure-store/secure-fs.ts","../src/secure-store/entity-file-read.ts"],"sourcesContent":["/**\n * Transparent file-level encryption for the secure-store module.\n *\n * Issue #690 (PR 3/4) — storage.ts integration layer.\n *\n * This module sits between the raw filesystem and StorageManager.\n * Every memory file is either:\n * - a plain UTF-8 text file (legacy, back-compat), or\n * - a REMNIC-ENC sealed file (AES-256-GCM, see format below).\n *\n * On-disk format\n * --------------\n * Encrypted files begin with a 9-byte magic header:\n *\n * REMNIC-ENC (7 ASCII bytes)\n * VER (1 byte, currently 0x01)\n * FLAGS (1 byte, reserved, must be 0x00)\n *\n * Followed immediately by a `seal()` envelope from `cipher.ts`:\n *\n * [VERSION:1][SALT:16][IV:12][AUTHTAG:16][CIPHERTEXT:...]\n *\n * The magic header makes encrypted files sniffable without attempting\n * a full `open()` call and gives operators a clear signal that the\n * file cannot be read by opening it in an editor.\n *\n * AAD\n * ---\n * The file path relative to the memory root is bound as Associated\n * Authenticated Data (AAD) on both encrypt and decrypt. This means\n * moving or renaming an encrypted file without re-encrypting it will\n * cause auth-tag failure on the next read — the file is tied to its\n * path. Callers that move files must re-encrypt them.\n *\n * Back-compat\n * -----------\n * `readMaybeEncryptedFile` transparently handles both formats: if the\n * file does NOT start with the magic bytes, it is returned as-is (plain\n * text). This lets an operator migrate incrementally: newly-written\n * files are encrypted while existing files continue to be read in plain\n * form until `migrateMemoryDirToEncrypted` is run.\n *\n * Naming: `secure-fs.ts` (not `vault-fs.ts`) — see `kdf.ts` naming note.\n */\n\nimport { createCipheriv, createDecipheriv, randomBytes, randomUUID } from \"node:crypto\";\nimport { lstat, mkdir, open as openFile, readFile, readdir, rename, unlink, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport {\n AUTH_TAG_LENGTH,\n ENVELOPE_HEADER_SIZE,\n ENVELOPE_LAYOUT,\n ENVELOPE_SALT_LENGTH,\n ENVELOPE_VERSION,\n IV_LENGTH,\n generateSalt,\n open as openEnvelope,\n parseEnvelope,\n seal,\n} from \"./cipher.js\";\nimport { RECALL_FALLBACK_DIRS } from \"../utils/category-dir.js\";\nimport { isErrnoCode } from \"../utils/errno.js\";\nimport { readStoreFileBufferTolerant as readStoreFileBuffer } from \"./entity-file-read.js\";\n\n// ---------------------------------------------------------------------------\n// Error classes\n// ---------------------------------------------------------------------------\n\n/**\n * Thrown when a read is attempted but the keyring entry for this\n * store is absent (i.e. `secure-store unlock` has not been run\n * since the last daemon start).\n */\nexport class SecureStoreLockedError extends Error {\n constructor(message = \"secure-store is locked — run `remnic secure-store unlock` to decrypt\") {\n super(message);\n this.name = \"SecureStoreLockedError\";\n }\n}\n\n/**\n * Thrown when `open()` fails because the auth tag does not validate.\n * This covers both wrong-key and tampered-ciphertext scenarios —\n * intentionally indistinguishable from the caller's perspective.\n */\nexport class SecureStoreDecryptError extends Error {\n constructor(message = \"secure-store decryption failed — wrong key or tampered ciphertext\") {\n super(message);\n this.name = \"SecureStoreDecryptError\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Magic header\n// ---------------------------------------------------------------------------\n\n/** Magic bytes: the ASCII string \"REMNIC-ENC\" (10 bytes). */\nexport const MAGIC_BYTES = Buffer.from(\"REMNIC-ENC\", \"ascii\");\n\n/** Current on-disk version byte. */\nexport const FILE_FORMAT_VERSION = 0x01;\n\n/** Reserved flags byte — must be 0x00. */\nexport const FILE_FORMAT_FLAGS = 0x00;\n\n/** Total size of the magic header prefix (magic + version + flags). */\nexport const MAGIC_HEADER_SIZE = MAGIC_BYTES.length + 2; // 12 bytes\n\n/**\n * Exact, deterministic bytes `writeMaybeEncryptedFile` adds to an encrypted\n * file beyond its plaintext body: the magic header prefix plus the fixed\n * envelope header (version + salt + IV + auth tag). AES-256-GCM ciphertext\n * matches the plaintext length and the state-file write path applies no\n * compression, so an encrypted state file is exactly `plaintext +\n * SECURE_STORE_ENVELOPE_OVERHEAD_BYTES` on disk. Compaction reserves this so an\n * encrypted ledger lands strictly below the reader's whole-file decrypt cap\n * (issue #2033).\n */\nexport const SECURE_STORE_ENVELOPE_OVERHEAD_BYTES = MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE;\n\n// ---------------------------------------------------------------------------\n// Detection\n// ---------------------------------------------------------------------------\n\n/**\n * Return true iff `buf` begins with the REMNIC-ENC magic header.\n * Does not validate the envelope; just identifies the format.\n */\nexport function isEncryptedFile(buf: Uint8Array): boolean {\n if (buf.length < MAGIC_HEADER_SIZE) return false;\n const b = Buffer.isBuffer(buf) ? buf : Buffer.from(buf);\n return b.subarray(0, MAGIC_BYTES.length).equals(MAGIC_BYTES);\n}\n\ntype PositionedFileReader = {\n read: (buffer: Buffer, offset: number, length: number, position: number) => Promise<{ bytesRead: number }>;\n};\n\nasync function readPositionedPrefix(handle: PositionedFileReader, buffer: Buffer): Promise<number> {\n let bytesRead = 0;\n while (bytesRead < buffer.length) {\n const result = await handle.read(buffer, bytesRead, buffer.length - bytesRead, bytesRead);\n if (result.bytesRead === 0) break;\n bytesRead += result.bytesRead;\n }\n return bytesRead;\n}\n\n/**\n * True when the REGULAR file at `probePath` is encrypted at rest (its first\n * bytes are the REMNIC-ENC magic header). ENOENT resolves to false — an absent\n * file has nothing to probe. A symlink, FIFO, device, or any other non-regular\n * path is REFUSED with a throw rather than opened: opening a FIFO blocks until a\n * writer appears and a symlink would redirect the probe outside our tree. The\n * ledgers and per-event spills we manage are always regular files, so a\n * non-regular path is an anomaly the caller must treat as unsafe, never follow\n * (#2033). Shared by the CLI ledger recovery probe and the auto-compaction probe\n * so both enforce identical open safety.\n */\nexport async function probeEncryptedRegularFileHeader(probePath: string): Promise<boolean> {\n let entryStat;\n try {\n entryStat = await lstat(probePath);\n } catch (err) {\n if (isErrnoCode(err, \"ENOENT\")) return false;\n throw err;\n }\n if (!entryStat.isFile()) {\n throw new Error(\n `refusing to probe non-regular state path ${probePath} ` +\n `(symlink/FIFO/device); opening it could block or escape containment (#2033)`\n );\n }\n const handle = await openFile(probePath, \"r\");\n try {\n const header = Buffer.alloc(MAGIC_HEADER_SIZE);\n const bytesRead = await readPositionedPrefix(handle, header);\n return isEncryptedFile(header.subarray(0, bytesRead));\n } finally {\n await handle.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Encrypt / decrypt file body\n// ---------------------------------------------------------------------------\n\n/**\n * Encrypt `plain` (UTF-8 content of a memory file) and return a\n * Buffer ready to write to disk.\n *\n * @param plain Plain-text file content (UTF-8 string or Buffer).\n * @param key 32-byte AES-256 key from the keyring.\n * @param aad Optional associated data — defaults to empty if omitted.\n * Callers should pass the file path relative to memoryDir\n * so the ciphertext is bound to its location.\n */\nexport function encryptFileBody(plain: string | Buffer, key: Buffer, aad?: Buffer): Buffer {\n const plainBuf = typeof plain === \"string\" ? Buffer.from(plain, \"utf8\") : plain;\n const salt = generateSalt();\n const envelope = seal(key, salt, plainBuf, aad ? { aad } : {});\n\n const header = Buffer.alloc(MAGIC_HEADER_SIZE);\n MAGIC_BYTES.copy(header, 0);\n header.writeUInt8(FILE_FORMAT_VERSION, MAGIC_BYTES.length);\n header.writeUInt8(FILE_FORMAT_FLAGS, MAGIC_BYTES.length + 1);\n\n return Buffer.concat([header, envelope]);\n}\n\n/**\n * Decrypt a buffer produced by `encryptFileBody` and return the\n * original UTF-8 content.\n *\n * Throws `SecureStoreDecryptError` on auth failure (wrong key or\n * tampered ciphertext). Throws a plain `Error` for structural problems\n * (truncated buffer, wrong magic, unsupported version).\n */\nexport function decryptFileBody(buf: Buffer, key: Buffer, aad?: Buffer): Buffer {\n if (!isEncryptedFile(buf)) {\n throw new Error(\"decryptFileBody: buffer does not start with REMNIC-ENC magic header\");\n }\n const version = buf.readUInt8(MAGIC_BYTES.length);\n if (version !== FILE_FORMAT_VERSION) {\n throw new Error(\n `decryptFileBody: unsupported file format version ${version} (this build supports ${FILE_FORMAT_VERSION})`\n );\n }\n const flags = buf.readUInt8(MAGIC_BYTES.length + 1);\n if (flags !== FILE_FORMAT_FLAGS) {\n throw new Error(`decryptFileBody: unknown flags byte 0x${flags.toString(16).padStart(2, \"0\")}`);\n }\n const envelope = buf.subarray(MAGIC_HEADER_SIZE);\n parseEnvelope(envelope);\n try {\n return openEnvelope(key, envelope, aad ? { aad } : {});\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new SecureStoreDecryptError(`secure-store decryption failed: ${msg}`);\n }\n}\n\nfunction buildHeaderAad(salt: Uint8Array): Buffer {\n const out = Buffer.alloc(1 + ENVELOPE_SALT_LENGTH);\n out.writeUInt8(ENVELOPE_VERSION, 0);\n Buffer.from(salt).copy(out, 1);\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Path → AAD helper\n// ---------------------------------------------------------------------------\n\n/**\n * Build the AAD buffer for a file at `filePath` relative to\n * `memoryDir`. The AAD binds the ciphertext to its path so a\n * file cannot be silently relocated without re-encryption.\n *\n * When `memoryDir` is supplied and `filePath` is absolute, the\n * relative sub-path is used. Otherwise `filePath` is used verbatim.\n */\nexport function filePathAad(filePath: string, memoryDir?: string): Buffer {\n let rel = filePath;\n if (memoryDir && path.isAbsolute(filePath)) {\n rel = path.relative(memoryDir, filePath);\n }\n return Buffer.from(rel, \"utf8\");\n}\n\n// ---------------------------------------------------------------------------\n// High-level read / write helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Read a file from `filePath`.\n *\n * - If the file is plaintext (no magic header), return its content\n * as-is — back-compat with unencrypted stores.\n * - If the file is encrypted AND `key` is provided, decrypt and return\n * the plaintext content.\n * - If the file is encrypted AND `key` is null, throw\n * `SecureStoreLockedError`.\n *\n * @param filePath Absolute path to the file.\n * @param key 32-byte AES-256 key, or null when the store is locked.\n * @param memoryDir Memory root for path-bound AAD. Should be absolute.\n */\nexport async function readMaybeEncryptedFileBuffer(\n filePath: string,\n key: Buffer | null,\n memoryDir?: string,\n opts?: { aadFilePath?: string }\n): Promise<Buffer> {\n const buf = memoryDir ? await readStoreFileBuffer(filePath, memoryDir) : await readFile(filePath);\n if (!isEncryptedFile(buf)) {\n // Plain file — legacy or unencrypted store.\n return buf;\n }\n // Encrypted — key required.\n if (key === null) {\n throw new SecureStoreLockedError(\n `secure-store is locked — cannot read encrypted file at ${filePath}. Run \\`remnic secure-store unlock\\` to decrypt.`\n );\n }\n return decryptFileBodyForPath(buf, key, filePath, memoryDir, opts?.aadFilePath);\n}\n\nexport async function readMaybeEncryptedFile(\n filePath: string,\n key: Buffer | null,\n memoryDir?: string\n): Promise<string> {\n return (await readMaybeEncryptedFileBuffer(filePath, key, memoryDir)).toString(\"utf8\");\n}\n\nexport interface WriteMaybeEncryptedFileOptions {\n /**\n * File mode bits. Default 0o600 (owner read/write only).\n * Applied only on create; existing files inherit their existing mode.\n */\n mode?: number;\n /**\n * If true, write atomically via a temp file + rename (CLAUDE.md gotcha #54).\n * Default true.\n */\n atomic?: boolean;\n /**\n * ABSOLUTE final path whose canonical AAD (via {@link filePathAad})\n * replaces the physical `filePath`'s. Used by the offline-sync generation\n * transaction: staged ciphertext physically lives under a staging\n * directory but must decrypt at its FINAL published path after the\n * staging directory is swapped into place by rename. Deriving the AAD\n * from the absolute final path through the SAME filePathAad logic as\n * ordinary reads keeps write and read derivations identical on every\n * platform (POSIX and Windows separators alike). (issue #3148, round 4/5)\n */\n aadFilePath?: string;\n}\n\n/**\n * Write `content` to `filePath`.\n *\n * - If `key` is provided and non-null, encrypt the content first.\n * - If `key` is null, write the content as plain UTF-8 (unencrypted store).\n *\n * Writes atomically: content is written to a unique temp file\n * first, then renamed into place (CLAUDE.md gotcha #54 — never delete\n * before write).\n */\nexport async function writeMaybeEncryptedFile(\n filePath: string,\n content: string | Buffer,\n key: Buffer | null,\n options: WriteMaybeEncryptedFileOptions = {},\n memoryDir?: string\n): Promise<void> {\n const { mode = 0o600, atomic = true, aadFilePath } = options;\n await mkdir(path.dirname(filePath), { recursive: true });\n\n let data: Buffer | string;\n if (key !== null) {\n const aad = aadFilePath !== undefined ? filePathAad(aadFilePath, memoryDir) : filePathAad(filePath, memoryDir);\n data = encryptFileBody(content, key, aad);\n } else {\n data = content;\n }\n\n if (atomic) {\n const tempPath = uniqueAtomicTempPath(filePath, \"tmp\");\n try {\n await writeFile(tempPath, data, { mode });\n await rename(tempPath, filePath);\n } catch (err) {\n // Best-effort cleanup of the temp file.\n try {\n await unlink(tempPath);\n } catch {\n // ignore\n }\n throw err;\n }\n } else {\n await writeFile(filePath, data, { mode });\n }\n}\n\nexport async function writeMaybeEncryptedFileFromChunks(\n filePath: string,\n chunks: AsyncIterable<Buffer>,\n key: Buffer | null,\n options: WriteMaybeEncryptedFileOptions = {},\n memoryDir?: string\n): Promise<void> {\n const { mode = 0o600, atomic = true } = options;\n await mkdir(path.dirname(filePath), { recursive: true });\n const writePath = atomic ? `${filePath}.tmp-${process.pid}-${Date.now()}` : filePath;\n let completed = false;\n try {\n const handle = await openFile(writePath, \"w\", mode);\n try {\n if (key !== null) {\n const salt = generateSalt();\n const iv = randomBytes(IV_LENGTH);\n const header = Buffer.alloc(MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE);\n MAGIC_BYTES.copy(header, 0);\n header.writeUInt8(FILE_FORMAT_VERSION, MAGIC_BYTES.length);\n header.writeUInt8(FILE_FORMAT_FLAGS, MAGIC_BYTES.length + 1);\n const envelopeOffset = MAGIC_HEADER_SIZE;\n header.writeUInt8(ENVELOPE_VERSION, envelopeOffset + ENVELOPE_LAYOUT.version);\n salt.copy(header, envelopeOffset + ENVELOPE_LAYOUT.salt);\n iv.copy(header, envelopeOffset + ENVELOPE_LAYOUT.iv);\n await handle.write(header);\n\n const cipher = createCipheriv(\"aes-256-gcm\", key, iv, { authTagLength: AUTH_TAG_LENGTH });\n const aad = filePathAad(filePath, memoryDir);\n cipher.setAAD(Buffer.concat([buildHeaderAad(salt), aad]));\n for await (const chunk of chunks) {\n if (chunk.length === 0) continue;\n const encrypted = cipher.update(chunk);\n if (encrypted.length > 0) await handle.write(encrypted);\n }\n const final = cipher.final();\n if (final.length > 0) await handle.write(final);\n const authTag = cipher.getAuthTag();\n await handle.write(authTag, 0, authTag.length, MAGIC_HEADER_SIZE + ENVELOPE_LAYOUT.authTag);\n } else {\n for await (const chunk of chunks) {\n if (chunk.length > 0) await handle.write(chunk);\n }\n }\n } finally {\n await handle.close();\n }\n if (atomic) {\n await rename(writePath, filePath);\n }\n completed = true;\n } finally {\n if (!completed && atomic) {\n await unlink(writePath).catch(() => {});\n }\n }\n}\n\n/**\n * Stream a file through the secure-store reader without buffering the whole\n * payload. Plaintext files are passed through unchanged; encrypted files are\n * authenticated and decrypted incrementally.\n */\nexport async function* readMaybeEncryptedFileFromChunks(\n filePath: string,\n key: Buffer | null,\n memoryDir?: string,\n chunkSize = 64 * 1024\n): AsyncIterable<Buffer> {\n const handle = await openFile(filePath, \"r\");\n const headerSize = MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE;\n try {\n const prefix = Buffer.alloc(headerSize);\n const prefixBytes = await readPositionedPrefix(handle, prefix);\n const prefixView = prefix.subarray(0, prefixBytes);\n if (!isEncryptedFile(prefixView)) {\n if (prefixBytes > 0) yield Buffer.from(prefixView);\n let position = prefixBytes;\n const buffer = Buffer.alloc(chunkSize);\n for (;;) {\n const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);\n if (bytesRead === 0) break;\n position += bytesRead;\n yield Buffer.from(buffer.subarray(0, bytesRead));\n }\n return;\n }\n if (prefixBytes < headerSize) {\n throw new Error(`encrypted file header is truncated: ${filePath}`);\n }\n if (key === null) {\n throw new SecureStoreLockedError(\n `secure-store is locked — cannot read encrypted file at ${filePath}. ` +\n \"Run `remnic secure-store unlock` to decrypt.\"\n );\n }\n const envelope = prefix.subarray(MAGIC_HEADER_SIZE);\n const version = envelope.readUInt8(ENVELOPE_LAYOUT.version);\n if (version !== ENVELOPE_VERSION) {\n throw new Error(`secure-store: unsupported envelope version ${version}`);\n }\n const salt = envelope.subarray(ENVELOPE_LAYOUT.salt, ENVELOPE_LAYOUT.salt + ENVELOPE_SALT_LENGTH);\n const iv = envelope.subarray(ENVELOPE_LAYOUT.iv, ENVELOPE_LAYOUT.iv + IV_LENGTH);\n const authTag = envelope.subarray(ENVELOPE_LAYOUT.authTag, ENVELOPE_LAYOUT.authTag + AUTH_TAG_LENGTH);\n const decipher = createDecipheriv(\"aes-256-gcm\", key, iv, { authTagLength: AUTH_TAG_LENGTH });\n decipher.setAAD(Buffer.concat([buildHeaderAad(salt), filePathAad(filePath, memoryDir)]));\n decipher.setAuthTag(authTag);\n let position = headerSize;\n const buffer = Buffer.alloc(chunkSize);\n try {\n for (;;) {\n const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);\n if (bytesRead === 0) break;\n position += bytesRead;\n const plain = decipher.update(buffer.subarray(0, bytesRead));\n if (plain.length > 0) yield plain;\n }\n const final = decipher.final();\n if (final.length > 0) yield final;\n } catch (err) {\n if (err instanceof SecureStoreDecryptError) throw err;\n const message = err instanceof Error ? err.message : String(err);\n throw new SecureStoreDecryptError(`secure-store decryption failed: ${message}`);\n }\n } finally {\n await handle.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Migration\n// ---------------------------------------------------------------------------\n\nexport interface MigrateResult {\n /** Number of files successfully encrypted. */\n encrypted: number;\n /** Number of files already encrypted (skipped). */\n skipped: number;\n /** Files that failed to encrypt (path → error message). */\n errors: Array<{ filePath: string; error: string }>;\n}\n\nexport interface DecryptResult {\n /** Number of files successfully decrypted back to plaintext. */\n decrypted: number;\n /** Number of files already plaintext (skipped). */\n skipped: number;\n /** Files that failed to decrypt (path → error message). */\n errors: Array<{ filePath: string; error: string }>;\n}\n\n/**\n * Walk `dir` recursively, find encryptable storage-managed files that are not\n * yet encrypted, and re-write them as encrypted files under `key`.\n *\n * Safety rules per CLAUDE.md gotchas #54 and #25:\n * 1. A page-version snapshot is taken (via `createVersion`) BEFORE\n * each overwrite so the plaintext version is preserved in history.\n * Since this module has no direct access to `page-versioning.ts`\n * internals, callers who have page-versioning configured should\n * pass `onBeforeEncrypt` to take the snapshot.\n * 2. The new encrypted content is written to a temp file first,\n * then renamed atomically — never deleted before written.\n * 3. If encryption of any file fails, the error is recorded and the\n * original file is left intact (partial migration is safe).\n *\n * @param dir Absolute path to the memory directory.\n * @param key 32-byte AES-256 key.\n * @param onBeforeEncrypt Optional callback invoked before encrypting\n * each file. Can be used to take page-version\n * snapshots. Errors here are non-fatal.\n */\nexport async function migrateMemoryDirToEncrypted(\n dir: string,\n key: Buffer,\n onBeforeEncrypt?: (filePath: string) => Promise<void>\n): Promise<MigrateResult> {\n const result: MigrateResult = { encrypted: 0, skipped: 0, errors: [] };\n\n const files = await collectEncryptableStorageFiles(dir);\n for (const filePath of files) {\n try {\n const buf = await readFile(filePath);\n if (isEncryptedFile(buf)) {\n result.skipped++;\n continue;\n }\n // Call optional pre-encryption hook (e.g. page-version snapshot).\n if (onBeforeEncrypt) {\n try {\n await onBeforeEncrypt(filePath);\n } catch {\n // Non-fatal — continue with encryption even if snapshot fails.\n }\n }\n const content = buf.toString(\"utf8\");\n const aad = filePathAad(filePath, dir);\n const encrypted = encryptFileBody(content, key, aad);\n\n // Atomic write: temp → rename (gotcha #54).\n const tempPath = uniqueAtomicTempPath(filePath, \"enc-tmp\");\n try {\n await writeFile(tempPath, encrypted, { mode: 0o600 });\n await rename(tempPath, filePath);\n result.encrypted++;\n } catch (writeErr) {\n // Clean up temp file, leave original intact.\n try {\n const { unlink } = await import(\"node:fs/promises\");\n await unlink(tempPath);\n } catch {\n // ignore\n }\n throw writeErr;\n }\n } catch (err) {\n result.errors.push({\n filePath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return result;\n}\n\n/**\n * Walk `dir` recursively, find storage-managed encrypted files, and\n * re-write them as plaintext under the same paths.\n *\n * This is the reversible counterpart to {@link migrateMemoryDirToEncrypted}.\n * It only touches files under the same storage-managed roots, skips\n * plaintext files, skips symlinks, excludes `.secure-store/`, and writes\n * each plaintext replacement via temp-file + rename so a per-file failure\n * leaves the ciphertext intact.\n */\nexport async function decryptMemoryDirToPlaintext(dir: string, key: Buffer): Promise<DecryptResult> {\n const result: DecryptResult = { decrypted: 0, skipped: 0, errors: [] };\n\n const files = await collectStorageManagedFiles(dir, isDecryptableStoragePath);\n for (const filePath of files) {\n try {\n const buf = await readFile(filePath);\n if (!isEncryptedFile(buf)) {\n result.skipped++;\n continue;\n }\n\n const plaintext = decryptFileBodyForPath(buf, key, filePath, dir);\n const tempPath = uniqueAtomicTempPath(filePath, \"dec-tmp\");\n try {\n await writeFile(tempPath, plaintext, { mode: 0o600 });\n await rename(tempPath, filePath);\n result.decrypted++;\n } catch (writeErr) {\n try {\n await unlink(tempPath);\n } catch {\n // ignore cleanup errors; original ciphertext is still intact.\n }\n throw writeErr;\n }\n } catch (err) {\n result.errors.push({\n filePath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction uniqueAtomicTempPath(filePath: string, label: string): string {\n return `${filePath}.${label}-${process.pid}-${Date.now()}-${randomUUID()}`;\n}\n\nfunction decryptFileBodyForPath(\n buf: Buffer,\n key: Buffer,\n filePath: string,\n memoryDir?: string,\n aadFilePath?: string\n): Buffer {\n // A canonical-AAD stage read (offline-sync generation transaction) binds\n // the FINAL published path, not the physical staging path.\n const aad = aadFilePath !== undefined ? filePathAad(aadFilePath, memoryDir) : filePathAad(filePath, memoryDir);\n try {\n return decryptFileBody(buf, key, aad);\n } catch (err) {\n if (!(err instanceof SecureStoreDecryptError)) {\n throw err;\n }\n const legacyRoot = legacyNamespaceAadRootForFile(filePath, memoryDir);\n if (legacyRoot) {\n try {\n return decryptFileBody(buf, key, filePathAad(filePath, legacyRoot));\n } catch {\n // Fall through to the namespace-reader fallback below.\n }\n }\n\n const topLevelRoot = topLevelAadRootForNamespaceReader(filePath, memoryDir);\n if (topLevelRoot) {\n try {\n return decryptFileBody(buf, key, filePathAad(filePath, topLevelRoot));\n } catch {\n // Preserve the caller-facing error from the canonical decrypt attempt.\n }\n }\n\n throw err;\n }\n}\n\nfunction topLevelAadRootForNamespaceReader(filePath: string, memoryDir?: string): string | null {\n if (!memoryDir || !path.isAbsolute(filePath)) return null;\n const resolvedMemoryDir = path.resolve(memoryDir);\n const rel = path.relative(resolvedMemoryDir, filePath);\n if (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) return null;\n const parts = resolvedMemoryDir.split(path.sep);\n if (parts.length < 3 || parts.at(-2) !== \"namespaces\" || !parts.at(-1)) return null;\n const topLevelRoot = parts.slice(0, -2).join(path.sep) || path.sep;\n const topRel = path.relative(topLevelRoot, filePath);\n if (topRel === \"\" || topRel.startsWith(\"..\") || path.isAbsolute(topRel)) {\n return null;\n }\n const topParts = topRel.split(path.sep);\n if (topParts[0] !== \"namespaces\" || topParts[1] !== parts.at(-1)) {\n return null;\n }\n return topLevelRoot;\n}\n\nfunction legacyNamespaceAadRootForFile(filePath: string, memoryDir?: string): string | null {\n if (!memoryDir || !path.isAbsolute(filePath)) return null;\n const rel = path.relative(memoryDir, filePath);\n if (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) return null;\n const parts = rel.split(path.sep);\n if (parts[0] === \"namespaces\" && parts.length >= 3 && parts[1]) {\n return path.join(memoryDir, \"namespaces\", parts[1]);\n }\n return null;\n}\n\n/**\n * Recursively collect files under `dir` that are read through the\n * storage-layer secure-store helpers, excluding symlinked entries and\n * `.secure-store/` metadata.\n */\nasync function collectEncryptableStorageFiles(dir: string, rootDir = dir): Promise<string[]> {\n return collectStorageManagedFiles(dir, isEncryptableStoragePath, rootDir);\n}\n\n/**\n * Recursively collect regular files under storage-managed roots, excluding\n * symlinked entries and `.secure-store/` metadata. This broader collector is\n * used by the decrypt/disable path so future encrypted sidecars can be\n * restored without requiring extension-specific logic.\n */\nasync function collectStorageManagedFiles(\n dir: string,\n includeFile: (filePath: string, rootDir: string) => boolean,\n rootDir = dir\n): Promise<string[]> {\n const results: string[] = [];\n let scanDir = dir;\n let scanRootDir = rootDir;\n if (path.resolve(dir) === path.resolve(rootDir)) {\n const normalizedRoot = await resolveStorageManagedRootForScan(dir);\n if (!normalizedRoot) return results;\n scanDir = normalizedRoot;\n scanRootDir = normalizedRoot;\n }\n let names: string[];\n try {\n names = await readdir(scanDir, { encoding: \"utf8\" });\n } catch {\n return results;\n }\n for (const name of names) {\n if (name.startsWith(\".secure-store\")) continue;\n const full = path.join(scanDir, name);\n let isDir = false;\n let isFile = false;\n try {\n const s = await lstat(full);\n if (s.isSymbolicLink()) continue;\n isDir = s.isDirectory();\n isFile = s.isFile();\n } catch {\n continue;\n }\n if (isDir) {\n const sub = await collectStorageManagedFiles(full, includeFile, scanRootDir);\n results.push(...sub);\n } else if (isFile && includeFile(full, scanRootDir)) {\n results.push(full);\n }\n }\n return results;\n}\n\nasync function resolveStorageManagedRootForScan(dir: string): Promise<string | null> {\n const lstatPath = normalizePathForLstat(dir);\n let stat: Awaited<ReturnType<typeof lstat>>;\n try {\n stat = await lstat(lstatPath);\n } catch (error) {\n if (isFsErrorWithCode(error, \"ENOENT\")) return null;\n throw error;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`secure-store migration root must not be a symlink: ${dir}`);\n }\n if (!stat.isDirectory()) {\n throw new Error(`secure-store migration root must be a directory: ${dir}`);\n }\n return lstatPath;\n}\n\nfunction normalizePathForLstat(filePath: string): string {\n return stripTrailingPathSeparators(path.normalize(filePath));\n}\n\nfunction isFsErrorWithCode(error: unknown, code: string): boolean {\n return typeof error === \"object\" && error !== null && (error as { code?: unknown }).code === code;\n}\n\nfunction stripTrailingPathSeparators(filePath: string): string {\n const root = path.parse(filePath).root;\n let end = filePath.length;\n while (\n end > root.length &&\n (filePath[end - 1] === path.sep || filePath[end - 1] === path.posix.sep || filePath[end - 1] === path.win32.sep)\n ) {\n end -= 1;\n }\n return end === filePath.length ? filePath : filePath.slice(0, end);\n}\n\nfunction isEncryptableStoragePath(filePath: string, rootDir: string): boolean {\n const rel = path.relative(rootDir, filePath);\n if (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) return false;\n const normalized = normalizeStorageRelativePath(rel);\n if (normalized === \"profile.md\") return true;\n if (isEncryptableStateSidecar(normalized)) return true;\n if (isEncryptableSummarySidecar(normalized)) return true;\n const firstSegment = normalized.split(\"/\", 1)[0];\n return ENCRYPTABLE_MARKDOWN_STORAGE_ROOTS.has(firstSegment) && normalized.endsWith(\".md\");\n}\n\nfunction isDecryptableStoragePath(filePath: string, rootDir: string): boolean {\n if (isEncryptableStoragePath(filePath, rootDir)) return true;\n const rel = path.relative(rootDir, filePath);\n if (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) return false;\n const normalized = normalizeStorageRelativePath(rel);\n const firstSegment = normalized.split(\"/\", 1)[0];\n return DECRYPTABLE_SIDECAR_ROOTS.has(firstSegment);\n}\n\nfunction normalizeStorageRelativePath(rel: string): string {\n const normalized = rel.split(path.sep).join(\"/\");\n const parts = normalized.split(\"/\");\n if (parts[0] === \"namespaces\" && parts.length >= 3) {\n return parts.slice(2).join(\"/\");\n }\n return normalized;\n}\n\n// Every recall MEMORY category directory (facts/ + the CATEGORY_DIR_MAP memory\n// dirs) must be encryptable at rest — #1546 routes decision/preference/moment/...\n// memories into their own dirs, so hardcoding only facts/corrections/procedures/\n// reasoning-traces would silently write those categories in plaintext on\n// encrypted stores. RECALL_FALLBACK_DIRS is the single source of truth for that\n// set; the extra non-category markdown roots (artifacts/archive/entities/\n// identity) are appended.\n//\n// questions/ is deliberately EXCLUDED (RECALL_FALLBACK_DIRS omits the non-memory\n// queue dirs): the question queue is written/resolved through plain readFile/\n// writeFile (StorageManager.writeQuestion/resolveQuestion), NOT the secure-file\n// helpers. Encrypting questions/ here would make migration seal those files,\n// after which resolveQuestion() would read ciphertext as UTF-8, fail to update\n// the frontmatter, and overwrite/corrupt the file while returning success\n// (codex #1563 review). Keeping questions/ plaintext preserves the queue's\n// existing behavior; #1546 does not change question encryption.\nconst ENCRYPTABLE_MARKDOWN_STORAGE_ROOTS = new Set<string>([\n ...RECALL_FALLBACK_DIRS,\n \"artifacts\",\n \"archive\",\n \"entities\",\n \"identity\",\n]);\n\nconst ENCRYPTABLE_STATE_SIDECARS = new Set([\n \"state/behavior-signals.jsonl\",\n \"state/buffer-surprise-ledger.jsonl\",\n \"state/buffer.json\",\n \"state/compression-guideline-draft-state.json\",\n \"state/compression-guideline-state.json\",\n \"state/compression-guidelines.draft.md\",\n \"state/compression-guidelines.md\",\n \"state/entity-synthesis-queue.json\",\n \"state/fact-hashes.txt\",\n \"state/memory-actions.jsonl\",\n \"state/memory-lifecycle-ledger.jsonl\",\n \"state/meta.json\",\n \"state/reextract-jobs.jsonl\",\n \"state/topics.json\",\n]);\n\nfunction isEncryptableStateSidecar(normalized: string): boolean {\n return ENCRYPTABLE_STATE_SIDECARS.has(normalized);\n}\n\nfunction isEncryptableSummarySidecar(normalized: string): boolean {\n return normalized.startsWith(\"summaries/\") && normalized.endsWith(\".json\");\n}\n\nconst DECRYPTABLE_SIDECAR_ROOTS = new Set([\"state\", \"indexes\", \"index\", \"provenance\"]);\n","import { constants } from \"node:fs\";\nimport { lstat, open, readFile, realpath } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { assertPathInsideRoot } from \"../utils/path-containment.js\";\n\nexport async function readStoreFileBuffer(filePath: string, memoryDir: string): Promise<Buffer> {\n const root = path.resolve(memoryDir);\n const entities = path.join(root, \"entities\");\n const target = path.resolve(filePath);\n const relative = path.relative(entities, target);\n if (relative === \"..\" || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {\n return readFile(filePath);\n }\n const handle = await open(target, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0));\n try {\n for (let parent = path.dirname(target); ; parent = path.dirname(parent)) {\n const info = await lstat(parent);\n if (info.isSymbolicLink() || !info.isDirectory()) {\n throw new Error(`Refusing unsafe entity directory: ${parent}.`);\n }\n if (parent === root) break;\n }\n const [opened, entry, realRoot, realEntities, realTarget] = await Promise.all([\n handle.stat(), lstat(target), realpath(root), realpath(entities), realpath(target),\n ]);\n if (!opened.isFile() || entry.isSymbolicLink() || !entry.isFile()\n || opened.dev !== entry.dev || opened.ino !== entry.ino\n || realEntities !== path.join(realRoot, \"entities\")) {\n throw new Error(`Refusing unsafe entity file: ${filePath}.`);\n }\n assertPathInsideRoot(realEntities, realTarget, filePath);\n return await handle.readFile();\n } finally {\n await handle.close();\n }\n}\n\n\n/**\n * Atomic-replacement-tolerant variant: entity writes ship as temp-file +\n * rename, so an open/lstat identity mismatch during a concurrent write is an\n * ordinary race, not tampering. Retry a bounded number of times with fresh\n * containment checks before surfacing the mismatch as unsafe.\n */\nexport async function readStoreFileBufferTolerant(filePath: string, memoryDir: string, attempts = 3): Promise<Buffer> {\n for (let attempt = 0; ; attempt += 1) {\n try {\n return await readStoreFileBuffer(filePath, memoryDir);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const identityRace = /unsafe entity (?:file|directory)/.test(message);\n if (!identityRace || attempt >= attempts - 1) throw err;\n }\n }\n}\n"],"mappings":"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","names":["lstat","readFile","path","open","open","lstat","path","readFile","position","buffer","unlink"]}
|
|
1
|
+
{"version":3,"sources":["../src/secure-store/secure-fs.ts","../src/secure-store/entity-file-read.ts"],"sourcesContent":["/**\n * Transparent file-level encryption for the secure-store module.\n *\n * Issue #690 (PR 3/4) — storage.ts integration layer.\n *\n * This module sits between the raw filesystem and StorageManager.\n * Every memory file is either:\n * - a plain UTF-8 text file (legacy, back-compat), or\n * - a REMNIC-ENC sealed file (AES-256-GCM, see format below).\n *\n * On-disk format\n * --------------\n * Encrypted files begin with a 9-byte magic header:\n *\n * REMNIC-ENC (7 ASCII bytes)\n * VER (1 byte, currently 0x01)\n * FLAGS (1 byte, reserved, must be 0x00)\n *\n * Followed immediately by a `seal()` envelope from `cipher.ts`:\n *\n * [VERSION:1][SALT:16][IV:12][AUTHTAG:16][CIPHERTEXT:...]\n *\n * The magic header makes encrypted files sniffable without attempting\n * a full `open()` call and gives operators a clear signal that the\n * file cannot be read by opening it in an editor.\n *\n * AAD\n * ---\n * The file path relative to the memory root is bound as Associated\n * Authenticated Data (AAD) on both encrypt and decrypt. This means\n * moving or renaming an encrypted file without re-encrypting it will\n * cause auth-tag failure on the next read — the file is tied to its\n * path. Callers that move files must re-encrypt them.\n *\n * Back-compat\n * -----------\n * `readMaybeEncryptedFile` transparently handles both formats: if the\n * file does NOT start with the magic bytes, it is returned as-is (plain\n * text). This lets an operator migrate incrementally: newly-written\n * files are encrypted while existing files continue to be read in plain\n * form until `migrateMemoryDirToEncrypted` is run.\n *\n * Naming: `secure-fs.ts` (not `vault-fs.ts`) — see `kdf.ts` naming note.\n */\n\nimport { createCipheriv, createDecipheriv, randomBytes, randomUUID } from \"node:crypto\";\nimport { lstat, mkdir, open as openFile, readFile, readdir, rename, unlink, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport {\n AUTH_TAG_LENGTH,\n ENVELOPE_HEADER_SIZE,\n ENVELOPE_LAYOUT,\n ENVELOPE_SALT_LENGTH,\n ENVELOPE_VERSION,\n IV_LENGTH,\n generateSalt,\n open as openEnvelope,\n parseEnvelope,\n seal,\n} from \"./cipher.js\";\nimport { RECALL_FALLBACK_DIRS } from \"../utils/category-dir.js\";\nimport { isErrnoCode } from \"../utils/errno.js\";\nimport { readStoreFileBufferTolerant as readStoreFileBuffer } from \"./entity-file-read.js\";\n\n// ---------------------------------------------------------------------------\n// Error classes\n// ---------------------------------------------------------------------------\n\n/**\n * Thrown when a read is attempted but the keyring entry for this\n * store is absent (i.e. `secure-store unlock` has not been run\n * since the last daemon start).\n */\nexport class SecureStoreLockedError extends Error {\n constructor(message = \"secure-store is locked — run `remnic secure-store unlock` to decrypt\") {\n super(message);\n this.name = \"SecureStoreLockedError\";\n }\n}\n\n/**\n * Thrown when `open()` fails because the auth tag does not validate.\n * This covers both wrong-key and tampered-ciphertext scenarios —\n * intentionally indistinguishable from the caller's perspective.\n */\nexport class SecureStoreDecryptError extends Error {\n constructor(message = \"secure-store decryption failed — wrong key or tampered ciphertext\") {\n super(message);\n this.name = \"SecureStoreDecryptError\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Magic header\n// ---------------------------------------------------------------------------\n\n/** Magic bytes: the ASCII string \"REMNIC-ENC\" (10 bytes). */\nexport const MAGIC_BYTES = Buffer.from(\"REMNIC-ENC\", \"ascii\");\n\n/** Current on-disk version byte. */\nexport const FILE_FORMAT_VERSION = 0x01;\n\n/** Reserved flags byte — must be 0x00. */\nexport const FILE_FORMAT_FLAGS = 0x00;\n\n/** Total size of the magic header prefix (magic + version + flags). */\nexport const MAGIC_HEADER_SIZE = MAGIC_BYTES.length + 2; // 12 bytes\n\n/**\n * Exact, deterministic bytes `writeMaybeEncryptedFile` adds to an encrypted\n * file beyond its plaintext body: the magic header prefix plus the fixed\n * envelope header (version + salt + IV + auth tag). AES-256-GCM ciphertext\n * matches the plaintext length and the state-file write path applies no\n * compression, so an encrypted state file is exactly `plaintext +\n * SECURE_STORE_ENVELOPE_OVERHEAD_BYTES` on disk. Compaction reserves this so an\n * encrypted ledger lands strictly below the reader's whole-file decrypt cap\n * (issue #2033).\n */\nexport const SECURE_STORE_ENVELOPE_OVERHEAD_BYTES = MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE;\n\n// ---------------------------------------------------------------------------\n// Detection\n// ---------------------------------------------------------------------------\n\n/**\n * Return true iff `buf` begins with the REMNIC-ENC magic header.\n * Does not validate the envelope; just identifies the format.\n */\nexport function isEncryptedFile(buf: Uint8Array): boolean {\n if (buf.length < MAGIC_HEADER_SIZE) return false;\n const b = Buffer.isBuffer(buf) ? buf : Buffer.from(buf);\n return b.subarray(0, MAGIC_BYTES.length).equals(MAGIC_BYTES);\n}\n\ntype PositionedFileReader = {\n read: (buffer: Buffer, offset: number, length: number, position: number) => Promise<{ bytesRead: number }>;\n};\n\nasync function readPositionedPrefix(handle: PositionedFileReader, buffer: Buffer): Promise<number> {\n let bytesRead = 0;\n while (bytesRead < buffer.length) {\n const result = await handle.read(buffer, bytesRead, buffer.length - bytesRead, bytesRead);\n if (result.bytesRead === 0) break;\n bytesRead += result.bytesRead;\n }\n return bytesRead;\n}\n\n/**\n * True when the REGULAR file at `probePath` is encrypted at rest (its first\n * bytes are the REMNIC-ENC magic header). ENOENT resolves to false — an absent\n * file has nothing to probe. A symlink, FIFO, device, or any other non-regular\n * path is REFUSED with a throw rather than opened: opening a FIFO blocks until a\n * writer appears and a symlink would redirect the probe outside our tree. The\n * ledgers and per-event spills we manage are always regular files, so a\n * non-regular path is an anomaly the caller must treat as unsafe, never follow\n * (#2033). Shared by the CLI ledger recovery probe and the auto-compaction probe\n * so both enforce identical open safety.\n */\nexport async function probeEncryptedRegularFileHeader(probePath: string): Promise<boolean> {\n let entryStat;\n try {\n entryStat = await lstat(probePath);\n } catch (err) {\n if (isErrnoCode(err, \"ENOENT\")) return false;\n throw err;\n }\n if (!entryStat.isFile()) {\n throw new Error(\n `refusing to probe non-regular state path ${probePath} ` +\n `(symlink/FIFO/device); opening it could block or escape containment (#2033)`\n );\n }\n const handle = await openFile(probePath, \"r\");\n try {\n const header = Buffer.alloc(MAGIC_HEADER_SIZE);\n const bytesRead = await readPositionedPrefix(handle, header);\n return isEncryptedFile(header.subarray(0, bytesRead));\n } finally {\n await handle.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Encrypt / decrypt file body\n// ---------------------------------------------------------------------------\n\n/**\n * Encrypt `plain` (UTF-8 content of a memory file) and return a\n * Buffer ready to write to disk.\n *\n * @param plain Plain-text file content (UTF-8 string or Buffer).\n * @param key 32-byte AES-256 key from the keyring.\n * @param aad Optional associated data — defaults to empty if omitted.\n * Callers should pass the file path relative to memoryDir\n * so the ciphertext is bound to its location.\n */\nexport function encryptFileBody(plain: string | Buffer, key: Buffer, aad?: Buffer): Buffer {\n const plainBuf = typeof plain === \"string\" ? Buffer.from(plain, \"utf8\") : plain;\n const salt = generateSalt();\n const envelope = seal(key, salt, plainBuf, aad ? { aad } : {});\n\n const header = Buffer.alloc(MAGIC_HEADER_SIZE);\n MAGIC_BYTES.copy(header, 0);\n header.writeUInt8(FILE_FORMAT_VERSION, MAGIC_BYTES.length);\n header.writeUInt8(FILE_FORMAT_FLAGS, MAGIC_BYTES.length + 1);\n\n return Buffer.concat([header, envelope]);\n}\n\n/**\n * Decrypt a buffer produced by `encryptFileBody` and return the\n * original UTF-8 content.\n *\n * Throws `SecureStoreDecryptError` on auth failure (wrong key or\n * tampered ciphertext). Throws a plain `Error` for structural problems\n * (truncated buffer, wrong magic, unsupported version).\n */\nexport function decryptFileBody(buf: Buffer, key: Buffer, aad?: Buffer): Buffer {\n if (!isEncryptedFile(buf)) {\n throw new Error(\"decryptFileBody: buffer does not start with REMNIC-ENC magic header\");\n }\n const version = buf.readUInt8(MAGIC_BYTES.length);\n if (version !== FILE_FORMAT_VERSION) {\n throw new Error(\n `decryptFileBody: unsupported file format version ${version} (this build supports ${FILE_FORMAT_VERSION})`\n );\n }\n const flags = buf.readUInt8(MAGIC_BYTES.length + 1);\n if (flags !== FILE_FORMAT_FLAGS) {\n throw new Error(`decryptFileBody: unknown flags byte 0x${flags.toString(16).padStart(2, \"0\")}`);\n }\n const envelope = buf.subarray(MAGIC_HEADER_SIZE);\n parseEnvelope(envelope);\n try {\n return openEnvelope(key, envelope, aad ? { aad } : {});\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new SecureStoreDecryptError(`secure-store decryption failed: ${msg}`);\n }\n}\n\nfunction buildHeaderAad(salt: Uint8Array): Buffer {\n const out = Buffer.alloc(1 + ENVELOPE_SALT_LENGTH);\n out.writeUInt8(ENVELOPE_VERSION, 0);\n Buffer.from(salt).copy(out, 1);\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Path → AAD helper\n// ---------------------------------------------------------------------------\n\n/**\n * Build the AAD buffer for a file at `filePath` relative to\n * `memoryDir`. The AAD binds the ciphertext to its path so a\n * file cannot be silently relocated without re-encryption.\n *\n * When `memoryDir` is supplied and `filePath` is absolute, the\n * relative sub-path is used. Otherwise `filePath` is used verbatim.\n */\nexport function filePathAad(filePath: string, memoryDir?: string): Buffer {\n let rel = filePath;\n if (memoryDir && path.isAbsolute(filePath)) {\n rel = path.relative(memoryDir, filePath);\n }\n return Buffer.from(rel, \"utf8\");\n}\n\n// ---------------------------------------------------------------------------\n// High-level read / write helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Read a file from `filePath`.\n *\n * - If the file is plaintext (no magic header), return its content\n * as-is — back-compat with unencrypted stores.\n * - If the file is encrypted AND `key` is provided, decrypt and return\n * the plaintext content.\n * - If the file is encrypted AND `key` is null, throw\n * `SecureStoreLockedError`.\n *\n * @param filePath Absolute path to the file.\n * @param key 32-byte AES-256 key, or null when the store is locked.\n * @param memoryDir Memory root for path-bound AAD. Should be absolute.\n */\nexport async function readMaybeEncryptedFileBuffer(\n filePath: string,\n key: Buffer | null,\n memoryDir?: string,\n opts?: { aadFilePath?: string }\n): Promise<Buffer> {\n const buf = memoryDir ? await readStoreFileBuffer(filePath, memoryDir) : await readFile(filePath);\n if (!isEncryptedFile(buf)) {\n // Plain file — legacy or unencrypted store.\n return buf;\n }\n // Encrypted — key required.\n if (key === null) {\n throw new SecureStoreLockedError(\n `secure-store is locked — cannot read encrypted file at ${filePath}. Run \\`remnic secure-store unlock\\` to decrypt.`\n );\n }\n return decryptFileBodyForPath(buf, key, filePath, memoryDir, opts?.aadFilePath);\n}\n\nexport async function readMaybeEncryptedFile(\n filePath: string,\n key: Buffer | null,\n memoryDir?: string\n): Promise<string> {\n return (await readMaybeEncryptedFileBuffer(filePath, key, memoryDir)).toString(\"utf8\");\n}\n\nexport interface WriteMaybeEncryptedFileOptions {\n /**\n * File mode bits. Default 0o600 (owner read/write only).\n * Applied only on create; existing files inherit their existing mode.\n */\n mode?: number;\n /**\n * If true, write atomically via a temp file + rename (CLAUDE.md gotcha #54).\n * Default true.\n */\n atomic?: boolean;\n /**\n * ABSOLUTE final path whose canonical AAD (via {@link filePathAad})\n * replaces the physical `filePath`'s. Used by the offline-sync generation\n * transaction: staged ciphertext physically lives under a staging\n * directory but must decrypt at its FINAL published path after the\n * staging directory is swapped into place by rename. Deriving the AAD\n * from the absolute final path through the SAME filePathAad logic as\n * ordinary reads keeps write and read derivations identical on every\n * platform (POSIX and Windows separators alike). (issue #3148, round 4/5)\n */\n aadFilePath?: string;\n}\n\n/**\n * Write `content` to `filePath`.\n *\n * - If `key` is provided and non-null, encrypt the content first.\n * - If `key` is null, write the content as plain UTF-8 (unencrypted store).\n *\n * Writes atomically: content is written to a unique temp file\n * first, then renamed into place (CLAUDE.md gotcha #54 — never delete\n * before write).\n */\nexport async function writeMaybeEncryptedFile(\n filePath: string,\n content: string | Buffer,\n key: Buffer | null,\n options: WriteMaybeEncryptedFileOptions = {},\n memoryDir?: string\n): Promise<void> {\n const { mode = 0o600, atomic = true, aadFilePath } = options;\n await mkdir(path.dirname(filePath), { recursive: true });\n\n let data: Buffer | string;\n if (key !== null) {\n const aad = aadFilePath !== undefined ? filePathAad(aadFilePath, memoryDir) : filePathAad(filePath, memoryDir);\n data = encryptFileBody(content, key, aad);\n } else {\n data = content;\n }\n\n if (atomic) {\n const tempPath = uniqueAtomicTempPath(filePath, \"tmp\");\n try {\n await writeFile(tempPath, data, { mode });\n await rename(tempPath, filePath);\n } catch (err) {\n // Best-effort cleanup of the temp file.\n try {\n await unlink(tempPath);\n } catch {\n // ignore\n }\n throw err;\n }\n } else {\n await writeFile(filePath, data, { mode });\n }\n}\n\nexport async function writeMaybeEncryptedFileFromChunks(\n filePath: string,\n chunks: AsyncIterable<Buffer>,\n key: Buffer | null,\n options: WriteMaybeEncryptedFileOptions = {},\n memoryDir?: string\n): Promise<void> {\n const { mode = 0o600, atomic = true } = options;\n await mkdir(path.dirname(filePath), { recursive: true });\n const writePath = atomic ? `${filePath}.tmp-${process.pid}-${Date.now()}` : filePath;\n let completed = false;\n try {\n const handle = await openFile(writePath, \"w\", mode);\n try {\n if (key !== null) {\n const salt = generateSalt();\n const iv = randomBytes(IV_LENGTH);\n const header = Buffer.alloc(MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE);\n MAGIC_BYTES.copy(header, 0);\n header.writeUInt8(FILE_FORMAT_VERSION, MAGIC_BYTES.length);\n header.writeUInt8(FILE_FORMAT_FLAGS, MAGIC_BYTES.length + 1);\n const envelopeOffset = MAGIC_HEADER_SIZE;\n header.writeUInt8(ENVELOPE_VERSION, envelopeOffset + ENVELOPE_LAYOUT.version);\n salt.copy(header, envelopeOffset + ENVELOPE_LAYOUT.salt);\n iv.copy(header, envelopeOffset + ENVELOPE_LAYOUT.iv);\n await handle.write(header);\n\n const cipher = createCipheriv(\"aes-256-gcm\", key, iv, { authTagLength: AUTH_TAG_LENGTH });\n const aad = filePathAad(filePath, memoryDir);\n cipher.setAAD(Buffer.concat([buildHeaderAad(salt), aad]));\n for await (const chunk of chunks) {\n if (chunk.length === 0) continue;\n const encrypted = cipher.update(chunk);\n if (encrypted.length > 0) await handle.write(encrypted);\n }\n const final = cipher.final();\n if (final.length > 0) await handle.write(final);\n const authTag = cipher.getAuthTag();\n await handle.write(authTag, 0, authTag.length, MAGIC_HEADER_SIZE + ENVELOPE_LAYOUT.authTag);\n } else {\n for await (const chunk of chunks) {\n if (chunk.length > 0) await handle.write(chunk);\n }\n }\n } finally {\n await handle.close();\n }\n if (atomic) {\n await rename(writePath, filePath);\n }\n completed = true;\n } finally {\n if (!completed && atomic) {\n await unlink(writePath).catch(() => {});\n }\n }\n}\n\n/**\n * Stream a file through the secure-store reader without buffering the whole\n * payload. Plaintext files are passed through unchanged; encrypted files are\n * authenticated and decrypted incrementally.\n */\nexport async function* readMaybeEncryptedFileFromChunks(\n filePath: string,\n key: Buffer | null,\n memoryDir?: string,\n chunkSize = 64 * 1024\n): AsyncIterable<Buffer> {\n const handle = await openFile(filePath, \"r\");\n const headerSize = MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE;\n try {\n const prefix = Buffer.alloc(headerSize);\n const prefixBytes = await readPositionedPrefix(handle, prefix);\n const prefixView = prefix.subarray(0, prefixBytes);\n if (!isEncryptedFile(prefixView)) {\n if (prefixBytes > 0) yield Buffer.from(prefixView);\n let position = prefixBytes;\n const buffer = Buffer.alloc(chunkSize);\n for (;;) {\n const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);\n if (bytesRead === 0) break;\n position += bytesRead;\n yield Buffer.from(buffer.subarray(0, bytesRead));\n }\n return;\n }\n if (prefixBytes < headerSize) {\n throw new Error(`encrypted file header is truncated: ${filePath}`);\n }\n if (key === null) {\n throw new SecureStoreLockedError(\n `secure-store is locked — cannot read encrypted file at ${filePath}. ` +\n \"Run `remnic secure-store unlock` to decrypt.\"\n );\n }\n const envelope = prefix.subarray(MAGIC_HEADER_SIZE);\n const version = envelope.readUInt8(ENVELOPE_LAYOUT.version);\n if (version !== ENVELOPE_VERSION) {\n throw new Error(`secure-store: unsupported envelope version ${version}`);\n }\n const salt = envelope.subarray(ENVELOPE_LAYOUT.salt, ENVELOPE_LAYOUT.salt + ENVELOPE_SALT_LENGTH);\n const iv = envelope.subarray(ENVELOPE_LAYOUT.iv, ENVELOPE_LAYOUT.iv + IV_LENGTH);\n const authTag = envelope.subarray(ENVELOPE_LAYOUT.authTag, ENVELOPE_LAYOUT.authTag + AUTH_TAG_LENGTH);\n const decipher = createDecipheriv(\"aes-256-gcm\", key, iv, { authTagLength: AUTH_TAG_LENGTH });\n decipher.setAAD(Buffer.concat([buildHeaderAad(salt), filePathAad(filePath, memoryDir)]));\n decipher.setAuthTag(authTag);\n let position = headerSize;\n const buffer = Buffer.alloc(chunkSize);\n try {\n for (;;) {\n const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);\n if (bytesRead === 0) break;\n position += bytesRead;\n const plain = decipher.update(buffer.subarray(0, bytesRead));\n if (plain.length > 0) yield plain;\n }\n const final = decipher.final();\n if (final.length > 0) yield final;\n } catch (err) {\n if (err instanceof SecureStoreDecryptError) throw err;\n const message = err instanceof Error ? err.message : String(err);\n throw new SecureStoreDecryptError(`secure-store decryption failed: ${message}`);\n }\n } finally {\n await handle.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Migration\n// ---------------------------------------------------------------------------\n\nexport interface MigrateResult {\n /** Number of files successfully encrypted. */\n encrypted: number;\n /** Number of files already encrypted (skipped). */\n skipped: number;\n /** Files that failed to encrypt (path → error message). */\n errors: Array<{ filePath: string; error: string }>;\n}\n\nexport interface DecryptResult {\n /** Number of files successfully decrypted back to plaintext. */\n decrypted: number;\n /** Number of files already plaintext (skipped). */\n skipped: number;\n /** Files that failed to decrypt (path → error message). */\n errors: Array<{ filePath: string; error: string }>;\n}\n\n/**\n * Walk `dir` recursively, find encryptable storage-managed files that are not\n * yet encrypted, and re-write them as encrypted files under `key`.\n *\n * Safety rules per CLAUDE.md gotchas #54 and #25:\n * 1. A page-version snapshot is taken (via `createVersion`) BEFORE\n * each overwrite so the plaintext version is preserved in history.\n * Since this module has no direct access to `page-versioning.ts`\n * internals, callers who have page-versioning configured should\n * pass `onBeforeEncrypt` to take the snapshot.\n * 2. The new encrypted content is written to a temp file first,\n * then renamed atomically — never deleted before written.\n * 3. If encryption of any file fails, the error is recorded and the\n * original file is left intact (partial migration is safe).\n *\n * @param dir Absolute path to the memory directory.\n * @param key 32-byte AES-256 key.\n * @param onBeforeEncrypt Optional callback invoked before encrypting\n * each file. Can be used to take page-version\n * snapshots. Errors here are non-fatal.\n */\nexport async function migrateMemoryDirToEncrypted(\n dir: string,\n key: Buffer,\n onBeforeEncrypt?: (filePath: string) => Promise<void>\n): Promise<MigrateResult> {\n const result: MigrateResult = { encrypted: 0, skipped: 0, errors: [] };\n\n const files = await collectEncryptableStorageFiles(dir);\n for (const filePath of files) {\n try {\n const buf = await readFile(filePath);\n if (isEncryptedFile(buf)) {\n result.skipped++;\n continue;\n }\n // Call optional pre-encryption hook (e.g. page-version snapshot).\n if (onBeforeEncrypt) {\n try {\n await onBeforeEncrypt(filePath);\n } catch {\n // Non-fatal — continue with encryption even if snapshot fails.\n }\n }\n const content = buf.toString(\"utf8\");\n const aad = filePathAad(filePath, dir);\n const encrypted = encryptFileBody(content, key, aad);\n\n // Atomic write: temp → rename (gotcha #54).\n const tempPath = uniqueAtomicTempPath(filePath, \"enc-tmp\");\n try {\n await writeFile(tempPath, encrypted, { mode: 0o600 });\n await rename(tempPath, filePath);\n result.encrypted++;\n } catch (writeErr) {\n // Clean up temp file, leave original intact.\n try {\n const { unlink } = await import(\"node:fs/promises\");\n await unlink(tempPath);\n } catch {\n // ignore\n }\n throw writeErr;\n }\n } catch (err) {\n result.errors.push({\n filePath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return result;\n}\n\n/**\n * Walk `dir` recursively, find storage-managed encrypted files, and\n * re-write them as plaintext under the same paths.\n *\n * This is the reversible counterpart to {@link migrateMemoryDirToEncrypted}.\n * It only touches files under the same storage-managed roots, skips\n * plaintext files, skips symlinks, excludes `.secure-store/`, and writes\n * each plaintext replacement via temp-file + rename so a per-file failure\n * leaves the ciphertext intact.\n */\nexport async function decryptMemoryDirToPlaintext(dir: string, key: Buffer): Promise<DecryptResult> {\n const result: DecryptResult = { decrypted: 0, skipped: 0, errors: [] };\n\n const files = await collectStorageManagedFiles(dir, isDecryptableStoragePath);\n for (const filePath of files) {\n try {\n const buf = await readFile(filePath);\n if (!isEncryptedFile(buf)) {\n result.skipped++;\n continue;\n }\n\n const plaintext = decryptFileBodyForPath(buf, key, filePath, dir);\n const tempPath = uniqueAtomicTempPath(filePath, \"dec-tmp\");\n try {\n await writeFile(tempPath, plaintext, { mode: 0o600 });\n await rename(tempPath, filePath);\n result.decrypted++;\n } catch (writeErr) {\n try {\n await unlink(tempPath);\n } catch {\n // ignore cleanup errors; original ciphertext is still intact.\n }\n throw writeErr;\n }\n } catch (err) {\n result.errors.push({\n filePath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction uniqueAtomicTempPath(filePath: string, label: string): string {\n return `${filePath}.${label}-${process.pid}-${Date.now()}-${randomUUID()}`;\n}\n\nfunction decryptFileBodyForPath(\n buf: Buffer,\n key: Buffer,\n filePath: string,\n memoryDir?: string,\n aadFilePath?: string\n): Buffer {\n // A canonical-AAD stage read (offline-sync generation transaction) binds\n // the FINAL published path, not the physical staging path.\n const aad = aadFilePath !== undefined ? filePathAad(aadFilePath, memoryDir) : filePathAad(filePath, memoryDir);\n try {\n return decryptFileBody(buf, key, aad);\n } catch (err) {\n if (!(err instanceof SecureStoreDecryptError)) {\n throw err;\n }\n const legacyRoot = legacyNamespaceAadRootForFile(filePath, memoryDir);\n if (legacyRoot) {\n try {\n return decryptFileBody(buf, key, filePathAad(filePath, legacyRoot));\n } catch {\n // Fall through to the namespace-reader fallback below.\n }\n }\n\n const topLevelRoot = topLevelAadRootForNamespaceReader(filePath, memoryDir);\n if (topLevelRoot) {\n try {\n return decryptFileBody(buf, key, filePathAad(filePath, topLevelRoot));\n } catch {\n // Preserve the caller-facing error from the canonical decrypt attempt.\n }\n }\n\n throw err;\n }\n}\n\nfunction topLevelAadRootForNamespaceReader(filePath: string, memoryDir?: string): string | null {\n if (!memoryDir || !path.isAbsolute(filePath)) return null;\n const resolvedMemoryDir = path.resolve(memoryDir);\n const rel = path.relative(resolvedMemoryDir, filePath);\n if (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) return null;\n const parts = resolvedMemoryDir.split(path.sep);\n if (parts.length < 3 || parts.at(-2) !== \"namespaces\" || !parts.at(-1)) return null;\n const topLevelRoot = parts.slice(0, -2).join(path.sep) || path.sep;\n const topRel = path.relative(topLevelRoot, filePath);\n if (topRel === \"\" || topRel.startsWith(\"..\") || path.isAbsolute(topRel)) {\n return null;\n }\n const topParts = topRel.split(path.sep);\n if (topParts[0] !== \"namespaces\" || topParts[1] !== parts.at(-1)) {\n return null;\n }\n return topLevelRoot;\n}\n\nfunction legacyNamespaceAadRootForFile(filePath: string, memoryDir?: string): string | null {\n if (!memoryDir || !path.isAbsolute(filePath)) return null;\n const rel = path.relative(memoryDir, filePath);\n if (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) return null;\n const parts = rel.split(path.sep);\n if (parts[0] === \"namespaces\" && parts.length >= 3 && parts[1]) {\n return path.join(memoryDir, \"namespaces\", parts[1]);\n }\n return null;\n}\n\n/**\n * Recursively collect files under `dir` that are read through the\n * storage-layer secure-store helpers, excluding symlinked entries and\n * `.secure-store/` metadata.\n */\nasync function collectEncryptableStorageFiles(dir: string, rootDir = dir): Promise<string[]> {\n return collectStorageManagedFiles(dir, isEncryptableStoragePath, rootDir);\n}\n\n/**\n * Recursively collect regular files under storage-managed roots, excluding\n * symlinked entries and `.secure-store/` metadata. This broader collector is\n * used by the decrypt/disable path so future encrypted sidecars can be\n * restored without requiring extension-specific logic.\n */\nasync function collectStorageManagedFiles(\n dir: string,\n includeFile: (filePath: string, rootDir: string) => boolean,\n rootDir = dir\n): Promise<string[]> {\n const results: string[] = [];\n let scanDir = dir;\n let scanRootDir = rootDir;\n if (path.resolve(dir) === path.resolve(rootDir)) {\n const normalizedRoot = await resolveStorageManagedRootForScan(dir);\n if (!normalizedRoot) return results;\n scanDir = normalizedRoot;\n scanRootDir = normalizedRoot;\n }\n let names: string[];\n try {\n names = await readdir(scanDir, { encoding: \"utf8\" });\n } catch {\n return results;\n }\n for (const name of names) {\n if (name.startsWith(\".secure-store\")) continue;\n const full = path.join(scanDir, name);\n let isDir = false;\n let isFile = false;\n try {\n const s = await lstat(full);\n if (s.isSymbolicLink()) continue;\n isDir = s.isDirectory();\n isFile = s.isFile();\n } catch {\n continue;\n }\n if (isDir) {\n const sub = await collectStorageManagedFiles(full, includeFile, scanRootDir);\n results.push(...sub);\n } else if (isFile && includeFile(full, scanRootDir)) {\n results.push(full);\n }\n }\n return results;\n}\n\nasync function resolveStorageManagedRootForScan(dir: string): Promise<string | null> {\n const lstatPath = normalizePathForLstat(dir);\n let stat: Awaited<ReturnType<typeof lstat>>;\n try {\n stat = await lstat(lstatPath);\n } catch (error) {\n if (isFsErrorWithCode(error, \"ENOENT\")) return null;\n throw error;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`secure-store migration root must not be a symlink: ${dir}`);\n }\n if (!stat.isDirectory()) {\n throw new Error(`secure-store migration root must be a directory: ${dir}`);\n }\n return lstatPath;\n}\n\nfunction normalizePathForLstat(filePath: string): string {\n return stripTrailingPathSeparators(path.normalize(filePath));\n}\n\nfunction isFsErrorWithCode(error: unknown, code: string): boolean {\n return typeof error === \"object\" && error !== null && (error as { code?: unknown }).code === code;\n}\n\nfunction stripTrailingPathSeparators(filePath: string): string {\n const root = path.parse(filePath).root;\n let end = filePath.length;\n while (\n end > root.length &&\n (filePath[end - 1] === path.sep || filePath[end - 1] === path.posix.sep || filePath[end - 1] === path.win32.sep)\n ) {\n end -= 1;\n }\n return end === filePath.length ? filePath : filePath.slice(0, end);\n}\n\nfunction isEncryptableStoragePath(filePath: string, rootDir: string): boolean {\n const rel = path.relative(rootDir, filePath);\n if (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) return false;\n const normalized = normalizeStorageRelativePath(rel);\n if (normalized === \"profile.md\") return true;\n if (isEncryptableStateSidecar(normalized)) return true;\n if (isEncryptableBufferSessionFile(normalized)) return true;\n if (isEncryptableSummarySidecar(normalized)) return true;\n const firstSegment = normalized.split(\"/\", 1)[0];\n return ENCRYPTABLE_MARKDOWN_STORAGE_ROOTS.has(firstSegment) && normalized.endsWith(\".md\");\n}\n\nfunction isDecryptableStoragePath(filePath: string, rootDir: string): boolean {\n if (isEncryptableStoragePath(filePath, rootDir)) return true;\n const rel = path.relative(rootDir, filePath);\n if (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) return false;\n const normalized = normalizeStorageRelativePath(rel);\n const firstSegment = normalized.split(\"/\", 1)[0];\n return DECRYPTABLE_SIDECAR_ROOTS.has(firstSegment);\n}\n\nfunction normalizeStorageRelativePath(rel: string): string {\n const normalized = rel.split(path.sep).join(\"/\");\n const parts = normalized.split(\"/\");\n if (parts[0] === \"namespaces\" && parts.length >= 3) {\n return parts.slice(2).join(\"/\");\n }\n return normalized;\n}\n\n// Every recall MEMORY category directory (facts/ + the CATEGORY_DIR_MAP memory\n// dirs) must be encryptable at rest — #1546 routes decision/preference/moment/...\n// memories into their own dirs, so hardcoding only facts/corrections/procedures/\n// reasoning-traces would silently write those categories in plaintext on\n// encrypted stores. RECALL_FALLBACK_DIRS is the single source of truth for that\n// set; the extra non-category markdown roots (artifacts/archive/entities/\n// identity) are appended.\n//\n// questions/ is deliberately EXCLUDED (RECALL_FALLBACK_DIRS omits the non-memory\n// queue dirs): the question queue is written/resolved through plain readFile/\n// writeFile (StorageManager.writeQuestion/resolveQuestion), NOT the secure-file\n// helpers. Encrypting questions/ here would make migration seal those files,\n// after which resolveQuestion() would read ciphertext as UTF-8, fail to update\n// the frontmatter, and overwrite/corrupt the file while returning success\n// (codex #1563 review). Keeping questions/ plaintext preserves the queue's\n// existing behavior; #1546 does not change question encryption.\nconst ENCRYPTABLE_MARKDOWN_STORAGE_ROOTS = new Set<string>([\n ...RECALL_FALLBACK_DIRS,\n \"artifacts\",\n \"archive\",\n \"entities\",\n \"identity\",\n]);\n\nconst ENCRYPTABLE_STATE_SIDECARS = new Set([\n \"state/behavior-signals.jsonl\",\n \"state/buffer-surprise-ledger.jsonl\",\n \"state/buffer.json\",\n \"state/compression-guideline-draft-state.json\",\n \"state/compression-guideline-state.json\",\n \"state/compression-guidelines.draft.md\",\n \"state/compression-guidelines.md\",\n \"state/entity-synthesis-queue.json\",\n \"state/fact-hashes.txt\",\n \"state/memory-actions.jsonl\",\n \"state/memory-lifecycle-ledger.jsonl\",\n \"state/meta.json\",\n \"state/reextract-jobs.jsonl\",\n \"state/topics.json\",\n]);\n\nfunction isEncryptableStateSidecar(normalized: string): boolean {\n return ENCRYPTABLE_STATE_SIDECARS.has(normalized);\n}\n\n/**\n * Per-session buffer state files (`state/buffer/<session>.json`) hold the same\n * buffered turns the legacy `state/buffer.json` held, so they are encryptable\n * state sidecars too — otherwise enabling the secure store would leave the\n * daemon's buffered turns in plaintext on disk.\n */\nfunction isEncryptableBufferSessionFile(normalized: string): boolean {\n return normalized.startsWith(\"state/buffer/\") && normalized.endsWith(\".json\");\n}\n\nfunction isEncryptableSummarySidecar(normalized: string): boolean {\n return normalized.startsWith(\"summaries/\") && normalized.endsWith(\".json\");\n}\n\nconst DECRYPTABLE_SIDECAR_ROOTS = new Set([\"state\", \"indexes\", \"index\", \"provenance\"]);\n","import { constants } from \"node:fs\";\nimport { lstat, open, readFile, realpath } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { assertPathInsideRoot } from \"../utils/path-containment.js\";\n\nexport async function readStoreFileBuffer(filePath: string, memoryDir: string): Promise<Buffer> {\n const root = path.resolve(memoryDir);\n const entities = path.join(root, \"entities\");\n const target = path.resolve(filePath);\n const relative = path.relative(entities, target);\n if (relative === \"..\" || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {\n return readFile(filePath);\n }\n const handle = await open(target, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0));\n try {\n for (let parent = path.dirname(target); ; parent = path.dirname(parent)) {\n const info = await lstat(parent);\n if (info.isSymbolicLink() || !info.isDirectory()) {\n throw new Error(`Refusing unsafe entity directory: ${parent}.`);\n }\n if (parent === root) break;\n }\n const [opened, entry, realRoot, realEntities, realTarget] = await Promise.all([\n handle.stat(), lstat(target), realpath(root), realpath(entities), realpath(target),\n ]);\n if (!opened.isFile() || entry.isSymbolicLink() || !entry.isFile()\n || opened.dev !== entry.dev || opened.ino !== entry.ino\n || realEntities !== path.join(realRoot, \"entities\")) {\n throw new Error(`Refusing unsafe entity file: ${filePath}.`);\n }\n assertPathInsideRoot(realEntities, realTarget, filePath);\n return await handle.readFile();\n } finally {\n await handle.close();\n }\n}\n\n\n/**\n * Atomic-replacement-tolerant variant: entity writes ship as temp-file +\n * rename, so an open/lstat identity mismatch during a concurrent write is an\n * ordinary race, not tampering. Retry a bounded number of times with fresh\n * containment checks before surfacing the mismatch as unsafe.\n */\nexport async function readStoreFileBufferTolerant(filePath: string, memoryDir: string, attempts = 3): Promise<Buffer> {\n for (let attempt = 0; ; attempt += 1) {\n try {\n return await readStoreFileBuffer(filePath, memoryDir);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const identityRace = /unsafe entity (?:file|directory)/.test(message);\n if (!identityRace || attempt >= attempts - 1) throw err;\n }\n }\n}\n"],"mappings":"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","names":["lstat","readFile","path","open","open","lstat","path","readFile","position","buffer","unlink"]}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "./chunk-5TJEOU6Y.js";
|
|
11
11
|
import {
|
|
12
12
|
deriveSourceConnector
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-UFBIOF7C.js";
|
|
14
14
|
import {
|
|
15
15
|
resolvePipelineProcessingCapabilities
|
|
16
16
|
} from "./chunk-C5LWPYS3.js";
|
|
@@ -112,4 +112,4 @@ export {
|
|
|
112
112
|
renderExtractionConversation,
|
|
113
113
|
applyGroundingWithConnector
|
|
114
114
|
};
|
|
115
|
-
//# sourceMappingURL=chunk-
|
|
115
|
+
//# sourceMappingURL=chunk-U4JXQCJ3.js.map
|
|
@@ -5,13 +5,13 @@ import {
|
|
|
5
5
|
embeddingGenerationMembership,
|
|
6
6
|
embeddingShardDirOf,
|
|
7
7
|
isEmbeddingGenerationDirPath
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import {
|
|
10
|
-
EMBEDDING_SHARD_FILE_PATTERN
|
|
11
|
-
} from "./chunk-TYT4TEQV.js";
|
|
8
|
+
} from "./chunk-I73ZSORJ.js";
|
|
12
9
|
import {
|
|
13
10
|
resolveSafeArchiveTarget
|
|
14
11
|
} from "./chunk-GCGJW34D.js";
|
|
12
|
+
import {
|
|
13
|
+
EMBEDDING_SHARD_FILE_PATTERN
|
|
14
|
+
} from "./chunk-LDWE7OZS.js";
|
|
15
15
|
import {
|
|
16
16
|
EmbeddingIndexStorageError
|
|
17
17
|
} from "./chunk-Y57GJDG5.js";
|
|
@@ -369,4 +369,4 @@ export {
|
|
|
369
369
|
applyChangesetEmbeddingGenerations,
|
|
370
370
|
buildChangesetGenerationManifest
|
|
371
371
|
};
|
|
372
|
-
//# sourceMappingURL=chunk-
|
|
372
|
+
//# sourceMappingURL=chunk-U7HJBODU.js.map
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
SecureStoreLockedError,
|
|
10
10
|
readMaybeEncryptedFile,
|
|
11
11
|
writeMaybeEncryptedFile
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-TYGLV6ZG.js";
|
|
13
13
|
import {
|
|
14
14
|
isErrnoCode
|
|
15
15
|
} from "./chunk-KNA3BXXL.js";
|
|
@@ -577,4 +577,4 @@ export {
|
|
|
577
577
|
FactHashIndexNotAuthoritativeError,
|
|
578
578
|
ContentHashIndex
|
|
579
579
|
};
|
|
580
|
-
//# sourceMappingURL=chunk-
|
|
580
|
+
//# sourceMappingURL=chunk-UBN5AOLT.js.map
|