@remnic/core 10.0.91 → 10.0.93
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 +334 -61
- package/dist/access-authorization-probe.js +2 -283
- package/dist/access-boundary.d.ts +1 -1
- package/dist/access-boundary.d.ts.map +1 -1
- package/dist/access-boundary.js +1 -282
- package/dist/access-cli.js +288 -304
- package/dist/access-cli.js.map +1 -1
- package/dist/access-coding-context-resolution.js +3 -3
- package/dist/access-extraction-force-flush.js +334 -61
- package/dist/access-http-briefing.js +334 -62
- package/dist/access-http-query.js +334 -63
- package/dist/access-http.js +332 -114
- package/dist/access-identity-continuity-surface.js +334 -61
- package/dist/access-lcm-surface.js +334 -61
- package/dist/access-mcp-frames.d.ts +20 -6
- package/dist/access-mcp-frames.d.ts.map +1 -1
- package/dist/access-mcp-frames.js +1 -1
- package/dist/access-mcp.d.ts +5 -0
- package/dist/access-mcp.d.ts.map +1 -1
- package/dist/access-mcp.js +332 -89
- package/dist/access-namespace-preflight.js +2 -282
- package/dist/access-observe-transcript.js +3 -3
- package/dist/access-observe-write-category.js +6 -6
- package/dist/access-observe-write-surface.js +334 -61
- package/dist/access-offline-manifest.js +12 -11
- package/dist/access-operations-batch.js +332 -66
- package/dist/access-operations.js +332 -75
- package/dist/access-recall-concurrency.js +334 -61
- package/dist/access-recall-diagnostics.js +6 -5
- package/dist/access-recall-response.js +334 -61
- package/dist/access-recall-surface.js +334 -61
- package/dist/access-schema.js +2 -2
- package/dist/access-service.d.ts +1 -0
- package/dist/access-service.d.ts.map +1 -1
- package/dist/access-service.js +337 -64
- package/dist/access-surface-catalog.js +6 -283
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/active-context-transform.js +1 -1
- package/dist/active-memory-bridge.js +3 -2
- package/dist/active-recall.js +36 -37
- package/dist/active-recall.js.map +1 -1
- package/dist/activity/journal-extract.d.ts +13 -1
- package/dist/activity/journal-extract.d.ts.map +1 -1
- package/dist/{auto-sync-3AZW7MXI.js → auto-sync-BO5YZKM6.js} +13 -15
- package/dist/{auto-sync-3AZW7MXI.js.map → auto-sync-BO5YZKM6.js.map} +1 -1
- package/dist/briefing.js +20 -20
- package/dist/buffer.js +16 -16
- package/dist/calibration.js +10 -10
- package/dist/{capsule-merge-2IIXFXN5.js → capsule-merge-6IDAYTKG.js} +6 -6
- package/dist/causal-consolidation.js +23 -23
- package/dist/{chunk-QL7RHYK6.js → chunk-2LMA22UU.js} +5 -5
- package/dist/{chunk-RGUUBAT5.js → chunk-2WCLTWV2.js} +3 -3
- package/dist/{chunk-RSK2WIEM.js → chunk-2YODOG2O.js} +2 -2
- package/dist/{chunk-WYG5UNDY.js → chunk-3AUDELZL.js} +2 -2
- package/dist/{chunk-OFVCFKDH.js → chunk-3CYOCE6U.js} +3 -3
- package/dist/{chunk-6HM6HARE.js → chunk-3FBAEPCU.js} +2 -2
- package/dist/{chunk-PIITB2YZ.js → chunk-3ZS3NUEI.js} +4 -4
- package/dist/{chunk-V4XE263I.js → chunk-43AA5FVD.js} +1 -1
- package/dist/chunk-43AA5FVD.js.map +1 -0
- package/dist/{chunk-ANZZLRUX.js → chunk-4IVRQMZW.js} +2 -2
- package/dist/{chunk-63E37JCN.js → chunk-4RAJT5L2.js} +4 -4
- package/dist/{chunk-45VUHDV2.js → chunk-5CO5G4MN.js} +2 -2
- package/dist/{chunk-PUEO37QG.js → chunk-5YT2IIFX.js} +729 -17
- package/dist/chunk-5YT2IIFX.js.map +1 -0
- package/dist/{chunk-P5VZSSYL.js → chunk-622V2AZA.js} +233 -40
- package/dist/chunk-622V2AZA.js.map +1 -0
- package/dist/{chunk-3UMDRGFS.js → chunk-65HWAQ4F.js} +3 -3
- package/dist/{chunk-T67LUYCR.js → chunk-77NTFICT.js} +5 -5
- package/dist/chunk-A2WFUMUN.js +86 -0
- package/dist/chunk-A2WFUMUN.js.map +1 -0
- package/dist/{chunk-UE6RRXMF.js → chunk-A4F7OIC5.js} +2 -2
- package/dist/chunk-AHA5VQGP.js +117 -0
- package/dist/chunk-AHA5VQGP.js.map +1 -0
- package/dist/{chunk-AOFEMUHG.js → chunk-ATG2PQ4V.js} +56 -5
- package/dist/chunk-ATG2PQ4V.js.map +1 -0
- package/dist/{chunk-63O7LVMN.js → chunk-B33YWUJV.js} +3 -3
- package/dist/{chunk-QVMOCJRV.js → chunk-B377OKUP.js} +2 -2
- package/dist/{chunk-IUKYFTGW.js → chunk-B3DR2JBZ.js} +140 -125
- package/dist/chunk-B3DR2JBZ.js.map +1 -0
- package/dist/{chunk-ZT6WPSII.js → chunk-BGHCSLE5.js} +2 -2
- package/dist/{chunk-YYOMGOHB.js → chunk-BOK6BN5M.js} +2 -2
- package/dist/{chunk-OH7QLI6W.js → chunk-BPPUH5JM.js} +5 -5
- package/dist/{chunk-EV76IZ37.js → chunk-BTI4C4MA.js} +2 -2
- package/dist/{chunk-O6ZSWHVR.js → chunk-C2KE5DMD.js} +4 -4
- package/dist/{chunk-47F6WWLQ.js → chunk-C73N66PR.js} +2 -2
- package/dist/{chunk-3XIWW4TB.js → chunk-CGJYL633.js} +2 -2
- package/dist/{chunk-WRT7GJ2D.js → chunk-CIWMTNHW.js} +4 -4
- package/dist/{chunk-O55EKIEC.js → chunk-D3ZKHLN2.js} +2 -2
- package/dist/{chunk-CD4BCCNP.js → chunk-DJ7WVEWX.js} +2 -2
- package/dist/{chunk-RO7RUBLD.js → chunk-EFL24KOI.js} +2 -2
- package/dist/{chunk-4CJTQHEG.js → chunk-ESLOLCD4.js} +3 -3
- package/dist/{chunk-Z4Q6USKJ.js → chunk-ETBVCRVL.js} +4 -4
- package/dist/{chunk-VILPVT4S.js → chunk-EZ47HOJ6.js} +4 -4
- package/dist/{chunk-EFYN5I7Z.js → chunk-F4QFU5IZ.js} +2 -2
- package/dist/{chunk-T6OZW2WR.js → chunk-FA7SLQ5X.js} +7 -1
- package/dist/chunk-FA7SLQ5X.js.map +1 -0
- package/dist/{chunk-SHUBTAY2.js → chunk-FMKILENU.js} +14 -14
- package/dist/{chunk-YLXETSAH.js → chunk-GCZJ7XRI.js} +2 -2
- package/dist/{chunk-J4MMPQ7Q.js → chunk-GQXOBKPL.js} +7 -9
- package/dist/{chunk-J4MMPQ7Q.js.map → chunk-GQXOBKPL.js.map} +1 -1
- package/dist/{chunk-UDKWFDV2.js → chunk-HLUHEMN4.js} +27 -3
- package/dist/chunk-HLUHEMN4.js.map +1 -0
- package/dist/{chunk-QFEDITXM.js → chunk-JNDMIAJ4.js} +2 -2
- package/dist/chunk-JTSSSR4L.js +359 -0
- package/dist/chunk-JTSSSR4L.js.map +1 -0
- package/dist/{chunk-5KBIOI4G.js → chunk-JVBAPAWZ.js} +217 -13
- package/dist/chunk-JVBAPAWZ.js.map +1 -0
- package/dist/{chunk-E5PPE4VA.js → chunk-K2VRKCPA.js} +2 -2
- package/dist/{chunk-MKRYQROF.js → chunk-LGEHAQHK.js} +4 -4
- package/dist/{chunk-7HTUYFR3.js → chunk-LLPMMMWG.js} +27 -10
- package/dist/chunk-LLPMMMWG.js.map +1 -0
- package/dist/{chunk-XYTQJD7F.js → chunk-LMJVP5CM.js} +2 -2
- package/dist/{chunk-JC36FPQ5.js → chunk-LPV72KRQ.js} +2 -2
- package/dist/{chunk-2LCRG2YV.js → chunk-M2XR6B6V.js} +2 -2
- package/dist/{chunk-GGRRF4PZ.js → chunk-MDPT6QFK.js} +1 -1
- package/dist/{chunk-GGRRF4PZ.js.map → chunk-MDPT6QFK.js.map} +1 -1
- package/dist/{chunk-VPQFK63H.js → chunk-MH632KPO.js} +2 -2
- package/dist/{chunk-EQ4U73UH.js → chunk-MLAMXQAW.js} +14 -2
- package/dist/chunk-MLAMXQAW.js.map +1 -0
- package/dist/{chunk-ZU5JDLOH.js → chunk-MMRTPH3J.js} +2 -2
- package/dist/{chunk-ZZ67UTQX.js → chunk-MUX7IL77.js} +2 -2
- package/dist/{chunk-RYWROSL5.js → chunk-NAQ4A5IX.js} +7 -7
- package/dist/{chunk-TVIVY7KU.js → chunk-NED7IR7B.js} +4 -4
- package/dist/{chunk-ADYP7HXE.js → chunk-NHVAX7MH.js} +3 -3
- package/dist/{chunk-TG3R2SID.js → chunk-NMGHGTVR.js} +5 -5
- package/dist/{chunk-YCNIGRRF.js → chunk-NS7U4ZYU.js} +13 -117
- package/dist/chunk-NS7U4ZYU.js.map +1 -0
- package/dist/{chunk-NGHDW6UU.js → chunk-NUQEREIP.js} +2 -2
- package/dist/{chunk-5IIQU4JZ.js → chunk-OB72KNMF.js} +4 -4
- package/dist/{chunk-7L3TODI7.js → chunk-OJXJWNFY.js} +2 -2
- package/dist/{chunk-66L5ZRJC.js → chunk-P3WRNWWM.js} +157 -4
- package/dist/chunk-P3WRNWWM.js.map +1 -0
- package/dist/{chunk-E6M225BJ.js → chunk-PC5DRBGD.js} +18 -8
- package/dist/chunk-PC5DRBGD.js.map +1 -0
- package/dist/{chunk-EURPSF4Y.js → chunk-PH5ZQM35.js} +2 -2
- package/dist/{chunk-BMWMTAQF.js → chunk-PRAUN6PN.js} +3 -3
- package/dist/{chunk-RWGRSKN5.js → chunk-PT4DZDJA.js} +2 -2
- package/dist/{chunk-7EPY3IQL.js → chunk-PZJQ46DH.js} +2 -2
- package/dist/{chunk-AH637HZ5.js → chunk-QDESY76F.js} +3 -3
- package/dist/{chunk-I6QGI65T.js → chunk-QRMG4HK4.js} +3 -3
- package/dist/{chunk-ZU3NJNO7.js → chunk-QSF5SJAM.js} +4 -4
- package/dist/{chunk-5IYOKDM4.js → chunk-R23JWJD3.js} +5 -5
- package/dist/{chunk-EWJZZDRG.js → chunk-R4BBGPI2.js} +219 -219
- package/dist/chunk-R4BBGPI2.js.map +1 -0
- package/dist/{chunk-LJQFJF4J.js → chunk-RM2YUFH6.js} +6 -6
- package/dist/{chunk-IPT2OY4B.js → chunk-RQFXZYTF.js} +35 -33
- package/dist/chunk-RQFXZYTF.js.map +1 -0
- package/dist/{chunk-AP6NCP3V.js → chunk-S3S72HRM.js} +8 -6
- package/dist/chunk-S3S72HRM.js.map +1 -0
- package/dist/{chunk-6QMWECO5.js → chunk-SGQPR5SX.js} +7 -7
- package/dist/{chunk-ZGFLIK5H.js → chunk-SPAKBUJY.js} +2 -2
- package/dist/{chunk-ER7XANP3.js → chunk-SQ3GEIEL.js} +4 -4
- package/dist/{chunk-5UCMYPVF.js → chunk-SVT5IA2H.js} +2 -2
- package/dist/{chunk-HVKUCT3W.js → chunk-T4KY6UYF.js} +5 -3
- package/dist/{chunk-HVKUCT3W.js.map → chunk-T4KY6UYF.js.map} +1 -1
- package/dist/{chunk-FRSHF6FU.js → chunk-TJ4F6H4H.js} +3 -3
- package/dist/{chunk-R6HMAIKM.js → chunk-TOSPNM4T.js} +2 -2
- package/dist/{chunk-NJJMVS5Y.js → chunk-TRQE3RYR.js} +5 -5
- package/dist/{chunk-RMXS4I66.js → chunk-TVDJEUV5.js} +2 -2
- package/dist/{chunk-PLCYKAWH.js → chunk-TYRWSE4B.js} +2 -2
- package/dist/{chunk-YS2GHSWN.js → chunk-UBEKET2V.js} +389 -39
- package/dist/chunk-UBEKET2V.js.map +1 -0
- package/dist/{chunk-IQWUACVG.js → chunk-UHMHJX5G.js} +3 -3
- package/dist/{chunk-YODUDA6R.js → chunk-UN4RVUG2.js} +22 -22
- package/dist/{chunk-KZSGBWOJ.js → chunk-V2KGDESV.js} +2 -2
- package/dist/{chunk-E6Z4FBX6.js → chunk-V2QSNFCT.js} +2 -2
- package/dist/{chunk-BCCG7VWM.js → chunk-V65H7MI6.js} +396 -13
- package/dist/chunk-V65H7MI6.js.map +1 -0
- package/dist/chunk-W54Z2MOP.js +4500 -0
- package/dist/chunk-W54Z2MOP.js.map +1 -0
- package/dist/{chunk-N74SGESS.js → chunk-WKSQTMU7.js} +2 -2
- package/dist/{chunk-RIZ5NUNN.js → chunk-WQ4XKIOX.js} +4 -4
- package/dist/{chunk-WWK6J6SE.js → chunk-WQURALGV.js} +4996 -2748
- package/dist/chunk-WQURALGV.js.map +1 -0
- package/dist/{chunk-QEYTRFR3.js → chunk-X26YOFGS.js} +2 -2
- package/dist/{chunk-ITHZFHST.js → chunk-X2BVB74H.js} +2 -2
- package/dist/{chunk-GNXGJBCA.js → chunk-XIYLWEXC.js} +2 -2
- package/dist/{chunk-IBJ6U753.js → chunk-XKZBZOKV.js} +4 -4
- package/dist/{chunk-7O2MC42R.js → chunk-XNRLRNQF.js} +2 -2
- package/dist/{chunk-65AUYLU3.js → chunk-XPXHQI6N.js} +2 -2
- package/dist/{chunk-JBCCWVU2.js → chunk-XRDYRWZI.js} +2 -2
- package/dist/chunk-XRWWCW4H.js +102 -0
- package/dist/chunk-XRWWCW4H.js.map +1 -0
- package/dist/chunk-YKJBSMMP.js +67137 -0
- package/dist/chunk-YKJBSMMP.js.map +1 -0
- package/dist/{chunk-2JSDPUKD.js → chunk-YMFPFXEX.js} +2 -2
- package/dist/{chunk-645WOU5I.js → chunk-ZDZJNXL6.js} +4 -4
- package/dist/{chunk-6TCXIRP5.js → chunk-ZKE4CO6B.js} +4 -4
- package/dist/{chunk-KEPXC4KN.js → chunk-ZSE774RV.js} +2 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +305 -187
- package/dist/coding/export-okf-codegraph.js +29 -29
- package/dist/compounding/engine.js +23 -24
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +36 -37
- package/dist/connectors/codex-materialize-runner.js +20 -20
- package/dist/connectors/index.js +21 -21
- package/dist/connectors/live/framework.d.ts +7 -0
- package/dist/connectors/live/framework.d.ts.map +1 -1
- package/dist/connectors/live/google-drive.d.ts +17 -3
- package/dist/connectors/live/google-drive.d.ts.map +1 -1
- package/dist/consolidation-provenance-check.js +4 -4
- package/dist/consolidation-undo.js +5 -5
- package/dist/contradiction/index.js +7 -6
- package/dist/{contradiction-scan-P2MHYFVG.js → contradiction-scan-IBPYGQZF.js} +4 -3
- package/dist/convergence-refresh.js +22 -22
- package/dist/corpus-watermark.js +23 -23
- package/dist/deep-recall-http-glue.js +2 -2
- package/dist/deep-recall-policy-llm.js +4 -4
- package/dist/direct-answer-wiring.js +2 -2
- package/dist/embedding-fallback.js +1 -1
- package/dist/entity-retrieval.js +21 -21
- package/dist/eval-trajectory-store.js +2 -2
- package/dist/eval-trajectory.js +2 -2
- package/dist/event-order-recall.js +2 -2
- package/dist/explicit-capture.d.ts +3 -2
- package/dist/explicit-capture.d.ts.map +1 -1
- package/dist/explicit-capture.js +530 -6
- package/dist/explicit-cue-recall.js +2 -2
- package/dist/external-wiki-access.js +2 -283
- package/dist/extraction-faithfulness.js +4 -4
- package/dist/extraction-judge.js +5 -5
- package/dist/extraction-normalization.js +1 -1
- package/dist/extraction-quote-grounding.d.ts.map +1 -1
- package/dist/extraction-quote-grounding.js +1 -1
- package/dist/extraction-source-grounding-rules.js +2 -2
- package/dist/extraction-source-grounding.js +3 -3
- package/dist/extraction.js +43 -44
- package/dist/fallback-llm-codex-subscription.js +2 -2
- package/dist/fallback-llm.js +3 -3
- package/dist/{first-start-migration-QFI7B5SH.js → first-start-migration-FILJH3RO.js} +6 -5
- package/dist/{first-start-migration-QFI7B5SH.js.map → first-start-migration-FILJH3RO.js.map} +1 -1
- package/dist/focused-list-recall.js +2 -2
- package/dist/harmonic-retrieval.js +20 -20
- package/dist/index.js +776 -797
- package/dist/lcm/index.js +2 -2
- package/dist/live-connectors-runner.d.ts +2 -0
- package/dist/live-connectors-runner.d.ts.map +1 -1
- package/dist/live-connectors-runner.js +3 -4
- package/dist/local-llm-config.js +2 -2
- package/dist/local-llm-helpers.d.ts +4 -0
- package/dist/local-llm-helpers.d.ts.map +1 -1
- package/dist/local-llm-helpers.js +3 -1
- package/dist/local-llm.d.ts +4 -1
- package/dist/local-llm.d.ts.map +1 -1
- package/dist/local-llm.js +4 -4
- package/dist/location/index.js +27 -29
- package/dist/maintenance/memory-governance.js +20 -20
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +20 -20
- package/dist/maintenance/rebuild-memory-projection-runner.js +22 -22
- package/dist/maintenance/rebuild-memory-projection.js +21 -21
- package/dist/memory-browse-operations.js +2 -283
- package/dist/memory-browse.js +8 -7
- package/dist/memory-controller.js +2 -2
- package/dist/memory-subject.js +2 -2
- package/dist/namespaces/migrate.js +21 -21
- package/dist/namespaces/search.js +5 -5
- package/dist/namespaces/storage.js +20 -20
- package/dist/offline-sync-changeset-generations.js +1 -1
- package/dist/offline-sync-embedding-generation.js +1 -1
- package/dist/offline-sync-impression-drain.js +6 -6
- package/dist/offline-sync-snapshot-stream.js +1 -1
- package/dist/offline-sync.js +1 -1
- package/dist/operator-doctor-corpus.js +24 -24
- package/dist/operator-toolkit.js +58 -61
- package/dist/orchestration/backlog-scheduler.js +279 -295
- package/dist/orchestration/backlog-scheduler.js.map +1 -1
- package/dist/orchestration/buffer-drain-runner.js +279 -295
- package/dist/orchestration/extraction-run.d.ts +4 -0
- package/dist/orchestration/extraction-run.d.ts.map +1 -1
- package/dist/orchestration/fact-dedup-window.d.ts +1 -1
- package/dist/orchestration/fact-dedup-window.d.ts.map +1 -1
- package/dist/orchestration/maintenance.js +39 -42
- package/dist/orchestrator.d.ts +11 -11
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +291 -308
- package/dist/page-versioning.js +3 -3
- package/dist/qmd-daemon-session.js +2 -2
- package/dist/qmd.js +3 -3
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-navigate-access.js +2 -2
- package/dist/recall-navigate-disclosure.js +2 -2
- package/dist/recall-navigate.js +2 -2
- package/dist/recall-navigation-operations.js +2 -283
- package/dist/recall-navigation.js +334 -61
- package/dist/recall-planner-llm.js +3 -3
- package/dist/recall-state-view-admission.js +6 -6
- package/dist/recall-why-service.js +10 -9
- package/dist/recall-why.js +4 -3
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.js +2 -2
- package/dist/reconcile/manifest.js +3 -3
- package/dist/{resolution-N7H3F2E4.js → resolution-OVVIF7ZY.js} +8 -7
- package/dist/response-guidance-recall.js +2 -2
- package/dist/resume-bundles.js +40 -41
- package/dist/salvage-envelope.js +4 -4
- package/dist/search/factory.js +4 -4
- package/dist/search/index.js +4 -4
- package/dist/semantic-consolidation.js +23 -23
- package/dist/semantic-rule-promotion.js +20 -20
- package/dist/semantic-rule-verifier.js +20 -20
- package/dist/shared-context/manager.js +2 -3
- package/dist/source-agent-qualifier.js +32 -33
- package/dist/standup/build.js +20 -20
- package/dist/{state-store-I5HLZTZT.js → state-store-EEAX2MBS.js} +2 -2
- package/dist/storage/content-hash-index.d.ts +10 -0
- package/dist/storage/content-hash-index.d.ts.map +1 -1
- package/dist/storage.js +19 -19
- package/dist/subject-config.js +3 -3
- package/dist/summarizer.d.ts +4 -1
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +6 -6
- package/dist/support-passport/index.js +53 -297
- package/dist/targeted-fact-recall.js +2 -2
- package/dist/tier-routing.js +3 -2
- package/dist/{tier-stats-FQGQSFET.js → tier-stats-FXEUJN73.js} +6 -5
- package/dist/{tier-stats-FQGQSFET.js.map → tier-stats-FXEUJN73.js.map} +1 -1
- package/dist/transcript.js +2 -2
- package/dist/transfer/capsule-import.js +4 -4
- package/dist/transfer/export-okf.js +20 -20
- package/dist/types.d.ts +10 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/verified-recall.js +20 -20
- package/dist/wearables/memory-gen.d.ts +5 -1
- package/dist/wearables/memory-gen.d.ts.map +1 -1
- package/dist/wearables/service.d.ts.map +1 -1
- package/dist/who-knows-cli.js +21 -21
- package/dist/who-knows.js +20 -20
- package/dist/write-envelope.js +3 -3
- package/package.json +2 -2
- package/src/access-boundary.ts +5 -1
- package/src/access-category-alias.test.ts +5 -0
- package/src/access-mcp-frames.test.ts +60 -5
- package/src/access-mcp-frames.ts +72 -18
- package/src/access-mcp.test.ts +66 -2
- package/src/access-mcp.ts +19 -13
- package/src/access-service-coding-write.test.ts +7 -0
- package/src/access-service.ts +3 -0
- package/src/activity/journal-extract-storage.test.ts +31 -0
- package/src/activity/journal-extract.test.ts +1 -0
- package/src/activity/journal-extract.ts +24 -3
- package/src/cli.ts +1 -2
- package/src/config.test.ts +17 -0
- package/src/config.ts +25 -7
- package/src/connectors/live/framework.ts +8 -0
- package/src/connectors/live/google-drive.test.ts +74 -0
- package/src/connectors/live/google-drive.ts +138 -11
- package/src/explicit-capture.test.ts +109 -17
- package/src/explicit-capture.ts +90 -37
- package/src/extraction-quote-grounding.test.ts +20 -0
- package/src/extraction-quote-grounding.ts +21 -11
- package/src/fact-dedup-window.test.ts +37 -0
- package/src/live-connectors-runner.ts +3 -0
- package/src/live-connectors-secretref.test.ts +126 -0
- package/src/local-llm-helpers.ts +12 -0
- package/src/local-llm-shutdown.test.ts +194 -0
- package/src/local-llm.ts +32 -30
- package/src/orchestration/extraction-run.ts +12 -3
- package/src/orchestration/fact-dedup-window.ts +5 -1
- package/src/orchestrator-connector-dedup.test.ts +9 -5
- package/src/orchestrator.ts +52 -55
- package/src/storage/content-hash-index.ts +27 -2
- package/src/storage-fact-hash-publish-failure.test.ts +87 -0
- package/src/summarizer-shutdown.test.ts +195 -0
- package/src/summarizer.ts +15 -2
- package/src/types.ts +10 -10
- package/src/wearables/memory-gen.test.ts +20 -0
- package/src/wearables/memory-gen.ts +14 -7
- package/src/wearables/service.ts +14 -1
- package/src/wearables/storage-io.test.ts +25 -0
- package/dist/chunk-27XYV2XL.js +0 -43
- package/dist/chunk-27XYV2XL.js.map +0 -1
- package/dist/chunk-5KBIOI4G.js.map +0 -1
- package/dist/chunk-66L5ZRJC.js.map +0 -1
- package/dist/chunk-7HTUYFR3.js.map +0 -1
- package/dist/chunk-AOFEMUHG.js.map +0 -1
- package/dist/chunk-AP6NCP3V.js.map +0 -1
- package/dist/chunk-APYY5CX3.js +0 -335
- package/dist/chunk-APYY5CX3.js.map +0 -1
- package/dist/chunk-ATPWMT2E.js +0 -223
- package/dist/chunk-ATPWMT2E.js.map +0 -1
- package/dist/chunk-BCCG7VWM.js.map +0 -1
- package/dist/chunk-E6M225BJ.js.map +0 -1
- package/dist/chunk-EQ4U73UH.js.map +0 -1
- package/dist/chunk-ETSJD5IV.js +0 -361
- package/dist/chunk-ETSJD5IV.js.map +0 -1
- package/dist/chunk-EWJZZDRG.js.map +0 -1
- package/dist/chunk-FHGWKODN.js +0 -2300
- package/dist/chunk-FHGWKODN.js.map +0 -1
- package/dist/chunk-GRTKLWYX.js +0 -621
- package/dist/chunk-GRTKLWYX.js.map +0 -1
- package/dist/chunk-IGLJNHOX.js +0 -62
- package/dist/chunk-IGLJNHOX.js.map +0 -1
- package/dist/chunk-IPT2OY4B.js.map +0 -1
- package/dist/chunk-IUKYFTGW.js.map +0 -1
- package/dist/chunk-KUIRDD74.js +0 -3991
- package/dist/chunk-KUIRDD74.js.map +0 -1
- package/dist/chunk-MVMRUGBH.js +0 -1919
- package/dist/chunk-MVMRUGBH.js.map +0 -1
- package/dist/chunk-ODL4ZTNI.js +0 -3644
- package/dist/chunk-ODL4ZTNI.js.map +0 -1
- package/dist/chunk-OGXXGHW6.js +0 -223
- package/dist/chunk-OGXXGHW6.js.map +0 -1
- package/dist/chunk-P5VZSSYL.js.map +0 -1
- package/dist/chunk-PPCLZ4JU.js +0 -28
- package/dist/chunk-PPCLZ4JU.js.map +0 -1
- package/dist/chunk-PUEO37QG.js.map +0 -1
- package/dist/chunk-RRTVGD3J.js +0 -792
- package/dist/chunk-RRTVGD3J.js.map +0 -1
- package/dist/chunk-STSLB4HT.js +0 -18793
- package/dist/chunk-STSLB4HT.js.map +0 -1
- package/dist/chunk-T6OZW2WR.js.map +0 -1
- package/dist/chunk-TRBGGJKZ.js +0 -32218
- package/dist/chunk-TRBGGJKZ.js.map +0 -1
- package/dist/chunk-UDKWFDV2.js.map +0 -1
- package/dist/chunk-V4XE263I.js.map +0 -1
- package/dist/chunk-V7IZKJI3.js +0 -22
- package/dist/chunk-V7IZKJI3.js.map +0 -1
- package/dist/chunk-VMVVSZTD.js +0 -61
- package/dist/chunk-VMVVSZTD.js.map +0 -1
- package/dist/chunk-VNUOAXWP.js +0 -203
- package/dist/chunk-VNUOAXWP.js.map +0 -1
- package/dist/chunk-WLBEZZK5.js +0 -729
- package/dist/chunk-WLBEZZK5.js.map +0 -1
- package/dist/chunk-WUUES6CU.js +0 -25
- package/dist/chunk-WUUES6CU.js.map +0 -1
- package/dist/chunk-WWK6J6SE.js.map +0 -1
- package/dist/chunk-XICOH5HT.js +0 -346
- package/dist/chunk-XICOH5HT.js.map +0 -1
- package/dist/chunk-YCNIGRRF.js.map +0 -1
- package/dist/chunk-YS2GHSWN.js.map +0 -1
- package/dist/chunk-ZCLEYJPB.js +0 -10143
- package/dist/chunk-ZCLEYJPB.js.map +0 -1
- /package/dist/{capsule-merge-2IIXFXN5.js.map → capsule-merge-6IDAYTKG.js.map} +0 -0
- /package/dist/{chunk-QL7RHYK6.js.map → chunk-2LMA22UU.js.map} +0 -0
- /package/dist/{chunk-RGUUBAT5.js.map → chunk-2WCLTWV2.js.map} +0 -0
- /package/dist/{chunk-RSK2WIEM.js.map → chunk-2YODOG2O.js.map} +0 -0
- /package/dist/{chunk-WYG5UNDY.js.map → chunk-3AUDELZL.js.map} +0 -0
- /package/dist/{chunk-OFVCFKDH.js.map → chunk-3CYOCE6U.js.map} +0 -0
- /package/dist/{chunk-6HM6HARE.js.map → chunk-3FBAEPCU.js.map} +0 -0
- /package/dist/{chunk-PIITB2YZ.js.map → chunk-3ZS3NUEI.js.map} +0 -0
- /package/dist/{chunk-ANZZLRUX.js.map → chunk-4IVRQMZW.js.map} +0 -0
- /package/dist/{chunk-63E37JCN.js.map → chunk-4RAJT5L2.js.map} +0 -0
- /package/dist/{chunk-45VUHDV2.js.map → chunk-5CO5G4MN.js.map} +0 -0
- /package/dist/{chunk-3UMDRGFS.js.map → chunk-65HWAQ4F.js.map} +0 -0
- /package/dist/{chunk-T67LUYCR.js.map → chunk-77NTFICT.js.map} +0 -0
- /package/dist/{chunk-UE6RRXMF.js.map → chunk-A4F7OIC5.js.map} +0 -0
- /package/dist/{chunk-63O7LVMN.js.map → chunk-B33YWUJV.js.map} +0 -0
- /package/dist/{chunk-QVMOCJRV.js.map → chunk-B377OKUP.js.map} +0 -0
- /package/dist/{chunk-ZT6WPSII.js.map → chunk-BGHCSLE5.js.map} +0 -0
- /package/dist/{chunk-YYOMGOHB.js.map → chunk-BOK6BN5M.js.map} +0 -0
- /package/dist/{chunk-OH7QLI6W.js.map → chunk-BPPUH5JM.js.map} +0 -0
- /package/dist/{chunk-EV76IZ37.js.map → chunk-BTI4C4MA.js.map} +0 -0
- /package/dist/{chunk-O6ZSWHVR.js.map → chunk-C2KE5DMD.js.map} +0 -0
- /package/dist/{chunk-47F6WWLQ.js.map → chunk-C73N66PR.js.map} +0 -0
- /package/dist/{chunk-3XIWW4TB.js.map → chunk-CGJYL633.js.map} +0 -0
- /package/dist/{chunk-WRT7GJ2D.js.map → chunk-CIWMTNHW.js.map} +0 -0
- /package/dist/{chunk-O55EKIEC.js.map → chunk-D3ZKHLN2.js.map} +0 -0
- /package/dist/{chunk-CD4BCCNP.js.map → chunk-DJ7WVEWX.js.map} +0 -0
- /package/dist/{chunk-RO7RUBLD.js.map → chunk-EFL24KOI.js.map} +0 -0
- /package/dist/{chunk-4CJTQHEG.js.map → chunk-ESLOLCD4.js.map} +0 -0
- /package/dist/{chunk-Z4Q6USKJ.js.map → chunk-ETBVCRVL.js.map} +0 -0
- /package/dist/{chunk-VILPVT4S.js.map → chunk-EZ47HOJ6.js.map} +0 -0
- /package/dist/{chunk-EFYN5I7Z.js.map → chunk-F4QFU5IZ.js.map} +0 -0
- /package/dist/{chunk-SHUBTAY2.js.map → chunk-FMKILENU.js.map} +0 -0
- /package/dist/{chunk-YLXETSAH.js.map → chunk-GCZJ7XRI.js.map} +0 -0
- /package/dist/{chunk-QFEDITXM.js.map → chunk-JNDMIAJ4.js.map} +0 -0
- /package/dist/{chunk-E5PPE4VA.js.map → chunk-K2VRKCPA.js.map} +0 -0
- /package/dist/{chunk-MKRYQROF.js.map → chunk-LGEHAQHK.js.map} +0 -0
- /package/dist/{chunk-XYTQJD7F.js.map → chunk-LMJVP5CM.js.map} +0 -0
- /package/dist/{chunk-JC36FPQ5.js.map → chunk-LPV72KRQ.js.map} +0 -0
- /package/dist/{chunk-2LCRG2YV.js.map → chunk-M2XR6B6V.js.map} +0 -0
- /package/dist/{chunk-VPQFK63H.js.map → chunk-MH632KPO.js.map} +0 -0
- /package/dist/{chunk-ZU5JDLOH.js.map → chunk-MMRTPH3J.js.map} +0 -0
- /package/dist/{chunk-ZZ67UTQX.js.map → chunk-MUX7IL77.js.map} +0 -0
- /package/dist/{chunk-RYWROSL5.js.map → chunk-NAQ4A5IX.js.map} +0 -0
- /package/dist/{chunk-TVIVY7KU.js.map → chunk-NED7IR7B.js.map} +0 -0
- /package/dist/{chunk-ADYP7HXE.js.map → chunk-NHVAX7MH.js.map} +0 -0
- /package/dist/{chunk-TG3R2SID.js.map → chunk-NMGHGTVR.js.map} +0 -0
- /package/dist/{chunk-NGHDW6UU.js.map → chunk-NUQEREIP.js.map} +0 -0
- /package/dist/{chunk-5IIQU4JZ.js.map → chunk-OB72KNMF.js.map} +0 -0
- /package/dist/{chunk-7L3TODI7.js.map → chunk-OJXJWNFY.js.map} +0 -0
- /package/dist/{chunk-EURPSF4Y.js.map → chunk-PH5ZQM35.js.map} +0 -0
- /package/dist/{chunk-BMWMTAQF.js.map → chunk-PRAUN6PN.js.map} +0 -0
- /package/dist/{chunk-RWGRSKN5.js.map → chunk-PT4DZDJA.js.map} +0 -0
- /package/dist/{chunk-7EPY3IQL.js.map → chunk-PZJQ46DH.js.map} +0 -0
- /package/dist/{chunk-AH637HZ5.js.map → chunk-QDESY76F.js.map} +0 -0
- /package/dist/{chunk-I6QGI65T.js.map → chunk-QRMG4HK4.js.map} +0 -0
- /package/dist/{chunk-ZU3NJNO7.js.map → chunk-QSF5SJAM.js.map} +0 -0
- /package/dist/{chunk-5IYOKDM4.js.map → chunk-R23JWJD3.js.map} +0 -0
- /package/dist/{chunk-LJQFJF4J.js.map → chunk-RM2YUFH6.js.map} +0 -0
- /package/dist/{chunk-6QMWECO5.js.map → chunk-SGQPR5SX.js.map} +0 -0
- /package/dist/{chunk-ZGFLIK5H.js.map → chunk-SPAKBUJY.js.map} +0 -0
- /package/dist/{chunk-ER7XANP3.js.map → chunk-SQ3GEIEL.js.map} +0 -0
- /package/dist/{chunk-5UCMYPVF.js.map → chunk-SVT5IA2H.js.map} +0 -0
- /package/dist/{chunk-FRSHF6FU.js.map → chunk-TJ4F6H4H.js.map} +0 -0
- /package/dist/{chunk-R6HMAIKM.js.map → chunk-TOSPNM4T.js.map} +0 -0
- /package/dist/{chunk-NJJMVS5Y.js.map → chunk-TRQE3RYR.js.map} +0 -0
- /package/dist/{chunk-RMXS4I66.js.map → chunk-TVDJEUV5.js.map} +0 -0
- /package/dist/{chunk-PLCYKAWH.js.map → chunk-TYRWSE4B.js.map} +0 -0
- /package/dist/{chunk-IQWUACVG.js.map → chunk-UHMHJX5G.js.map} +0 -0
- /package/dist/{chunk-YODUDA6R.js.map → chunk-UN4RVUG2.js.map} +0 -0
- /package/dist/{chunk-KZSGBWOJ.js.map → chunk-V2KGDESV.js.map} +0 -0
- /package/dist/{chunk-E6Z4FBX6.js.map → chunk-V2QSNFCT.js.map} +0 -0
- /package/dist/{chunk-N74SGESS.js.map → chunk-WKSQTMU7.js.map} +0 -0
- /package/dist/{chunk-RIZ5NUNN.js.map → chunk-WQ4XKIOX.js.map} +0 -0
- /package/dist/{chunk-QEYTRFR3.js.map → chunk-X26YOFGS.js.map} +0 -0
- /package/dist/{chunk-ITHZFHST.js.map → chunk-X2BVB74H.js.map} +0 -0
- /package/dist/{chunk-GNXGJBCA.js.map → chunk-XIYLWEXC.js.map} +0 -0
- /package/dist/{chunk-IBJ6U753.js.map → chunk-XKZBZOKV.js.map} +0 -0
- /package/dist/{chunk-7O2MC42R.js.map → chunk-XNRLRNQF.js.map} +0 -0
- /package/dist/{chunk-65AUYLU3.js.map → chunk-XPXHQI6N.js.map} +0 -0
- /package/dist/{chunk-JBCCWVU2.js.map → chunk-XRDYRWZI.js.map} +0 -0
- /package/dist/{chunk-2JSDPUKD.js.map → chunk-YMFPFXEX.js.map} +0 -0
- /package/dist/{chunk-645WOU5I.js.map → chunk-ZDZJNXL6.js.map} +0 -0
- /package/dist/{chunk-6TCXIRP5.js.map → chunk-ZKE4CO6B.js.map} +0 -0
- /package/dist/{chunk-KEPXC4KN.js.map → chunk-ZSE774RV.js.map} +0 -0
- /package/dist/{contradiction-scan-P2MHYFVG.js.map → contradiction-scan-IBPYGQZF.js.map} +0 -0
- /package/dist/{resolution-N7H3F2E4.js.map → resolution-OVVIF7ZY.js.map} +0 -0
- /package/dist/{state-store-I5HLZTZT.js.map → state-store-EEAX2MBS.js.map} +0 -0
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "./chunk-QDW3E4RD.js";
|
|
7
7
|
import {
|
|
8
8
|
gatewayTaskChainOptions
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-MMRTPH3J.js";
|
|
10
10
|
import {
|
|
11
11
|
extractJsonCandidates
|
|
12
12
|
} from "./chunk-YBA52OKG.js";
|
|
@@ -496,4 +496,4 @@ export {
|
|
|
496
496
|
createVerdictCache,
|
|
497
497
|
createDeferCountMap
|
|
498
498
|
};
|
|
499
|
-
//# sourceMappingURL=chunk-
|
|
499
|
+
//# sourceMappingURL=chunk-LMJVP5CM.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
defineOperation
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-JTSSSR4L.js";
|
|
4
4
|
import {
|
|
5
5
|
EngramAccessInputError
|
|
6
6
|
} from "./chunk-CJCTKL6Y.js";
|
|
@@ -71,4 +71,4 @@ defineOperation({
|
|
|
71
71
|
})
|
|
72
72
|
})
|
|
73
73
|
});
|
|
74
|
-
//# sourceMappingURL=chunk-
|
|
74
|
+
//# sourceMappingURL=chunk-LPV72KRQ.js.map
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "./chunk-7RJUVTVF.js";
|
|
13
13
|
import {
|
|
14
14
|
ContentHashIndex
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-HLUHEMN4.js";
|
|
16
16
|
import {
|
|
17
17
|
computeLegacyContentHash,
|
|
18
18
|
normalizeLegacyContent
|
|
@@ -348,4 +348,4 @@ export {
|
|
|
348
348
|
buildReconcileManifest,
|
|
349
349
|
collapseActiveFactDuplicates
|
|
350
350
|
};
|
|
351
|
-
//# sourceMappingURL=chunk-
|
|
351
|
+
//# sourceMappingURL=chunk-M2XR6B6V.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/connectors/live/state-store.ts","../src/connectors/live/framework.ts"],"sourcesContent":["/**\n * @remnic/core — Live Connectors State Store (issue #683 PR 1/N)\n *\n * Persists per-connector cursor + sync metadata to\n * `<memoryDir>/state/connectors/<id>.json`\n *\n * Reasons this lives next to memory data, not in user config:\n * - cursors are *operational* state that should travel with the memory\n * directory when a user moves it across machines;\n * - it keeps memory + ingest provenance co-located so tooling that backs up\n * the memory directory captures cursor state too.\n *\n * Atomic-write contract (CLAUDE.md gotcha #54):\n * - We NEVER `rmSync(target)` before `renameSync(tmp, target)`.\n * - Writes go to a sibling tmp file and `rename()` swaps it in.\n * - On error, the tmp file is best-effort cleaned up; the previous good\n * state file is left untouched.\n *\n * Privacy: cursors are opaque connector-defined strings. We do not log them\n * and do not surface them through user-visible APIs. Document content NEVER\n * touches this module.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\n\nimport { expandTildePath } from \"../../utils/path.js\";\n\nimport { type ConnectorCursor, isValidConnectorId } from \"./framework.js\";\n\n/**\n * Status of the most recent sync attempt for a connector.\n *\n * `\"never\"` is distinct from `\"success\"` so callers can detect\n * \"registered but never run\" without inspecting timestamps. Per CLAUDE.md\n * gotcha #34, we deliberately distinguish empty/unknown from failure states.\n */\nexport type ConnectorSyncStatus = \"success\" | \"error\" | \"never\";\n\n/**\n * Persisted per-connector state.\n *\n * Stored as pretty-printed JSON for human inspection — the file is small\n * (one record per connector) and operators may need to debug stuck cursors\n * by hand.\n */\nexport interface ConnectorState {\n /** Connector id. Matches the filename stem. */\n readonly id: string;\n /** Last persisted cursor, or `null` if the connector has never synced. */\n readonly cursor: ConnectorCursor | null;\n /** ISO 8601 timestamp of the last completed sync attempt, or `null`. */\n readonly lastSyncAt: string | null;\n /** Status of the last completed sync attempt. */\n readonly lastSyncStatus: ConnectorSyncStatus;\n /** Optional error message from the last failed sync. Truncated to 1 KB. */\n readonly lastSyncError?: string;\n /** Cumulative count of documents successfully imported across all syncs. */\n readonly totalDocsImported: number;\n /** ISO 8601 timestamp of when this state record was last written. */\n readonly updatedAt: string;\n}\n\nconst STATE_DIR_NAME = \"state\";\nconst CONNECTORS_DIR_NAME = \"connectors\";\nconst CONNECTOR_LOCKS_DIR_NAME = \"connector-locks\";\nconst MAX_ERROR_LENGTH = 1024;\nconst CONNECTOR_LOCK_STALE_MS = 10 * 60 * 1000;\nconst CONNECTOR_LOCK_HEARTBEAT_MS = Math.max(1_000, Math.floor(CONNECTOR_LOCK_STALE_MS / 4));\nconst CONNECTOR_LOCK_TIMEOUT_MS = 60 * 1000;\nconst CONNECTOR_LOCK_RETRY_MS = 50;\nconst VALID_SYNC_STATUSES: ReadonlySet<ConnectorSyncStatus> = new Set([\"success\", \"error\", \"never\"]);\n\n/**\n * Internal error thrown when a state file's JSON is unparseable or its shape\n * doesn't match `ConnectorState`. Used by `listConnectorStates` to distinguish\n * \"skip this corrupt file\" cases from genuine I/O failures (`EACCES`, `EIO`)\n * that the caller must see.\n */\nclass ConnectorStateCorruptionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ConnectorStateCorruptionError\";\n }\n}\n\nexport class ConnectorStateLockLostError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ConnectorStateLockLostError\";\n }\n}\n\ninterface ConnectorLockLease {\n readonly path: string;\n readonly token: string;\n}\n\ninterface ConnectorLockMetadata {\n readonly pid: number;\n readonly token: string;\n readonly createdAt: string;\n readonly refreshedAt: string;\n}\n\ninterface ConnectorStateLockOptions {\n readonly heartbeatMs?: number;\n readonly unrefHeartbeat?: boolean;\n}\n\n/**\n * Resolve `<memoryDir>/state/connectors/`, expanding `~` per CLAUDE.md #17.\n */\nfunction resolveConnectorsDir(memoryDir: string): string {\n if (typeof memoryDir !== \"string\" || memoryDir.length === 0) {\n throw new TypeError(\"memoryDir must be a non-empty string\");\n }\n return path.join(expandTildePath(memoryDir), STATE_DIR_NAME, CONNECTORS_DIR_NAME);\n}\n\nfunction resolveConnectorLocksDir(memoryDir: string): string {\n if (typeof memoryDir !== \"string\" || memoryDir.length === 0) {\n throw new TypeError(\"memoryDir must be a non-empty string\");\n }\n return path.join(expandTildePath(memoryDir), STATE_DIR_NAME, CONNECTOR_LOCKS_DIR_NAME);\n}\n\n/**\n * Resolve the state file path for a single connector. Throws on invalid id\n * to prevent path traversal via crafted ids.\n */\nfunction resolveConnectorStatePath(memoryDir: string, id: string): string {\n if (!isValidConnectorId(id)) {\n throw new TypeError(\n `invalid connector id ${JSON.stringify(id)} — must match /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/`\n );\n }\n return path.join(resolveConnectorsDir(memoryDir), `${id}.json`);\n}\n\nfunction resolveConnectorLockPath(memoryDir: string, id: string): string {\n if (!isValidConnectorId(id)) {\n throw new TypeError(\n `invalid connector id ${JSON.stringify(id)} — must match /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/`\n );\n }\n return path.join(resolveConnectorLocksDir(memoryDir), `${id}.lock`);\n}\n\n/**\n * Type guard for parsed state records. Validates the on-disk shape so a\n * corrupted/edited file produces a clear error rather than crashing later.\n *\n * Per CLAUDE.md gotcha #18, JSON.parse('null') yields `null` which would\n * pass a naive truthy check. We explicitly require an object.\n */\nfunction isConnectorStateShape(value: unknown): value is ConnectorState {\n if (typeof value !== \"object\" || value === null) return false;\n const v = value as Record<string, unknown>;\n if (typeof v.id !== \"string\") return false;\n if (typeof v.lastSyncStatus !== \"string\") return false;\n if (![\"success\", \"error\", \"never\"].includes(v.lastSyncStatus)) return false;\n // totalDocsImported is a cumulative count — fractional values would corrupt\n // metrics on later increments. Mirror the boundary check in writeConnectorState.\n if (typeof v.totalDocsImported !== \"number\" || !Number.isInteger(v.totalDocsImported)) return false;\n if (v.totalDocsImported < 0) return false;\n if (typeof v.updatedAt !== \"string\") return false;\n if (v.lastSyncAt !== null && typeof v.lastSyncAt !== \"string\") return false;\n if (v.cursor !== null) {\n if (typeof v.cursor !== \"object\" || v.cursor === null) return false;\n const c = v.cursor as Record<string, unknown>;\n if (typeof c.kind !== \"string\" || typeof c.value !== \"string\" || typeof c.updatedAt !== \"string\") {\n return false;\n }\n }\n if (v.lastSyncError !== undefined && typeof v.lastSyncError !== \"string\") return false;\n return true;\n}\n\nfunction isConnectorLockMetadata(value: unknown): value is ConnectorLockMetadata {\n if (typeof value !== \"object\" || value === null) return false;\n const v = value as Record<string, unknown>;\n return (\n typeof v.pid === \"number\" &&\n Number.isInteger(v.pid) &&\n v.pid > 0 &&\n typeof v.token === \"string\" &&\n v.token.length > 0 &&\n typeof v.createdAt === \"string\" &&\n typeof v.refreshedAt === \"string\"\n );\n}\n\nfunction connectorLockMetadata(token: string, createdAt = new Date().toISOString()): ConnectorLockMetadata {\n return {\n pid: process.pid,\n token,\n createdAt,\n refreshedAt: new Date().toISOString(),\n };\n}\n\nasync function readConnectorLockMetadata(lockPath: string): Promise<ConnectorLockMetadata | null> {\n let raw: string;\n try {\n raw = await fs.readFile(lockPath, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n try {\n const parsed = JSON.parse(raw) as unknown;\n return isConnectorLockMetadata(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\nfunction parseConnectorLockMetadata(raw: string): ConnectorLockMetadata | null {\n try {\n const parsed = JSON.parse(raw) as unknown;\n return isConnectorLockMetadata(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\nfunction isSameFileIdentity(left: import(\"node:fs\").Stats, right: import(\"node:fs\").Stats): boolean {\n return left.dev === right.dev && left.ino === right.ino;\n}\n\n/**\n * Reject any path component along `<memoryDir>/state/connectors/<id>.json`\n * that is a symlink. Without this guard, a symlink in any of those\n * components would let `fs.readFile` escape the memory root and consume an\n * arbitrary outside file as cursor state — silently poisoning sync state and\n * violating the project-wide rule against symlink traversal.\n *\n * `lstat` is used (not `stat`) so we observe the link itself rather than its\n * target. Missing components are tolerated — the caller's `readFile` /\n * `mkdir` will surface ENOENT in its normal way.\n *\n * (PR #724 review.)\n */\nasync function assertNoSymlinkOnPath(memoryDir: string, filePath: string): Promise<void> {\n const expandedRoot = expandTildePath(memoryDir);\n // Normalize so `..` segments can't bypass the prefix check below.\n const root = path.resolve(expandedRoot);\n const target = path.resolve(filePath);\n const rel = path.relative(root, target);\n // path.relative() yields a \"../...\" prefix when target escapes root.\n if (rel.startsWith(\"..\") || path.isAbsolute(rel)) {\n throw new Error(`connector state path ${target} escapes memory root ${root}`);\n }\n // Walk every component from root to target (inclusive) and lstat each.\n const segments = rel.length === 0 ? [] : rel.split(path.sep);\n let current = root;\n const componentsToCheck = [current];\n for (const seg of segments) {\n current = path.join(current, seg);\n componentsToCheck.push(current);\n }\n for (const component of componentsToCheck) {\n let stat: import(\"node:fs\").Stats;\n try {\n stat = await fs.lstat(component);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n // Not yet created — that's fine; caller's readFile/mkdir handles it.\n continue;\n }\n throw err;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`connector state path component ${component} is a symlink; refusing to follow`);\n }\n }\n}\n\nasync function tryAcquireConnectorLock(memoryDir: string, id: string): Promise<ConnectorLockLease | null> {\n const dir = resolveConnectorLocksDir(memoryDir);\n const lockPath = resolveConnectorLockPath(memoryDir, id);\n await assertNoSymlinkOnPath(memoryDir, lockPath);\n await fs.mkdir(dir, { recursive: true });\n let handle: Awaited<ReturnType<typeof fs.open>> | null = null;\n let acquiredLockFile = false;\n const token = randomUUID();\n try {\n handle = await fs.open(lockPath, \"wx\", 0o600);\n acquiredLockFile = true;\n await handle.writeFile(`${JSON.stringify(connectorLockMetadata(token))}\\n`, \"utf8\");\n await handle.close();\n handle = null;\n return { path: lockPath, token };\n } catch (err) {\n if (acquiredLockFile) {\n if (handle !== null) {\n try {\n await handle.close();\n } catch {\n // The original write/close failure is more actionable.\n }\n }\n try {\n await fs.unlink(lockPath);\n } catch (cleanupErr) {\n if ((cleanupErr as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw new AggregateError(\n [err, cleanupErr],\n `failed to initialize connector state lock at ${lockPath}; cleanup also failed`\n );\n }\n }\n throw err;\n }\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") {\n throw err;\n }\n let stat: import(\"node:fs\").Stats;\n try {\n stat = await fs.lstat(lockPath);\n } catch (statErr) {\n if ((statErr as NodeJS.ErrnoException).code === \"ENOENT\") {\n return null;\n }\n throw statErr;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`connector state path component ${lockPath} is a symlink; refusing to follow`);\n }\n if (Date.now() - stat.mtimeMs > CONNECTOR_LOCK_STALE_MS) {\n await unlinkStaleConnectorLock(lockPath);\n return null;\n }\n return null;\n }\n}\n\nasync function shouldUnlinkStaleConnectorLock(lockPath: string, stat: import(\"node:fs\").Stats): Promise<boolean> {\n if (Date.now() - stat.mtimeMs <= CONNECTOR_LOCK_STALE_MS) return false;\n const metadata = await readConnectorLockMetadata(lockPath);\n if (metadata === null) return true;\n const refreshedAtMs = Date.parse(metadata.refreshedAt);\n if (!Number.isFinite(refreshedAtMs)) return true;\n return Date.now() - refreshedAtMs > CONNECTOR_LOCK_STALE_MS;\n}\n\nasync function unlinkStaleConnectorLock(lockPath: string): Promise<void> {\n const reclaimHandle = await openConnectorReclaimLock(lockPath);\n if (!reclaimHandle) return;\n try {\n await reclaimHandle.writeFile(`${process.pid}:${new Date().toISOString()}`, \"utf8\");\n await unlinkStaleConnectorLockWhileReclaimHeld(lockPath);\n } finally {\n await reclaimHandle.close().catch(() => undefined);\n await fs.unlink(connectorReclaimLockPath(lockPath)).catch(() => undefined);\n }\n}\n\nasync function unlinkStaleConnectorLockWhileReclaimHeld(lockPath: string): Promise<void> {\n let stat: import(\"node:fs\").Stats;\n try {\n stat = await fs.lstat(lockPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return;\n throw err;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`connector state path component ${lockPath} is a symlink; refusing to follow`);\n }\n if (!(await shouldUnlinkStaleConnectorLock(lockPath, stat))) return;\n try {\n await fs.unlink(lockPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw err;\n }\n }\n}\n\nfunction connectorReclaimLockPath(lockPath: string): string {\n return `${lockPath}.reclaim`;\n}\n\nasync function openConnectorReclaimLock(lockPath: string): Promise<Awaited<ReturnType<typeof fs.open>> | null> {\n const reclaimPath = connectorReclaimLockPath(lockPath);\n try {\n return await fs.open(reclaimPath, \"wx\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n }\n\n let reclaimStat: import(\"node:fs\").Stats;\n try {\n reclaimStat = await fs.lstat(reclaimPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n if (reclaimStat.isSymbolicLink()) {\n throw new Error(`connector state path component ${reclaimPath} is a symlink; refusing to follow`);\n }\n if (Date.now() - reclaimStat.mtimeMs <= CONNECTOR_LOCK_STALE_MS) return null;\n await fs.unlink(reclaimPath);\n\n try {\n return await fs.open(reclaimPath, \"wx\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"EEXIST\") return null;\n throw err;\n }\n}\n\nasync function refreshConnectorLock(lease: ConnectorLockLease): Promise<boolean> {\n const reclaimHandle = await openConnectorReclaimLock(lease.path);\n if (!reclaimHandle) return false;\n try {\n await reclaimHandle.writeFile(`${process.pid}:${new Date().toISOString()}`, \"utf8\");\n return await refreshConnectorLockWhileReclaimHeld(lease);\n } finally {\n await reclaimHandle.close().catch(() => undefined);\n await fs.unlink(connectorReclaimLockPath(lease.path)).catch(() => undefined);\n }\n}\n\nasync function refreshConnectorLockWhileReclaimHeld(lease: ConnectorLockLease): Promise<boolean> {\n let handle: Awaited<ReturnType<typeof fs.open>>;\n try {\n handle = await fs.open(lease.path, \"r+\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw err;\n }\n try {\n const openedStat = await handle.stat();\n const metadata = parseConnectorLockMetadata(await handle.readFile(\"utf8\"));\n if (metadata?.token !== lease.token) return false;\n const body = `${JSON.stringify(connectorLockMetadata(lease.token, metadata.createdAt))}\\n`;\n await handle.truncate(0);\n await handle.write(body, 0, \"utf8\");\n let pathStat: import(\"node:fs\").Stats;\n try {\n pathStat = await fs.lstat(lease.path);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw err;\n }\n if (pathStat.isSymbolicLink()) {\n throw new Error(`connector state path component ${lease.path} is a symlink; refusing to follow`);\n }\n return isSameFileIdentity(openedStat, pathStat);\n } finally {\n await handle.close();\n }\n}\n\nasync function releaseConnectorLock(lease: ConnectorLockLease): Promise<void> {\n const reclaimHandle = await openConnectorReclaimLock(lease.path);\n if (!reclaimHandle) return;\n try {\n await reclaimHandle.writeFile(`${process.pid}:${new Date().toISOString()}`, \"utf8\");\n await releaseConnectorLockWhileReclaimHeld(lease);\n } finally {\n await reclaimHandle.close().catch(() => undefined);\n await fs.unlink(connectorReclaimLockPath(lease.path)).catch(() => undefined);\n }\n}\n\nasync function releaseConnectorLockWhileReclaimHeld(lease: ConnectorLockLease): Promise<void> {\n let handle: Awaited<ReturnType<typeof fs.open>>;\n try {\n handle = await fs.open(lease.path, \"r\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return;\n throw err;\n }\n try {\n const openedStat = await handle.stat();\n const metadata = parseConnectorLockMetadata(await handle.readFile(\"utf8\"));\n if (metadata?.token !== lease.token) return;\n let pathStat: import(\"node:fs\").Stats;\n try {\n pathStat = await fs.lstat(lease.path);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return;\n throw err;\n }\n if (pathStat.isSymbolicLink()) {\n throw new Error(`connector state path component ${lease.path} is a symlink; refusing to follow`);\n }\n if (!isSameFileIdentity(openedStat, pathStat)) return;\n try {\n await fs.unlink(lease.path);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw err;\n }\n }\n } finally {\n await handle.close();\n }\n}\n\nasync function withConnectorStateLockInternal<T>(\n memoryDir: string,\n id: string,\n run: (abortSignal: AbortSignal) => Promise<T>,\n options: ConnectorStateLockOptions = {}\n): Promise<T> {\n const deadline = Date.now() + CONNECTOR_LOCK_TIMEOUT_MS;\n let lease: ConnectorLockLease | null = null;\n while (lease === null) {\n lease = await tryAcquireConnectorLock(memoryDir, id);\n if (lease !== null) break;\n if (Date.now() >= deadline) {\n throw new Error(`timed out waiting for connector \"${id}\" state lock`);\n }\n await delay(CONNECTOR_LOCK_RETRY_MS);\n }\n const abortController = new AbortController();\n let rejectLockLost!: (err: Error) => void;\n let lockLost = false;\n const lockLostPromise = new Promise<never>((_resolve, reject) => {\n rejectLockLost = reject;\n });\n const failLostLock = (message: string): void => {\n if (lockLost) return;\n lockLost = true;\n const err = new ConnectorStateLockLostError(message);\n abortController.abort(err);\n rejectLockLost(err);\n };\n const heartbeat = setInterval(() => {\n void refreshConnectorLock(lease)\n .then((refreshed) => {\n if (!refreshed) {\n failLostLock(`lost connector \"${id}\" state lock`);\n }\n })\n .catch((err) => {\n const message = err instanceof Error ? err.message : String(err);\n failLostLock(`lost connector \"${id}\" state lock: ${message}`);\n });\n }, options.heartbeatMs ?? CONNECTOR_LOCK_HEARTBEAT_MS);\n if (options.unrefHeartbeat !== false) {\n heartbeat.unref?.();\n }\n const runPromise = run(abortController.signal);\n try {\n return await Promise.race([runPromise, lockLostPromise]);\n } catch (err) {\n if (err instanceof ConnectorStateLockLostError) {\n await runPromise.catch(() => undefined);\n }\n throw err;\n } finally {\n clearInterval(heartbeat);\n await releaseConnectorLock(lease);\n }\n}\n\nexport async function withConnectorStateLock<T>(\n memoryDir: string,\n id: string,\n run: (abortSignal: AbortSignal) => Promise<T>\n): Promise<T> {\n return withConnectorStateLockInternal(memoryDir, id, run);\n}\n\nexport async function _withConnectorStateLockForTest<T>(\n memoryDir: string,\n id: string,\n run: (abortSignal: AbortSignal) => Promise<T>,\n options: ConnectorStateLockOptions\n): Promise<T> {\n return withConnectorStateLockInternal(memoryDir, id, run, options);\n}\n\n/**\n * Read the persisted state for a single connector.\n *\n * Returns `null` if the file does not exist (ENOENT). Throws on any other\n * I/O error or on shape mismatch — operators should see corruption loudly.\n *\n * Rejects symlinks anywhere on the path so a planted symlink can't redirect\n * reads outside the memory root. (PR #724 review.)\n */\nexport async function readConnectorState(memoryDir: string, id: string): Promise<ConnectorState | null> {\n const filePath = resolveConnectorStatePath(memoryDir, id);\n await assertNoSymlinkOnPath(memoryDir, filePath);\n let raw: string;\n try {\n raw = await fs.readFile(filePath, \"utf-8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new ConnectorStateCorruptionError(\n `connector state at ${filePath} is not valid JSON: ${(err as Error).message}`\n );\n }\n if (!isConnectorStateShape(parsed)) {\n throw new ConnectorStateCorruptionError(`connector state at ${filePath} does not match ConnectorState shape`);\n }\n if (parsed.id !== id) {\n throw new ConnectorStateCorruptionError(\n `connector state at ${filePath} has mismatched id ${JSON.stringify(parsed.id)}; expected ${JSON.stringify(id)}`\n );\n }\n return parsed;\n}\n\n/**\n * Write state atomically: create-tmp + rename. Never destroys the previous\n * file before the new one is in place — see CLAUDE.md gotcha #54.\n *\n * We accept `Omit<ConnectorState, \"updatedAt\">` and stamp `updatedAt`\n * ourselves so callers can't accidentally persist a stale timestamp.\n */\nexport async function writeConnectorState(\n memoryDir: string,\n id: string,\n state: Omit<ConnectorState, \"updatedAt\">\n): Promise<ConnectorState> {\n if (!isValidConnectorId(id)) {\n throw new TypeError(\n `invalid connector id ${JSON.stringify(id)} — must match /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/`\n );\n }\n if (state.id !== id) {\n throw new Error(\n `writeConnectorState(): state.id ${JSON.stringify(state.id)} does not match id argument ${JSON.stringify(id)}`\n );\n }\n // Full boundary validation. Persisting an out-of-shape record would brick\n // the connector's cursor file: subsequent `readConnectorState` calls would\n // throw `ConnectorStateCorruptionError` until manual repair. JS callers\n // bypassing TS types must be rejected here, not later. (PR #724 review.)\n if (!VALID_SYNC_STATUSES.has(state.lastSyncStatus as ConnectorSyncStatus)) {\n throw new Error(\n `writeConnectorState(): lastSyncStatus must be one of ${[...VALID_SYNC_STATUSES].join(\", \")}, got ${JSON.stringify(state.lastSyncStatus)}`\n );\n }\n if (state.lastSyncAt !== null && typeof state.lastSyncAt !== \"string\") {\n throw new Error(`writeConnectorState(): lastSyncAt must be a string or null, got ${typeof state.lastSyncAt}`);\n }\n if (state.cursor !== null) {\n if (typeof state.cursor !== \"object\") {\n throw new Error(`writeConnectorState(): cursor must be an object or null`);\n }\n if (\n typeof state.cursor.kind !== \"string\" ||\n typeof state.cursor.value !== \"string\" ||\n typeof state.cursor.updatedAt !== \"string\"\n ) {\n throw new Error(`writeConnectorState(): cursor must have string kind, value, and updatedAt`);\n }\n }\n if (\n typeof state.totalDocsImported !== \"number\" ||\n !Number.isInteger(state.totalDocsImported) ||\n state.totalDocsImported < 0\n ) {\n throw new Error(`writeConnectorState(): totalDocsImported must be a non-negative integer`);\n }\n if (state.lastSyncError !== undefined && typeof state.lastSyncError !== \"string\") {\n throw new Error(`writeConnectorState(): lastSyncError must be a string when provided`);\n }\n const truncatedError =\n state.lastSyncError !== undefined && state.lastSyncError.length > MAX_ERROR_LENGTH\n ? state.lastSyncError.slice(0, MAX_ERROR_LENGTH)\n : state.lastSyncError;\n\n const finalState: ConnectorState = {\n id: state.id,\n cursor: state.cursor,\n lastSyncAt: state.lastSyncAt,\n lastSyncStatus: state.lastSyncStatus,\n ...(truncatedError !== undefined ? { lastSyncError: truncatedError } : {}),\n totalDocsImported: state.totalDocsImported,\n updatedAt: new Date().toISOString(),\n };\n\n const dir = resolveConnectorsDir(memoryDir);\n const targetPath = path.join(dir, `${id}.json`);\n // Reject planted symlinks before mkdir/write so a redirected target can't\n // overwrite an arbitrary file outside the memory root. (PR #724 review.)\n await assertNoSymlinkOnPath(memoryDir, targetPath);\n await fs.mkdir(dir, { recursive: true });\n const tmpPath = `${targetPath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n\n const body = `${JSON.stringify(finalState, null, 2)}\\n`;\n try {\n await fs.writeFile(tmpPath, body, { encoding: \"utf-8\", mode: 0o600 });\n await fs.rename(tmpPath, targetPath);\n } catch (err) {\n // Best-effort cleanup of the tmp file. Never touch `targetPath` — the\n // previous good state must remain readable on failure.\n try {\n await fs.unlink(tmpPath);\n } catch {\n // ignore\n }\n throw err;\n }\n return finalState;\n}\n\n/**\n * Enumerate every persisted connector state. Returns an empty array when\n * the directory does not exist yet (clean install, no syncs ever run).\n *\n * Files that do not match the `<id>.json` naming rule are skipped — this\n * keeps stray editor backups (`.json~`, `.swp`) from breaking enumeration.\n *\n * Corruption (unparseable JSON, shape mismatch, id mismatch) is also\n * skipped so one bad file doesn't take down the listing. Operators\n * inspecting `state/connectors/` can still see the offending file by hand.\n *\n * **Genuine I/O failures (`EACCES`, `EIO`, etc.) are NOT swallowed** —\n * silently returning an incomplete state set would make active connectors\n * appear missing and trigger duplicate ingestion on the next scheduler tick.\n * (PR #724 review.)\n */\nexport async function listConnectorStates(memoryDir: string): Promise<ConnectorState[]> {\n const dir = resolveConnectorsDir(memoryDir);\n // Refuse to enumerate through a symlinked state directory — a planted\n // symlink at <memoryDir>/state or <memoryDir>/state/connectors would\n // otherwise let reads escape the memory root. (PR #724 review.)\n await assertNoSymlinkOnPath(memoryDir, dir);\n let entries: string[];\n try {\n entries = await fs.readdir(dir);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n throw err;\n }\n const out: ConnectorState[] = [];\n for (const entry of entries) {\n if (!entry.endsWith(\".json\")) continue;\n const id = entry.slice(0, -\".json\".length);\n if (!isValidConnectorId(id)) continue;\n try {\n const state = await readConnectorState(memoryDir, id);\n if (state !== null) out.push(state);\n } catch (err) {\n if (err instanceof ConnectorStateCorruptionError) {\n // Skip corrupt files; preserve availability of the rest.\n continue;\n }\n // Anything else (EACCES, EIO, ENOTDIR, ...) is a real operational\n // failure. Fail loudly so the scheduler / CLI can surface it.\n throw err;\n }\n }\n out.sort((a, b) => a.id.localeCompare(b.id));\n return out;\n}\n\n/**\n * Test-only helper: resolve where a given connector's state lives. Exported\n * so tests can assert the on-disk layout without duplicating the path math.\n * Not part of the stable public API.\n *\n * @internal\n */\nexport function _connectorStatePathForTest(memoryDir: string, id: string): string {\n return resolveConnectorStatePath(memoryDir, id);\n}\n\nexport const _unlinkStaleConnectorLockForTest = unlinkStaleConnectorLock;\nexport const _tryAcquireConnectorLockForTest = tryAcquireConnectorLock;\nexport const _refreshConnectorLockForTest = refreshConnectorLock;\nexport const _releaseConnectorLockForTest = releaseConnectorLock;\n","/**\n * @remnic/core — Live Connectors Framework (issue #683 PR 1/N)\n *\n * Defines the contract that every \"live\" connector (Drive, Notion, Gmail,\n * GitHub, ...) must satisfy. A live connector is **continuous**: it runs on a\n * schedule, persists a cursor to disk, and ingests *new* documents since the\n * last sync. This is distinct from one-shot importers in\n * `packages/remnic-core/src/importers/` which transform an entire export file\n * in a single pass.\n *\n * This module is intentionally pure types + interfaces. No I/O. No schedule\n * wiring. Concrete connectors (PRs 2–5), the maintenance scheduler hookup\n * (separate PR), and the CLI surface (PR 6) are deferred.\n *\n * Naming caveat: `packages/remnic-core/src/connectors/` is already scoped to\n * the Codex marketplace integration. The live-connector framework lives under\n * the `live/` subdirectory to avoid collision. Do not import Codex symbols\n * from here, and do not import live-connector symbols from the Codex code.\n */\n\n/**\n * Free-form connector configuration. Validated by each connector's\n * `validateConfig` implementation and passed to `syncIncremental` for runtime\n * use. MUST be JSON-serializable: no functions, no class instances, no\n * circular references.\n *\n * Runtime configs may contain hydrated credentials. Do not persist a runtime\n * config directly. Any code that needs to store connector settings must call\n * `persistableConnectorConfig()` so credentials are stripped or replaced by\n * connector-owned references.\n */\nexport type ConnectorConfig = Record<string, unknown>;\n\nconst DEFAULT_SECRET_KEY_PARTS = [\n \"token\",\n \"secret\",\n \"password\",\n \"credential\",\n \"apikey\",\n \"accesskey\",\n \"privatekey\",\n \"authorization\",\n \"authheader\",\n \"cookie\",\n] as const;\n\n/**\n * Redact secret-looking keys from a JSON-serializable connector config.\n * Built-in connectors provide explicit projections, but this default keeps\n * third-party connectors from accidentally persisting obvious credentials.\n */\nexport function redactConnectorConfigSecrets(config: ConnectorConfig): ConnectorConfig {\n return redactConnectorConfigValue(config) as ConnectorConfig;\n}\n\nfunction redactConnectorConfigValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => redactConnectorConfigValue(item));\n }\n if (typeof value !== \"object\" || value === null) {\n return value;\n }\n const out: Record<string, unknown> = {};\n for (const [key, nested] of Object.entries(value as Record<string, unknown>)) {\n if (isConnectorSecretConfigKey(key)) {\n continue;\n }\n out[key] = redactConnectorConfigValue(nested);\n }\n return out;\n}\n\nfunction isConnectorSecretConfigKey(key: string): boolean {\n const normalized = key.replace(/[^a-z0-9]/giu, \"\").toLowerCase();\n return DEFAULT_SECRET_KEY_PARTS.some((part) => normalized.includes(part));\n}\n\n/**\n * Opaque cursor describing \"where the last sync left off\". Each connector\n * defines what `kind` and `value` mean (e.g. Drive: `{kind: \"pageToken\",\n * value: \"...\"}`, Gmail: `{kind: \"historyId\", value: \"...\"}`). The orchestrator\n * treats it as opaque and only round-trips it through the state store.\n *\n * `updatedAt` is an ISO 8601 timestamp set by the framework when the cursor is\n * written. It is informational — connectors MUST NOT use it to decide\n * monotonicity. They own the `value` semantics.\n */\nexport interface ConnectorCursor {\n /** Connector-defined cursor kind (e.g. `\"pageToken\"`, `\"historyId\"`, `\"sinceTs\"`). */\n readonly kind: string;\n /** Connector-defined opaque cursor value. */\n readonly value: string;\n /** ISO 8601 timestamp of when this cursor was last written. */\n readonly updatedAt: string;\n}\n\n/**\n * Provenance for a connector-ingested document. Required so downstream recall\n * can attribute facts back to their origin and avoid re-ingesting on the next\n * incremental pass.\n */\nexport interface ConnectorDocumentSource {\n /** Stable connector id (matches `LiveConnector.id`). */\n readonly connector: string;\n /** Source-system identifier (Drive file id, Notion page id, Gmail msg id, ...). */\n readonly externalId: string;\n /** Optional source-system revision/version (etag, page version, history id). */\n readonly externalRevision?: string;\n /** Optional canonical URL pointing back at the source document. */\n readonly externalUrl?: string;\n /** ISO 8601 timestamp of when the connector fetched this document. */\n readonly fetchedAt: string;\n}\n\n/**\n * A single document yielded by an incremental sync. Connectors are responsible\n * for chunking large source documents themselves if needed; the orchestrator\n * ingests `content` as a unit.\n */\nexport interface ConnectorDocument {\n /** Connector-local stable id for this document. SHOULD match `source.externalId`. */\n readonly id: string;\n /** Optional human-readable title. */\n readonly title?: string;\n /** Body content. Plaintext or Markdown — connectors document their format. */\n readonly content: string;\n /** Provenance. Required. */\n readonly source: ConnectorDocumentSource;\n}\n\n/**\n * Arguments passed to `syncIncremental`. The framework owns cursor/config\n * lifecycle; connectors only read these and return the next cursor.\n */\nexport interface SyncIncrementalArgs {\n /** Last persisted cursor, or `null` on the first ever sync. */\n readonly cursor: ConnectorCursor | null;\n /** Validated connector config (already passed through `validateConfig`). */\n readonly config: ConnectorConfig;\n /** Optional abort signal. Connectors SHOULD honor it for cooperative cancellation. */\n readonly abortSignal?: AbortSignal;\n}\n\n/**\n * Result of a single incremental sync pass.\n *\n * `newDocs` MAY be empty (no new documents since the last cursor). `nextCursor`\n * MUST always be returned — even on no-op syncs the framework persists it so\n * `updatedAt` reflects the most recent attempt.\n */\nexport interface SyncIncrementalResult {\n readonly newDocs: ConnectorDocument[];\n readonly nextCursor: ConnectorCursor;\n}\n\n/**\n * The contract every live connector implements.\n *\n * Connectors MUST be:\n * - **Idempotent**: re-running with the same cursor MUST NOT duplicate\n * documents. The `source.externalId` + `source.externalRevision` pair is\n * used downstream for dedup.\n * - **Read-only on the source**: live connectors never mutate the upstream\n * system (no marking emails read, no editing Notion pages).\n * - **Cancellable**: long-running syncs SHOULD periodically check\n * `abortSignal.aborted` and bail cleanly.\n * - **Privacy-aware**: connectors MUST NOT log document content. Logging\n * metadata (counts, ids, timings) is fine.\n */\nexport interface LiveConnector {\n /**\n * Stable connector id. MUST match `CONNECTOR_ID_PATTERN` — lowercase\n * alphanumeric plus dash, 1–64 chars, must start AND end with alphanumeric\n * (no leading or trailing dash). The registry enforces this.\n */\n readonly id: string;\n /** Short human-readable name shown in CLI / status output. */\n readonly displayName: string;\n /** Optional longer description. */\n readonly description?: string;\n\n /**\n * Validate raw user-supplied config. MUST throw on malformed input — never\n * silently default. The returned object is the runtime config passed back to\n * `syncIncremental`; it may contain hydrated credentials and MUST NOT be\n * persisted directly. Connectors SHOULD strip unknown fields.\n */\n validateConfig(raw: unknown): ConnectorConfig;\n\n /**\n * Return the subset of validated config that may be written to disk. Use this\n * for state/config persistence instead of `validateConfig()`. Connectors that\n * need credentials should omit the raw values here and store only non-secret\n * scope/schedule fields plus any connector-owned secret references.\n */\n persistConfig?(validated: ConnectorConfig): ConnectorConfig;\n\n /**\n * Run one incremental sync pass. See `SyncIncrementalArgs` /\n * `SyncIncrementalResult` for the contract.\n */\n syncIncremental(args: SyncIncrementalArgs): Promise<SyncIncrementalResult>;\n}\n\nexport function persistableConnectorConfig(\n connector: Pick<LiveConnector, \"persistConfig\">,\n validated: ConnectorConfig,\n): ConnectorConfig {\n if (typeof connector.persistConfig === \"function\") {\n return connector.persistConfig(validated);\n }\n return redactConnectorConfigSecrets(validated);\n}\n\n/**\n * Regex enforcing the connector-id naming rule. Exported so connectors and\n * tests can validate ids consistently with the registry.\n *\n * Rule: lowercase alphanumeric + dash, 1..64 chars, must start AND end with\n * alphanumeric (no leading or trailing dash). Single-char ids are allowed.\n */\nexport const CONNECTOR_ID_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;\n\n/**\n * Returns `true` if `id` is a syntactically valid connector id.\n */\nexport function isValidConnectorId(id: unknown): id is string {\n return typeof id === \"string\" && CONNECTOR_ID_PATTERN.test(id);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/connectors/live/state-store.ts","../src/connectors/live/framework.ts"],"sourcesContent":["/**\n * @remnic/core — Live Connectors State Store (issue #683 PR 1/N)\n *\n * Persists per-connector cursor + sync metadata to\n * `<memoryDir>/state/connectors/<id>.json`\n *\n * Reasons this lives next to memory data, not in user config:\n * - cursors are *operational* state that should travel with the memory\n * directory when a user moves it across machines;\n * - it keeps memory + ingest provenance co-located so tooling that backs up\n * the memory directory captures cursor state too.\n *\n * Atomic-write contract (CLAUDE.md gotcha #54):\n * - We NEVER `rmSync(target)` before `renameSync(tmp, target)`.\n * - Writes go to a sibling tmp file and `rename()` swaps it in.\n * - On error, the tmp file is best-effort cleaned up; the previous good\n * state file is left untouched.\n *\n * Privacy: cursors are opaque connector-defined strings. We do not log them\n * and do not surface them through user-visible APIs. Document content NEVER\n * touches this module.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\n\nimport { expandTildePath } from \"../../utils/path.js\";\n\nimport { type ConnectorCursor, isValidConnectorId } from \"./framework.js\";\n\n/**\n * Status of the most recent sync attempt for a connector.\n *\n * `\"never\"` is distinct from `\"success\"` so callers can detect\n * \"registered but never run\" without inspecting timestamps. Per CLAUDE.md\n * gotcha #34, we deliberately distinguish empty/unknown from failure states.\n */\nexport type ConnectorSyncStatus = \"success\" | \"error\" | \"never\";\n\n/**\n * Persisted per-connector state.\n *\n * Stored as pretty-printed JSON for human inspection — the file is small\n * (one record per connector) and operators may need to debug stuck cursors\n * by hand.\n */\nexport interface ConnectorState {\n /** Connector id. Matches the filename stem. */\n readonly id: string;\n /** Last persisted cursor, or `null` if the connector has never synced. */\n readonly cursor: ConnectorCursor | null;\n /** ISO 8601 timestamp of the last completed sync attempt, or `null`. */\n readonly lastSyncAt: string | null;\n /** Status of the last completed sync attempt. */\n readonly lastSyncStatus: ConnectorSyncStatus;\n /** Optional error message from the last failed sync. Truncated to 1 KB. */\n readonly lastSyncError?: string;\n /** Cumulative count of documents successfully imported across all syncs. */\n readonly totalDocsImported: number;\n /** ISO 8601 timestamp of when this state record was last written. */\n readonly updatedAt: string;\n}\n\nconst STATE_DIR_NAME = \"state\";\nconst CONNECTORS_DIR_NAME = \"connectors\";\nconst CONNECTOR_LOCKS_DIR_NAME = \"connector-locks\";\nconst MAX_ERROR_LENGTH = 1024;\nconst CONNECTOR_LOCK_STALE_MS = 10 * 60 * 1000;\nconst CONNECTOR_LOCK_HEARTBEAT_MS = Math.max(1_000, Math.floor(CONNECTOR_LOCK_STALE_MS / 4));\nconst CONNECTOR_LOCK_TIMEOUT_MS = 60 * 1000;\nconst CONNECTOR_LOCK_RETRY_MS = 50;\nconst VALID_SYNC_STATUSES: ReadonlySet<ConnectorSyncStatus> = new Set([\"success\", \"error\", \"never\"]);\n\n/**\n * Internal error thrown when a state file's JSON is unparseable or its shape\n * doesn't match `ConnectorState`. Used by `listConnectorStates` to distinguish\n * \"skip this corrupt file\" cases from genuine I/O failures (`EACCES`, `EIO`)\n * that the caller must see.\n */\nclass ConnectorStateCorruptionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ConnectorStateCorruptionError\";\n }\n}\n\nexport class ConnectorStateLockLostError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ConnectorStateLockLostError\";\n }\n}\n\ninterface ConnectorLockLease {\n readonly path: string;\n readonly token: string;\n}\n\ninterface ConnectorLockMetadata {\n readonly pid: number;\n readonly token: string;\n readonly createdAt: string;\n readonly refreshedAt: string;\n}\n\ninterface ConnectorStateLockOptions {\n readonly heartbeatMs?: number;\n readonly unrefHeartbeat?: boolean;\n}\n\n/**\n * Resolve `<memoryDir>/state/connectors/`, expanding `~` per CLAUDE.md #17.\n */\nfunction resolveConnectorsDir(memoryDir: string): string {\n if (typeof memoryDir !== \"string\" || memoryDir.length === 0) {\n throw new TypeError(\"memoryDir must be a non-empty string\");\n }\n return path.join(expandTildePath(memoryDir), STATE_DIR_NAME, CONNECTORS_DIR_NAME);\n}\n\nfunction resolveConnectorLocksDir(memoryDir: string): string {\n if (typeof memoryDir !== \"string\" || memoryDir.length === 0) {\n throw new TypeError(\"memoryDir must be a non-empty string\");\n }\n return path.join(expandTildePath(memoryDir), STATE_DIR_NAME, CONNECTOR_LOCKS_DIR_NAME);\n}\n\n/**\n * Resolve the state file path for a single connector. Throws on invalid id\n * to prevent path traversal via crafted ids.\n */\nfunction resolveConnectorStatePath(memoryDir: string, id: string): string {\n if (!isValidConnectorId(id)) {\n throw new TypeError(\n `invalid connector id ${JSON.stringify(id)} — must match /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/`\n );\n }\n return path.join(resolveConnectorsDir(memoryDir), `${id}.json`);\n}\n\nfunction resolveConnectorLockPath(memoryDir: string, id: string): string {\n if (!isValidConnectorId(id)) {\n throw new TypeError(\n `invalid connector id ${JSON.stringify(id)} — must match /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/`\n );\n }\n return path.join(resolveConnectorLocksDir(memoryDir), `${id}.lock`);\n}\n\n/**\n * Type guard for parsed state records. Validates the on-disk shape so a\n * corrupted/edited file produces a clear error rather than crashing later.\n *\n * Per CLAUDE.md gotcha #18, JSON.parse('null') yields `null` which would\n * pass a naive truthy check. We explicitly require an object.\n */\nfunction isConnectorStateShape(value: unknown): value is ConnectorState {\n if (typeof value !== \"object\" || value === null) return false;\n const v = value as Record<string, unknown>;\n if (typeof v.id !== \"string\") return false;\n if (typeof v.lastSyncStatus !== \"string\") return false;\n if (![\"success\", \"error\", \"never\"].includes(v.lastSyncStatus)) return false;\n // totalDocsImported is a cumulative count — fractional values would corrupt\n // metrics on later increments. Mirror the boundary check in writeConnectorState.\n if (typeof v.totalDocsImported !== \"number\" || !Number.isInteger(v.totalDocsImported)) return false;\n if (v.totalDocsImported < 0) return false;\n if (typeof v.updatedAt !== \"string\") return false;\n if (v.lastSyncAt !== null && typeof v.lastSyncAt !== \"string\") return false;\n if (v.cursor !== null) {\n if (typeof v.cursor !== \"object\" || v.cursor === null) return false;\n const c = v.cursor as Record<string, unknown>;\n if (typeof c.kind !== \"string\" || typeof c.value !== \"string\" || typeof c.updatedAt !== \"string\") {\n return false;\n }\n }\n if (v.lastSyncError !== undefined && typeof v.lastSyncError !== \"string\") return false;\n return true;\n}\n\nfunction isConnectorLockMetadata(value: unknown): value is ConnectorLockMetadata {\n if (typeof value !== \"object\" || value === null) return false;\n const v = value as Record<string, unknown>;\n return (\n typeof v.pid === \"number\" &&\n Number.isInteger(v.pid) &&\n v.pid > 0 &&\n typeof v.token === \"string\" &&\n v.token.length > 0 &&\n typeof v.createdAt === \"string\" &&\n typeof v.refreshedAt === \"string\"\n );\n}\n\nfunction connectorLockMetadata(token: string, createdAt = new Date().toISOString()): ConnectorLockMetadata {\n return {\n pid: process.pid,\n token,\n createdAt,\n refreshedAt: new Date().toISOString(),\n };\n}\n\nasync function readConnectorLockMetadata(lockPath: string): Promise<ConnectorLockMetadata | null> {\n let raw: string;\n try {\n raw = await fs.readFile(lockPath, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n try {\n const parsed = JSON.parse(raw) as unknown;\n return isConnectorLockMetadata(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\nfunction parseConnectorLockMetadata(raw: string): ConnectorLockMetadata | null {\n try {\n const parsed = JSON.parse(raw) as unknown;\n return isConnectorLockMetadata(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\nfunction isSameFileIdentity(left: import(\"node:fs\").Stats, right: import(\"node:fs\").Stats): boolean {\n return left.dev === right.dev && left.ino === right.ino;\n}\n\n/**\n * Reject any path component along `<memoryDir>/state/connectors/<id>.json`\n * that is a symlink. Without this guard, a symlink in any of those\n * components would let `fs.readFile` escape the memory root and consume an\n * arbitrary outside file as cursor state — silently poisoning sync state and\n * violating the project-wide rule against symlink traversal.\n *\n * `lstat` is used (not `stat`) so we observe the link itself rather than its\n * target. Missing components are tolerated — the caller's `readFile` /\n * `mkdir` will surface ENOENT in its normal way.\n *\n * (PR #724 review.)\n */\nasync function assertNoSymlinkOnPath(memoryDir: string, filePath: string): Promise<void> {\n const expandedRoot = expandTildePath(memoryDir);\n // Normalize so `..` segments can't bypass the prefix check below.\n const root = path.resolve(expandedRoot);\n const target = path.resolve(filePath);\n const rel = path.relative(root, target);\n // path.relative() yields a \"../...\" prefix when target escapes root.\n if (rel.startsWith(\"..\") || path.isAbsolute(rel)) {\n throw new Error(`connector state path ${target} escapes memory root ${root}`);\n }\n // Walk every component from root to target (inclusive) and lstat each.\n const segments = rel.length === 0 ? [] : rel.split(path.sep);\n let current = root;\n const componentsToCheck = [current];\n for (const seg of segments) {\n current = path.join(current, seg);\n componentsToCheck.push(current);\n }\n for (const component of componentsToCheck) {\n let stat: import(\"node:fs\").Stats;\n try {\n stat = await fs.lstat(component);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n // Not yet created — that's fine; caller's readFile/mkdir handles it.\n continue;\n }\n throw err;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`connector state path component ${component} is a symlink; refusing to follow`);\n }\n }\n}\n\nasync function tryAcquireConnectorLock(memoryDir: string, id: string): Promise<ConnectorLockLease | null> {\n const dir = resolveConnectorLocksDir(memoryDir);\n const lockPath = resolveConnectorLockPath(memoryDir, id);\n await assertNoSymlinkOnPath(memoryDir, lockPath);\n await fs.mkdir(dir, { recursive: true });\n let handle: Awaited<ReturnType<typeof fs.open>> | null = null;\n let acquiredLockFile = false;\n const token = randomUUID();\n try {\n handle = await fs.open(lockPath, \"wx\", 0o600);\n acquiredLockFile = true;\n await handle.writeFile(`${JSON.stringify(connectorLockMetadata(token))}\\n`, \"utf8\");\n await handle.close();\n handle = null;\n return { path: lockPath, token };\n } catch (err) {\n if (acquiredLockFile) {\n if (handle !== null) {\n try {\n await handle.close();\n } catch {\n // The original write/close failure is more actionable.\n }\n }\n try {\n await fs.unlink(lockPath);\n } catch (cleanupErr) {\n if ((cleanupErr as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw new AggregateError(\n [err, cleanupErr],\n `failed to initialize connector state lock at ${lockPath}; cleanup also failed`\n );\n }\n }\n throw err;\n }\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") {\n throw err;\n }\n let stat: import(\"node:fs\").Stats;\n try {\n stat = await fs.lstat(lockPath);\n } catch (statErr) {\n if ((statErr as NodeJS.ErrnoException).code === \"ENOENT\") {\n return null;\n }\n throw statErr;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`connector state path component ${lockPath} is a symlink; refusing to follow`);\n }\n if (Date.now() - stat.mtimeMs > CONNECTOR_LOCK_STALE_MS) {\n await unlinkStaleConnectorLock(lockPath);\n return null;\n }\n return null;\n }\n}\n\nasync function shouldUnlinkStaleConnectorLock(lockPath: string, stat: import(\"node:fs\").Stats): Promise<boolean> {\n if (Date.now() - stat.mtimeMs <= CONNECTOR_LOCK_STALE_MS) return false;\n const metadata = await readConnectorLockMetadata(lockPath);\n if (metadata === null) return true;\n const refreshedAtMs = Date.parse(metadata.refreshedAt);\n if (!Number.isFinite(refreshedAtMs)) return true;\n return Date.now() - refreshedAtMs > CONNECTOR_LOCK_STALE_MS;\n}\n\nasync function unlinkStaleConnectorLock(lockPath: string): Promise<void> {\n const reclaimHandle = await openConnectorReclaimLock(lockPath);\n if (!reclaimHandle) return;\n try {\n await reclaimHandle.writeFile(`${process.pid}:${new Date().toISOString()}`, \"utf8\");\n await unlinkStaleConnectorLockWhileReclaimHeld(lockPath);\n } finally {\n await reclaimHandle.close().catch(() => undefined);\n await fs.unlink(connectorReclaimLockPath(lockPath)).catch(() => undefined);\n }\n}\n\nasync function unlinkStaleConnectorLockWhileReclaimHeld(lockPath: string): Promise<void> {\n let stat: import(\"node:fs\").Stats;\n try {\n stat = await fs.lstat(lockPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return;\n throw err;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`connector state path component ${lockPath} is a symlink; refusing to follow`);\n }\n if (!(await shouldUnlinkStaleConnectorLock(lockPath, stat))) return;\n try {\n await fs.unlink(lockPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw err;\n }\n }\n}\n\nfunction connectorReclaimLockPath(lockPath: string): string {\n return `${lockPath}.reclaim`;\n}\n\nasync function openConnectorReclaimLock(lockPath: string): Promise<Awaited<ReturnType<typeof fs.open>> | null> {\n const reclaimPath = connectorReclaimLockPath(lockPath);\n try {\n return await fs.open(reclaimPath, \"wx\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n }\n\n let reclaimStat: import(\"node:fs\").Stats;\n try {\n reclaimStat = await fs.lstat(reclaimPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n if (reclaimStat.isSymbolicLink()) {\n throw new Error(`connector state path component ${reclaimPath} is a symlink; refusing to follow`);\n }\n if (Date.now() - reclaimStat.mtimeMs <= CONNECTOR_LOCK_STALE_MS) return null;\n await fs.unlink(reclaimPath);\n\n try {\n return await fs.open(reclaimPath, \"wx\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"EEXIST\") return null;\n throw err;\n }\n}\n\nasync function refreshConnectorLock(lease: ConnectorLockLease): Promise<boolean> {\n const reclaimHandle = await openConnectorReclaimLock(lease.path);\n if (!reclaimHandle) return false;\n try {\n await reclaimHandle.writeFile(`${process.pid}:${new Date().toISOString()}`, \"utf8\");\n return await refreshConnectorLockWhileReclaimHeld(lease);\n } finally {\n await reclaimHandle.close().catch(() => undefined);\n await fs.unlink(connectorReclaimLockPath(lease.path)).catch(() => undefined);\n }\n}\n\nasync function refreshConnectorLockWhileReclaimHeld(lease: ConnectorLockLease): Promise<boolean> {\n let handle: Awaited<ReturnType<typeof fs.open>>;\n try {\n handle = await fs.open(lease.path, \"r+\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw err;\n }\n try {\n const openedStat = await handle.stat();\n const metadata = parseConnectorLockMetadata(await handle.readFile(\"utf8\"));\n if (metadata?.token !== lease.token) return false;\n const body = `${JSON.stringify(connectorLockMetadata(lease.token, metadata.createdAt))}\\n`;\n await handle.truncate(0);\n await handle.write(body, 0, \"utf8\");\n let pathStat: import(\"node:fs\").Stats;\n try {\n pathStat = await fs.lstat(lease.path);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw err;\n }\n if (pathStat.isSymbolicLink()) {\n throw new Error(`connector state path component ${lease.path} is a symlink; refusing to follow`);\n }\n return isSameFileIdentity(openedStat, pathStat);\n } finally {\n await handle.close();\n }\n}\n\nasync function releaseConnectorLock(lease: ConnectorLockLease): Promise<void> {\n const reclaimHandle = await openConnectorReclaimLock(lease.path);\n if (!reclaimHandle) return;\n try {\n await reclaimHandle.writeFile(`${process.pid}:${new Date().toISOString()}`, \"utf8\");\n await releaseConnectorLockWhileReclaimHeld(lease);\n } finally {\n await reclaimHandle.close().catch(() => undefined);\n await fs.unlink(connectorReclaimLockPath(lease.path)).catch(() => undefined);\n }\n}\n\nasync function releaseConnectorLockWhileReclaimHeld(lease: ConnectorLockLease): Promise<void> {\n let handle: Awaited<ReturnType<typeof fs.open>>;\n try {\n handle = await fs.open(lease.path, \"r\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return;\n throw err;\n }\n try {\n const openedStat = await handle.stat();\n const metadata = parseConnectorLockMetadata(await handle.readFile(\"utf8\"));\n if (metadata?.token !== lease.token) return;\n let pathStat: import(\"node:fs\").Stats;\n try {\n pathStat = await fs.lstat(lease.path);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return;\n throw err;\n }\n if (pathStat.isSymbolicLink()) {\n throw new Error(`connector state path component ${lease.path} is a symlink; refusing to follow`);\n }\n if (!isSameFileIdentity(openedStat, pathStat)) return;\n try {\n await fs.unlink(lease.path);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw err;\n }\n }\n } finally {\n await handle.close();\n }\n}\n\nasync function withConnectorStateLockInternal<T>(\n memoryDir: string,\n id: string,\n run: (abortSignal: AbortSignal) => Promise<T>,\n options: ConnectorStateLockOptions = {}\n): Promise<T> {\n const deadline = Date.now() + CONNECTOR_LOCK_TIMEOUT_MS;\n let lease: ConnectorLockLease | null = null;\n while (lease === null) {\n lease = await tryAcquireConnectorLock(memoryDir, id);\n if (lease !== null) break;\n if (Date.now() >= deadline) {\n throw new Error(`timed out waiting for connector \"${id}\" state lock`);\n }\n await delay(CONNECTOR_LOCK_RETRY_MS);\n }\n const abortController = new AbortController();\n let rejectLockLost!: (err: Error) => void;\n let lockLost = false;\n const lockLostPromise = new Promise<never>((_resolve, reject) => {\n rejectLockLost = reject;\n });\n const failLostLock = (message: string): void => {\n if (lockLost) return;\n lockLost = true;\n const err = new ConnectorStateLockLostError(message);\n abortController.abort(err);\n rejectLockLost(err);\n };\n const heartbeat = setInterval(() => {\n void refreshConnectorLock(lease)\n .then((refreshed) => {\n if (!refreshed) {\n failLostLock(`lost connector \"${id}\" state lock`);\n }\n })\n .catch((err) => {\n const message = err instanceof Error ? err.message : String(err);\n failLostLock(`lost connector \"${id}\" state lock: ${message}`);\n });\n }, options.heartbeatMs ?? CONNECTOR_LOCK_HEARTBEAT_MS);\n if (options.unrefHeartbeat !== false) {\n heartbeat.unref?.();\n }\n const runPromise = run(abortController.signal);\n try {\n return await Promise.race([runPromise, lockLostPromise]);\n } catch (err) {\n if (err instanceof ConnectorStateLockLostError) {\n await runPromise.catch(() => undefined);\n }\n throw err;\n } finally {\n clearInterval(heartbeat);\n await releaseConnectorLock(lease);\n }\n}\n\nexport async function withConnectorStateLock<T>(\n memoryDir: string,\n id: string,\n run: (abortSignal: AbortSignal) => Promise<T>\n): Promise<T> {\n return withConnectorStateLockInternal(memoryDir, id, run);\n}\n\nexport async function _withConnectorStateLockForTest<T>(\n memoryDir: string,\n id: string,\n run: (abortSignal: AbortSignal) => Promise<T>,\n options: ConnectorStateLockOptions\n): Promise<T> {\n return withConnectorStateLockInternal(memoryDir, id, run, options);\n}\n\n/**\n * Read the persisted state for a single connector.\n *\n * Returns `null` if the file does not exist (ENOENT). Throws on any other\n * I/O error or on shape mismatch — operators should see corruption loudly.\n *\n * Rejects symlinks anywhere on the path so a planted symlink can't redirect\n * reads outside the memory root. (PR #724 review.)\n */\nexport async function readConnectorState(memoryDir: string, id: string): Promise<ConnectorState | null> {\n const filePath = resolveConnectorStatePath(memoryDir, id);\n await assertNoSymlinkOnPath(memoryDir, filePath);\n let raw: string;\n try {\n raw = await fs.readFile(filePath, \"utf-8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new ConnectorStateCorruptionError(\n `connector state at ${filePath} is not valid JSON: ${(err as Error).message}`\n );\n }\n if (!isConnectorStateShape(parsed)) {\n throw new ConnectorStateCorruptionError(`connector state at ${filePath} does not match ConnectorState shape`);\n }\n if (parsed.id !== id) {\n throw new ConnectorStateCorruptionError(\n `connector state at ${filePath} has mismatched id ${JSON.stringify(parsed.id)}; expected ${JSON.stringify(id)}`\n );\n }\n return parsed;\n}\n\n/**\n * Write state atomically: create-tmp + rename. Never destroys the previous\n * file before the new one is in place — see CLAUDE.md gotcha #54.\n *\n * We accept `Omit<ConnectorState, \"updatedAt\">` and stamp `updatedAt`\n * ourselves so callers can't accidentally persist a stale timestamp.\n */\nexport async function writeConnectorState(\n memoryDir: string,\n id: string,\n state: Omit<ConnectorState, \"updatedAt\">\n): Promise<ConnectorState> {\n if (!isValidConnectorId(id)) {\n throw new TypeError(\n `invalid connector id ${JSON.stringify(id)} — must match /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/`\n );\n }\n if (state.id !== id) {\n throw new Error(\n `writeConnectorState(): state.id ${JSON.stringify(state.id)} does not match id argument ${JSON.stringify(id)}`\n );\n }\n // Full boundary validation. Persisting an out-of-shape record would brick\n // the connector's cursor file: subsequent `readConnectorState` calls would\n // throw `ConnectorStateCorruptionError` until manual repair. JS callers\n // bypassing TS types must be rejected here, not later. (PR #724 review.)\n if (!VALID_SYNC_STATUSES.has(state.lastSyncStatus as ConnectorSyncStatus)) {\n throw new Error(\n `writeConnectorState(): lastSyncStatus must be one of ${[...VALID_SYNC_STATUSES].join(\", \")}, got ${JSON.stringify(state.lastSyncStatus)}`\n );\n }\n if (state.lastSyncAt !== null && typeof state.lastSyncAt !== \"string\") {\n throw new Error(`writeConnectorState(): lastSyncAt must be a string or null, got ${typeof state.lastSyncAt}`);\n }\n if (state.cursor !== null) {\n if (typeof state.cursor !== \"object\") {\n throw new Error(`writeConnectorState(): cursor must be an object or null`);\n }\n if (\n typeof state.cursor.kind !== \"string\" ||\n typeof state.cursor.value !== \"string\" ||\n typeof state.cursor.updatedAt !== \"string\"\n ) {\n throw new Error(`writeConnectorState(): cursor must have string kind, value, and updatedAt`);\n }\n }\n if (\n typeof state.totalDocsImported !== \"number\" ||\n !Number.isInteger(state.totalDocsImported) ||\n state.totalDocsImported < 0\n ) {\n throw new Error(`writeConnectorState(): totalDocsImported must be a non-negative integer`);\n }\n if (state.lastSyncError !== undefined && typeof state.lastSyncError !== \"string\") {\n throw new Error(`writeConnectorState(): lastSyncError must be a string when provided`);\n }\n const truncatedError =\n state.lastSyncError !== undefined && state.lastSyncError.length > MAX_ERROR_LENGTH\n ? state.lastSyncError.slice(0, MAX_ERROR_LENGTH)\n : state.lastSyncError;\n\n const finalState: ConnectorState = {\n id: state.id,\n cursor: state.cursor,\n lastSyncAt: state.lastSyncAt,\n lastSyncStatus: state.lastSyncStatus,\n ...(truncatedError !== undefined ? { lastSyncError: truncatedError } : {}),\n totalDocsImported: state.totalDocsImported,\n updatedAt: new Date().toISOString(),\n };\n\n const dir = resolveConnectorsDir(memoryDir);\n const targetPath = path.join(dir, `${id}.json`);\n // Reject planted symlinks before mkdir/write so a redirected target can't\n // overwrite an arbitrary file outside the memory root. (PR #724 review.)\n await assertNoSymlinkOnPath(memoryDir, targetPath);\n await fs.mkdir(dir, { recursive: true });\n const tmpPath = `${targetPath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n\n const body = `${JSON.stringify(finalState, null, 2)}\\n`;\n try {\n await fs.writeFile(tmpPath, body, { encoding: \"utf-8\", mode: 0o600 });\n await fs.rename(tmpPath, targetPath);\n } catch (err) {\n // Best-effort cleanup of the tmp file. Never touch `targetPath` — the\n // previous good state must remain readable on failure.\n try {\n await fs.unlink(tmpPath);\n } catch {\n // ignore\n }\n throw err;\n }\n return finalState;\n}\n\n/**\n * Enumerate every persisted connector state. Returns an empty array when\n * the directory does not exist yet (clean install, no syncs ever run).\n *\n * Files that do not match the `<id>.json` naming rule are skipped — this\n * keeps stray editor backups (`.json~`, `.swp`) from breaking enumeration.\n *\n * Corruption (unparseable JSON, shape mismatch, id mismatch) is also\n * skipped so one bad file doesn't take down the listing. Operators\n * inspecting `state/connectors/` can still see the offending file by hand.\n *\n * **Genuine I/O failures (`EACCES`, `EIO`, etc.) are NOT swallowed** —\n * silently returning an incomplete state set would make active connectors\n * appear missing and trigger duplicate ingestion on the next scheduler tick.\n * (PR #724 review.)\n */\nexport async function listConnectorStates(memoryDir: string): Promise<ConnectorState[]> {\n const dir = resolveConnectorsDir(memoryDir);\n // Refuse to enumerate through a symlinked state directory — a planted\n // symlink at <memoryDir>/state or <memoryDir>/state/connectors would\n // otherwise let reads escape the memory root. (PR #724 review.)\n await assertNoSymlinkOnPath(memoryDir, dir);\n let entries: string[];\n try {\n entries = await fs.readdir(dir);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n throw err;\n }\n const out: ConnectorState[] = [];\n for (const entry of entries) {\n if (!entry.endsWith(\".json\")) continue;\n const id = entry.slice(0, -\".json\".length);\n if (!isValidConnectorId(id)) continue;\n try {\n const state = await readConnectorState(memoryDir, id);\n if (state !== null) out.push(state);\n } catch (err) {\n if (err instanceof ConnectorStateCorruptionError) {\n // Skip corrupt files; preserve availability of the rest.\n continue;\n }\n // Anything else (EACCES, EIO, ENOTDIR, ...) is a real operational\n // failure. Fail loudly so the scheduler / CLI can surface it.\n throw err;\n }\n }\n out.sort((a, b) => a.id.localeCompare(b.id));\n return out;\n}\n\n/**\n * Test-only helper: resolve where a given connector's state lives. Exported\n * so tests can assert the on-disk layout without duplicating the path math.\n * Not part of the stable public API.\n *\n * @internal\n */\nexport function _connectorStatePathForTest(memoryDir: string, id: string): string {\n return resolveConnectorStatePath(memoryDir, id);\n}\n\nexport const _unlinkStaleConnectorLockForTest = unlinkStaleConnectorLock;\nexport const _tryAcquireConnectorLockForTest = tryAcquireConnectorLock;\nexport const _refreshConnectorLockForTest = refreshConnectorLock;\nexport const _releaseConnectorLockForTest = releaseConnectorLock;\n","import type { ResolveSecretRefFn } from \"../../resolve-auth-token.js\";\n\n/**\n * @remnic/core — Live Connectors Framework (issue #683 PR 1/N)\n *\n * Defines the contract that every \"live\" connector (Drive, Notion, Gmail,\n * GitHub, ...) must satisfy. A live connector is **continuous**: it runs on a\n * schedule, persists a cursor to disk, and ingests *new* documents since the\n * last sync. This is distinct from one-shot importers in\n * `packages/remnic-core/src/importers/` which transform an entire export file\n * in a single pass.\n *\n * This module is intentionally pure types + interfaces. No I/O. No schedule\n * wiring. Concrete connectors (PRs 2–5), the maintenance scheduler hookup\n * (separate PR), and the CLI surface (PR 6) are deferred.\n *\n * Naming caveat: `packages/remnic-core/src/connectors/` is already scoped to\n * the Codex marketplace integration. The live-connector framework lives under\n * the `live/` subdirectory to avoid collision. Do not import Codex symbols\n * from here, and do not import live-connector symbols from the Codex code.\n */\n\n/**\n * Free-form connector configuration. Validated by each connector's\n * `validateConfig` implementation and passed to `syncIncremental` for runtime\n * use. MUST be JSON-serializable: no functions, no class instances, no\n * circular references.\n *\n * Runtime configs may contain hydrated credentials. Do not persist a runtime\n * config directly. Any code that needs to store connector settings must call\n * `persistableConnectorConfig()` so credentials are stripped or replaced by\n * connector-owned references.\n */\nexport type ConnectorConfig = Record<string, unknown>;\n\nconst DEFAULT_SECRET_KEY_PARTS = [\n \"token\",\n \"secret\",\n \"password\",\n \"credential\",\n \"apikey\",\n \"accesskey\",\n \"privatekey\",\n \"authorization\",\n \"authheader\",\n \"cookie\",\n] as const;\n\n/**\n * Redact secret-looking keys from a JSON-serializable connector config.\n * Built-in connectors provide explicit projections, but this default keeps\n * third-party connectors from accidentally persisting obvious credentials.\n */\nexport function redactConnectorConfigSecrets(config: ConnectorConfig): ConnectorConfig {\n return redactConnectorConfigValue(config) as ConnectorConfig;\n}\n\nfunction redactConnectorConfigValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => redactConnectorConfigValue(item));\n }\n if (typeof value !== \"object\" || value === null) {\n return value;\n }\n const out: Record<string, unknown> = {};\n for (const [key, nested] of Object.entries(value as Record<string, unknown>)) {\n if (isConnectorSecretConfigKey(key)) {\n continue;\n }\n out[key] = redactConnectorConfigValue(nested);\n }\n return out;\n}\n\nfunction isConnectorSecretConfigKey(key: string): boolean {\n const normalized = key.replace(/[^a-z0-9]/giu, \"\").toLowerCase();\n return DEFAULT_SECRET_KEY_PARTS.some((part) => normalized.includes(part));\n}\n\n/**\n * Opaque cursor describing \"where the last sync left off\". Each connector\n * defines what `kind` and `value` mean (e.g. Drive: `{kind: \"pageToken\",\n * value: \"...\"}`, Gmail: `{kind: \"historyId\", value: \"...\"}`). The orchestrator\n * treats it as opaque and only round-trips it through the state store.\n *\n * `updatedAt` is an ISO 8601 timestamp set by the framework when the cursor is\n * written. It is informational — connectors MUST NOT use it to decide\n * monotonicity. They own the `value` semantics.\n */\nexport interface ConnectorCursor {\n /** Connector-defined cursor kind (e.g. `\"pageToken\"`, `\"historyId\"`, `\"sinceTs\"`). */\n readonly kind: string;\n /** Connector-defined opaque cursor value. */\n readonly value: string;\n /** ISO 8601 timestamp of when this cursor was last written. */\n readonly updatedAt: string;\n}\n\n/**\n * Provenance for a connector-ingested document. Required so downstream recall\n * can attribute facts back to their origin and avoid re-ingesting on the next\n * incremental pass.\n */\nexport interface ConnectorDocumentSource {\n /** Stable connector id (matches `LiveConnector.id`). */\n readonly connector: string;\n /** Source-system identifier (Drive file id, Notion page id, Gmail msg id, ...). */\n readonly externalId: string;\n /** Optional source-system revision/version (etag, page version, history id). */\n readonly externalRevision?: string;\n /** Optional canonical URL pointing back at the source document. */\n readonly externalUrl?: string;\n /** ISO 8601 timestamp of when the connector fetched this document. */\n readonly fetchedAt: string;\n}\n\n/**\n * A single document yielded by an incremental sync. Connectors are responsible\n * for chunking large source documents themselves if needed; the orchestrator\n * ingests `content` as a unit.\n */\nexport interface ConnectorDocument {\n /** Connector-local stable id for this document. SHOULD match `source.externalId`. */\n readonly id: string;\n /** Optional human-readable title. */\n readonly title?: string;\n /** Body content. Plaintext or Markdown — connectors document their format. */\n readonly content: string;\n /** Provenance. Required. */\n readonly source: ConnectorDocumentSource;\n}\n\n/**\n * Arguments passed to `syncIncremental`. The framework owns cursor/config\n * lifecycle; connectors only read these and return the next cursor.\n */\nexport interface SyncIncrementalArgs {\n /** Last persisted cursor, or `null` on the first ever sync. */\n readonly cursor: ConnectorCursor | null;\n /** Validated connector config (already passed through `validateConfig`). */\n readonly config: ConnectorConfig;\n /** Optional abort signal. Connectors SHOULD honor it for cooperative cancellation. */\n readonly abortSignal?: AbortSignal;\n /**\n * Host resolver for credential fields that hold a SecretRef. A connector\n * resolves those at poll time. Absent a resolver, a plain string still works\n * and a SecretRef fails that connector only.\n */\n readonly resolveSecretRef?: ResolveSecretRefFn | null;\n}\n\n/**\n * Result of a single incremental sync pass.\n *\n * `newDocs` MAY be empty (no new documents since the last cursor). `nextCursor`\n * MUST always be returned — even on no-op syncs the framework persists it so\n * `updatedAt` reflects the most recent attempt.\n */\nexport interface SyncIncrementalResult {\n readonly newDocs: ConnectorDocument[];\n readonly nextCursor: ConnectorCursor;\n}\n\n/**\n * The contract every live connector implements.\n *\n * Connectors MUST be:\n * - **Idempotent**: re-running with the same cursor MUST NOT duplicate\n * documents. The `source.externalId` + `source.externalRevision` pair is\n * used downstream for dedup.\n * - **Read-only on the source**: live connectors never mutate the upstream\n * system (no marking emails read, no editing Notion pages).\n * - **Cancellable**: long-running syncs SHOULD periodically check\n * `abortSignal.aborted` and bail cleanly.\n * - **Privacy-aware**: connectors MUST NOT log document content. Logging\n * metadata (counts, ids, timings) is fine.\n */\nexport interface LiveConnector {\n /**\n * Stable connector id. MUST match `CONNECTOR_ID_PATTERN` — lowercase\n * alphanumeric plus dash, 1–64 chars, must start AND end with alphanumeric\n * (no leading or trailing dash). The registry enforces this.\n */\n readonly id: string;\n /** Short human-readable name shown in CLI / status output. */\n readonly displayName: string;\n /** Optional longer description. */\n readonly description?: string;\n\n /**\n * Validate raw user-supplied config. MUST throw on malformed input — never\n * silently default. The returned object is the runtime config passed back to\n * `syncIncremental`; it may contain hydrated credentials and MUST NOT be\n * persisted directly. Connectors SHOULD strip unknown fields.\n */\n validateConfig(raw: unknown): ConnectorConfig;\n\n /**\n * Return the subset of validated config that may be written to disk. Use this\n * for state/config persistence instead of `validateConfig()`. Connectors that\n * need credentials should omit the raw values here and store only non-secret\n * scope/schedule fields plus any connector-owned secret references.\n */\n persistConfig?(validated: ConnectorConfig): ConnectorConfig;\n\n /**\n * Run one incremental sync pass. See `SyncIncrementalArgs` /\n * `SyncIncrementalResult` for the contract.\n */\n syncIncremental(args: SyncIncrementalArgs): Promise<SyncIncrementalResult>;\n}\n\nexport function persistableConnectorConfig(\n connector: Pick<LiveConnector, \"persistConfig\">,\n validated: ConnectorConfig,\n): ConnectorConfig {\n if (typeof connector.persistConfig === \"function\") {\n return connector.persistConfig(validated);\n }\n return redactConnectorConfigSecrets(validated);\n}\n\n/**\n * Regex enforcing the connector-id naming rule. Exported so connectors and\n * tests can validate ids consistently with the registry.\n *\n * Rule: lowercase alphanumeric + dash, 1..64 chars, must start AND end with\n * alphanumeric (no leading or trailing dash). Single-char ids are allowed.\n */\nexport const CONNECTOR_ID_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;\n\n/**\n * Returns `true` if `id` is a syntactically valid connector id.\n */\nexport function isValidConnectorId(id: unknown): id is string {\n return typeof id === \"string\" && CONNECTOR_ID_PATTERN.test(id);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
StorageManager
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-WQURALGV.js";
|
|
4
4
|
import {
|
|
5
5
|
getCachedRuleMemories,
|
|
6
6
|
setCachedRuleMemories
|
|
@@ -119,4 +119,4 @@ export {
|
|
|
119
119
|
compareVerifiedSemanticRuleResults,
|
|
120
120
|
searchVerifiedSemanticRules
|
|
121
121
|
};
|
|
122
|
-
//# sourceMappingURL=chunk-
|
|
122
|
+
//# sourceMappingURL=chunk-MH632KPO.js.map
|
|
@@ -123,6 +123,10 @@ function negatedScope(clause) {
|
|
|
123
123
|
const match = NEGATOR_PATTERN.exec(clause.replace(/[\u2018\u2019]/gu, "'"));
|
|
124
124
|
return match ? clause.slice(match.index + match[0].length) : clause;
|
|
125
125
|
}
|
|
126
|
+
function textBeforeNegator(clause) {
|
|
127
|
+
const match = NEGATOR_PATTERN.exec(clause.replace(/[\u2018\u2019]/gu, "'"));
|
|
128
|
+
return match ? clause.slice(0, match.index) : "";
|
|
129
|
+
}
|
|
126
130
|
function misstatesQuote(content, quote) {
|
|
127
131
|
const claimClauses = splitClaimClauses(content);
|
|
128
132
|
return (quote.match(CLAUSE_PATTERN) ?? []).some((quoteClause) => {
|
|
@@ -135,8 +139,16 @@ function misstatesQuote(content, quote) {
|
|
|
135
139
|
const clausesSharing = (tokens) => claimClauses.filter((claimClause) => clauseOverlap(tokens, claimClause).shared > 0);
|
|
136
140
|
if (needsNegation) {
|
|
137
141
|
const scopeTokens = [...tokenize(negatedScope(quoteClause))];
|
|
142
|
+
const subjectTokens = [...tokenize(textBeforeNegator(quoteClause))];
|
|
143
|
+
const scopeHas = new Set(scopeTokens);
|
|
138
144
|
const related = clausesSharing(scopeTokens.length > 0 ? scopeTokens : clauseTokens);
|
|
139
|
-
|
|
145
|
+
const keepsDenial = related.some((text) => {
|
|
146
|
+
if (!hasNegation(text) && !EXCLUSION_PATTERN.test(text)) return false;
|
|
147
|
+
if (subjectTokens.length === 0 || clauseOverlap(subjectTokens, text).shared > 0) return true;
|
|
148
|
+
const ownSubject = [...tokenize(textBeforeNegator(text))].filter((token) => !scopeHas.has(token));
|
|
149
|
+
return ownSubject.length === 0;
|
|
150
|
+
});
|
|
151
|
+
if (!keepsDenial) return true;
|
|
140
152
|
}
|
|
141
153
|
return needsReporting && !clausesSharing(clauseTokens).some((text) => hasNonAssertiveReporting(text) || text.trim().endsWith("?"));
|
|
142
154
|
});
|
|
@@ -202,4 +214,4 @@ export {
|
|
|
202
214
|
quoteAnchoredEntitySupport,
|
|
203
215
|
isQuoteAnchoredRelationship
|
|
204
216
|
};
|
|
205
|
-
//# sourceMappingURL=chunk-
|
|
217
|
+
//# sourceMappingURL=chunk-MLAMXQAW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/extraction-quote-grounding.ts"],"sourcesContent":["/**\n * Quote-anchored source grounding.\n *\n * An extracted fact carries the exact words that support it in `quote`. A quote\n * that is a verbatim substring of the source (whitespace-normalized) is real\n * evidence: the claim only has to be supported by the text around that span,\n * not by word overlap against the whole conversation. Items without such a\n * quote never get an anchor here and stay on the whole-source rules.\n */\n\nimport type { ExtractionResult } from \"./types.js\";\nimport {\n GROUNDING_MIN_SHARED_TOKENS,\n areGroundingTokensCompatible,\n entityNameStart,\n hasDenialReporting,\n hasNonAssertiveReporting,\n isGroundedEntityName,\n normalizedEntityIdentifierTokens,\n sourceSentences,\n tokenize,\n} from \"./extraction-source-grounding-helpers.js\";\n\n/** Shortest normalized quote that counts as evidence; \"the\" or \"I\" prove nothing. */\nexport const QUOTE_ANCHOR_MIN_CHARS = 12;\n/** Characters of context kept on each side of the quote inside its turn. */\nexport const QUOTE_ANCHOR_CONTEXT_CHARS = 400;\n/** Share of a claim's content words that must occur in the quote's window. */\nexport const QUOTE_ANCHOR_MIN_WINDOW_COVERAGE = 0.35;\n/** Share of a claim's content words that must occur anywhere in the source; the rest is invented. */\nexport const QUOTE_ANCHOR_MIN_SOURCE_COVERAGE = 0.75;\nconst QUOTE_ANCHOR_MAX_OCCURRENCES = 8;\nconst TURN_SEPARATOR = \"\\n\\n\";\nconst NEGATOR_PATTERN = /\\b(?:not|no|never|cannot|without|neither|nor)\\b|n't\\b/iu;\nconst EXCLUSION_PATTERN =\n /\\b(?:exclu\\w*|avoid\\w*|omit\\w*|skip\\w*|ignor\\w*|forbid\\w*|prohibit\\w*|disallow\\w*|prevent\\w*|reject\\w*|refus\\w*|insufficient|instead|rather|unless|except)\\b/iu;\n\n/** A verbatim quote and the text around it inside its turn. */\nexport interface QuoteAnchor {\n /** Whitespace-normalized quote, exactly as it appears in the source. */\n quote: string;\n /** `quote` with its turn-local context; `;` inside the quote is rewritten to `,`. */\n window: string;\n}\n\nexport type TokenPool = (token: string) => boolean;\n\ntype Entity = ExtractionResult[\"entities\"][number];\ntype Relationship = NonNullable<ExtractionResult[\"relationships\"]>[number];\n\nfunction normalizeWhitespaceWithMap(source: string): { text: string; originalIndex: number[] } {\n const chars: string[] = [];\n const originalIndex: number[] = [];\n let inSpace = false;\n for (let index = 0; index < source.length; index += 1) {\n const char = source[index]!;\n if (/\\s/u.test(char)) {\n if (inSpace) continue;\n inSpace = true;\n chars.push(\" \");\n } else {\n inSpace = false;\n chars.push(char);\n }\n originalIndex.push(index);\n }\n return { text: chars.join(\"\"), originalIndex };\n}\n\nfunction turnStartOffsets(source: string, turns: readonly string[]): readonly number[] | undefined {\n const offsets: number[] = [];\n let at = 0;\n for (const turn of turns) {\n if (!source.startsWith(turn, at)) return undefined;\n offsets.push(at);\n at += turn.length + TURN_SEPARATOR.length;\n }\n return at - TURN_SEPARATOR.length === source.length ? offsets : undefined;\n}\n\nfunction findQuoteAnchors(quote: string, source: string, bounds?: readonly number[]): QuoteAnchor[] {\n const needle = quote.replace(/\\s+/gu, \" \").trim();\n if (needle.length < QUOTE_ANCHOR_MIN_CHARS) return [];\n const normalized = normalizeWhitespaceWithMap(source);\n const anchors: QuoteAnchor[] = [];\n let from = 0;\n while (anchors.length < QUOTE_ANCHOR_MAX_OCCURRENCES) {\n const at = normalized.text.indexOf(needle, from);\n if (at < 0) break;\n from = at + 1;\n const start = normalized.originalIndex[at]!;\n const end = normalized.originalIndex[at + needle.length - 1]! + 1;\n\n let before: string;\n let after: string;\n if (bounds === undefined) {\n if (source.slice(start, end).includes(TURN_SEPARATOR)) continue;\n before = source.slice(Math.max(0, start - QUOTE_ANCHOR_CONTEXT_CHARS), start);\n const sepBefore = before.lastIndexOf(TURN_SEPARATOR);\n if (sepBefore >= 0) before = before.slice(sepBefore + TURN_SEPARATOR.length);\n else if (start > QUOTE_ANCHOR_CONTEXT_CHARS) before = before.replace(/^\\S+/u, \"\");\n after = source.slice(end, end + QUOTE_ANCHOR_CONTEXT_CHARS);\n const sepAfter = after.indexOf(TURN_SEPARATOR);\n if (sepAfter >= 0) after = after.slice(0, sepAfter);\n else if (source.length - end > QUOTE_ANCHOR_CONTEXT_CHARS) after = after.replace(/\\S+$/u, \"\");\n } else {\n let turnStart = 0;\n let turnEnd = source.length;\n for (const bound of bounds) {\n if (bound <= start) turnStart = bound;\n else {\n turnEnd = bound - TURN_SEPARATOR.length;\n break;\n }\n }\n if (turnEnd < end) continue;\n before = source.slice(Math.max(turnStart, start - QUOTE_ANCHOR_CONTEXT_CHARS), start);\n if (start - QUOTE_ANCHOR_CONTEXT_CHARS > turnStart) before = before.replace(/^\\S+/u, \"\");\n const contextEnd = Math.min(turnEnd, end + QUOTE_ANCHOR_CONTEXT_CHARS);\n after = source.slice(end, contextEnd);\n if (end + QUOTE_ANCHOR_CONTEXT_CHARS < turnEnd) after = after.replace(/\\S+$/u, \"\");\n }\n\n anchors.push({ quote: needle, window: `${before}${needle.replace(/;/gu, \",\")}${after}` });\n }\n return anchors;\n}\n\n/**\n * Returns one anchor per place `quote` occurs verbatim in `source`, or [] when\n * the quote is absent, too short, or not an exact substring. A quote that\n * spans a turn separator is not one turn's words and yields no anchor; windows\n * never extend past their turn.\n *\n * The quote is one evidence unit: a `;` inside it is rewritten to `,` in the\n * window so sentence splitting cannot cut it, or a claim that merges two of\n * its clauses could never be grounded in a single sentence.\n *\n * `source` may have been rewritten before grounding (temporal anchoring,\n * pronoun resolution) while the model quoted the original words. When the raw\n * quote is not found, `transformQuote` applies the same rewrite to the quote.\n *\n * `turns` are the texts `source` was joined from; when they tile `source`\n * exactly, quotes and windows cut at real turn boundaries instead of any\n * blank line. Without them, every `TURN_SEPARATOR` run is a boundary.\n */\nexport function quoteAnchors(\n quote: string | undefined,\n source: string,\n transformQuote?: (quote: string) => string,\n turns?: readonly string[],\n): QuoteAnchor[] {\n if (typeof quote !== \"string\") return [];\n const bounds = turns === undefined ? undefined : turnStartOffsets(source, turns);\n const exact = findQuoteAnchors(quote, source, bounds);\n if (exact.length > 0 || transformQuote === undefined) return exact;\n return findQuoteAnchors(transformQuote(quote), source, bounds);\n}\n\nexport function tokenPool(tokens: ReadonlySet<string>): TokenPool {\n const list = [...tokens];\n return (token) => tokens.has(token) || list.some((poolToken) => areGroundingTokensCompatible(token, poolToken));\n}\n\nfunction hasNegation(text: string): boolean {\n return NEGATOR_PATTERN.test(text.replace(/[\\u2018\\u2019]/gu, \"'\")) || hasDenialReporting(text);\n}\n\nconst CLAUSE_PATTERN = /[^;.!?\\n]+[;.!?]*/gu;\n/** Split \"X, and Y\" claim coordination without touching list commas. */\nconst CLAIM_COORD_SPLIT = /,\\s{1,8}(?:and|but|or|nor)\\s{1,8}/iu;\n\nfunction splitClaimClauses(content: string): string[] {\n return (content.match(CLAUSE_PATTERN) ?? [content]).flatMap((clause) => {\n const parts = clause\n .split(CLAIM_COORD_SPLIT)\n .map((part) => part.trim())\n .filter((part) => part.length > 0);\n return parts.length > 0 ? parts : [clause];\n });\n}\n\nfunction clauseOverlap(clauseTokens: readonly string[], target: string) {\n const has = tokenPool(tokenize(target));\n const shared = clauseTokens.filter(has).length;\n return { shared, ratio: clauseTokens.length === 0 ? 0 : shared / clauseTokens.length };\n}\n\n/** What a quote clause's first negator governs: the words after it. */\nfunction negatedScope(clause: string): string {\n const match = NEGATOR_PATTERN.exec(clause.replace(/[\\u2018\\u2019]/gu, \"'\"));\n return match ? clause.slice(match.index + match[0].length) : clause;\n}\n\n/** The words before a clause's first negator, empty when it has none. */\nfunction textBeforeNegator(clause: string): string {\n const match = NEGATOR_PATTERN.exec(clause.replace(/[\\u2018\\u2019]/gu, \"'\"));\n return match ? clause.slice(0, match.index) : \"\";\n}\n\n/**\n * True when the claim asserts what a quote clause denies or does not assert.\n * A quote denial is kept only by a claim clause that shares what the negator\n * governs and does not name a different subject, so a negator about Bob does\n * not keep a denial about Alice. A clause that names no subject of its own\n * still counts, which keeps a pronoun clause and a subject-less passive. A\n * quote clause with no subject of its own keeps the clause-level check.\n */\nfunction misstatesQuote(content: string, quote: string): boolean {\n const claimClauses = splitClaimClauses(content);\n return (quote.match(CLAUSE_PATTERN) ?? []).some((quoteClause) => {\n const needsNegation = hasNegation(quoteClause);\n const needsReporting = quoteClause.trim().endsWith(\"?\") || hasNonAssertiveReporting(quoteClause);\n if (!needsNegation && !needsReporting) return false;\n const clauseTokens = [...tokenize(quoteClause)];\n const whole = clauseOverlap(clauseTokens, content);\n if (whole.shared < GROUNDING_MIN_SHARED_TOKENS || whole.ratio < 0.5) return false;\n const clausesSharing = (tokens: readonly string[]) =>\n claimClauses.filter((claimClause) => clauseOverlap(tokens, claimClause).shared > 0);\n if (needsNegation) {\n const scopeTokens = [...tokenize(negatedScope(quoteClause))];\n const subjectTokens = [...tokenize(textBeforeNegator(quoteClause))];\n const scopeHas = new Set(scopeTokens);\n const related = clausesSharing(scopeTokens.length > 0 ? scopeTokens : clauseTokens);\n const keepsDenial = related.some((text) => {\n if (!hasNegation(text) && !EXCLUSION_PATTERN.test(text)) return false;\n if (subjectTokens.length === 0 || clauseOverlap(subjectTokens, text).shared > 0) return true;\n const ownSubject = [...tokenize(textBeforeNegator(text))].filter((token) => !scopeHas.has(token));\n return ownSubject.length === 0;\n });\n if (!keepsDenial) return true;\n }\n return needsReporting\n && !clausesSharing(clauseTokens).some((text) => hasNonAssertiveReporting(text) || text.trim().endsWith(\"?\"));\n });\n}\n\n/**\n * A claim is supported by a verbatim quote when it shares at least\n * GROUNDING_MIN_SHARED_TOKENS content words with the quote, a good part of its\n * words sit in the text around the quote, nearly all of them occur in the\n * source at all, and it does not invert a negated clause of the quote. Reworded\n * and lightly elaborated claims pass; claims that bring words the conversation\n * never used, or that have nothing to do with the quote, do not.\n */\nexport function isQuoteAnchoredClaim(content: string, anchor: QuoteAnchor, sourceHas: TokenPool): boolean {\n const contentTokens = [...tokenize(content)];\n if (contentTokens.length === 0) return false;\n const quoteHas = tokenPool(tokenize(anchor.quote));\n if (contentTokens.filter(quoteHas).length < GROUNDING_MIN_SHARED_TOKENS) return false;\n const windowHas = tokenPool(tokenize(anchor.window));\n if (contentTokens.filter(windowHas).length / contentTokens.length < QUOTE_ANCHOR_MIN_WINDOW_COVERAGE) {\n return false;\n }\n return contentTokens.filter(sourceHas).length / contentTokens.length >= QUOTE_ANCHOR_MIN_SOURCE_COVERAGE\n && !misstatesQuote(content, anchor.quote);\n}\n\n/** Keeps the entity facts (flat and sectioned) `isSupported` accepts; undefined when none remain. */\nexport function filterEntityFacts(entity: Entity, isSupported: (fact: string) => boolean): Entity | undefined {\n const facts = entity.facts.filter(isSupported);\n const structuredSections = entity.structuredSections?.flatMap((section) => {\n const groundedFacts = section.facts.filter(isSupported);\n return groundedFacts.length > 0 ? [{ ...section, facts: groundedFacts }] : [];\n });\n if (facts.length === 0 && (structuredSections === undefined || structuredSections.length === 0)) {\n return undefined;\n }\n const { structuredSections: _structuredSections, ...entityWithoutSections } = entity;\n return {\n ...entityWithoutSections,\n facts,\n ...(structuredSections && structuredSections.length > 0 ? { structuredSections } : {}),\n };\n}\n\n/**\n * Entities carry no quote of their own; the anchors of facts that kept support\n * them. Returns a fact predicate, or undefined when no anchor window names the entity.\n */\nexport function quoteAnchoredEntitySupport(\n entity: Entity,\n anchors: readonly QuoteAnchor[],\n sourceHas: TokenPool,\n): ((fact: string) => boolean) | undefined {\n const nameAnchors = anchors.filter((anchor) => isGroundedEntityName(entity.name, anchor.window));\n if (nameAnchors.length === 0) return undefined;\n return (fact) => nameAnchors.some((anchor) => isQuoteAnchoredClaim(fact, anchor, sourceHas));\n}\n\n/**\n * The label words and both names must sit in one anchor-window sentence, source\n * before target, so an invented label or a reversed edge never passes.\n */\nexport function isQuoteAnchoredRelationship(\n relationship: Relationship,\n anchors: readonly QuoteAnchor[],\n sourceHas: TokenPool,\n): boolean {\n const labelTokens = [...tokenize(relationship.label)];\n if (labelTokens.length === 0) return false;\n const relationText = [\n normalizedEntityIdentifierTokens(relationship.source).join(\" \"),\n relationship.label,\n normalizedEntityIdentifierTokens(relationship.target).join(\" \"),\n ].join(\" \");\n return anchors.some((anchor) => {\n const windowHas = tokenPool(tokenize(anchor.window));\n return labelTokens.every(windowHas)\n && sourceSentences(anchor.window).some((sentence) => {\n const sourceStart = entityNameStart(relationship.source, sentence);\n return sourceStart >= 0 && entityNameStart(relationship.target, sentence) > sourceStart;\n })\n && isQuoteAnchoredClaim(relationText, anchor, sourceHas);\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
readGatewayWorkspaceDir,
|
|
27
27
|
tryCodexSubscriptionProvider,
|
|
28
28
|
withCodexRuntimeShutdown
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-QSF5SJAM.js";
|
|
30
30
|
import {
|
|
31
31
|
extractJsonCandidates
|
|
32
32
|
} from "./chunk-YBA52OKG.js";
|
|
@@ -843,4 +843,4 @@ export {
|
|
|
843
843
|
gatewayTaskChainOptions,
|
|
844
844
|
FallbackLlmClient
|
|
845
845
|
};
|
|
846
|
-
//# sourceMappingURL=chunk-
|
|
846
|
+
//# sourceMappingURL=chunk-MMRTPH3J.js.map
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from "./chunk-3UXOZBHV.js";
|
|
15
15
|
import {
|
|
16
16
|
StorageManager
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-WQURALGV.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-MUX7IL77.js.map
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
toBackupStamp
|
|
3
3
|
} from "./chunk-KJTKLXTH.js";
|
|
4
|
-
import {
|
|
5
|
-
loadPersistedNativeKnowledgeChunks
|
|
6
|
-
} from "./chunk-2DQ2XREU.js";
|
|
7
4
|
import {
|
|
8
5
|
listMemoryGovernanceRuns,
|
|
9
6
|
readMemoryGovernanceRunArtifact
|
|
10
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-RM2YUFH6.js";
|
|
8
|
+
import {
|
|
9
|
+
loadPersistedNativeKnowledgeChunks
|
|
10
|
+
} from "./chunk-2DQ2XREU.js";
|
|
11
11
|
import {
|
|
12
12
|
StorageManager
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-WQURALGV.js";
|
|
14
14
|
import {
|
|
15
15
|
normalizeProjectionPreview,
|
|
16
16
|
normalizeProjectionTags
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
} from "./chunk-RL6NEVXT.js";
|
|
46
46
|
import {
|
|
47
47
|
isSupportPassportPrivateMemory
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-NS7U4ZYU.js";
|
|
49
49
|
import {
|
|
50
50
|
commitPreparedFileAtomically
|
|
51
51
|
} from "./chunk-BTNNL7ZW.js";
|
|
@@ -1156,4 +1156,4 @@ export {
|
|
|
1156
1156
|
verifyMemoryProjection,
|
|
1157
1157
|
repairMemoryProjection
|
|
1158
1158
|
};
|
|
1159
|
-
//# sourceMappingURL=chunk-
|
|
1159
|
+
//# sourceMappingURL=chunk-NAQ4A5IX.js.map
|
|
@@ -5,13 +5,13 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
unifiedDedupeAndRank
|
|
7
7
|
} from "./chunk-LUPVCGYK.js";
|
|
8
|
-
import {
|
|
9
|
-
buildEvidencePack
|
|
10
|
-
} from "./chunk-DGFU5MSX.js";
|
|
11
8
|
import {
|
|
12
9
|
lcmArchiveRowId,
|
|
13
10
|
lcmEvidenceIdentity
|
|
14
11
|
} from "./chunk-C37ZRXX7.js";
|
|
12
|
+
import {
|
|
13
|
+
buildEvidencePack
|
|
14
|
+
} from "./chunk-DGFU5MSX.js";
|
|
15
15
|
|
|
16
16
|
// src/explicit-cue-recall.ts
|
|
17
17
|
var DEFAULT_MAX_CHARS = 2400;
|
|
@@ -4258,4 +4258,4 @@ export {
|
|
|
4258
4258
|
collectStructuredPlanCues,
|
|
4259
4259
|
collectTemporalLexicalCues
|
|
4260
4260
|
};
|
|
4261
|
-
//# sourceMappingURL=chunk-
|
|
4261
|
+
//# sourceMappingURL=chunk-NED7IR7B.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseXrayFormat
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-R4BBGPI2.js";
|
|
4
4
|
import {
|
|
5
5
|
RECALL_DISCLOSURE_LEVELS,
|
|
6
6
|
isRecallDisclosure
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-43AA5FVD.js";
|
|
8
8
|
|
|
9
9
|
// src/recall-xray-cli.ts
|
|
10
10
|
function parseXrayDisclosureFlag(value) {
|
|
@@ -50,4 +50,4 @@ export {
|
|
|
50
50
|
parseXrayBudgetFlag,
|
|
51
51
|
parseXrayCliOptions
|
|
52
52
|
};
|
|
53
|
-
//# sourceMappingURL=chunk-
|
|
53
|
+
//# sourceMappingURL=chunk-NHVAX7MH.js.map
|
|
@@ -3,10 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-QV2VZKD5.js";
|
|
4
4
|
import {
|
|
5
5
|
createVersion
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import {
|
|
8
|
-
bumpMemoryCorpusVersionForDir
|
|
9
|
-
} from "./chunk-M5GVCJSJ.js";
|
|
6
|
+
} from "./chunk-SQ3GEIEL.js";
|
|
10
7
|
import {
|
|
11
8
|
HistoricalEntityCanonicalIdCache,
|
|
12
9
|
canonicalizeEntityRefFrontmatter,
|
|
@@ -16,6 +13,9 @@ import {
|
|
|
16
13
|
requestEntityCanonicalIdReconcile,
|
|
17
14
|
withRawEntityPageMutation
|
|
18
15
|
} from "./chunk-IAYUTGF2.js";
|
|
16
|
+
import {
|
|
17
|
+
bumpMemoryCorpusVersionForDir
|
|
18
|
+
} from "./chunk-M5GVCJSJ.js";
|
|
19
19
|
import {
|
|
20
20
|
parseExportBundle
|
|
21
21
|
} from "./chunk-WEHSQBFR.js";
|
|
@@ -296,4 +296,4 @@ function mintForkId(capsuleId, originalId, now) {
|
|
|
296
296
|
export {
|
|
297
297
|
importCapsule
|
|
298
298
|
};
|
|
299
|
-
//# sourceMappingURL=chunk-
|
|
299
|
+
//# sourceMappingURL=chunk-NMGHGTVR.js.map
|