@remnic/core 10.0.92 → 10.0.94
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 +332 -59
- 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 +287 -303
- package/dist/access-cli.js.map +1 -1
- package/dist/access-coding-context-resolution.js +3 -3
- package/dist/access-extraction-force-flush.js +332 -59
- package/dist/access-http-briefing.js +332 -60
- package/dist/access-http-query.js +332 -61
- package/dist/access-http.js +330 -112
- package/dist/access-identity-continuity-surface.js +332 -59
- package/dist/access-lcm-surface.js +332 -59
- 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 +330 -87
- 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 +332 -59
- package/dist/access-offline-manifest.js +12 -11
- package/dist/access-operations-batch.js +330 -64
- package/dist/access-operations.js +330 -73
- package/dist/access-recall-concurrency.js +332 -59
- package/dist/access-recall-diagnostics.js +5 -4
- package/dist/access-recall-response.js +332 -59
- package/dist/access-recall-surface.js +332 -59
- package/dist/access-schema.js +2 -2
- package/dist/access-service.js +335 -62
- 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-RGUUBAT5.js → chunk-2WCLTWV2.js} +3 -3
- package/dist/{chunk-BQZEGOZ4.js → chunk-2YODOG2O.js} +2 -2
- package/dist/{chunk-WYG5UNDY.js → chunk-3AUDELZL.js} +2 -2
- package/dist/{chunk-MB6ZJDN6.js → chunk-3FBAEPCU.js} +2 -2
- package/dist/{chunk-PIITB2YZ.js → chunk-3ZS3NUEI.js} +4 -4
- package/dist/{chunk-ATPWZ4F7.js → chunk-4EZWSDFL.js} +5 -5
- 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-LUJCQQ3L.js → chunk-65HWAQ4F.js} +2 -2
- 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-RRJBDXAQ.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-Z7JIZ42P.js → chunk-BN2EBCLB.js} +3 -3
- package/dist/{chunk-YYOMGOHB.js → chunk-BOK6BN5M.js} +2 -2
- package/dist/{chunk-U6UNF5ZS.js → chunk-BPK32FRM.js} +3 -3
- package/dist/{chunk-OH7QLI6W.js → chunk-BPPUH5JM.js} +5 -5
- package/dist/{chunk-O6ZSWHVR.js → chunk-C2KE5DMD.js} +4 -4
- package/dist/{chunk-47F6WWLQ.js → chunk-C73N66PR.js} +2 -2
- package/dist/{chunk-WRT7GJ2D.js → chunk-CIWMTNHW.js} +4 -4
- package/dist/{chunk-HF3BKUVV.js → chunk-CL4JU3DF.js} +7 -7
- package/dist/{chunk-O55EKIEC.js → chunk-D3ZKHLN2.js} +2 -2
- package/dist/chunk-D5S6RL32.js +67137 -0
- package/dist/chunk-D5S6RL32.js.map +1 -0
- package/dist/{chunk-ETZLQOUR.js → chunk-D5XG2KPS.js} +2 -2
- package/dist/{chunk-CD4BCCNP.js → chunk-DJ7WVEWX.js} +2 -2
- package/dist/{chunk-PWXPRVLK.js → chunk-DNY63QWJ.js} +2 -2
- package/dist/{chunk-4CJTQHEG.js → chunk-ESLOLCD4.js} +3 -3
- 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-BRLXOZLM.js → chunk-FQAQ63QA.js} +729 -17
- package/dist/chunk-FQAQ63QA.js.map +1 -0
- package/dist/{chunk-FSO3G4JJ.js → chunk-FWRXW7CZ.js} +5000 -2748
- package/dist/chunk-FWRXW7CZ.js.map +1 -0
- package/dist/{chunk-XYKBWTWI.js → chunk-FZZMLGWQ.js} +2 -2
- package/dist/{chunk-FNXQZGUS.js → chunk-GBJUTX44.js} +2 -2
- package/dist/{chunk-YLXETSAH.js → chunk-GCZJ7XRI.js} +2 -2
- package/dist/{chunk-UDKWFDV2.js → chunk-HLUHEMN4.js} +27 -3
- package/dist/chunk-HLUHEMN4.js.map +1 -0
- package/dist/{chunk-SUSW74WU.js → chunk-IL3RHBWM.js} +56 -5
- package/dist/chunk-IL3RHBWM.js.map +1 -0
- package/dist/{chunk-JV4S6VDH.js → chunk-ILY5SSOM.js} +2 -2
- package/dist/{chunk-EXHXJETE.js → chunk-IQ55QGY5.js} +2 -2
- package/dist/{chunk-4J363OAA.js → chunk-J7BIFOYP.js} +2 -2
- 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-E7YZXWIS.js → chunk-LNGELD2M.js} +2 -2
- package/dist/{chunk-FQKBJJYB.js → chunk-LPV72KRQ.js} +2 -2
- package/dist/{chunk-2LCRG2YV.js → chunk-M2XR6B6V.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-TVIVY7KU.js → chunk-NED7IR7B.js} +4 -4
- package/dist/{chunk-6F5MJFXY.js → chunk-NHVAX7MH.js} +2 -2
- 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-RIMK327Y.js → chunk-OUVYYKYU.js} +7 -7
- package/dist/{chunk-QJPJXCS4.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-RWGRSKN5.js → chunk-PT4DZDJA.js} +2 -2
- package/dist/{chunk-7EPY3IQL.js → chunk-PZJQ46DH.js} +2 -2
- package/dist/{chunk-MXHMNKWG.js → chunk-QDESY76F.js} +3 -3
- package/dist/{chunk-E3A7ELSG.js → chunk-QHARXWTC.js} +2 -2
- package/dist/{chunk-BLJT6JP2.js → chunk-QMFAMOT2.js} +2 -2
- 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-RHQLIHMB.js → chunk-R4BBGPI2.js} +218 -218
- package/dist/chunk-R4BBGPI2.js.map +1 -0
- package/dist/{chunk-NT7F7WJQ.js → chunk-RFUL2ZIQ.js} +291 -93
- package/dist/chunk-RFUL2ZIQ.js.map +1 -0
- package/dist/{chunk-E4IRW2FG.js → chunk-RKABLU5M.js} +233 -40
- package/dist/chunk-RKABLU5M.js.map +1 -0
- package/dist/{chunk-P4WNBSTW.js → chunk-RNG7S3FY.js} +2 -2
- package/dist/{chunk-IPT2OY4B.js → chunk-RQFXZYTF.js} +35 -33
- package/dist/chunk-RQFXZYTF.js.map +1 -0
- package/dist/{chunk-5HFXGXE5.js → chunk-RUTN5MMC.js} +7 -9
- package/dist/{chunk-5HFXGXE5.js.map → chunk-RUTN5MMC.js.map} +1 -1
- package/dist/{chunk-AP6NCP3V.js → chunk-S3S72HRM.js} +8 -6
- package/dist/chunk-S3S72HRM.js.map +1 -0
- package/dist/{chunk-ZGFLIK5H.js → chunk-SPAKBUJY.js} +2 -2
- package/dist/{chunk-ER7XANP3.js → chunk-SQ3GEIEL.js} +4 -4
- package/dist/{chunk-PMJFRDJI.js → chunk-SUTEBYGI.js} +2 -2
- package/dist/{chunk-HVX4LPNY.js → chunk-T4KY6UYF.js} +5 -3
- package/dist/{chunk-HVX4LPNY.js.map → chunk-T4KY6UYF.js.map} +1 -1
- package/dist/{chunk-R6HMAIKM.js → chunk-TOSPNM4T.js} +2 -2
- package/dist/{chunk-RMXS4I66.js → chunk-TVDJEUV5.js} +2 -2
- package/dist/{chunk-PLCYKAWH.js → chunk-TYRWSE4B.js} +2 -2
- package/dist/{chunk-B4QYQFGY.js → chunk-UBEKET2V.js} +383 -36
- 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-E6Z4FBX6.js → chunk-V2QSNFCT.js} +2 -2
- package/dist/{chunk-NBH6K6G5.js → chunk-V65H7MI6.js} +345 -16
- 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-7W3RXOIG.js → chunk-WGGRZYSQ.js} +14 -14
- package/dist/{chunk-RIZ5NUNN.js → chunk-WQ4XKIOX.js} +4 -4
- package/dist/{chunk-QEYTRFR3.js → chunk-X26YOFGS.js} +2 -2
- package/dist/{chunk-ITHZFHST.js → chunk-X2BVB74H.js} +2 -2
- package/dist/{chunk-6F42QONS.js → chunk-X5HGU2EM.js} +4 -4
- package/dist/{chunk-GVGAO3WV.js → chunk-XJ2S5UBF.js} +3 -3
- package/dist/{chunk-IBJ6U753.js → chunk-XKZBZOKV.js} +4 -4
- package/dist/{chunk-65AUYLU3.js → chunk-XPXHQI6N.js} +2 -2
- package/dist/chunk-XRWWCW4H.js +102 -0
- package/dist/chunk-XRWWCW4H.js.map +1 -0
- package/dist/{chunk-HH5LY6RR.js → chunk-YIXJSW5M.js} +5 -5
- package/dist/{chunk-2JSDPUKD.js → chunk-YMFPFXEX.js} +2 -2
- package/dist/{chunk-N4KAE2NB.js → chunk-Z3FTSHT5.js} +3 -3
- package/dist/{chunk-7GLJMDEQ.js → chunk-ZDV5YWID.js} +6 -6
- package/dist/{chunk-645WOU5I.js → chunk-ZDZJNXL6.js} +4 -4
- package/dist/{chunk-6TCXIRP5.js → chunk-ZKE4CO6B.js} +4 -4
- package/dist/cli.js +303 -185
- package/dist/coding/export-okf-codegraph.js +29 -29
- package/dist/compounding/engine.js +23 -24
- 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/google-drive.d.ts +2 -0
- 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 +773 -794
- package/dist/lcm/index.js +2 -2
- package/dist/live-connectors-runner.js +2 -3
- 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 +278 -294
- package/dist/orchestration/backlog-scheduler.js.map +1 -1
- package/dist/orchestration/buffer-drain-runner.js +278 -294
- 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 +9 -11
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +290 -307
- 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 +1 -1
- package/dist/recall-navigation-operations.js +2 -283
- package/dist/recall-navigation.js +332 -59
- 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 +2 -2
- package/dist/recall-xray-renderer.js +1 -1
- 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/storage/buffer-drain-claims.d.ts +97 -12
- package/dist/storage/buffer-drain-claims.d.ts.map +1 -1
- package/dist/storage/buffer-drain-claims.js +1 -1
- package/dist/storage/buffer-session-files.d.ts +4 -0
- package/dist/storage/buffer-session-files.d.ts.map +1 -1
- package/dist/storage/buffer-session-files.js +1 -1
- package/dist/storage/content-hash-index.d.ts +10 -0
- package/dist/storage/content-hash-index.d.ts.map +1 -1
- package/dist/storage.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/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/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/buffer-drain-handover.test.ts +5 -1
- package/src/connectors/live/google-drive.test.ts +39 -0
- package/src/connectors/live/google-drive.ts +41 -12
- 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/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 +47 -50
- package/src/storage/buffer-drain-claims.test.ts +385 -1
- package/src/storage/buffer-drain-claims.ts +380 -97
- package/src/storage/buffer-session-files.ts +4 -0
- package/src/storage/content-hash-index.ts +27 -2
- package/src/storage-fact-hash-publish-failure.test.ts +87 -0
- package/src/storage.ts +5 -1
- package/src/summarizer-shutdown.test.ts +195 -0
- package/src/summarizer.ts +15 -2
- 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-2FGLEF7C.js +0 -61
- package/dist/chunk-2FGLEF7C.js.map +0 -1
- package/dist/chunk-3RLK65ZK.js +0 -203
- package/dist/chunk-3RLK65ZK.js.map +0 -1
- package/dist/chunk-4WR2OD7K.js +0 -43
- package/dist/chunk-4WR2OD7K.js.map +0 -1
- package/dist/chunk-5KBIOI4G.js.map +0 -1
- package/dist/chunk-5M5M22GW.js +0 -346
- package/dist/chunk-5M5M22GW.js.map +0 -1
- package/dist/chunk-7HTUYFR3.js.map +0 -1
- package/dist/chunk-AP6NCP3V.js.map +0 -1
- package/dist/chunk-B4QYQFGY.js.map +0 -1
- package/dist/chunk-BJRTMNCB.js +0 -1919
- package/dist/chunk-BJRTMNCB.js.map +0 -1
- package/dist/chunk-BRLXOZLM.js.map +0 -1
- package/dist/chunk-BSACVS6R.js +0 -621
- package/dist/chunk-BSACVS6R.js.map +0 -1
- package/dist/chunk-DKAA2IHQ.js +0 -3644
- package/dist/chunk-DKAA2IHQ.js.map +0 -1
- package/dist/chunk-E4IRW2FG.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-F55YQP7N.js +0 -336
- package/dist/chunk-F55YQP7N.js.map +0 -1
- package/dist/chunk-FHGWKODN.js +0 -2300
- package/dist/chunk-FHGWKODN.js.map +0 -1
- package/dist/chunk-FLRP63GU.js +0 -32219
- package/dist/chunk-FLRP63GU.js.map +0 -1
- package/dist/chunk-FSO3G4JJ.js.map +0 -1
- package/dist/chunk-ID6JGIMD.js +0 -25
- package/dist/chunk-ID6JGIMD.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-NBH6K6G5.js.map +0 -1
- package/dist/chunk-NT7F7WJQ.js.map +0 -1
- package/dist/chunk-NTRUKAAE.js +0 -223
- package/dist/chunk-NTRUKAAE.js.map +0 -1
- package/dist/chunk-OGXXGHW6.js +0 -223
- package/dist/chunk-OGXXGHW6.js.map +0 -1
- package/dist/chunk-PPCLZ4JU.js +0 -28
- package/dist/chunk-PPCLZ4JU.js.map +0 -1
- package/dist/chunk-QJPJXCS4.js.map +0 -1
- package/dist/chunk-R2LBF4HH.js +0 -729
- package/dist/chunk-R2LBF4HH.js.map +0 -1
- package/dist/chunk-RHQLIHMB.js.map +0 -1
- package/dist/chunk-RRTVGD3J.js +0 -792
- package/dist/chunk-RRTVGD3J.js.map +0 -1
- package/dist/chunk-SUSW74WU.js.map +0 -1
- package/dist/chunk-T6OZW2WR.js.map +0 -1
- package/dist/chunk-UDKWFDV2.js.map +0 -1
- package/dist/chunk-V7IZKJI3.js +0 -22
- package/dist/chunk-V7IZKJI3.js.map +0 -1
- package/dist/chunk-X562NQFA.js +0 -10144
- package/dist/chunk-X562NQFA.js.map +0 -1
- package/dist/chunk-XDSCJOH7.js +0 -3991
- package/dist/chunk-XDSCJOH7.js.map +0 -1
- package/dist/chunk-YCNIGRRF.js.map +0 -1
- package/dist/chunk-ZTTCDQ4I.js +0 -18796
- package/dist/chunk-ZTTCDQ4I.js.map +0 -1
- /package/dist/{capsule-merge-2IIXFXN5.js.map → capsule-merge-6IDAYTKG.js.map} +0 -0
- /package/dist/{chunk-RGUUBAT5.js.map → chunk-2WCLTWV2.js.map} +0 -0
- /package/dist/{chunk-BQZEGOZ4.js.map → chunk-2YODOG2O.js.map} +0 -0
- /package/dist/{chunk-WYG5UNDY.js.map → chunk-3AUDELZL.js.map} +0 -0
- /package/dist/{chunk-MB6ZJDN6.js.map → chunk-3FBAEPCU.js.map} +0 -0
- /package/dist/{chunk-PIITB2YZ.js.map → chunk-3ZS3NUEI.js.map} +0 -0
- /package/dist/{chunk-ATPWZ4F7.js.map → chunk-4EZWSDFL.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-LUJCQQ3L.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-RRJBDXAQ.js.map → chunk-B377OKUP.js.map} +0 -0
- /package/dist/{chunk-Z7JIZ42P.js.map → chunk-BN2EBCLB.js.map} +0 -0
- /package/dist/{chunk-YYOMGOHB.js.map → chunk-BOK6BN5M.js.map} +0 -0
- /package/dist/{chunk-U6UNF5ZS.js.map → chunk-BPK32FRM.js.map} +0 -0
- /package/dist/{chunk-OH7QLI6W.js.map → chunk-BPPUH5JM.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-WRT7GJ2D.js.map → chunk-CIWMTNHW.js.map} +0 -0
- /package/dist/{chunk-HF3BKUVV.js.map → chunk-CL4JU3DF.js.map} +0 -0
- /package/dist/{chunk-O55EKIEC.js.map → chunk-D3ZKHLN2.js.map} +0 -0
- /package/dist/{chunk-ETZLQOUR.js.map → chunk-D5XG2KPS.js.map} +0 -0
- /package/dist/{chunk-CD4BCCNP.js.map → chunk-DJ7WVEWX.js.map} +0 -0
- /package/dist/{chunk-PWXPRVLK.js.map → chunk-DNY63QWJ.js.map} +0 -0
- /package/dist/{chunk-4CJTQHEG.js.map → chunk-ESLOLCD4.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-XYKBWTWI.js.map → chunk-FZZMLGWQ.js.map} +0 -0
- /package/dist/{chunk-FNXQZGUS.js.map → chunk-GBJUTX44.js.map} +0 -0
- /package/dist/{chunk-YLXETSAH.js.map → chunk-GCZJ7XRI.js.map} +0 -0
- /package/dist/{chunk-JV4S6VDH.js.map → chunk-ILY5SSOM.js.map} +0 -0
- /package/dist/{chunk-EXHXJETE.js.map → chunk-IQ55QGY5.js.map} +0 -0
- /package/dist/{chunk-4J363OAA.js.map → chunk-J7BIFOYP.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-E7YZXWIS.js.map → chunk-LNGELD2M.js.map} +0 -0
- /package/dist/{chunk-FQKBJJYB.js.map → chunk-LPV72KRQ.js.map} +0 -0
- /package/dist/{chunk-2LCRG2YV.js.map → chunk-M2XR6B6V.js.map} +0 -0
- /package/dist/{chunk-ZU5JDLOH.js.map → chunk-MMRTPH3J.js.map} +0 -0
- /package/dist/{chunk-TVIVY7KU.js.map → chunk-NED7IR7B.js.map} +0 -0
- /package/dist/{chunk-6F5MJFXY.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-RIMK327Y.js.map → chunk-OUVYYKYU.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-MXHMNKWG.js.map → chunk-QDESY76F.js.map} +0 -0
- /package/dist/{chunk-E3A7ELSG.js.map → chunk-QHARXWTC.js.map} +0 -0
- /package/dist/{chunk-BLJT6JP2.js.map → chunk-QMFAMOT2.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-P4WNBSTW.js.map → chunk-RNG7S3FY.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-PMJFRDJI.js.map → chunk-SUTEBYGI.js.map} +0 -0
- /package/dist/{chunk-R6HMAIKM.js.map → chunk-TOSPNM4T.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-E6Z4FBX6.js.map → chunk-V2QSNFCT.js.map} +0 -0
- /package/dist/{chunk-7W3RXOIG.js.map → chunk-WGGRZYSQ.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-6F42QONS.js.map → chunk-X5HGU2EM.js.map} +0 -0
- /package/dist/{chunk-GVGAO3WV.js.map → chunk-XJ2S5UBF.js.map} +0 -0
- /package/dist/{chunk-IBJ6U753.js.map → chunk-XKZBZOKV.js.map} +0 -0
- /package/dist/{chunk-65AUYLU3.js.map → chunk-XPXHQI6N.js.map} +0 -0
- /package/dist/{chunk-HH5LY6RR.js.map → chunk-YIXJSW5M.js.map} +0 -0
- /package/dist/{chunk-2JSDPUKD.js.map → chunk-YMFPFXEX.js.map} +0 -0
- /package/dist/{chunk-N4KAE2NB.js.map → chunk-Z3FTSHT5.js.map} +0 -0
- /package/dist/{chunk-7GLJMDEQ.js.map → chunk-ZDV5YWID.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/{contradiction-scan-P2MHYFVG.js.map → contradiction-scan-IBPYGQZF.js.map} +0 -0
- /package/dist/{resolution-N7H3F2E4.js.map → resolution-OVVIF7ZY.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseFrontmatter
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-FWRXW7CZ.js";
|
|
4
4
|
import {
|
|
5
5
|
activityDigestPath,
|
|
6
6
|
isValidActivityDate
|
|
@@ -199,4 +199,4 @@ export {
|
|
|
199
199
|
buildStandup,
|
|
200
200
|
standupHelp
|
|
201
201
|
};
|
|
202
|
-
//# sourceMappingURL=chunk-
|
|
202
|
+
//# sourceMappingURL=chunk-FZZMLGWQ.js.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
computeCorpusWatermarks,
|
|
6
6
|
resolveCorpusNamespaceRoots
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-D5XG2KPS.js";
|
|
8
8
|
|
|
9
9
|
// src/operator-doctor-corpus.ts
|
|
10
10
|
var DIGEST_PREFIX_LENGTH = 12;
|
|
@@ -78,4 +78,4 @@ export {
|
|
|
78
78
|
corpusWatermarksFromCheck,
|
|
79
79
|
summarizeCorpusAndReplica
|
|
80
80
|
};
|
|
81
|
-
//# sourceMappingURL=chunk-
|
|
81
|
+
//# sourceMappingURL=chunk-GBJUTX44.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
lcmSessionKeyForNamespace
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-4RAJT5L2.js";
|
|
4
4
|
import {
|
|
5
5
|
log
|
|
6
6
|
} from "./chunk-3WCASNQZ.js";
|
|
@@ -77,4 +77,4 @@ export {
|
|
|
77
77
|
observeTranscriptSessionKey,
|
|
78
78
|
ObserveTranscriptPersister
|
|
79
79
|
};
|
|
80
|
-
//# sourceMappingURL=chunk-
|
|
80
|
+
//# sourceMappingURL=chunk-GCZJ7XRI.js.map
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
} from "./chunk-3WCASNQZ.js";
|
|
19
19
|
|
|
20
20
|
// src/storage/content-hash-index.ts
|
|
21
|
-
import { mkdir, stat } from "fs/promises";
|
|
21
|
+
import { mkdir, stat, utimes } from "fs/promises";
|
|
22
22
|
import path from "path";
|
|
23
23
|
var CONTENT_HASH_INDEX_LOCK_STALE_MS = 3e4;
|
|
24
24
|
var CONTENT_HASH_INDEX_FORMAT_HEADER = "# remnic-content-hash-index:v2";
|
|
@@ -185,6 +185,24 @@ var ContentHashIndex = class _ContentHashIndex {
|
|
|
185
185
|
}
|
|
186
186
|
return current.mtimeMs === last.mtimeMs && current.size === last.size && current.dev === last.dev && current.ino === last.ino && current.ctimeMs === last.ctimeMs && current.birthtimeMs === last.birthtimeMs;
|
|
187
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Change the durable file's mtime without touching its content. A publish
|
|
190
|
+
* that throws or stays deferred leaves the file as it was, so a peer that
|
|
191
|
+
* already synced it keeps answering authoritative MISSes for deltas this
|
|
192
|
+
* instance holds only in memory (#3355). A new stamp makes every holder of the
|
|
193
|
+
* old fingerprint, this instance included, fail the freshness check and
|
|
194
|
+
* confirm against the corpus. Best effort: a file that cannot be stamped
|
|
195
|
+
* leaves the callers' existing retry and rebuild paths as the only repair.
|
|
196
|
+
*/
|
|
197
|
+
async invalidatePeerFreshness() {
|
|
198
|
+
try {
|
|
199
|
+
const { mtimeMs } = await stat(this.filePath);
|
|
200
|
+
const bumped = new Date(Math.max(Date.now(), mtimeMs + 1));
|
|
201
|
+
await utimes(this.filePath, bumped, bumped);
|
|
202
|
+
} catch (err) {
|
|
203
|
+
log.debug(`content-hash index: could not invalidate peer freshness of ${this.filePath}: ${err}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
188
206
|
/** Check if content already exists in the index. */
|
|
189
207
|
has(content) {
|
|
190
208
|
return this.hashes.has(_ContentHashIndex.computeHash(content));
|
|
@@ -305,9 +323,15 @@ var ContentHashIndex = class _ContentHashIndex {
|
|
|
305
323
|
`content-hash index: lock not acquired for ${this.filePath}; deferring reconcile save (dirty retained, scheduling durable retry)`
|
|
306
324
|
);
|
|
307
325
|
this.scheduleReconcileRetry();
|
|
326
|
+
await this.invalidatePeerFreshness();
|
|
308
327
|
return;
|
|
309
328
|
}
|
|
310
|
-
|
|
329
|
+
try {
|
|
330
|
+
await publish();
|
|
331
|
+
} catch (err) {
|
|
332
|
+
await this.invalidatePeerFreshness();
|
|
333
|
+
throw err;
|
|
334
|
+
}
|
|
311
335
|
if (this.dirty) {
|
|
312
336
|
this.scheduleReconcileRetry();
|
|
313
337
|
} else {
|
|
@@ -577,4 +601,4 @@ export {
|
|
|
577
601
|
FactHashIndexNotAuthoritativeError,
|
|
578
602
|
ContentHashIndex
|
|
579
603
|
};
|
|
580
|
-
//# sourceMappingURL=chunk-
|
|
604
|
+
//# sourceMappingURL=chunk-HLUHEMN4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/storage/content-hash-index.ts"],"sourcesContent":["// Content-hash dedup index for facts, extracted from storage.ts (issue #1909 /\n// PR #2016 write-hot-paths). Self-contained: cross-process locked reconcile /\n// rebuild saves plus the deferred durable-retry machinery. storage.ts imports\n// these back and re-exports the class + companion types that were previously\n// part of its public API.\nimport { mkdir, stat, utimes } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { computeContentHash, normalizeContent } from \"../content-hash.js\";\nimport { log } from \"../logger.js\";\nimport { SecureStoreLockedError, readMaybeEncryptedFile, writeMaybeEncryptedFile } from \"../secure-store/secure-fs.js\";\nimport { isErrnoCode } from \"../utils/errno.js\";\nimport { withHeldFileLock } from \"../utils/serialize-mutations.js\";\n\n/**\n * Stale threshold (ms) for the per-file advisory lock guarding a fact-hash\n * index merge-save (issue #1909 review round 7). The critical section is a\n * single read+atomic-rewrite of one index file (≤ a few MB), so a holder older\n * than this is treated as crashed. Generous to tolerate slow/encrypted disks\n * without breaking a legitimately in-progress publish.\n */\nconst CONTENT_HASH_INDEX_LOCK_STALE_MS = 30_000;\n/** Version marker for the normalized Unicode hash index format. */\nconst CONTENT_HASH_INDEX_FORMAT_HEADER = \"# remnic-content-hash-index:v2\";\n\nfunction parseHashIndex(raw: string): { versioned: boolean; hashes: Set<string> } {\n const lines = raw\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n if (lines[0] !== CONTENT_HASH_INDEX_FORMAT_HEADER) {\n return { versioned: false, hashes: new Set() };\n }\n return { versioned: true, hashes: new Set(lines.slice(1)) };\n}\n\nfunction serializeHashIndex(hashes: Set<string>): string {\n return `${CONTENT_HASH_INDEX_FORMAT_HEADER}\\n${[...hashes].join(\"\\n\")}\\n`;\n}\n\n/**\n * Durable-retry policy for a deferred content-hash reconcile save whose\n * cross-process lock acquire timed out (issue #1909 / PR #2016). When the lock\n * is contended past `withHeldFileLock`'s bounded wait, the added hashes are held\n * only in this process's memory; relying on \"some later batch save\" is not\n * durable (the run may be the last before idle/shutdown), and a long-lived peer\n * that already built its authoritative in-memory index will not see the write.\n * So we schedule a bounded, exponentially-backed-off background retry that keeps\n * re-attempting the locked publish until it lands. Bounded because the durable\n * fact `.md` is already on disk and a process restart rebuilds the index from\n * the corpus regardless — exhausting retries degrades to that rebuild safety\n * net, never to data loss or a fatal append failure.\n */\nconst CONTENT_HASH_INDEX_RETRY_MAX_ATTEMPTS = 5;\n/** Base backoff (ms) before the first deferred reconcile-save retry; doubles each attempt. */\nconst CONTENT_HASH_INDEX_RETRY_BASE_MS = 500;\n/** Ceiling (ms) for the exponential reconcile-save retry backoff. */\nexport const CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS = 5_000;\n\n/**\n * Tuning for {@link ContentHashIndex} cross-process lock waits and the\n * deferred reconcile-save durable retry (issue #1909 / PR #2016). All optional;\n * production uses the module defaults. Tests inject small values so the\n * lock-timeout + retry path is exercised deterministically without real\n * multi-second waits.\n */\nexport interface ContentHashIndexLockOptions {\n /** Bounded wait to acquire the per-file advisory lock; passed to `withHeldFileLock` (default 5000ms). */\n readonly maxWaitMs?: number;\n /** Poll interval while waiting for a busy lock (default 50ms). */\n readonly pollMs?: number;\n /** Base backoff before the first deferred-save retry (default 500ms). */\n readonly retryBaseMs?: number;\n /** Max deferred-save retries before falling back to the corpus-rebuild safety net (default 5). */\n readonly retryMaxAttempts?: number;\n /**\n * Tombstone-blocked capture write-lock backoff (COS-392). These do not\n * change the fact-hash index lock. Omitted fields use the 50ms / 2s / 60s\n * defaults. Converge copies its config here before replicating a file.\n */\n readonly captureWriteLockInitialDelayMs?: number;\n readonly captureWriteLockMaxDelayMs?: number;\n readonly captureWriteLockDeadlineMs?: number;\n /** Apply abort signal. Checked before each probe, during backoff, and before the write. */\n readonly captureWriteLockSignal?: AbortSignal;\n}\n\n/**\n * Thrown when the fact-hash index cannot be made authoritative — the\n * cross-process rebuild lock could not be acquired within the bounded retry\n * budget (PR #2016). Callers that require an authoritative dedup answer surface\n * this instead of silently trusting a stale loaded snapshot.\n */\nexport class FactHashIndexNotAuthoritativeError extends Error {\n constructor(stateDir: string) {\n super(\n `fact-hash index is not authoritative: the cross-process rebuild lock for ${stateDir} could not be acquired within the bounded retry budget`\n );\n this.name = \"FactHashIndexNotAuthoritativeError\";\n }\n}\n\n/**\n * One dedup lookup: whether the shared index holds the hash, and whether that\n * answer can be trusted as a clean MISS. Both fields describe the same instant.\n */\nexport interface ContentHashLookup {\n readonly present: boolean;\n readonly authoritative: boolean;\n}\n\ntype DiskFingerprint = {\n mtimeMs: number;\n size: number;\n dev: number;\n ino: number;\n ctimeMs: number;\n birthtimeMs: number;\n};\nexport interface ContentHashPathEntry {\n readonly path: string;\n readonly contentHash: string;\n}\n\n/**\n * Content-hash dedup index for facts.\n * Normalizes content (lowercase, strip punctuation, collapse whitespace),\n * computes SHA-256, and stores hashes in a line-delimited file.\n * Prevents writing semantically identical facts.\n */\nexport class ContentHashIndex {\n private hashes: Set<string> = new Set();\n /**\n * Fact-ONLY hash partition (issue #2474). `hashes` above is category-blind\n * (every write category registers there); this subset carries only\n * `category === \"fact\"` hashes so fact-only membership checks\n * (`hasFact*`) can never be satisfied by an unrelated decision/preference\n * sharing the same normalized body. In-memory only — never serialized to\n * fact-hashes.txt; `clear()` drops it and every authoritative rebuild\n * repopulates it via `addFactByHash`. Subset invariant: `factHashes ⊆\n * hashes` — `addFact*` registers in both, and the removal sites that drop\n * a shared hash also drop the partition entry (removal stays caller-paired\n * because shared-index removal is category-blind: archiving a non-fact\n * must not evict an active fact's fact-only membership).\n */\n private factHashes: Set<string> = new Set();\n private dirty = false;\n /** True when load() found the pre-Unicode, markerless on-disk format. */\n private formatMigrationRequired = false;\n /**\n * Hashes explicitly removed since the last successful save (issue #1909 review\n * round 8 thread 3). Tracked separately so `saveMergingWithDisk` can be\n * removal-AWARE: under the file lock it reads the latest on-disk set, DROPS\n * these, then unions our additions — so a removal batch cannot resurrect a\n * hash (union alone would) and cannot clobber a concurrent extraction's\n * appended hash (blind overwrite would). Consumed + cleared by every save.\n */\n private removed: Set<string> = new Set();\n /**\n * Hashes added by THIS instance since the last successful save (issue #1909\n * review round 9 finding 3). Tracked separately from the loaded snapshot so a\n * reconciling save publishes `(on-disk \\ removed) ∪ added` — it re-applies only\n * OUR additions, never the stale loaded entries. Without this, seeding the\n * merge from `this.hashes` (loaded ∪ added) would resurrect a hash a peer\n * removed on disk while this instance held a stale snapshot.\n */\n private added: Set<string> = new Set();\n private readonly filePath: string;\n private readonly secureStoreKeyProvider: () => Buffer | null;\n private readonly secureStoreWriteKeyProvider: () => Buffer | null;\n private readonly memoryDir: string;\n private readonly lockOptions: ContentHashIndexLockOptions;\n /**\n * Deferred reconcile-save durable-retry state (issue #1909 / PR #2016). At\n * most ONE retry is ever armed (`reconcileRetryTimer` is the reentrancy guard)\n * so successive timed-out saves cannot stack duplicate/parallel retries.\n * `reconcileRetryBarrier` resolves when the chain settles (published or gave\n * up) — awaited by tests and any caller wanting the eventual result.\n */\n private reconcileRetryTimer: NodeJS.Timeout | null = null;\n private reconcileRetryAttempts = 0;\n private reconcileRetryBarrier: Promise<void> | null = null;\n private reconcileRetryResolve: (() => void) | null = null;\n /**\n * On-disk fingerprint captured at the last point THIS instance's in-memory\n * set matched disk — after `load()`, `save()`, or a reconcile publish. A peer\n * process that advances the durable index changes its file generation even\n * when byte size and mtime collide, so the freshness check remains safe\n * without an O(file-size) read on the hot path.\n */\n private lastSyncedFingerprint: DiskFingerprint | null = null;\n constructor(\n stateDir: string,\n secureStoreKeyProvider: () => Buffer | null = () => null,\n secureStoreWriteKeyProvider: () => Buffer | null = secureStoreKeyProvider,\n memoryDir: string = path.dirname(stateDir),\n lockOptions: ContentHashIndexLockOptions = {}\n ) {\n this.filePath = path.join(stateDir, \"fact-hashes.txt\");\n this.secureStoreKeyProvider = secureStoreKeyProvider;\n this.secureStoreWriteKeyProvider = secureStoreWriteKeyProvider;\n this.memoryDir = memoryDir;\n this.lockOptions = lockOptions;\n }\n /** Whether a caller with an authoritative corpus must rebuild this legacy index. */\n get requiresFormatMigration(): boolean {\n return this.formatMigrationRequired;\n }\n\n /** Load existing hashes from disk. Safe to call multiple times. */\n async load(): Promise<void> {\n try {\n const raw = await readMaybeEncryptedFile(this.filePath, this.secureStoreKeyProvider(), this.memoryDir);\n const parsed = parseHashIndex(raw);\n this.formatMigrationRequired = !parsed.versioned;\n if (!parsed.versioned) {\n // Hashes from the pre-NFC format are unsafe after normalization changes.\n // Ignore them until an authoritative corpus rebuild publishes v2.\n this.hashes.clear();\n log.warn(`content-hash index: ignored legacy unversioned index at ${this.filePath}`);\n } else {\n for (const hash of parsed.hashes) this.hashes.add(hash);\n }\n log.debug(`content-hash index: loaded ${this.hashes.size} hashes`);\n } catch (err) {\n if (err instanceof SecureStoreLockedError) throw err;\n if (!isErrnoCode(err, \"ENOENT\")) throw err;\n log.debug(\"content-hash index: no existing index — starting fresh\");\n }\n // The loaded set now reflects the on-disk file — baseline the fingerprint so\n // a later peer write is detectable (PR #2016 review).\n await this.captureSyncedFingerprint();\n }\n\n /** Cheap `stat` of the durable index file; null when it does not exist yet. */\n private async statIndexFile(): Promise<DiskFingerprint | null> {\n try {\n const s = await stat(this.filePath);\n return {\n mtimeMs: s.mtimeMs,\n size: s.size,\n dev: s.dev,\n ino: s.ino,\n ctimeMs: s.ctimeMs,\n birthtimeMs: s.birthtimeMs,\n };\n } catch (err) {\n if (isErrnoCode(err, \"ENOENT\")) return null;\n throw err;\n }\n }\n\n /** Record the current on-disk fingerprint as this instance's synced baseline. */\n private async captureSyncedFingerprint(): Promise<void> {\n try {\n this.lastSyncedFingerprint = await this.statIndexFile();\n } catch (err) {\n // A stat failure other than ENOENT leaves no baseline; the next freshness\n // check reports non-current and the caller confirms against the corpus.\n this.lastSyncedFingerprint = null;\n log.debug(`content-hash index: could not fingerprint ${this.filePath}: ${err}`);\n }\n }\n\n /**\n * True when the durable index file is unchanged since this instance last\n * synced it (load / save / reconcile). False when a peer advanced it, when\n * the file appeared or vanished, or when freshness cannot be established\n * (stat error) — the caller then treats the in-memory snapshot as\n * non-authoritative and confirms a dedup miss against the durable corpus.\n * The stat includes file identity and generation metadata so same-size,\n * same-mtime atomic replacements are detected without reading the file body.\n */\n async isDiskFingerprintCurrent(): Promise<boolean> {\n let current: DiskFingerprint | null;\n try {\n current = await this.statIndexFile();\n } catch {\n return false;\n }\n const last = this.lastSyncedFingerprint;\n if (last === null || current === null) {\n return last === null && current === null;\n }\n return (\n current.mtimeMs === last.mtimeMs &&\n current.size === last.size &&\n current.dev === last.dev &&\n current.ino === last.ino &&\n current.ctimeMs === last.ctimeMs &&\n current.birthtimeMs === last.birthtimeMs\n );\n }\n\n /**\n * Change the durable file's mtime without touching its content. A publish\n * that throws or stays deferred leaves the file as it was, so a peer that\n * already synced it keeps answering authoritative MISSes for deltas this\n * instance holds only in memory (#3355). A new stamp makes every holder of the\n * old fingerprint, this instance included, fail the freshness check and\n * confirm against the corpus. Best effort: a file that cannot be stamped\n * leaves the callers' existing retry and rebuild paths as the only repair.\n */\n private async invalidatePeerFreshness(): Promise<void> {\n try {\n const { mtimeMs } = await stat(this.filePath);\n const bumped = new Date(Math.max(Date.now(), mtimeMs + 1));\n await utimes(this.filePath, bumped, bumped);\n } catch (err) {\n log.debug(`content-hash index: could not invalidate peer freshness of ${this.filePath}: ${err}`);\n }\n }\n\n /** Check if content already exists in the index. */\n has(content: string): boolean {\n return this.hashes.has(ContentHashIndex.computeHash(content));\n }\n\n /** Add content hash to the index. */\n add(content: string): void {\n const hash = ContentHashIndex.computeHash(content);\n // A re-add supersedes a pending removal of the same hash (round 8 thread 3).\n this.removed.delete(hash);\n // Record OUR durable delta even when the local snapshot already holds the\n // hash (PR #2016 thread SD-nG): local membership can be STALE — a peer\n // removed this hash on disk while this instance kept it in memory. Skipping\n // `added`/`dirty` on that path let the reconciling save compute\n // (on-disk \\ removed) ∪ added and silently drop the reintroduced hash, so\n // the peer never saw it again. Set semantics keep this idempotent (no\n // duplicate entries) and the `removed.delete` above preserves remove/add\n // ordering.\n this.hashes.add(hash);\n this.added.add(hash);\n this.dirty = true;\n }\n\n get size(): number {\n return this.hashes.size;\n }\n\n /** Clear all loaded hashes so the next save rewrites the index from scratch. */\n clear(): void {\n if (this.hashes.size > 0) {\n this.hashes.clear();\n }\n // The fact-only partition is rebuilt with the shared set on every\n // authoritative corpus rebuild, so it clears here too (issue #2474).\n this.factHashes.clear();\n // A full clear is always paired with a plain overwrite save() (rebuild);\n // pending per-hash add/remove deltas are subsumed by writing the rebuilt set.\n // Cancel any armed deferred-reconcile retry (PR #2016) so it cannot fire in\n // the clear→rebuild→save window and union the pre-clear on-disk rows back.\n this.added.clear();\n this.removed.clear();\n this.cancelReconcileRetry();\n this.dirty = true;\n }\n\n /** Persist index to disk if changed (plain whole-file overwrite). */\n async save(): Promise<void> {\n if (!this.dirty) return;\n await mkdir(path.dirname(this.filePath), { recursive: true });\n // Snapshot the deltas + overwrite body THIS save publishes BEFORE the write\n // yields the event loop. `writeMaybeEncryptedFile` materializes `serialized`\n // eagerly, then the disk await lets a concurrent same-process add()/remove()\n // land a late delta (PR #2016 thread PRRT_kwDORJXyws6SEBri). Clearing the\n // LIVE added/removed sets afterward — as the previous unconditional\n // clear()+`dirty = false` did — would drop that late hash from the index AND\n // leave no retry, so peers never see it until a corpus rebuild. Consume only\n // what we published; late deltas stay pending. Mirrors saveMergingWithDisk.\n const publishedAdded = new Set<string>(this.added);\n const publishedRemoved = new Set<string>(this.removed);\n const serialized = serializeHashIndex(this.hashes);\n await writeMaybeEncryptedFile(this.filePath, serialized, this.secureStoreWriteKeyProvider(), {}, this.memoryDir);\n this.formatMigrationRequired = false;\n // Consume ONLY the deltas this overwrite published; anything that arrived\n // during the awaits above remains pending for the next save.\n for (const h of publishedAdded) this.added.delete(h);\n for (const h of publishedRemoved) this.removed.delete(h);\n // Honest dirty state: true iff late deltas remain unpersisted.\n this.dirty = this.added.size > 0 || this.removed.size > 0;\n // This overwrite is now the on-disk state — re-baseline the freshness\n // fingerprint so our own write is not later mistaken for a peer's (PR #2016).\n await this.captureSyncedFingerprint();\n if (this.dirty) {\n // Late deltas landed mid-save: arm a bounded durable retry so they reach\n // disk even without a further batch save — never a silent in-memory-only\n // hash. The locked reconcile republishes only OUR delta onto the overwrite.\n this.scheduleReconcileRetry();\n } else {\n // Clean overwrite supersedes any pending deferred reconcile work.\n this.cancelReconcileRetry();\n }\n log.debug(`content-hash index: saved ${this.hashes.size} hashes`);\n }\n\n /**\n * Persist the index by RECONCILING with the latest on-disk state under a\n * cross-process lock (issue #1909). Used by the extraction-persist batch save\n * for BOTH append and removal batches, so appends and removals to the same\n * index serialize against each other:\n * final = (on-disk \\ this.removed) ∪ this.added\n * Note it re-applies only OUR local additions (`added`), NOT the loaded\n * snapshot (review round 9 finding 3): seeding from `this.hashes` would\n * resurrect a hash a peer removed on disk while we held a stale snapshot. This\n * preserves a concurrent writer's appended hashes (a blind overwrite would\n * drop them), never resurrects a removed hash, and never loses our additions.\n *\n * Hardening:\n * - Dirty short-circuit (round 7 finding 3): when this instance neither added\n * nor removed anything this run, skip the O(file) read+rewrite entirely.\n * - Cross-process lock (round 7 finding 2): serialize the read→reconcile→write\n * window on a per-file advisory lock. If the lock is NOT acquired (timeout),\n * do NOT publish (round 9 finding 2): keep `dirty` set AND schedule a bounded\n * durable background retry (PR #2016) that keeps re-attempting the locked\n * publish until the deferred additions land — never an unlocked write that\n * could clobber a peer, and never a silent \"batch save complete\" that leaves\n * the addition in this process's memory only.\n */\n async saveMergingWithDisk(): Promise<void> {\n if (!this.dirty) return;\n // The advisory lock file lives beside the index — ensure the dir exists\n // before acquiring it (the index dir may not exist on a first write).\n await mkdir(path.dirname(this.filePath), { recursive: true });\n const publish = async (): Promise<void> => {\n // Snapshot the deltas this publish is responsible for. The read+rewrite\n // below yields the event loop, so a concurrent add()/remove() can land\n // new deltas while we await disk (PR #2016 thread SD7Tj). Consuming the\n // LIVE sets afterward would drop those late deltas AND clear `dirty`,\n // stranding them in no save at all. We consume only what we published;\n // anything that arrived meanwhile stays pending for the next save.\n const publishedAdded = new Set<string>(this.added);\n const publishedRemoved = new Set<string>(this.removed);\n const merged = new Set<string>(publishedAdded);\n try {\n const raw = await readMaybeEncryptedFile(this.filePath, this.secureStoreKeyProvider(), this.memoryDir);\n const parsed = parseHashIndex(raw);\n if (parsed.versioned) {\n for (const hash of parsed.hashes) {\n // Keep prior/concurrent on-disk hashes EXCEPT the ones we removed.\n if (!publishedRemoved.has(hash)) merged.add(hash);\n }\n } else {\n // Do not carry hashes from the pre-NFC format into a v2 write.\n log.warn(`content-hash index: discarded legacy unversioned entries from ${this.filePath}`);\n }\n } catch (err) {\n if (err instanceof SecureStoreLockedError) throw err;\n if (!isErrnoCode(err, \"ENOENT\")) throw err;\n // ENOENT → no on-disk index yet; write our additions as-is.\n }\n await mkdir(path.dirname(this.filePath), { recursive: true });\n await writeMaybeEncryptedFile(\n this.filePath,\n serializeHashIndex(merged),\n this.secureStoreWriteKeyProvider(),\n {},\n this.memoryDir\n );\n this.formatMigrationRequired = false;\n // Consume ONLY the deltas we just published; deltas that arrived during\n // the awaits above remain pending.\n for (const h of publishedAdded) this.added.delete(h);\n for (const h of publishedRemoved) this.removed.delete(h);\n // Fold any still-pending deltas onto the reconciled set so the in-memory\n // view stays consistent with what the next save will publish.\n for (const h of this.added) merged.add(h);\n for (const h of this.removed) merged.delete(h);\n this.hashes = merged;\n // Honest dirty state: true iff late deltas remain to be persisted. A\n // subsequent batch save or the shutdown flush drains them.\n this.dirty = this.added.size > 0 || this.removed.size > 0;\n // The reconciled set is now the on-disk state — re-baseline the freshness\n // fingerprint (captured under the held lock) so our own publish is not\n // later mistaken for a peer's advance (PR #2016 review).\n await this.captureSyncedFingerprint();\n };\n await withHeldFileLock(\n `${this.filePath}.lock`,\n {\n staleMs: CONTENT_HASH_INDEX_LOCK_STALE_MS,\n ...(this.lockOptions.maxWaitMs !== undefined ? { maxWaitMs: this.lockOptions.maxWaitMs } : {}),\n ...(this.lockOptions.pollMs !== undefined ? { pollMs: this.lockOptions.pollMs } : {}),\n },\n async (acquired) => {\n if (!acquired) {\n // Never publish unlocked (round 9 finding 2): keep dirty AND schedule a\n // durable background retry (PR #2016). Relying on an unguaranteed later\n // batch save silently left the addition in this process's memory only;\n // the retry keeps re-attempting the locked publish until the addition\n // lands on disk.\n log.warn(\n `content-hash index: lock not acquired for ${this.filePath}; deferring reconcile save (dirty retained, scheduling durable retry)`\n );\n this.scheduleReconcileRetry();\n await this.invalidatePeerFreshness();\n return;\n }\n try {\n await publish();\n } catch (err) {\n await this.invalidatePeerFreshness();\n throw err;\n }\n if (this.dirty) {\n // A concurrent add()/remove() landed during publish's disk awaits, so\n // deltas remain unpersisted (PR #2016 thread SD7Tj). Arm a bounded\n // durable retry so they reach disk even if no further batch save comes\n // — never leave a settled barrier over pending work.\n this.scheduleReconcileRetry();\n } else {\n // Published cleanly: any pending deferred retry is now satisfied.\n this.cancelReconcileRetry();\n }\n }\n );\n log.debug(`content-hash index: reconcile-saved ${this.hashes.size} hashes`);\n }\n\n /**\n * Rebuild-and-publish the index from an authoritative source (the durable\n * corpus) under the SAME per-file cross-process lock the reconciling\n * append/removal saves (`saveMergingWithDisk`) use (issue #1909 / PR #2016).\n * The `populate` callback runs WHILE the lock is held and MUST repopulate this\n * index from the corpus (`clear()` then `addByHash(...)` for every corpus\n * hash); the rebuilt set is then published with a plain overwrite that is now\n * serialized against every locked writer.\n *\n * This closes the finding where the previous unlocked clear→scan→save() could\n * overwrite a peer's newer lock-merged or deferred additions: a locked writer\n * that committed BEFORE the rebuild acquired the lock has its memory `.md` on\n * disk and is re-scanned; one that commits AFTER the rebuild releases\n * reconciles its additions on top of the freshly-published set. No union with\n * the raw on-disk file is performed, so the rebuild keeps its\n * garbage-collection property (orphaned hashes for deleted memories drop).\n *\n * Lock-acquisition failure is explicit and non-destructive: `populate` is NOT\n * run and nothing is written (never an unlocked overwrite). Returns true when\n * the rebuild was published under the lock, false when the lock timed out —\n * the caller keeps the index non-authoritative and retries on next use. Uses\n * the same non-reentrant file lock as the reconcile path, so it can never\n * deadlock with the deferred reconcile-retry: a concurrent locked writer (or a\n * fired retry) simply yields `acquired=false` after the bounded wait.\n */\n async rebuildUnderLock(populate: () => Promise<void>): Promise<boolean> {\n // The advisory lock file lives beside the index — ensure the dir exists\n // before acquiring it (the index dir may not exist on a first rebuild).\n await mkdir(path.dirname(this.filePath), { recursive: true });\n let published = false;\n await withHeldFileLock(\n `${this.filePath}.lock`,\n {\n staleMs: CONTENT_HASH_INDEX_LOCK_STALE_MS,\n ...(this.lockOptions.maxWaitMs !== undefined ? { maxWaitMs: this.lockOptions.maxWaitMs } : {}),\n ...(this.lockOptions.pollMs !== undefined ? { pollMs: this.lockOptions.pollMs } : {}),\n },\n async (acquired) => {\n if (!acquired) {\n // Never publish unlocked: a concurrent locked writer holds the lock,\n // so overwriting now could clobber its addition. Leave the index as-is\n // and let the caller retry the authoritative rebuild on next use.\n log.warn(\n `content-hash index: lock not acquired for ${this.filePath}; deferring authoritative rebuild (index left non-authoritative, retried on next use)`\n );\n return;\n }\n await populate();\n // Publish under the held lock. save() takes no lock (no re-entrancy) and\n // is a full overwrite that supersedes any pending add/remove deltas.\n await this.save();\n published = true;\n }\n );\n return published;\n }\n\n /**\n * True while a background durable reconcile-save retry is armed (PR #2016).\n * Test/introspection hook.\n */\n get hasPendingReconcileRetry(): boolean {\n return this.reconcileRetryTimer !== null;\n }\n\n /** True while this index has in-memory changes not yet published to disk. */\n get hasPendingChanges(): boolean {\n return this.dirty;\n }\n\n /**\n * Resolve once any armed background reconcile-save retry chain has settled —\n * either it published the deferred additions, or it exhausted its bounded\n * attempts and fell back to the corpus-rebuild safety net. Resolves\n * immediately when nothing is armed. Lets a caller (or a test) await the\n * eventual durability outcome of a lock-timed-out save.\n */\n async whenReconcileRetrySettled(): Promise<void> {\n await (this.reconcileRetryBarrier ?? Promise.resolve());\n }\n\n /**\n * Drive any deferred lock-timeout reconcile save to completion INLINE at a\n * lifecycle boundary (PR #2016 finding 3). The background retry timer is\n * `unref`'d so it never keeps a long-lived daemon alive — but a short-lived\n * writer (a one-shot CLI) can exit before it fires, leaving a durable fact\n * `.md` whose hash never reached `fact-hashes.txt` for peers that already\n * built their in-memory index. `orchestrator.destroy()` calls this so the\n * addition publishes before the process exits; long-lived hosts only reach it\n * at their own shutdown, so their in-flight retries keep running in the\n * background until then. Bounded by the same attempt ceiling as the background\n * retry; a permanently contended lock falls back to the corpus-rebuild-on-\n * restart safety net (the fact `.md` is already durable). No deadlock: each\n * attempt is the same non-reentrant, bounded-wait file lock.\n */\n async flushReconcileRetry(): Promise<void> {\n if (!this.dirty) return;\n const maxAttempts = this.lockOptions.retryMaxAttempts ?? CONTENT_HASH_INDEX_RETRY_MAX_ATTEMPTS;\n const baseMs = this.lockOptions.retryBaseMs ?? CONTENT_HASH_INDEX_RETRY_BASE_MS;\n for (let attempt = 0; this.dirty && attempt < maxAttempts; attempt += 1) {\n // Cancel the unref'd background timer — we drive the locked publish inline.\n if (this.reconcileRetryTimer) {\n clearTimeout(this.reconcileRetryTimer);\n this.reconcileRetryTimer = null;\n }\n await this.saveMergingWithDisk();\n if (!this.dirty) break;\n if (attempt < maxAttempts - 1) {\n const wait = Math.min(baseMs * 2 ** attempt, CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS);\n await new Promise<void>((resolve) => setTimeout(resolve, wait));\n }\n }\n // Clear any timer the final saveMergingWithDisk armed and settle the barrier\n // so awaiters unblock — the bounded inline drain has done its best.\n if (this.reconcileRetryTimer) {\n clearTimeout(this.reconcileRetryTimer);\n this.reconcileRetryTimer = null;\n }\n this.reconcileRetryAttempts = 0;\n this.settleReconcileRetry();\n }\n\n /**\n * Schedule ONE bounded, exponentially-backed-off background retry of the\n * deferred reconcile save (PR #2016). Reentrant-safe: an already-armed timer\n * short-circuits, so successive timed-out saves never stack duplicate retries.\n * When attempts are exhausted the chain settles without publishing — the\n * durable fact `.md` is on disk and a process restart rebuilds the index from\n * the corpus, so this degrades to that safety net, never a fatal failure.\n */\n private scheduleReconcileRetry(): void {\n if (this.reconcileRetryTimer) return; // one retry in flight — no duplicate/reentrant retries\n const maxAttempts = this.lockOptions.retryMaxAttempts ?? CONTENT_HASH_INDEX_RETRY_MAX_ATTEMPTS;\n if (this.reconcileRetryAttempts >= maxAttempts) {\n log.warn(\n `content-hash index: exhausted ${maxAttempts} deferred reconcile-save retries for ${this.filePath}; ` +\n `${this.added.size} addition(s) remain dirty in-memory (a process restart rebuilds the index from the durable corpus)`\n );\n this.reconcileRetryAttempts = 0;\n this.settleReconcileRetry();\n return;\n }\n if (!this.reconcileRetryBarrier) {\n this.reconcileRetryBarrier = new Promise<void>((resolve) => {\n this.reconcileRetryResolve = resolve;\n });\n }\n const attempt = this.reconcileRetryAttempts + 1;\n const baseMs = this.lockOptions.retryBaseMs ?? CONTENT_HASH_INDEX_RETRY_BASE_MS;\n const delay = Math.min(baseMs * 2 ** (attempt - 1), CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS);\n this.reconcileRetryTimer = setTimeout(() => {\n this.reconcileRetryTimer = null;\n this.reconcileRetryAttempts = attempt;\n void this.saveMergingWithDisk()\n .catch((err) => {\n // A publish-time I/O/encryption error (not a lock timeout) is best-effort:\n // log and let the corpus-rebuild safety net cover it rather than loop.\n log.warn(`content-hash index: deferred reconcile-save retry failed for ${this.filePath}: ${err}`);\n })\n .finally(() => {\n // If the attempt neither published (dirty cleared → cancelReconcileRetry)\n // nor re-armed a further retry, the chain has ended — settle the barrier.\n if (this.dirty && !this.reconcileRetryTimer) this.settleReconcileRetry();\n });\n }, delay);\n this.reconcileRetryTimer.unref?.();\n }\n\n /** Cancel any armed reconcile-save retry and settle the barrier (a publish landed). */\n private cancelReconcileRetry(): void {\n if (this.reconcileRetryTimer) {\n clearTimeout(this.reconcileRetryTimer);\n this.reconcileRetryTimer = null;\n }\n this.reconcileRetryAttempts = 0;\n this.settleReconcileRetry();\n }\n\n /** Resolve and clear the retry barrier if one is outstanding. */\n private settleReconcileRetry(): void {\n const resolve = this.reconcileRetryResolve;\n this.reconcileRetryBarrier = null;\n this.reconcileRetryResolve = null;\n resolve?.();\n }\n\n /** Remove a hash from the index (used when archiving/deleting). */\n remove(content: string): void {\n const hash = ContentHashIndex.computeHash(content);\n // Record the removal (round 8 thread 3) even when the hash is not in our\n // in-memory set — a reconciling save must still drop it from the latest\n // on-disk state. A removal supersedes a pending add. Always mark dirty.\n this.added.delete(hash);\n this.removed.add(hash);\n this.hashes.delete(hash);\n this.dirty = true;\n }\n\n /**\n * Remove a pre-computed SHA-256 hash directly from the index without\n * re-hashing. Use this when the caller already holds the stored hash\n * (e.g. `memory.frontmatter.contentHash`) to avoid the double-hash bug\n * where `remove(hash)` would compute `hash(hash)` and never match the\n * entry.\n */\n removeByHash(hash: string): void {\n this.added.delete(hash);\n this.removed.add(hash);\n this.hashes.delete(hash);\n this.dirty = true;\n }\n\n /**\n * Add a pre-computed SHA-256 hash directly to the index without re-hashing.\n * Use this when the caller already holds the stored hash\n * (e.g. `memory.frontmatter.contentHash`) so that the index records the raw\n * content hash rather than re-hashing the citation-annotated body.\n *\n * @internal Only called from `StorageManager.ensureFactHashIndexAuthoritative`.\n * Not part of the public API — prefer `add(content)` for external callers.\n */\n addByHash(hash: string): void {\n // A re-add supersedes a pending removal of the same hash.\n this.removed.delete(hash);\n // Record OUR durable delta even when the local snapshot already holds the\n // hash (PR #2016 thread PRRT_kwDORJXyws6SEHve). Outside a rebuild\n // (StorageManager.addActiveFactContentHash on reactivation) local\n // membership can be STALE: a peer removed this hash on disk while this\n // instance kept it in memory. Skipping added/dirty on that path let the\n // reconciling save compute (on-disk \\ removed) ∪ added and silently drop\n // the reintroduced hash, so the peer never saw it again. Set semantics keep\n // this idempotent (no duplicate entries) and the removed.delete above\n // preserves remove/add ordering. Mirrors add().\n this.hashes.add(hash);\n this.added.add(hash);\n this.dirty = true;\n }\n\n // ── Fact-only partition (issue #2474) ─────────────────────────────────\n // Folded here from StorageManager's parallel `factOnlyHashes` set, which had\n // to be kept in lockstep by hand on every add/remove/rebuild site. The\n // partition lives on the index instance, so every path that mutates the\n // shared set and needs fact-only truth mutates it in the same breath.\n\n /** Register fact content in BOTH the shared index and the fact-only partition. */\n addFact(content: string): void {\n this.addFactByHash(ContentHashIndex.computeHash(content));\n }\n\n /** Register a pre-computed fact hash in both the shared index and the fact-only partition. */\n addFactByHash(hash: string): void {\n this.addByHash(hash);\n this.factHashes.add(hash);\n }\n\n /** Fact-ONLY membership: has this content been registered as a fact? */\n hasFact(content: string): boolean {\n return this.factHashes.has(ContentHashIndex.computeHash(content));\n }\n\n /** Fact-ONLY membership for a pre-computed SHA-256 hash. */\n hasFactByHash(hash: string): boolean {\n return this.factHashes.has(hash);\n }\n\n /**\n * Drop fact-only partition membership WITHOUT touching the shared index.\n * The shared removal stays caller-owned: a shared hash is dropped only when\n * no other active memory holds it, while fact-only membership follows the\n * fact's own lifecycle (supersede/archive/promotion).\n */\n removeFactByHash(hash: string): void {\n this.factHashes.delete(hash);\n }\n\n /**\n * Resolve a pre-computed semantic hash to one deterministic corpus path.\n *\n * This lookup is separate from durable hash membership: `this.hashes` stays\n * one hash per line and `has(content)` keeps its existing raw-content contract.\n * Reconciliation supplies short-lived manifest rows where hash and path coexist.\n */\n static resolvePathByHash(hash: string, entries: Iterable<ContentHashPathEntry>): string | undefined {\n const canonicalHash = hash.toLowerCase();\n if (!/^[a-f0-9]{64}$/.test(canonicalHash)) return undefined;\n let canonicalPath: string | undefined;\n for (const entry of entries) {\n if (entry.contentHash.toLowerCase() !== canonicalHash || entry.path.length === 0) continue;\n if (canonicalPath === undefined || entry.path < canonicalPath) canonicalPath = entry.path;\n }\n return canonicalPath;\n }\n\n /** Normalize content (delegates to content-hash.ts for a single source of truth). */\n static normalizeContent(content: string): string {\n return normalizeContent(content);\n }\n\n /** Compute SHA-256 hash (delegates to content-hash.ts for a single source of truth). */\n static computeHash(content: string): string {\n return computeContentHash(content);\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveDefaultNamespaceRoot
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-QHARXWTC.js";
|
|
4
4
|
import {
|
|
5
5
|
StorageManager
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-FWRXW7CZ.js";
|
|
7
7
|
import {
|
|
8
8
|
resolveNamespaceCapabilities
|
|
9
9
|
} from "./chunk-C5LWPYS3.js";
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
serializeLocationDay,
|
|
27
27
|
updateLocationSourceDay,
|
|
28
28
|
writeLocationDay
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-4IVRQMZW.js";
|
|
30
30
|
import {
|
|
31
31
|
readEnvVar
|
|
32
32
|
} from "./chunk-P2QRMMVQ.js";
|
|
@@ -707,6 +707,55 @@ ${USAGE}`);
|
|
|
707
707
|
}
|
|
708
708
|
}
|
|
709
709
|
|
|
710
|
+
// src/location/scheduler.ts
|
|
711
|
+
var LOCATION_SYNC_DEFAULT_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
712
|
+
function startLocationAutoSync(settings, deps = {}) {
|
|
713
|
+
const logger = deps.log ?? log;
|
|
714
|
+
const intervalMs = settings.intervalMs ?? LOCATION_SYNC_DEFAULT_INTERVAL_MS;
|
|
715
|
+
const setIntervalImpl = settings.setInterval ?? ((fn, ms) => setInterval(fn, ms));
|
|
716
|
+
const clearIntervalImpl = settings.clearInterval ?? ((handle) => clearInterval(handle));
|
|
717
|
+
const sync = deps.sync ?? ((deps2) => runLocationSync({ config: deps2.config, memoryDir: deps2.memoryDir }));
|
|
718
|
+
let inFlight = null;
|
|
719
|
+
let stopped = false;
|
|
720
|
+
const timer = setIntervalImpl(() => {
|
|
721
|
+
if (inFlight !== null || stopped) return;
|
|
722
|
+
inFlight = (async () => {
|
|
723
|
+
try {
|
|
724
|
+
const runs = await sync({ config: settings.config, memoryDir: settings.memoryDir });
|
|
725
|
+
const failed = runs.some((run) => run.results.some((result) => result.status === "failed"));
|
|
726
|
+
if (failed) {
|
|
727
|
+
logger.warn("location auto-sync: one or more sources failed \u2014 retrying on the next tick");
|
|
728
|
+
} else {
|
|
729
|
+
logger.info(
|
|
730
|
+
`location auto-sync: refreshed ${settings.config.syncDays}-day window across ${settings.config.sources.length} source(s)`
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
} catch (err) {
|
|
734
|
+
if (err instanceof Error && err.name === "AbortError") return;
|
|
735
|
+
logger.warn(
|
|
736
|
+
`location auto-sync failed: ${err instanceof Error ? err.name : "unknown error"} \u2014 retrying on the next tick`
|
|
737
|
+
);
|
|
738
|
+
} finally {
|
|
739
|
+
inFlight = null;
|
|
740
|
+
}
|
|
741
|
+
})();
|
|
742
|
+
}, intervalMs);
|
|
743
|
+
timer.unref?.();
|
|
744
|
+
return {
|
|
745
|
+
async tick() {
|
|
746
|
+
const runs = await sync({ config: settings.config, memoryDir: settings.memoryDir });
|
|
747
|
+
logger.info(
|
|
748
|
+
`location auto-sync tick: ${runs.length} day(s) across ${settings.config.sources.length} source(s)`
|
|
749
|
+
);
|
|
750
|
+
},
|
|
751
|
+
async stop() {
|
|
752
|
+
stopped = true;
|
|
753
|
+
clearIntervalImpl(timer);
|
|
754
|
+
await inFlight;
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
|
|
710
759
|
export {
|
|
711
760
|
registerLocationProvider,
|
|
712
761
|
getLocationProvider,
|
|
@@ -726,6 +775,8 @@ export {
|
|
|
726
775
|
runLocationDay,
|
|
727
776
|
parseLocationBackfillRange,
|
|
728
777
|
runLocationBackfill,
|
|
729
|
-
runLocationCliCommand
|
|
778
|
+
runLocationCliCommand,
|
|
779
|
+
LOCATION_SYNC_DEFAULT_INTERVAL_MS,
|
|
780
|
+
startLocationAutoSync
|
|
730
781
|
};
|
|
731
|
-
//# sourceMappingURL=chunk-
|
|
782
|
+
//# sourceMappingURL=chunk-IL3RHBWM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/location/registry.ts","../src/location/pipeline.ts","../src/location/cli.ts","../src/location/backfill.ts","../src/location/provider-setup.ts","../src/location/surfaces.ts","../src/location/scheduler.ts"],"sourcesContent":["/**\n * Location provider registry (issue #2044).\n *\n * In-memory only: host adapters register provider implementations at startup.\n * Core never statically imports an optional provider package — the registry\n * holds whatever the host wired in, and a configured source whose provider is\n * not registered is skipped (not an error), so partial installs keep working.\n */\n\nimport type { LocationProvider } from \"./types.js\";\n\nconst PROVIDER_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;\n\nconst providers = new Map<string, LocationProvider>();\n\nfunction assertValidProvider(provider: LocationProvider): void {\n if (typeof provider !== \"object\" || provider === null) {\n throw new TypeError(\"location provider must be an object\");\n }\n if (typeof provider.id !== \"string\" || !PROVIDER_ID_PATTERN.test(provider.id)) {\n throw new RangeError(\n \"location provider id must be a non-empty lowercase kebab string (a-z, 0-9, hyphens)\",\n );\n }\n if (typeof provider.displayName !== \"string\" || provider.displayName.trim().length === 0) {\n throw new TypeError(`location provider '${provider.id}' must declare a non-empty displayName`);\n }\n if (typeof provider.verify !== \"function\" || typeof provider.fetchObservations !== \"function\") {\n throw new TypeError(\n `location provider '${provider.id}' must implement verify() and fetchObservations()`,\n );\n }\n}\n\nexport function registerLocationProvider(provider: LocationProvider): void {\n assertValidProvider(provider);\n if (providers.has(provider.id)) {\n throw new Error(`location provider '${provider.id}' is already registered`);\n }\n providers.set(provider.id, provider);\n}\n\nexport function getLocationProvider(id: string): LocationProvider | undefined {\n return typeof id === \"string\" ? providers.get(id) : undefined;\n}\n\nexport function listLocationProviders(): string[] {\n return [...providers.keys()].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));\n}\n\n/** Test-only: reset the registry between cases. */\nexport function clearLocationProviders(): void {\n providers.clear();\n}\n","/**\n * Location day-sync pipeline (issue #2044).\n *\n * For one local day, each configured (enabled, registered) provider's\n * observations are fetched over the day's half-open UTC window, folded into\n * place-visit segments, merged with the other sources' last-known day\n * payloads from sync state, and rendered to the day document. Sync state\n * advances only AFTER the day document (when any) is durable, so a failed\n * write never moves the watermark past data that did not land. An empty\n * fetch is a successful sync — never conflated with a provider failure\n * (AGENTS.md rule 22).\n */\n\nimport { getLocationProvider } from \"./registry.js\";\nimport { locationDayWindow, observationSegments } from \"./intervals.js\";\nimport {\n composeLocationDayBody,\n composeLocationDayMeta,\n loadLocationSyncState,\n serializeLocationDay,\n updateLocationSourceDay,\n writeLocationDay,\n type LocationDaySourceEntry,\n} from \"./store.js\";\nimport type { LocationConfig, LocationObservation, LocationProvider, LocationSegment } from \"./types.js\";\n\nconst MAX_SYNC_PAGES = 10_000;\n\nexport interface LocationDaySyncOptions {\n config: LocationConfig;\n memoryDir: string;\n date: string;\n signal?: AbortSignal;\n /** Runaway-pagination guard; defaults to MAX_SYNC_PAGES. */\n maxPages?: number;\n now?: () => Date;\n /** Provider lookup seam (defaults to the registry); tests inject doubles. */\n getProvider?: (id: string) => LocationProvider | undefined;\n}\n\nexport interface LocationSourceSyncResult {\n sourceId: string;\n status: \"synced\" | \"skipped\" | \"failed\";\n skipReason?: \"source-disabled\" | \"provider-not-registered\";\n fetched: number;\n dayWritten: boolean;\n stateSaved: boolean;\n /** Sanitized provider error (no tokens, URLs, or coordinates). */\n error?: string;\n}\n\n/** Redact credential, URL query, and coordinate shapes from a provider error. */\nexport function sanitizeProviderError(error: unknown): string {\n const raw = error instanceof Error ? error.message : String(error);\n return raw\n .replace(/Bearer\\s+[A-Za-z0-9._~+/=-]+/gi, \"Bearer [redacted]\")\n .replace(/[?#][^ \\t\"']*/g, \"[redacted]\")\n .replace(/[+-]?\\d+\\.\\d{3,},\\s?[+-]?\\d+\\.\\d{3,}/g, \"[coordinates]\")\n .slice(0, 300);\n}\n\nfunction assertValidObservation(observation: LocationObservation): void {\n if (typeof observation !== \"object\" || observation === null) {\n throw new TypeError(\"location provider returned a non-object observation\");\n }\n if (typeof observation.observedAtUtc !== \"string\" || !Number.isFinite(Date.parse(observation.observedAtUtc))) {\n throw new TypeError(\"location provider returned an observation with an invalid observedAtUtc\");\n }\n const place = observation.place;\n if (typeof place !== \"object\" || place === null || typeof place.id !== \"string\" || place.id.length === 0) {\n throw new TypeError(\"location provider returned an observation without a valid place\");\n }\n if (typeof place.label !== \"string\" || place.label.trim().length === 0) {\n throw new TypeError(`location provider returned place '${place.id}' without a label`);\n }\n}\n\n/** Fetch every observation one provider holds for the window, all pages. */\nasync function fetchWindow(\n provider: LocationProvider,\n window: { startUtc: string; endUtc: string },\n options: LocationDaySyncOptions,\n): Promise<LocationObservation[]> {\n const maxPages = options.maxPages ?? MAX_SYNC_PAGES;\n if (!Number.isInteger(maxPages) || maxPages < 1) {\n throw new RangeError(\"location sync maxPages must be a positive integer\");\n }\n const observations: LocationObservation[] = [];\n const seenCursors = new Set<string>();\n let cursor: string | null = null;\n let pages = 0;\n let completed = false;\n while (pages < maxPages) {\n options.signal?.throwIfAborted();\n const page = await provider.fetchObservations({\n startUtc: window.startUtc,\n endUtc: window.endUtc,\n cursor,\n signal: options.signal,\n });\n pages += 1;\n for (const observation of page.observations) assertValidObservation(observation);\n observations.push(...page.observations);\n if (page.nextCursor === null) {\n completed = true;\n break;\n }\n if (typeof page.nextCursor !== \"string\" || page.nextCursor.length === 0) {\n throw new TypeError(\"location provider returned an invalid cursor\");\n }\n if (seenCursors.has(page.nextCursor)) {\n throw new Error(\"location provider returned a repeated cursor\");\n }\n seenCursors.add(page.nextCursor);\n cursor = page.nextCursor;\n }\n // Only a loop that ran out of its page budget without a terminal null\n // cursor is a runaway; completing on the final allowed page is normal.\n if (!completed) {\n throw new Error(`location provider exceeded ${maxPages} pages`);\n }\n return observations;\n}\n\n/**\n * Sync every configured source for one local day. The master gate\n * (`config.enabled === false`) short-circuits every path: no providers are\n * contacted and nothing touches the filesystem. A provider failure fails\n * that source only (state not advanced) and never blocks the others.\n */\nexport async function syncLocationDay(options: LocationDaySyncOptions): Promise<LocationSourceSyncResult[]> {\n if (!options.config.enabled) return [];\n const window = locationDayWindow(options.date, options.config.timezone);\n const now = options.now ?? (() => new Date());\n const resolveProvider = options.getProvider ?? getLocationProvider;\n\n const results: LocationSourceSyncResult[] = [];\n for (const source of options.config.sources) {\n if (!source.enabled) {\n results.push({\n sourceId: source.id,\n status: \"skipped\",\n skipReason: \"source-disabled\",\n fetched: 0,\n dayWritten: false,\n stateSaved: false,\n });\n continue;\n }\n const provider = resolveProvider(source.id);\n if (provider === undefined) {\n results.push({\n sourceId: source.id,\n status: \"skipped\",\n skipReason: \"provider-not-registered\",\n fetched: 0,\n dayWritten: false,\n stateSaved: false,\n });\n continue;\n }\n\n let observations: LocationObservation[];\n try {\n observations = await fetchWindow(provider, window, options);\n } catch (error) {\n results.push({\n sourceId: source.id,\n status: \"failed\",\n fetched: 0,\n dayWritten: false,\n stateSaved: false,\n ...(error instanceof Error && error.name === \"AbortError\"\n ? { error: \"aborted\" }\n : { error: sanitizeProviderError(error) }),\n });\n continue;\n }\n\n try {\n options.signal?.throwIfAborted();\n const segments = observationSegments(observations, window, {\n retainCoordinates: options.config.retainCoordinates,\n });\n const payload = { observationCount: observations.length, segments, providerDisplayName: provider.displayName };\n const merged = await mergeDaySources(options.memoryDir, source.id, options.date, payload);\n let dayWritten = false;\n if (Object.values(merged).some((entry) => entry.observationCount > 0)) {\n const body = composeLocationDayBody(options.date, options.config.timezone, merged);\n const meta = composeLocationDayMeta(options.date, options.config.timezone, merged, body);\n dayWritten = await writeLocationDay(options.memoryDir, options.date, serializeLocationDay(meta, body));\n }\n // State advances only after the durable day write above succeeded.\n await updateLocationSourceDay(\n options.memoryDir,\n source.id,\n options.date,\n payload,\n now().toISOString(),\n );\n results.push({\n sourceId: source.id,\n status: \"synced\",\n fetched: observations.length,\n dayWritten,\n stateSaved: true,\n });\n } catch (error) {\n results.push({\n sourceId: source.id,\n status: \"failed\",\n fetched: observations.length,\n dayWritten: false,\n stateSaved: false,\n ...(error instanceof Error && error.name === \"AbortError\"\n ? { error: \"aborted\" }\n : { error: sanitizeProviderError(error) }),\n });\n }\n }\n return results;\n}\n\n/**\n * The day-document source map: this source's fresh payload plus every OTHER\n * source's last-known payload from sync state. Rendered documents therefore\n * survive a re-sync of one source without re-fetching the others.\n */\nasync function mergeDaySources(\n memoryDir: string,\n sourceId: string,\n date: string,\n payload: { observationCount: number; segments: LocationSegment[] },\n): Promise<Record<string, LocationDaySourceEntry>> {\n const state = await loadLocationSyncState(memoryDir);\n const merged: Record<string, LocationDaySourceEntry> = {};\n for (const [otherId, otherState] of Object.entries(state.sources)) {\n if (otherId === sourceId) continue;\n const day = otherState.days?.[date];\n if (day === undefined) continue;\n merged[otherId] = { ...day, segments: [...day.segments] };\n }\n merged[sourceId] = payload;\n return merged;\n}\n","/**\n * Location surface helpers (issue #2044): status, provider health checks, and\n * multi-day sync. Pure `@remnic/core` — no optional packages, no host CLI\n * wiring; a host adapter (or the core CLI, in a later slice) renders these\n * structured results into its own command surface.\n */\n\nimport { getLocationProvider } from \"./registry.js\";\nimport { isValidLocationDate, localDayKey } from \"./intervals.js\";\nimport { listLocationDayDates, loadLocationSyncState, parseLocationDaySummary, readLocationDay } from \"./store.js\";\nimport { syncLocationDay, type LocationDaySyncOptions, type LocationSourceSyncResult } from \"./pipeline.js\";\nimport type { LocationConfig, LocationProvider } from \"./types.js\";\n\nexport interface LocationSourceStatus {\n id: string;\n enabled: boolean;\n providerRegistered: boolean;\n lastSyncedAtUtc?: string;\n trackedDays: number;\n}\n\nexport interface LocationDayStatus {\n date: string;\n sources: string[];\n observationCount: number;\n}\n\nexport interface LocationStatusReport {\n enabled: boolean;\n timezone: string;\n sources: LocationSourceStatus[];\n /** Stored day documents, newest first (at most `maxDays`). */\n recentDays: LocationDayStatus[];\n}\n\nexport interface LocationStatusOptions {\n /** Provider lookup seam (defaults to the registry); tests inject doubles. */\n getProvider?: (id: string) => LocationProvider | undefined;\n /** Cap on day documents listed; defaults to 7. */\n maxDays?: number;\n now?: () => Date;\n}\n\nexport async function locationStatus(\n config: LocationConfig,\n memoryDir: string,\n options: LocationStatusOptions = {},\n): Promise<LocationStatusReport> {\n const resolveProvider = options.getProvider ?? getLocationProvider;\n const state = await loadLocationSyncState(memoryDir);\n const sources: LocationSourceStatus[] = config.sources.map((source) => {\n const sourceState = state.sources[source.id];\n return {\n id: source.id,\n enabled: source.enabled,\n providerRegistered: resolveProvider(source.id) !== undefined,\n ...(sourceState?.lastSyncedAtUtc === undefined ? {} : { lastSyncedAtUtc: sourceState.lastSyncedAtUtc }),\n trackedDays: Object.keys(sourceState?.days ?? {}).length,\n };\n });\n\n const maxDays = options.maxDays ?? 7;\n const recentDays: LocationDayStatus[] = [];\n for (const date of (await listLocationDayDates(memoryDir)).slice(0, Math.max(0, maxDays))) {\n const raw = await readLocationDay(memoryDir, date);\n const summary = raw === null ? null : parseLocationDaySummary(raw);\n recentDays.push({\n date,\n sources: summary?.sources ?? [],\n observationCount: summary?.observationCount ?? 0,\n });\n }\n return { enabled: config.enabled, timezone: config.timezone, sources, recentDays };\n}\n\nexport interface LocationSourceCheckResult {\n id: string;\n ok: boolean;\n detail?: string;\n skipped?: \"location-disabled\" | \"source-disabled\" | \"provider-not-registered\";\n}\n\nexport interface LocationCheckOptions {\n signal?: AbortSignal;\n getProvider?: (id: string) => LocationProvider | undefined;\n}\n\n/** Probe every configured provider's health. The master gate short-circuits. */\nexport async function checkLocationSources(\n config: LocationConfig,\n options: LocationCheckOptions = {},\n): Promise<LocationSourceCheckResult[]> {\n if (!config.enabled) {\n return config.sources.map((source) => ({ id: source.id, ok: false, skipped: \"location-disabled\" as const }));\n }\n const resolveProvider = options.getProvider ?? getLocationProvider;\n const results: LocationSourceCheckResult[] = [];\n for (const source of config.sources) {\n if (!source.enabled) {\n results.push({ id: source.id, ok: false, skipped: \"source-disabled\" });\n continue;\n }\n const provider = resolveProvider(source.id);\n if (provider === undefined) {\n results.push({ id: source.id, ok: false, skipped: \"provider-not-registered\" });\n continue;\n }\n try {\n const check = await provider.verify(options.signal);\n results.push({\n id: source.id,\n ok: check.ok,\n ...(check.detail === undefined || check.detail.length === 0 ? {} : { detail: check.detail }),\n });\n } catch (error) {\n results.push({ id: source.id, ok: false, detail: error instanceof Error ? error.name : \"provider error\" });\n }\n }\n return results;\n}\n\nfunction shiftIsoDate(date: string, days: number): string {\n const parsed = new Date(`${date}T00:00:00Z`);\n parsed.setUTCDate(parsed.getUTCDate() + days);\n return parsed.toISOString().slice(0, 10);\n}\n\nexport interface LocationSyncRunOptions\n extends Omit<LocationDaySyncOptions, \"config\" | \"date\" | \"memoryDir\"> {\n config: LocationConfig;\n memoryDir: string;\n /** Exclusive end date (local YYYY-MM-DD); defaults to today in config.timezone. */\n endDate?: string;\n /** Number of days to sync ending at endDate; defaults to config.syncDays. */\n days?: number;\n now?: () => Date;\n}\n/**\n * Sync the trailing `days` local days ending AT `endDate` (inclusive).\n * `endDate` defaults to yesterday in config.timezone (today is incomplete);\n * days run oldest-first so a failure stops before newer days, matching the\n * incremental-sync intent of `syncDays`.\n */\nexport async function syncLocation(\n options: LocationSyncRunOptions,\n): Promise<Array<{ date: string; results: LocationSourceSyncResult[] }>> {\n const { config, memoryDir, endDate, days, ...rest } = options;\n if (endDate !== undefined && !isValidLocationDate(endDate)) {\n throw new RangeError(`Invalid location endDate \"${endDate}\"; expected a real YYYY-MM-DD day.`);\n }\n const now = rest.now ?? (() => new Date());\n const end = endDate ?? shiftIsoDate(localDayKey(now().toISOString(), config.timezone), -1);\n const count = days ?? config.syncDays;\n if (!Number.isInteger(count) || count < 1 || count > 90) {\n throw new RangeError(\"location sync days must be an integer from 1 to 90\");\n }\n const runs: Array<{ date: string; results: LocationSourceSyncResult[] }> = [];\n for (let offset = count; offset >= 1; offset -= 1) {\n const date = shiftIsoDate(end, -(offset - 1));\n runs.push({ date, results: await syncLocationDay({ ...rest, config, memoryDir, date, now }) });\n }\n return runs;\n}\n","/**\n * Location tag backfill (issue #2046) — idempotent, bounded, LLM-free.\n *\n * For each local day in an explicit [from, to] range, find the memories whose\n * own bi-temporal window touches that day and re-run the shared tagging core\n * over the stored location segments. Provider-owned fields update or remove\n * as the (corrected) observations dictate; manual metadata, unrelated tags,\n * supersession, corrections, and content hashes are untouched by the plan\n * core in `matching.ts`.\n *\n * Failure isolation: one bad day is reported as `failed` and the run\n * continues; one bad memory inside a day counts into `failed` without\n * blocking its neighbors. A day with no stored segments is `emptyDay`, never\n * an error (distinct from a failed day). Re-running the same range yields\n * `unchanged` everywhere and writes nothing.\n */\n\nimport type { StorageManager } from \"../index.js\";\nimport type { MemoryFile, PluginConfig } from \"../types.js\";\nimport { resolveNamespaceCapabilities } from \"../capabilities.js\";\nimport { resolveDefaultNamespaceRoot } from \"../namespaces/storage.js\";\nimport { StorageManager as StorageManagerClass } from \"../storage.js\";\nimport { localDayKey } from \"./intervals.js\";\nimport { memoryLocationWindow } from \"./matching.js\";\nimport {\n emptyEnrichmentCounts,\n enrichMemoriesWithLocation,\n loadDaySegmentIndex,\n locationTaggingEnabled,\n type DaySegmentIndex,\n type LocationEnrichmentCounts,\n} from \"./tagging.js\";\nimport type { LocationConfig } from \"./types.js\";\n\nexport interface LocationBackfillDayReport {\n date: string;\n /** No stored segments for any source on this day. */\n emptyDay: boolean;\n failed?: string;\n counts: LocationEnrichmentCounts;\n /** Memories whose window touched the day (empty days report 0). */\n considered: number;\n}\n\nexport interface LocationBackfillReport {\n from: string;\n to: string;\n dryRun: boolean;\n days: LocationBackfillDayReport[];\n}\n\nexport interface BackfillLocationTagsOptions {\n storage: Pick<StorageManager, \"readAllMemories\" | \"getMemoryById\" | \"writeMemoryFrontmatter\">;\n memoryDir: string;\n config: LocationConfig;\n from: string;\n to: string;\n dryRun?: boolean;\n}\n\nexport type BackfillMemoryStorage = Pick<StorageManager, \"readAllMemories\" | \"getMemoryById\" | \"writeMemoryFrontmatter\">;\n\n/**\n * The memory store tag backfill patches: the default-namespace store — the\n * root memory dir when namespaces are off, the resolved default-namespace\n * root when they are on.\n */\nexport async function backfillMemoryStorage(config: PluginConfig): Promise<StorageManager> {\n const root = resolveNamespaceCapabilities(config).namespaces\n ? await resolveDefaultNamespaceRoot(config)\n : config.memoryDir;\n return new StorageManagerClass(root);\n}\n\n\n/** Memories whose own window touches `day`, in deterministic id order. */\nfunction memoriesTouchingDay(memories: readonly MemoryFile[], day: string, config: LocationConfig): MemoryFile[] {\n const touched: MemoryFile[] = [];\n for (const memory of memories) {\n const result = memoryLocationWindow(memory.frontmatter, (instant) => localDayKey(instant, config.timezone));\n if (result.days !== undefined && result.days.includes(day)) touched.push(memory);\n }\n touched.sort((a, b) => (a.frontmatter.id < b.frontmatter.id ? -1 : a.frontmatter.id > b.frontmatter.id ? 1 : 0));\n return touched;\n}\n\n/**\n * Run the bounded backfill. Requires both tagging gates (`location.enabled`\n * and `location.tagging.enabled`); the caller surfaces a clear error for a\n * gated-off run so no surface silently no-ops.\n */\nexport async function backfillLocationTags(\n options: BackfillLocationTagsOptions,\n): Promise<LocationBackfillReport> {\n if (!locationTaggingEnabled(options.config)) {\n throw new Error(\n \"location backfill requires location.enabled and location.tagging.enabled (and location.tagging.backfillEnabled for the command surface)\",\n );\n }\n const index: DaySegmentIndex = await loadDaySegmentIndex(options.memoryDir);\n const memories = await options.storage.readAllMemories();\n\n const days: LocationBackfillDayReport[] = [];\n const cursor = new Date(`${options.from}T00:00:00Z`);\n const end = new Date(`${options.to}T00:00:00Z`);\n while (cursor <= end) {\n const date = cursor.toISOString().slice(0, 10);\n try {\n const daySegments = index.get(date);\n if (daySegments === undefined || daySegments.every((entry) => entry.segments.length === 0)) {\n days.push({ date, emptyDay: true, counts: emptyEnrichmentCounts(), considered: 0 });\n } else {\n const touching = memoriesTouchingDay(memories, date, options.config);\n const counts = await enrichMemoriesWithLocation({\n storage: options.storage,\n memoryDir: options.memoryDir,\n config: options.config,\n memories: touching,\n index,\n dryRun: options.dryRun === true,\n });\n days.push({ date, emptyDay: false, counts, considered: touching.length });\n }\n } catch (error) {\n // One failed day never blocks the rest of the range.\n days.push({\n date,\n emptyDay: false,\n failed: error instanceof Error ? error.message : String(error),\n counts: emptyEnrichmentCounts(),\n considered: 0,\n });\n }\n cursor.setUTCDate(cursor.getUTCDate() + 1);\n }\n return { from: options.from, to: options.to, dryRun: options.dryRun === true, days };\n}\n","/**\n * Location provider bootstrapping (issue #2047).\n *\n * Core never statically imports a provider package (adapter-isolation test),\n * so every surface calls this before status/check/sync: it registers\n * configured providers from optional packages via computed-specifier dynamic\n * imports, with credentials read from environment variables — tokens never\n * enter config files, errors, or logs. A source whose package or credentials\n * are absent stays unregistered and the pipeline reports\n * `provider-not-registered`; that is a skip reason, never an error (the\n * connector-reitti package's README contract).\n */\n\nimport { readEnvVar } from \"../runtime/env.js\";\nimport { log } from \"../logger.js\";\nimport { getLocationProvider } from \"./registry.js\";\nimport type { LocationConfig } from \"./types.js\";\n\n/** Environment shape the setup reads for the `reitti` source. */\nexport interface LocationProviderEnv {\n baseUrl?: string;\n token?: string;\n authMode?: string;\n}\n\nexport interface LocationProviderSetupDeps {\n /** Injectable env (tests); defaults to REITTI_* process variables. */\n env?: LocationProviderEnv;\n /** Injectable module loader (tests); defaults to a dynamic import. */\n importModule?: (specifier: string) => Promise<unknown>;\n}\n\ninterface ReittiProviderModule {\n ensureReittiProviderRegistered(options: {\n baseUrl: string;\n token: string;\n timezone: string;\n authMode?: \"x-api-token\" | \"bearer\";\n }): boolean;\n}\n\nconst loadFailuresWarned = new Set<string>();\n\nfunction readReittiEnv(): LocationProviderEnv {\n return {\n baseUrl: readEnvVar(\"REITTI_BASE_URL\"),\n token: readEnvVar(\"REITTI_TOKEN\"),\n authMode: readEnvVar(\"REITTI_AUTH_MODE\"),\n };\n}\n\n/**\n * Register every configured+enabled source's provider when its optional\n * package and credentials are available. Idempotent; returns the ids this\n * call registered. Never throws — a missing package or credential set is\n * reported downstream as `provider-not-registered`.\n */\nexport async function ensureConfiguredLocationProviders(\n config: LocationConfig,\n deps: LocationProviderSetupDeps = {},\n): Promise<string[]> {\n const registered: string[] = [];\n if (!config.enabled) return registered;\n for (const source of config.sources) {\n if (!source.enabled) continue;\n if (getLocationProvider(source.id) !== undefined) continue;\n // ponytail: only the `reitti` built-in exists today; add cases here as\n // provider packages land — a registry table when there is more than one.\n if (source.id !== \"reitti\") continue;\n const env = deps.env ?? readReittiEnv();\n const baseUrl = env.baseUrl?.trim();\n const token = env.token?.trim();\n if (!baseUrl || !token) continue;\n const authMode =\n env.authMode === \"x-api-token\" || env.authMode === \"bearer\" ? env.authMode : undefined;\n const specifier = \"@remnic/\" + \"connector-reitti\";\n const load = deps.importModule ?? ((s: string) => import(s));\n try {\n const mod = (await load(specifier)) as ReittiProviderModule;\n if (\n mod.ensureReittiProviderRegistered({\n baseUrl,\n token,\n timezone: config.timezone,\n ...(authMode !== undefined ? { authMode } : {}),\n })\n ) {\n registered.push(\"reitti\");\n }\n } catch (err) {\n // Absent optional package is the common case (skip silently); a real\n // load failure is worth one warn line, never credentials.\n const message = err instanceof Error ? err.message : String(err);\n if (!message.includes(specifier)) {\n if (!loadFailuresWarned.has(specifier)) {\n loadFailuresWarned.add(specifier);\n log.warn(`location: failed to load optional provider package ${specifier}: ${message}`);\n }\n }\n }\n }\n return registered;\n}\n","/**\n * Location surface runners (issues #2047, #2046) — ONE implementation shared\n * by the CLI (`remnic location ...`), the MCP tools, the HTTP routes, and the\n * scheduler, so no surface can fork validation or disabled/empty/failure\n * semantics (same rule as the wearables runner). Parsing here is strict:\n * unknown flags reject with the valid list, invalid values reject loudly.\n */\n\nimport { EngramAccessInputError } from \"../access-errors.js\";\nimport {\n checkLocationSources,\n locationStatus,\n syncLocation,\n type LocationSourceCheckResult,\n type LocationStatusReport,\n} from \"./cli.js\";\nimport { isValidLocationDate } from \"./intervals.js\";\nimport { backfillLocationTags, type LocationBackfillReport } from \"./backfill.js\";\nimport type { LocationSourceSyncResult } from \"./pipeline.js\";\nimport { ensureConfiguredLocationProviders } from \"./provider-setup.js\";\nimport { parseLocationDaySummary, readLocationDay } from \"./store.js\";\nimport type { LocationConfig } from \"./types.js\";\nimport type { StorageManager } from \"../index.js\";\n\nexport interface LocationSurfaceDeps {\n config: LocationConfig;\n memoryDir: string;\n /**\n * Memory storage for tag backfill (default-namespace store). Optional so\n * pure day-sync surfaces (status/check/day) never construct one; hosts\n * that expose backfill provide it (issue #2046).\n */\n getMemoryStorage?: () => Promise<StorageManager>;\n}\n\nexport async function runLocationStatus(\n deps: LocationSurfaceDeps,\n): Promise<LocationStatusReport> {\n await ensureConfiguredLocationProviders(deps.config);\n return locationStatus(deps.config, deps.memoryDir);\n}\n\nexport interface LocationSyncRun {\n date: string;\n results: LocationSourceSyncResult[];\n}\n\nexport type LocationSyncRuns = LocationSyncRun[];\nexport async function runLocationCheck(\n deps: LocationSurfaceDeps,\n signal?: AbortSignal,\n): Promise<LocationSourceCheckResult[]> {\n await ensureConfiguredLocationProviders(deps.config);\n return checkLocationSources(deps.config, signal !== undefined ? { signal } : {});\n}\n\nexport async function runLocationSync(\n deps: LocationSurfaceDeps,\n request: { endDate?: string; days?: number } = {},\n): Promise<LocationSyncRuns> {\n await ensureConfiguredLocationProviders(deps.config);\n const runs = await syncLocation({ ...deps, ...validatedSyncWindow(request) });\n return runs as LocationSyncRuns;\n}\n\nexport interface LocationDayView {\n date: string;\n found: boolean;\n sources: string[];\n observationCount: number;\n body: string | null;\n}\n\nexport async function runLocationDay(memoryDir: string, rawDate: unknown): Promise<LocationDayView> {\n const date = requireLocationDate(rawDate, \"date\");\n const body = await readLocationDay(memoryDir, date);\n const summary = body === null ? null : parseLocationDaySummary(body);\n return {\n date,\n found: body !== null,\n sources: summary?.sources ?? [],\n observationCount: summary?.observationCount ?? 0,\n body,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Shared validation (CLI, MCP, HTTP all reject identically)\n// ---------------------------------------------------------------------------\n\nfunction requireLocationDate(raw: unknown, label: string): string {\n if (typeof raw !== \"string\" || !isValidLocationDate(raw)) {\n throw new EngramAccessInputError(`location: ${label} is required (YYYY-MM-DD)`);\n }\n return raw;\n}\n\nfunction optionalLocationDays(raw: unknown): number | undefined {\n if (raw === undefined || raw === null || raw === \"\") return undefined;\n const parsed = typeof raw === \"number\" ? raw : Number(raw);\n if (!Number.isInteger(parsed) || parsed < 1 || parsed > 90) {\n throw new EngramAccessInputError(\"location: days expects an integer from 1 to 90\");\n }\n return parsed;\n}\n\nfunction validatedSyncWindow(request: { endDate?: string; days?: number }): {\n endDate?: string;\n days?: number;\n} {\n const days = optionalLocationDays(request.days);\n const endDate =\n request.endDate === undefined || request.endDate === null || request.endDate === \"\"\n ? undefined\n : requireLocationDate(request.endDate, \"date\");\n return { ...(endDate !== undefined ? { endDate } : {}), ...(days !== undefined ? { days } : {}) };\n}\n\n/** Day span between two inclusive local dates (from ≤ to, ≤ 90 days). */\nexport function parseLocationBackfillRange(\n rawFrom: unknown,\n rawTo: unknown,\n): { from: string; endDate: string; days: number } {\n const from = requireLocationDate(rawFrom, \"from\");\n const to = requireLocationDate(rawTo, \"to\");\n const fromMs = Date.parse(`${from}T00:00:00Z`);\n const toMs = Date.parse(`${to}T00:00:00Z`);\n if (toMs < fromMs) {\n throw new EngramAccessInputError(\"location: backfill --from must not be after --to\");\n }\n const days = Math.round((toMs - fromMs) / 86_400_000) + 1;\n if (days > 90) {\n throw new EngramAccessInputError(\"location: backfill range is capped at 90 days\");\n }\n return { from, endDate: to, days };\n}\n\nexport interface LocationBackfillResult {\n /** Per-day provider sync results (empty under `dryRun`). */\n days: LocationSyncRuns;\n /** Memory re-tag report; absent when no memory storage was provided. */\n memory?: LocationBackfillReport;\n}\n\n/**\n * Backfill an explicit historical range (issue #2046): sync the days (unless\n * `dryRun`, which persists NOTHING — not day files, not memory patches), then\n * re-run the shared tagging core over the stored segments. Requires\n * `location.enabled` + `location.tagging.enabled` +\n * `location.tagging.backfillEnabled`.\n */\nexport async function runLocationBackfill(\n deps: LocationSurfaceDeps,\n request: { from?: unknown; to?: unknown; dryRun?: boolean } = {},\n): Promise<LocationBackfillResult> {\n const range = parseLocationBackfillRange(request.from, request.to);\n if (!deps.config.enabled || !deps.config.tagging.enabled || !deps.config.tagging.backfillEnabled) {\n throw new EngramAccessInputError(\n \"location: backfill requires location.enabled, location.tagging.enabled, and location.tagging.backfillEnabled\",\n );\n }\n await ensureConfiguredLocationProviders(deps.config);\n const days =\n request.dryRun === true ? [] : (await syncLocation({ ...deps, endDate: range.endDate, days: range.days })) as LocationSyncRuns;\n let memory: LocationBackfillReport | undefined;\n if (deps.getMemoryStorage !== undefined) {\n memory = await backfillLocationTags({\n storage: await deps.getMemoryStorage(),\n memoryDir: deps.memoryDir,\n config: deps.config,\n from: range.from,\n to: range.endDate,\n dryRun: request.dryRun === true,\n });\n }\n return { days, ...(memory !== undefined ? { memory } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// CLI command (shared by every CLI host)\n// ---------------------------------------------------------------------------\n\nexport interface LocationCliIo {\n stdout: { write(chunk: string): unknown };\n stderr: { write(chunk: string): unknown };\n}\n\nconst USAGE = `Usage: location <command> [options]\n\nCommands:\n status Configured sources, registration, last sync\n check Probe every enabled provider (auth/connectivity)\n sync [options] Sync recent local days (default: config.syncDays\n ending yesterday)\n --date <YYYY-MM-DD> End the window on this day (inclusive)\n --days <n> Window size, 1..90 (default: location.syncDays)\n backfill --from <date> --to <date> [--dry-run]\n Sync an explicit historical range (≤ 90 days)\n and re-tag overlapping memories (requires\n location.tagging.backfillEnabled). --dry-run\n persists nothing: it only reports the memory\n changes stored segments would produce.\n day <YYYY-MM-DD> Print one stored location day\n\nAdd --json to status/sync/backfill/day for machine-readable output.\nCredentials for built-in providers come from the environment:\n reitti: REITTI_BASE_URL, REITTI_TOKEN[, REITTI_AUTH_MODE=x-api-token|bearer]\n`;\n\nconst VALUE_FLAGS = new Set([\"--date\", \"--days\", \"--from\", \"--to\"]);\nconst BOOLEAN_FLAGS = new Set([\"--json\", \"--dry-run\"]);\n\ninterface ParsedCliFlags {\n flags: Map<string, string | true>;\n positional: string[];\n}\n\nfunction parseCliFlags(args: string[]): ParsedCliFlags {\n const flags = new Map<string, string | true>();\n const positional: string[] = [];\n for (let index = 0; index < args.length; index++) {\n const arg = args[index];\n if (!arg.startsWith(\"--\")) {\n positional.push(arg);\n continue;\n }\n if (BOOLEAN_FLAGS.has(arg)) {\n flags.set(arg, true);\n continue;\n }\n if (VALUE_FLAGS.has(arg)) {\n const value = args[index + 1];\n if (value === undefined || value.startsWith(\"--\")) {\n throw new EngramAccessInputError(`location: ${arg} requires a value`);\n }\n flags.set(arg, value);\n index++;\n continue;\n }\n throw new EngramAccessInputError(\n `location: unknown flag '${arg}' — valid flags: ${[...VALUE_FLAGS, ...BOOLEAN_FLAGS].join(\", \")}`,\n );\n }\n return { flags, positional };\n}\n\nfunction renderBackfillReport(io: LocationCliIo, report: LocationBackfillReport): void {\n for (const day of report.days) {\n if (day.emptyDay) {\n io.stdout.write(`${day.date}: no stored location observations (empty day)\\n`);\n continue;\n }\n if (day.failed !== undefined) {\n io.stdout.write(`${day.date}: FAILED — ${day.failed}\\n`);\n continue;\n }\n const c = day.counts;\n io.stdout.write(\n `${day.date}: ${c.tagged} tagged, ${c.updated} updated, ${c.removed} removed, ` +\n `${c.unchanged} unchanged, ${c.unmatched} unmatched (incl. ambiguous), ` +\n `${c.manual} manual, ${c.untimed} untimed, ${c.failed} failed, ` +\n `${day.considered} considered${report.dryRun ? \" (dry run)\" : \"\"}\\n`,\n );\n }\n}\n\nfunction flagString(parsed: ParsedCliFlags, name: string): string | undefined {\n const value = parsed.flags.get(name);\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction renderStatus(report: LocationStatusReport): string {\n const lines = [\n `Location: ${report.enabled ? \"enabled\" : \"disabled\"} (timezone ${report.timezone})`,\n ];\n if (report.sources.length === 0) {\n lines.push(\"No sources configured. Add location.sources to the plugin config.\");\n return `${lines.join(\"\\n\")}\\n`;\n }\n for (const source of report.sources) {\n lines.push(\n ` ${source.id}: ${source.enabled ? \"enabled\" : \"disabled\"}, ` +\n `provider ${source.providerRegistered ? \"registered\" : \"not registered\"}, ` +\n `${source.trackedDays} tracked day${source.trackedDays === 1 ? \"\" : \"s\"}, ` +\n `last sync ${source.lastSyncedAtUtc ?? \"never\"}`,\n );\n }\n if (report.recentDays.length > 0) {\n lines.push(\" recent days:\");\n for (const day of report.recentDays) {\n lines.push(\n ` ${day.date} ${day.observationCount} observation${day.observationCount === 1 ? \"\" : \"s\"} [${day.sources.join(\", \")}]`,\n );\n }\n }\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nfunction anySourceFailed(runs: LocationSyncRuns): boolean {\n return runs.some((run) => run.results.some((result) => result.status === \"failed\"));\n}\n\nfunction renderRuns(io: LocationCliIo, runs: LocationSyncRuns): void {\n for (const run of runs) {\n for (const result of run.results) {\n io.stdout.write(\n `${run.date} ${result.sourceId}: ${result.status}${result.error ? ` — ${result.error}` : \"\"} (${result.fetched} fetched)\\n`,\n );\n }\n }\n}\n\n/** Run a `remnic location ...` command; returns a process exit code. */\nexport async function runLocationCliCommand(\n deps: LocationSurfaceDeps,\n args: string[],\n io: LocationCliIo,\n): Promise<number> {\n const [command, ...rest] = args;\n try {\n switch (command) {\n case undefined:\n case \"help\":\n case \"--help\": {\n io.stdout.write(USAGE);\n return command === undefined ? 1 : 0;\n }\n case \"status\": {\n const parsed = parseCliFlags(rest);\n const report = await runLocationStatus(deps);\n if (parsed.flags.has(\"--json\")) {\n io.stdout.write(`${JSON.stringify(report, null, 2)}\\n`);\n return 0;\n }\n io.stdout.write(renderStatus(report));\n return 0;\n }\n case \"check\": {\n const results = await runLocationCheck(deps);\n for (const result of results) {\n const skipped = result.skipped === undefined ? \"\" : ` (skipped: ${result.skipped})`;\n io.stdout.write(\n `${result.id}: ${result.ok ? \"OK\" : \"FAILED\"}${skipped}${result.detail ? ` — ${result.detail}` : \"\"}\\n`,\n );\n }\n return results.every((result) => result.ok) ? 0 : 1;\n }\n case \"sync\": {\n const parsed = parseCliFlags(rest);\n if (parsed.positional.length > 0) {\n throw new EngramAccessInputError(\n `location: unexpected argument '${parsed.positional[0]}' — sync takes flags only`,\n );\n }\n const daysValue = flagString(parsed, \"--days\");\n const runs = await runLocationSync(deps, {\n endDate: flagString(parsed, \"--date\"),\n ...(daysValue !== undefined ? { days: Number(daysValue) } : {}),\n });\n if (parsed.flags.has(\"--json\")) {\n io.stdout.write(`${JSON.stringify({ days: runs }, null, 2)}\\n`);\n } else {\n renderRuns(io, runs);\n }\n return anySourceFailed(runs) ? 1 : 0;\n }\n case \"backfill\": {\n const parsed = parseCliFlags(rest);\n const result = await runLocationBackfill(deps, {\n from: flagString(parsed, \"--from\"),\n to: flagString(parsed, \"--to\"),\n dryRun: parsed.flags.has(\"--dry-run\"),\n });\n if (parsed.flags.has(\"--json\")) {\n io.stdout.write(`${JSON.stringify(result, null, 2)}\\n`);\n } else {\n renderRuns(io, result.days);\n if (result.memory !== undefined) renderBackfillReport(io, result.memory);\n }\n const memoryFailed = result.memory?.days.some((day) => day.failed !== undefined) === true;\n return anySourceFailed(result.days) || memoryFailed ? 1 : 0;\n }\n case \"day\": {\n const parsed = parseCliFlags(rest);\n const date = parsed.positional[0] ?? flagString(parsed, \"--date\");\n const view = await runLocationDay(deps.memoryDir, date);\n if (!view.found) {\n io.stderr.write(`No stored location day for ${view.date}.\\n`);\n return 1;\n }\n if (parsed.flags.has(\"--json\")) {\n io.stdout.write(`${JSON.stringify(view, null, 2)}\\n`);\n return 0;\n }\n io.stdout.write(`${view.body}\\n`);\n return 0;\n }\n default:\n io.stderr.write(`location: unknown command '${command}'\\n${USAGE}`);\n return 1;\n }\n } catch (err) {\n io.stderr.write(`${err instanceof Error ? err.message : String(err)}\\n`);\n return 1;\n }\n}\n","/**\n * Location auto-sync — in-process periodic day sync (issue #2047).\n *\n * Arms one repeating timer that runs the SAME shared surface runner\n * (`runLocationSync`) as the CLI/MCP/HTTP surfaces over the configured\n * `location.syncDays` window ending yesterday, so late provider uploads are\n * picked up on the next tick. Master default-off: the caller (maintenance\n * scheduler registration) arms this only when `location.enabled` is true and\n * at least one source is enabled; a forced manual sync never bypasses those\n * gates — they live inside the pipeline itself.\n *\n * The timer is unref'd so one-shot CLI processes exit naturally; ticks are\n * serialized (an overlapping tick is skipped, not queued). ponytail: fixed\n * default cadence (daily) with no per-install override — add a\n * location.syncIntervalHours config field if installs need it.\n */\n\nimport { log } from \"../logger.js\";\nimport { runLocationSync, type LocationSyncRuns } from \"./surfaces.js\";\nimport type { LocationConfig } from \"./types.js\";\n\nexport const LOCATION_SYNC_DEFAULT_INTERVAL_MS = 24 * 60 * 60 * 1000;\n\nexport interface LocationAutoSyncSettings {\n config: LocationConfig;\n memoryDir: string;\n /** Tick cadence in ms; defaults to LOCATION_SYNC_DEFAULT_INTERVAL_MS. */\n intervalMs?: number;\n /** Clock/timer seam for tests. */\n setInterval?: (fn: () => void, ms: number) => { unref?: () => void };\n clearInterval?: (handle: unknown) => void;\n}\n\nexport interface LocationAutoSyncDeps {\n /** Run a sync (defaults to the shared surface runner). */\n sync?: (deps: { config: LocationConfig; memoryDir: string }) => Promise<LocationSyncRuns>;\n log?: { info(message: string): void; warn(message: string): void };\n}\n\nexport interface LocationAutoSyncHandle {\n /** Run one tick now (first-run hook and test seam). */\n tick(): Promise<void>;\n stop(): Promise<void>;\n}\n\nexport function startLocationAutoSync(\n settings: LocationAutoSyncSettings,\n deps: LocationAutoSyncDeps = {},\n): LocationAutoSyncHandle {\n const logger = deps.log ?? log;\n const intervalMs = settings.intervalMs ?? LOCATION_SYNC_DEFAULT_INTERVAL_MS;\n const setIntervalImpl = settings.setInterval ?? ((fn: () => void, ms: number) => setInterval(fn, ms));\n const clearIntervalImpl = settings.clearInterval ?? ((handle: unknown) => clearInterval(handle as NodeJS.Timeout));\n const sync =\n deps.sync ??\n ((deps: { config: LocationConfig; memoryDir: string }) =>\n runLocationSync({ config: deps.config, memoryDir: deps.memoryDir }));\n let inFlight: Promise<void> | null = null;\n let stopped = false;\n const timer = setIntervalImpl(() => {\n if (inFlight !== null || stopped) return;\n inFlight = (async () => {\n try {\n const runs = await sync({ config: settings.config, memoryDir: settings.memoryDir });\n const failed = runs.some((run) => run.results.some((result) => result.status === \"failed\"));\n if (failed) {\n logger.warn(\"location auto-sync: one or more sources failed — retrying on the next tick\");\n } else {\n logger.info(\n `location auto-sync: refreshed ${settings.config.syncDays}-day window across ${settings.config.sources.length} source(s)`,\n );\n }\n } catch (err) {\n // An abort raised by stop() is intentional shutdown, not a failure.\n if (err instanceof Error && err.name === \"AbortError\") return;\n logger.warn(\n `location auto-sync failed: ${err instanceof Error ? err.name : \"unknown error\"} — retrying on the next tick`,\n );\n } finally {\n inFlight = null;\n }\n })();\n }, intervalMs);\n timer.unref?.();\n return {\n async tick() {\n const runs = await sync({ config: settings.config, memoryDir: settings.memoryDir });\n logger.info(\n `location auto-sync tick: ${runs.length} day(s) across ${settings.config.sources.length} source(s)`,\n );\n },\n async stop() {\n stopped = true;\n clearIntervalImpl(timer);\n await inFlight;\n },\n };\n}\n"],"mappings":"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","names":["deps"]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-B7RWBZXW.js";
|
|
4
4
|
import {
|
|
5
5
|
StorageManager
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-FWRXW7CZ.js";
|
|
7
7
|
import {
|
|
8
8
|
getCachedEpisodeMap,
|
|
9
9
|
setCachedEpisodeMap
|
|
@@ -109,4 +109,4 @@ export {
|
|
|
109
109
|
compareVerifiedEpisodeResults,
|
|
110
110
|
searchVerifiedEpisodes
|
|
111
111
|
};
|
|
112
|
-
//# sourceMappingURL=chunk-
|
|
112
|
+
//# sourceMappingURL=chunk-ILY5SSOM.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseEntityFile
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-FWRXW7CZ.js";
|
|
4
4
|
import {
|
|
5
5
|
inferMemoryStatus
|
|
6
6
|
} from "./chunk-7RJUVTVF.js";
|
|
@@ -253,4 +253,4 @@ export {
|
|
|
253
253
|
WHO_KNOWS_MCP_TOOLS,
|
|
254
254
|
handleWhoKnowsHttpQuery
|
|
255
255
|
};
|
|
256
|
-
//# sourceMappingURL=chunk-
|
|
256
|
+
//# sourceMappingURL=chunk-IQ55QGY5.js.map
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
WHO_KNOWS_DEFAULT_LIMIT,
|
|
3
3
|
WHO_KNOWS_MAX_LIMIT,
|
|
4
4
|
validateWhoKnowsInput
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-IQ55QGY5.js";
|
|
6
6
|
|
|
7
7
|
// src/who-knows-cli.ts
|
|
8
8
|
function extractWhoKnowsRawArgs(rest) {
|
|
@@ -74,4 +74,4 @@ export {
|
|
|
74
74
|
renderWhoKnowsText,
|
|
75
75
|
renderWhoKnows
|
|
76
76
|
};
|
|
77
|
-
//# sourceMappingURL=chunk-
|
|
77
|
+
//# sourceMappingURL=chunk-J7BIFOYP.js.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-AAD2ZS6P.js";
|
|
5
5
|
import {
|
|
6
6
|
gatewayTaskChainOptions
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-MMRTPH3J.js";
|
|
8
8
|
import {
|
|
9
9
|
extractJsonCandidates
|
|
10
10
|
} from "./chunk-YBA52OKG.js";
|
|
@@ -685,4 +685,4 @@ export {
|
|
|
685
685
|
serializeFaithfulnessFields,
|
|
686
686
|
parseFaithfulnessField
|
|
687
687
|
};
|
|
688
|
-
//# sourceMappingURL=chunk-
|
|
688
|
+
//# sourceMappingURL=chunk-JNDMIAJ4.js.map
|