@remnic/core 10.0.92 → 10.0.93
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-admin-ops-surface.js +331 -58
- 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 +286 -302
- package/dist/access-cli.js.map +1 -1
- package/dist/access-coding-context-resolution.js +3 -3
- package/dist/access-extraction-force-flush.js +331 -58
- package/dist/access-http-briefing.js +331 -59
- package/dist/access-http-query.js +331 -60
- package/dist/access-http.js +329 -111
- package/dist/access-identity-continuity-surface.js +331 -58
- package/dist/access-lcm-surface.js +331 -58
- 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 +329 -86
- 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 +331 -58
- package/dist/access-offline-manifest.js +12 -11
- package/dist/access-operations-batch.js +329 -63
- package/dist/access-operations.js +329 -72
- package/dist/access-recall-concurrency.js +331 -58
- package/dist/access-recall-diagnostics.js +5 -4
- package/dist/access-recall-response.js +331 -58
- package/dist/access-recall-surface.js +331 -58
- package/dist/access-schema.js +2 -2
- package/dist/access-service.js +334 -61
- 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 +35 -36
- 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 +19 -19
- 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 +22 -22
- package/dist/{chunk-ATPWZ4F7.js → chunk-2LMA22UU.js} +5 -5
- 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-N4KAE2NB.js → chunk-3CYOCE6U.js} +3 -3
- package/dist/{chunk-MB6ZJDN6.js → chunk-3FBAEPCU.js} +2 -2
- package/dist/{chunk-PIITB2YZ.js → chunk-3ZS3NUEI.js} +4 -4
- 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-BRLXOZLM.js → chunk-5YT2IIFX.js} +729 -17
- package/dist/chunk-5YT2IIFX.js.map +1 -0
- package/dist/{chunk-E4IRW2FG.js → chunk-622V2AZA.js} +233 -40
- package/dist/chunk-622V2AZA.js.map +1 -0
- 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-SUSW74WU.js → chunk-ATG2PQ4V.js} +56 -5
- package/dist/chunk-ATG2PQ4V.js.map +1 -0
- package/dist/{chunk-U6UNF5ZS.js → chunk-B33YWUJV.js} +3 -3
- 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-ETZLQOUR.js → chunk-BGHCSLE5.js} +2 -2
- package/dist/{chunk-YYOMGOHB.js → chunk-BOK6BN5M.js} +2 -2
- package/dist/{chunk-OH7QLI6W.js → chunk-BPPUH5JM.js} +5 -5
- package/dist/{chunk-4J363OAA.js → chunk-BTI4C4MA.js} +2 -2
- package/dist/{chunk-O6ZSWHVR.js → chunk-C2KE5DMD.js} +4 -4
- package/dist/{chunk-47F6WWLQ.js → chunk-C73N66PR.js} +2 -2
- package/dist/{chunk-E3A7ELSG.js → chunk-CGJYL633.js} +2 -2
- package/dist/{chunk-WRT7GJ2D.js → chunk-CIWMTNHW.js} +4 -4
- package/dist/{chunk-O55EKIEC.js → chunk-D3ZKHLN2.js} +2 -2
- package/dist/{chunk-CD4BCCNP.js → chunk-DJ7WVEWX.js} +2 -2
- package/dist/{chunk-PWXPRVLK.js → chunk-EFL24KOI.js} +2 -2
- package/dist/{chunk-4CJTQHEG.js → chunk-ESLOLCD4.js} +3 -3
- package/dist/{chunk-6F42QONS.js → chunk-ETBVCRVL.js} +4 -4
- package/dist/{chunk-VILPVT4S.js → chunk-EZ47HOJ6.js} +4 -4
- package/dist/{chunk-EFYN5I7Z.js → chunk-F4QFU5IZ.js} +2 -2
- package/dist/{chunk-T6OZW2WR.js → chunk-FA7SLQ5X.js} +7 -1
- package/dist/chunk-FA7SLQ5X.js.map +1 -0
- package/dist/{chunk-7W3RXOIG.js → chunk-FMKILENU.js} +14 -14
- package/dist/{chunk-YLXETSAH.js → chunk-GCZJ7XRI.js} +2 -2
- package/dist/{chunk-5HFXGXE5.js → chunk-GQXOBKPL.js} +7 -9
- package/dist/{chunk-5HFXGXE5.js.map → chunk-GQXOBKPL.js.map} +1 -1
- package/dist/{chunk-UDKWFDV2.js → chunk-HLUHEMN4.js} +27 -3
- package/dist/chunk-HLUHEMN4.js.map +1 -0
- package/dist/{chunk-QFEDITXM.js → chunk-JNDMIAJ4.js} +2 -2
- package/dist/chunk-JTSSSR4L.js +359 -0
- package/dist/chunk-JTSSSR4L.js.map +1 -0
- package/dist/{chunk-5KBIOI4G.js → chunk-JVBAPAWZ.js} +217 -13
- package/dist/chunk-JVBAPAWZ.js.map +1 -0
- package/dist/{chunk-E5PPE4VA.js → chunk-K2VRKCPA.js} +2 -2
- package/dist/{chunk-MKRYQROF.js → chunk-LGEHAQHK.js} +4 -4
- package/dist/{chunk-7HTUYFR3.js → chunk-LLPMMMWG.js} +27 -10
- package/dist/chunk-LLPMMMWG.js.map +1 -0
- package/dist/{chunk-XYTQJD7F.js → chunk-LMJVP5CM.js} +2 -2
- package/dist/{chunk-FQKBJJYB.js → chunk-LPV72KRQ.js} +2 -2
- package/dist/{chunk-2LCRG2YV.js → chunk-M2XR6B6V.js} +2 -2
- package/dist/{chunk-P4WNBSTW.js → chunk-MH632KPO.js} +2 -2
- package/dist/{chunk-EQ4U73UH.js → chunk-MLAMXQAW.js} +14 -2
- package/dist/chunk-MLAMXQAW.js.map +1 -0
- package/dist/{chunk-ZU5JDLOH.js → chunk-MMRTPH3J.js} +2 -2
- package/dist/{chunk-PMJFRDJI.js → chunk-MUX7IL77.js} +2 -2
- package/dist/{chunk-HF3BKUVV.js → chunk-NAQ4A5IX.js} +7 -7
- 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-JV4S6VDH.js → chunk-OJXJWNFY.js} +2 -2
- 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-Z7JIZ42P.js → chunk-PRAUN6PN.js} +3 -3
- package/dist/{chunk-RWGRSKN5.js → chunk-PT4DZDJA.js} +2 -2
- package/dist/{chunk-7EPY3IQL.js → chunk-PZJQ46DH.js} +2 -2
- package/dist/{chunk-MXHMNKWG.js → chunk-QDESY76F.js} +3 -3
- package/dist/{chunk-I6QGI65T.js → chunk-QRMG4HK4.js} +3 -3
- package/dist/{chunk-ZU3NJNO7.js → chunk-QSF5SJAM.js} +4 -4
- package/dist/{chunk-5IYOKDM4.js → chunk-R23JWJD3.js} +5 -5
- package/dist/{chunk-RHQLIHMB.js → chunk-R4BBGPI2.js} +218 -218
- package/dist/chunk-R4BBGPI2.js.map +1 -0
- package/dist/{chunk-7GLJMDEQ.js → chunk-RM2YUFH6.js} +6 -6
- package/dist/{chunk-IPT2OY4B.js → chunk-RQFXZYTF.js} +35 -33
- package/dist/chunk-RQFXZYTF.js.map +1 -0
- package/dist/{chunk-AP6NCP3V.js → chunk-S3S72HRM.js} +8 -6
- package/dist/chunk-S3S72HRM.js.map +1 -0
- package/dist/{chunk-RIMK327Y.js → chunk-SGQPR5SX.js} +7 -7
- package/dist/{chunk-ZGFLIK5H.js → chunk-SPAKBUJY.js} +2 -2
- package/dist/{chunk-ER7XANP3.js → chunk-SQ3GEIEL.js} +4 -4
- package/dist/{chunk-BLJT6JP2.js → chunk-SVT5IA2H.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-GVGAO3WV.js → chunk-TJ4F6H4H.js} +3 -3
- package/dist/{chunk-R6HMAIKM.js → chunk-TOSPNM4T.js} +2 -2
- package/dist/{chunk-HH5LY6RR.js → chunk-TRQE3RYR.js} +5 -5
- package/dist/{chunk-RMXS4I66.js → chunk-TVDJEUV5.js} +2 -2
- package/dist/{chunk-PLCYKAWH.js → chunk-TYRWSE4B.js} +2 -2
- package/dist/{chunk-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-E7YZXWIS.js → chunk-WKSQTMU7.js} +2 -2
- package/dist/{chunk-RIZ5NUNN.js → chunk-WQ4XKIOX.js} +4 -4
- package/dist/{chunk-FSO3G4JJ.js → chunk-WQURALGV.js} +4995 -2747
- package/dist/chunk-WQURALGV.js.map +1 -0
- package/dist/{chunk-QEYTRFR3.js → chunk-X26YOFGS.js} +2 -2
- package/dist/{chunk-ITHZFHST.js → chunk-X2BVB74H.js} +2 -2
- package/dist/{chunk-XYKBWTWI.js → chunk-XIYLWEXC.js} +2 -2
- package/dist/{chunk-IBJ6U753.js → chunk-XKZBZOKV.js} +4 -4
- package/dist/{chunk-65AUYLU3.js → chunk-XPXHQI6N.js} +2 -2
- package/dist/{chunk-EXHXJETE.js → chunk-XRDYRWZI.js} +2 -2
- package/dist/chunk-XRWWCW4H.js +102 -0
- package/dist/chunk-XRWWCW4H.js.map +1 -0
- package/dist/chunk-YKJBSMMP.js +67137 -0
- package/dist/chunk-YKJBSMMP.js.map +1 -0
- package/dist/{chunk-2JSDPUKD.js → chunk-YMFPFXEX.js} +2 -2
- package/dist/{chunk-645WOU5I.js → chunk-ZDZJNXL6.js} +4 -4
- package/dist/{chunk-6TCXIRP5.js → chunk-ZKE4CO6B.js} +4 -4
- package/dist/{chunk-FNXQZGUS.js → chunk-ZSE774RV.js} +2 -2
- package/dist/cli.js +302 -184
- package/dist/coding/export-okf-codegraph.js +28 -28
- package/dist/compounding/engine.js +22 -23
- package/dist/config.js +35 -36
- package/dist/connectors/codex-materialize-runner.js +19 -19
- package/dist/connectors/index.js +20 -20
- 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 +21 -21
- package/dist/corpus-watermark.js +22 -22
- 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 +20 -20
- 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 +42 -43
- 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 +19 -19
- package/dist/index.js +772 -793
- 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 +26 -28
- package/dist/maintenance/memory-governance.js +19 -19
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +19 -19
- package/dist/maintenance/rebuild-memory-projection-runner.js +21 -21
- package/dist/maintenance/rebuild-memory-projection.js +20 -20
- 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 +20 -20
- package/dist/namespaces/search.js +5 -5
- package/dist/namespaces/storage.js +19 -19
- 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 +23 -23
- package/dist/operator-toolkit.js +57 -60
- package/dist/orchestration/backlog-scheduler.js +277 -293
- package/dist/orchestration/backlog-scheduler.js.map +1 -1
- package/dist/orchestration/buffer-drain-runner.js +277 -293
- 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 +38 -41
- package/dist/orchestrator.d.ts +9 -11
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +289 -306
- 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 +331 -58
- 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 +39 -40
- 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 +22 -22
- package/dist/semantic-rule-promotion.js +19 -19
- package/dist/semantic-rule-verifier.js +19 -19
- package/dist/shared-context/manager.js +2 -3
- package/dist/source-agent-qualifier.js +31 -32
- package/dist/standup/build.js +19 -19
- package/dist/storage/content-hash-index.d.ts +10 -0
- package/dist/storage/content-hash-index.d.ts.map +1 -1
- package/dist/storage.js +18 -18
- 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 +19 -19
- package/dist/verified-recall.js +19 -19
- 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 +20 -20
- package/dist/who-knows.js +19 -19
- 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/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/content-hash-index.ts +27 -2
- package/src/storage-fact-hash-publish-failure.test.ts +87 -0
- package/src/summarizer-shutdown.test.ts +195 -0
- package/src/summarizer.ts +15 -2
- package/src/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-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-ATPWZ4F7.js.map → chunk-2LMA22UU.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-N4KAE2NB.js.map → chunk-3CYOCE6U.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-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-U6UNF5ZS.js.map → chunk-B33YWUJV.js.map} +0 -0
- /package/dist/{chunk-RRJBDXAQ.js.map → chunk-B377OKUP.js.map} +0 -0
- /package/dist/{chunk-ETZLQOUR.js.map → chunk-BGHCSLE5.js.map} +0 -0
- /package/dist/{chunk-YYOMGOHB.js.map → chunk-BOK6BN5M.js.map} +0 -0
- /package/dist/{chunk-OH7QLI6W.js.map → chunk-BPPUH5JM.js.map} +0 -0
- /package/dist/{chunk-4J363OAA.js.map → chunk-BTI4C4MA.js.map} +0 -0
- /package/dist/{chunk-O6ZSWHVR.js.map → chunk-C2KE5DMD.js.map} +0 -0
- /package/dist/{chunk-47F6WWLQ.js.map → chunk-C73N66PR.js.map} +0 -0
- /package/dist/{chunk-E3A7ELSG.js.map → chunk-CGJYL633.js.map} +0 -0
- /package/dist/{chunk-WRT7GJ2D.js.map → chunk-CIWMTNHW.js.map} +0 -0
- /package/dist/{chunk-O55EKIEC.js.map → chunk-D3ZKHLN2.js.map} +0 -0
- /package/dist/{chunk-CD4BCCNP.js.map → chunk-DJ7WVEWX.js.map} +0 -0
- /package/dist/{chunk-PWXPRVLK.js.map → chunk-EFL24KOI.js.map} +0 -0
- /package/dist/{chunk-4CJTQHEG.js.map → chunk-ESLOLCD4.js.map} +0 -0
- /package/dist/{chunk-6F42QONS.js.map → chunk-ETBVCRVL.js.map} +0 -0
- /package/dist/{chunk-VILPVT4S.js.map → chunk-EZ47HOJ6.js.map} +0 -0
- /package/dist/{chunk-EFYN5I7Z.js.map → chunk-F4QFU5IZ.js.map} +0 -0
- /package/dist/{chunk-7W3RXOIG.js.map → chunk-FMKILENU.js.map} +0 -0
- /package/dist/{chunk-YLXETSAH.js.map → chunk-GCZJ7XRI.js.map} +0 -0
- /package/dist/{chunk-QFEDITXM.js.map → chunk-JNDMIAJ4.js.map} +0 -0
- /package/dist/{chunk-E5PPE4VA.js.map → chunk-K2VRKCPA.js.map} +0 -0
- /package/dist/{chunk-MKRYQROF.js.map → chunk-LGEHAQHK.js.map} +0 -0
- /package/dist/{chunk-XYTQJD7F.js.map → chunk-LMJVP5CM.js.map} +0 -0
- /package/dist/{chunk-FQKBJJYB.js.map → chunk-LPV72KRQ.js.map} +0 -0
- /package/dist/{chunk-2LCRG2YV.js.map → chunk-M2XR6B6V.js.map} +0 -0
- /package/dist/{chunk-P4WNBSTW.js.map → chunk-MH632KPO.js.map} +0 -0
- /package/dist/{chunk-ZU5JDLOH.js.map → chunk-MMRTPH3J.js.map} +0 -0
- /package/dist/{chunk-PMJFRDJI.js.map → chunk-MUX7IL77.js.map} +0 -0
- /package/dist/{chunk-HF3BKUVV.js.map → chunk-NAQ4A5IX.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-JV4S6VDH.js.map → chunk-OJXJWNFY.js.map} +0 -0
- /package/dist/{chunk-Z7JIZ42P.js.map → chunk-PRAUN6PN.js.map} +0 -0
- /package/dist/{chunk-RWGRSKN5.js.map → chunk-PT4DZDJA.js.map} +0 -0
- /package/dist/{chunk-7EPY3IQL.js.map → chunk-PZJQ46DH.js.map} +0 -0
- /package/dist/{chunk-MXHMNKWG.js.map → chunk-QDESY76F.js.map} +0 -0
- /package/dist/{chunk-I6QGI65T.js.map → chunk-QRMG4HK4.js.map} +0 -0
- /package/dist/{chunk-ZU3NJNO7.js.map → chunk-QSF5SJAM.js.map} +0 -0
- /package/dist/{chunk-5IYOKDM4.js.map → chunk-R23JWJD3.js.map} +0 -0
- /package/dist/{chunk-7GLJMDEQ.js.map → chunk-RM2YUFH6.js.map} +0 -0
- /package/dist/{chunk-RIMK327Y.js.map → chunk-SGQPR5SX.js.map} +0 -0
- /package/dist/{chunk-ZGFLIK5H.js.map → chunk-SPAKBUJY.js.map} +0 -0
- /package/dist/{chunk-ER7XANP3.js.map → chunk-SQ3GEIEL.js.map} +0 -0
- /package/dist/{chunk-BLJT6JP2.js.map → chunk-SVT5IA2H.js.map} +0 -0
- /package/dist/{chunk-GVGAO3WV.js.map → chunk-TJ4F6H4H.js.map} +0 -0
- /package/dist/{chunk-R6HMAIKM.js.map → chunk-TOSPNM4T.js.map} +0 -0
- /package/dist/{chunk-HH5LY6RR.js.map → chunk-TRQE3RYR.js.map} +0 -0
- /package/dist/{chunk-RMXS4I66.js.map → chunk-TVDJEUV5.js.map} +0 -0
- /package/dist/{chunk-PLCYKAWH.js.map → chunk-TYRWSE4B.js.map} +0 -0
- /package/dist/{chunk-IQWUACVG.js.map → chunk-UHMHJX5G.js.map} +0 -0
- /package/dist/{chunk-YODUDA6R.js.map → chunk-UN4RVUG2.js.map} +0 -0
- /package/dist/{chunk-E6Z4FBX6.js.map → chunk-V2QSNFCT.js.map} +0 -0
- /package/dist/{chunk-E7YZXWIS.js.map → chunk-WKSQTMU7.js.map} +0 -0
- /package/dist/{chunk-RIZ5NUNN.js.map → chunk-WQ4XKIOX.js.map} +0 -0
- /package/dist/{chunk-QEYTRFR3.js.map → chunk-X26YOFGS.js.map} +0 -0
- /package/dist/{chunk-ITHZFHST.js.map → chunk-X2BVB74H.js.map} +0 -0
- /package/dist/{chunk-XYKBWTWI.js.map → chunk-XIYLWEXC.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-EXHXJETE.js.map → chunk-XRDYRWZI.js.map} +0 -0
- /package/dist/{chunk-2JSDPUKD.js.map → chunk-YMFPFXEX.js.map} +0 -0
- /package/dist/{chunk-645WOU5I.js.map → chunk-ZDZJNXL6.js.map} +0 -0
- /package/dist/{chunk-6TCXIRP5.js.map → chunk-ZKE4CO6B.js.map} +0 -0
- /package/dist/{chunk-FNXQZGUS.js.map → chunk-ZSE774RV.js.map} +0 -0
- /package/dist/{contradiction-scan-P2MHYFVG.js.map → contradiction-scan-IBPYGQZF.js.map} +0 -0
- /package/dist/{resolution-N7H3F2E4.js.map → resolution-OVVIF7ZY.js.map} +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { StorageManager } from "./storage.js";
|
|
7
|
+
import { ContentHashIndex } from "./storage/content-hash-index.js";
|
|
8
|
+
|
|
9
|
+
const FACT = "the quarterly budget was approved by the board";
|
|
10
|
+
const CONTENDED_LOCK_OPTIONS = { maxWaitMs: 40, pollMs: 10, retryMaxAttempts: 2, retryBaseMs: 10 };
|
|
11
|
+
|
|
12
|
+
async function withMemoryDir(run: (dir: string) => Promise<void>): Promise<void> {
|
|
13
|
+
const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-fact-hash-publish-"));
|
|
14
|
+
try {
|
|
15
|
+
await run(dir);
|
|
16
|
+
} finally {
|
|
17
|
+
await rm(dir, { recursive: true, force: true });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Hold the fact-hash index lock the way a peer mid-publish does; returns the release. */
|
|
22
|
+
async function holdIndexLock(dir: string): Promise<() => Promise<void>> {
|
|
23
|
+
const lockPath = path.join(dir, "state", "fact-hashes.txt.lock");
|
|
24
|
+
await mkdir(path.dirname(lockPath), { recursive: true });
|
|
25
|
+
await writeFile(lockPath, `99999 peer-owner ${new Date().toISOString()}\n`);
|
|
26
|
+
return () => rm(lockPath, { force: true });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
test("a publish that throws leaves peers revalidating instead of trusting the old file (#3355)", async () => {
|
|
30
|
+
await withMemoryDir(async (dir) => {
|
|
31
|
+
const stateDir = path.join(dir, "state");
|
|
32
|
+
const peer = new ContentHashIndex(stateDir);
|
|
33
|
+
peer.add("seed fact");
|
|
34
|
+
await peer.saveMergingWithDisk();
|
|
35
|
+
assert.equal(await peer.isDiskFingerprintCurrent(), true, "the peer starts in sync with the file");
|
|
36
|
+
|
|
37
|
+
const writer = new ContentHashIndex(
|
|
38
|
+
stateDir,
|
|
39
|
+
() => null,
|
|
40
|
+
() => {
|
|
41
|
+
throw new Error("write key unavailable");
|
|
42
|
+
},
|
|
43
|
+
);
|
|
44
|
+
await writer.load();
|
|
45
|
+
writer.add("pending fact");
|
|
46
|
+
await assert.rejects(writer.saveMergingWithDisk(), /write key unavailable/);
|
|
47
|
+
|
|
48
|
+
assert.equal(writer.hasPendingChanges, true, "the failed addition stays pending in the writer");
|
|
49
|
+
assert.equal(
|
|
50
|
+
await peer.isDiskFingerprintCurrent(),
|
|
51
|
+
false,
|
|
52
|
+
"the file did not change, yet the peer must stop trusting its snapshot",
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("a writeMemory whose publish stays deferred does not leave a peer answering an authoritative MISS (#3355)", async () => {
|
|
58
|
+
await withMemoryDir(async (dir) => {
|
|
59
|
+
const peer = new StorageManager(dir);
|
|
60
|
+
const writer = new StorageManager(dir);
|
|
61
|
+
peer.factHashIndexLockOptions = CONTENDED_LOCK_OPTIONS;
|
|
62
|
+
writer.factHashIndexLockOptions = CONTENDED_LOCK_OPTIONS;
|
|
63
|
+
assert.deepEqual(
|
|
64
|
+
await peer.lookupContentHash(FACT),
|
|
65
|
+
{ present: false, authoritative: true },
|
|
66
|
+
"the peer starts with an authoritative, empty index",
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const release = await holdIndexLock(dir);
|
|
70
|
+
const written = await writer.writeMemory("fact", FACT);
|
|
71
|
+
assert.ok(written.id, "the memory is durable, so the write still succeeds");
|
|
72
|
+
|
|
73
|
+
const duringContention = await peer.lookupContentHash(FACT);
|
|
74
|
+
assert.ok(
|
|
75
|
+
duringContention.present || !duringContention.authoritative,
|
|
76
|
+
`a peer must not report an authoritative MISS for a fact on disk: ${JSON.stringify(duringContention)}`,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
await release();
|
|
80
|
+
assert.deepEqual(
|
|
81
|
+
await peer.lookupContentHash(FACT),
|
|
82
|
+
{ present: true, authoritative: true },
|
|
83
|
+
"once the lock is free the peer rebuilds from the corpus and sees the fact",
|
|
84
|
+
);
|
|
85
|
+
await writer.lookupContentHash(FACT);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// Shutdown-signal behavioral tests for HourlySummarizer: an aborted
|
|
2
|
+
// summarizer must abort its in-flight local LLM call (the abort reaches the
|
|
3
|
+
// client stub) and must refuse to start new summary work once aborted.
|
|
4
|
+
import assert from "node:assert/strict";
|
|
5
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import test from "node:test";
|
|
9
|
+
|
|
10
|
+
import { parseConfig } from "./config.js";
|
|
11
|
+
import { initLogger, resetLogger } from "./logger.js";
|
|
12
|
+
import { HourlySummarizer } from "./summarizer.js";
|
|
13
|
+
import type { LocalLlmChatCompletionOptions } from "./local-llm-helpers.js";
|
|
14
|
+
import type { TranscriptEntry } from "./types.js";
|
|
15
|
+
|
|
16
|
+
function createConfig(memoryDir: string, extended = false) {
|
|
17
|
+
return parseConfig({
|
|
18
|
+
openaiApiKey: "sk-test",
|
|
19
|
+
memoryDir,
|
|
20
|
+
workspaceDir: path.join(memoryDir, "workspace"),
|
|
21
|
+
localLlmEnabled: true,
|
|
22
|
+
localLlmModel: "test-local-model",
|
|
23
|
+
localLlmUrl: "http://127.0.0.1:9",
|
|
24
|
+
localLlmTimeoutMs: 600_000,
|
|
25
|
+
localLlmRetry5xxCount: 1,
|
|
26
|
+
localLlmRetryBackoffMs: 30_000,
|
|
27
|
+
hourlySummariesEnabled: true,
|
|
28
|
+
hourlySummariesExtendedEnabled: extended,
|
|
29
|
+
debug: false,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface LlmStub {
|
|
34
|
+
calls: { signal: AbortSignal | undefined }[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function stubLocalLlm(summarizer: HourlySummarizer, stub: LlmStub): void {
|
|
38
|
+
// SAFETY: localLlm is a private field, stubbed at the established field
|
|
39
|
+
// seam with the client's real call shape; the stub parks until its caller
|
|
40
|
+
// signal aborts (real transport behavior), mirroring a wedged local LLM.
|
|
41
|
+
(summarizer as any).localLlm = {
|
|
42
|
+
chatCompletion: async (
|
|
43
|
+
messages: Array<{ role: string; content: string }>,
|
|
44
|
+
options: LocalLlmChatCompletionOptions,
|
|
45
|
+
): Promise<null> => {
|
|
46
|
+
void messages;
|
|
47
|
+
stub.calls.push({ signal: options.signal });
|
|
48
|
+
const { promise, reject } = Promise.withResolvers<null>();
|
|
49
|
+
const onAbort = (): void => {
|
|
50
|
+
reject(new DOMException("This operation was aborted", "AbortError"));
|
|
51
|
+
};
|
|
52
|
+
const signal = options.signal;
|
|
53
|
+
if (signal) {
|
|
54
|
+
if (signal.aborted) onAbort();
|
|
55
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
56
|
+
}
|
|
57
|
+
return await promise;
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function withTestDeadline<T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T> {
|
|
63
|
+
const { promise: deadline, reject } = Promise.withResolvers<never>();
|
|
64
|
+
const timer = setTimeout(() => reject(new Error(`${label}: test deadline exceeded (${timeoutMs}ms)`)), timeoutMs);
|
|
65
|
+
timer.unref?.();
|
|
66
|
+
try {
|
|
67
|
+
return await Promise.race([promise, deadline]);
|
|
68
|
+
} finally {
|
|
69
|
+
clearTimeout(timer);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function quietLogger(): void {
|
|
74
|
+
initLogger({ info() {}, warn() {}, error() {}, debug() {} }, false);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function entriesFor(): TranscriptEntry[] {
|
|
78
|
+
return [
|
|
79
|
+
{
|
|
80
|
+
role: "user",
|
|
81
|
+
content: "bounded shutdown summarizer test entry",
|
|
82
|
+
timestamp: new Date(Date.now() - 3_600_000).toISOString(),
|
|
83
|
+
sessionKey: "sess-summary-shutdown",
|
|
84
|
+
turnId: "turn-shutdown-1",
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
test("aborting the shutdown signal aborts an in-flight generateSummary and reaches the LLM stub", async () => {
|
|
90
|
+
quietLogger();
|
|
91
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-summary-shutdown-inflight-"));
|
|
92
|
+
const stub: LlmStub = { calls: [] };
|
|
93
|
+
try {
|
|
94
|
+
const controller = new AbortController();
|
|
95
|
+
const summarizer = new HourlySummarizer(
|
|
96
|
+
createConfig(memoryDir),
|
|
97
|
+
undefined,
|
|
98
|
+
undefined,
|
|
99
|
+
undefined,
|
|
100
|
+
controller.signal,
|
|
101
|
+
);
|
|
102
|
+
stubLocalLlm(summarizer, stub);
|
|
103
|
+
|
|
104
|
+
const hourStart = new Date(Date.now() - 3_600_000);
|
|
105
|
+
hourStart.setMinutes(0, 0, 0);
|
|
106
|
+
const pending = summarizer.generateSummary("sess-summary-shutdown", hourStart, entriesFor());
|
|
107
|
+
|
|
108
|
+
await withTestDeadline((async () => {
|
|
109
|
+
while (stub.calls.length === 0) {
|
|
110
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
111
|
+
}
|
|
112
|
+
})(), 5_000, "LLM stub to be entered");
|
|
113
|
+
controller.abort();
|
|
114
|
+
|
|
115
|
+
const result = await withTestDeadline(pending, 2_000, "in-flight summarizer abort");
|
|
116
|
+
assert.equal(result, null);
|
|
117
|
+
assert.equal(stub.calls.length, 1, "no second LLM attempt may start after abort");
|
|
118
|
+
assert.equal(stub.calls[0]?.signal?.aborted, true, "the shutdown abort must reach the LLM stub");
|
|
119
|
+
} finally {
|
|
120
|
+
resetLogger();
|
|
121
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("an already-aborted summarizer refuses to start new work and never contacts the LLM", async () => {
|
|
126
|
+
quietLogger();
|
|
127
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-summary-shutdown-refuse-"));
|
|
128
|
+
const stub: LlmStub = { calls: [] };
|
|
129
|
+
try {
|
|
130
|
+
const controller = new AbortController();
|
|
131
|
+
controller.abort();
|
|
132
|
+
const summarizer = new HourlySummarizer(
|
|
133
|
+
createConfig(memoryDir),
|
|
134
|
+
undefined,
|
|
135
|
+
undefined,
|
|
136
|
+
undefined,
|
|
137
|
+
controller.signal,
|
|
138
|
+
);
|
|
139
|
+
stubLocalLlm(summarizer, stub);
|
|
140
|
+
|
|
141
|
+
const hourStart = new Date(Date.now() - 3_600_000);
|
|
142
|
+
hourStart.setMinutes(0, 0, 0);
|
|
143
|
+
await assert.rejects(
|
|
144
|
+
summarizer.generateSummary("sess-summary-shutdown", hourStart, entriesFor()),
|
|
145
|
+
);
|
|
146
|
+
assert.equal(stub.calls.length, 0, "aborted summarizer must not start new LLM work");
|
|
147
|
+
} finally {
|
|
148
|
+
resetLogger();
|
|
149
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
for (const extended of [false, true]) {
|
|
154
|
+
test(`a shutdown abort during the local call does not start a gateway call (${extended ? "extended" : "standard"} summary)`, async () => {
|
|
155
|
+
quietLogger();
|
|
156
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-summary-shutdown-gateway-"));
|
|
157
|
+
const stub: LlmStub = { calls: [] };
|
|
158
|
+
let gatewayCalls = 0;
|
|
159
|
+
try {
|
|
160
|
+
const controller = new AbortController();
|
|
161
|
+
const summarizer = new HourlySummarizer(
|
|
162
|
+
createConfig(memoryDir, extended),
|
|
163
|
+
undefined,
|
|
164
|
+
undefined,
|
|
165
|
+
undefined,
|
|
166
|
+
controller.signal,
|
|
167
|
+
);
|
|
168
|
+
stubLocalLlm(summarizer, stub);
|
|
169
|
+
// SAFETY: fallbackLlm is a private field, stubbed at the same field seam
|
|
170
|
+
// as localLlm; the stub counts gateway calls and returns no result.
|
|
171
|
+
(summarizer as any).fallbackLlm = {
|
|
172
|
+
parseWithSchema: async (): Promise<null> => {
|
|
173
|
+
gatewayCalls += 1;
|
|
174
|
+
return null;
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const hourStart = new Date(Date.now() - 3_600_000);
|
|
179
|
+
hourStart.setMinutes(0, 0, 0);
|
|
180
|
+
const pending = summarizer.generateSummary("sess-summary-shutdown", hourStart, entriesFor());
|
|
181
|
+
await withTestDeadline((async () => {
|
|
182
|
+
while (stub.calls.length === 0) {
|
|
183
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
184
|
+
}
|
|
185
|
+
})(), 5_000, "LLM stub to be entered");
|
|
186
|
+
controller.abort();
|
|
187
|
+
|
|
188
|
+
assert.equal(await withTestDeadline(pending, 2_000, "in-flight summarizer abort"), null);
|
|
189
|
+
assert.equal(gatewayCalls, 0, "no gateway call may start after the shutdown abort");
|
|
190
|
+
} finally {
|
|
191
|
+
resetLogger();
|
|
192
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
package/src/summarizer.ts
CHANGED
|
@@ -114,20 +114,25 @@ export class HourlySummarizer {
|
|
|
114
114
|
private fallbackLlm: FallbackLlmClient;
|
|
115
115
|
private modelRegistry: ModelRegistry;
|
|
116
116
|
private transcript?: TranscriptManager;
|
|
117
|
+
/** Orchestrator-owned stop signal: refuses new summaries and aborts the
|
|
118
|
+
* in-flight local LLM call once teardown begins. */
|
|
119
|
+
private readonly shutdownSignal: AbortSignal | undefined;
|
|
117
120
|
|
|
118
121
|
constructor(
|
|
119
122
|
config: PluginConfig,
|
|
120
123
|
gatewayConfig?: GatewayConfig,
|
|
121
124
|
modelRegistry?: ModelRegistry,
|
|
122
|
-
transcript?: TranscriptManager
|
|
125
|
+
transcript?: TranscriptManager,
|
|
126
|
+
shutdownSignal?: AbortSignal
|
|
123
127
|
) {
|
|
124
128
|
this.config = config;
|
|
125
129
|
this.summariesDir = path.join(config.memoryDir, "summaries", "hourly");
|
|
126
130
|
this.modelRegistry = modelRegistry ?? new ModelRegistry(config.memoryDir);
|
|
127
131
|
this.transcript = transcript;
|
|
132
|
+
this.shutdownSignal = shutdownSignal;
|
|
128
133
|
|
|
129
134
|
// Initialize local LLM client with shared model registry
|
|
130
|
-
this.localLlm = new LocalLlmClient(config, this.modelRegistry);
|
|
135
|
+
this.localLlm = new LocalLlmClient(config, this.modelRegistry, shutdownSignal);
|
|
131
136
|
|
|
132
137
|
// Initialize fallback client with gateway config
|
|
133
138
|
this.fallbackLlm = new FallbackLlmClient(gatewayConfig, fallbackLlmRuntimeContextFromConfig(config));
|
|
@@ -190,6 +195,7 @@ export class HourlySummarizer {
|
|
|
190
195
|
entries: TranscriptEntry[]
|
|
191
196
|
): Promise<HourlySummary | null> {
|
|
192
197
|
if (entries.length === 0) return null;
|
|
198
|
+
this.shutdownSignal?.throwIfAborted();
|
|
193
199
|
|
|
194
200
|
// Format entries for the LLM
|
|
195
201
|
const conversation = entries.map((e) => `[${e.role}] ${e.content}`).join("\n\n");
|
|
@@ -275,6 +281,8 @@ export class HourlySummarizer {
|
|
|
275
281
|
}
|
|
276
282
|
}
|
|
277
283
|
|
|
284
|
+
if (this.shutdownSignal?.aborted) return null;
|
|
285
|
+
|
|
278
286
|
// Fall back to gateway's default AI
|
|
279
287
|
log.info("summary generation: falling back to gateway default AI");
|
|
280
288
|
|
|
@@ -399,6 +407,7 @@ Respond with valid JSON matching this schema:
|
|
|
399
407
|
maxTokens: contextSizes.maxOutputTokens,
|
|
400
408
|
operation: "hourly_summary_extended",
|
|
401
409
|
priority: "background",
|
|
410
|
+
signal: this.shutdownSignal,
|
|
402
411
|
}
|
|
403
412
|
);
|
|
404
413
|
if (response?.content) {
|
|
@@ -422,6 +431,8 @@ Respond with valid JSON matching this schema:
|
|
|
422
431
|
}
|
|
423
432
|
}
|
|
424
433
|
|
|
434
|
+
if (this.shutdownSignal?.aborted) return null;
|
|
435
|
+
|
|
425
436
|
try {
|
|
426
437
|
const result = await this.fallbackLlm.parseWithSchema(
|
|
427
438
|
[
|
|
@@ -489,6 +500,7 @@ Respond with valid JSON matching this schema:
|
|
|
489
500
|
maxTokens: contextSizes.maxOutputTokens,
|
|
490
501
|
operation: "hourly_summary",
|
|
491
502
|
priority: "background",
|
|
503
|
+
signal: this.shutdownSignal,
|
|
492
504
|
}
|
|
493
505
|
);
|
|
494
506
|
|
|
@@ -775,6 +787,7 @@ Respond with valid JSON matching this schema:
|
|
|
775
787
|
|
|
776
788
|
// Main entry point for cron job
|
|
777
789
|
async runHourly(): Promise<HourlySummarizerRunStats> {
|
|
790
|
+
this.shutdownSignal?.throwIfAborted();
|
|
778
791
|
log.debug("running hourly summary generation");
|
|
779
792
|
|
|
780
793
|
// Get active sessions from transcript. The scan outcome is kept LOCAL to
|
|
@@ -224,6 +224,26 @@ test("auto mode writes active status", async () => {
|
|
|
224
224
|
assert.equal(writes[0].options.status, "active");
|
|
225
225
|
});
|
|
226
226
|
|
|
227
|
+
test("a fact write the dedup window suppressed counts as skipped, not created", async () => {
|
|
228
|
+
const { writer } = makeWriter();
|
|
229
|
+
writer.writeSealedMemory = async () => ({ duplicate: true });
|
|
230
|
+
const result = await generateWearableMemories(
|
|
231
|
+
"limitless",
|
|
232
|
+
"2026-06-10",
|
|
233
|
+
[LONG_CONVERSATION],
|
|
234
|
+
settings({ memoryMode: "auto" }),
|
|
235
|
+
REGISTRY,
|
|
236
|
+
{
|
|
237
|
+
extract: extractionReturning([
|
|
238
|
+
{ category: "fact", content: "Vendor call happens tomorrow morning.", confidence: 0.8, tags: [] },
|
|
239
|
+
]),
|
|
240
|
+
writer,
|
|
241
|
+
},
|
|
242
|
+
);
|
|
243
|
+
assert.equal(result.created, 0);
|
|
244
|
+
assert.equal(result.skippedByReason["duplicate-existing"], 1);
|
|
245
|
+
});
|
|
246
|
+
|
|
227
247
|
test("auto mode still refuses to write a high-impact personal claim active (#2294)", async () => {
|
|
228
248
|
// `auto` has no trust scoring, and an operator may lower minConfidence to or
|
|
229
249
|
// below the speculative ceiling the ambient clamp writes — which would let a
|
|
@@ -92,6 +92,11 @@ export function memoryStatusForMode(
|
|
|
92
92
|
return mode === "auto" || mode === "smart" ? "active" : "pending_review";
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
/** A fact write the fact-dedup window suppressed because a concurrent writer committed the same fact first. */
|
|
96
|
+
export interface WearableDuplicateWrite {
|
|
97
|
+
readonly duplicate: true;
|
|
98
|
+
}
|
|
99
|
+
|
|
95
100
|
/** Narrow writer interface satisfied by `StorageManager`. */
|
|
96
101
|
export interface WearableMemoryWriter {
|
|
97
102
|
/**
|
|
@@ -106,7 +111,7 @@ export interface WearableMemoryWriter {
|
|
|
106
111
|
status?: MemoryStatus;
|
|
107
112
|
memoryKind?: "episode" | "note" | "box" | "dream" | "procedural";
|
|
108
113
|
},
|
|
109
|
-
): Promise<MemoryWriteResult>;
|
|
114
|
+
): Promise<MemoryWriteResult | WearableDuplicateWrite>;
|
|
110
115
|
hasFactContentHash(content: string): Promise<boolean>;
|
|
111
116
|
/**
|
|
112
117
|
* Locate an earlier wearable write of the same content (any status).
|
|
@@ -663,7 +668,9 @@ export async function generateWearableMemories(
|
|
|
663
668
|
// #1645: a tombstone-blocked wearable write lands pending_review (no active
|
|
664
669
|
// copy) — count it as blocked, NOT created, so the sync summary and callers
|
|
665
670
|
// don't report blocked content as successfully created active memories.
|
|
666
|
-
if (writeResult
|
|
671
|
+
if ("duplicate" in writeResult) {
|
|
672
|
+
skip("duplicate-existing");
|
|
673
|
+
} else if (writeResult.tombstoneBlocked) {
|
|
667
674
|
result.tombstoneBlocked += 1;
|
|
668
675
|
} else {
|
|
669
676
|
result.created += 1;
|
|
@@ -872,11 +879,11 @@ export async function importNativeMemories(
|
|
|
872
879
|
// #1645: a tombstone-blocked native import lands pending_review (no active
|
|
873
880
|
// copy) — count it as blocked, NOT imported. Still record the provider id in
|
|
874
881
|
// importedIds so the next sync doesn't re-import and duplicate the pending
|
|
875
|
-
// row; the block is permanent for this content.
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
imported += 1;
|
|
882
|
+
// row; the block is permanent for this content. A write the dedup window
|
|
883
|
+
// suppressed (a concurrent writer committed the fact first) counts as neither.
|
|
884
|
+
if (!("duplicate" in nativeWriteResult)) {
|
|
885
|
+
if (nativeWriteResult.tombstoneBlocked) tombstoneBlocked += 1;
|
|
886
|
+
else imported += 1;
|
|
880
887
|
}
|
|
881
888
|
importedIds.push(memory.id);
|
|
882
889
|
}
|
package/src/wearables/service.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "./corrections.js";
|
|
14
14
|
import { describeErrorForOperator, WearablesInputError } from "./errors.js";
|
|
15
15
|
import { inferMemoryStatus } from "../memory-lifecycle-ledger-utils.js";
|
|
16
|
+
import { persistUnderFactDedupWindow } from "../orchestration/fact-dedup-window.js";
|
|
16
17
|
import type { LocationConfig } from "../location/types.js";
|
|
17
18
|
import { isSupportPassportPrivateMemory } from "../support-passport/card-projection.js";
|
|
18
19
|
import {
|
|
@@ -194,7 +195,19 @@ export function createWearableMemoryWriter(
|
|
|
194
195
|
storage: WearableStorageIo,
|
|
195
196
|
): WearableMemoryGenDeps["writer"] {
|
|
196
197
|
return {
|
|
197
|
-
writeSealedMemory:
|
|
198
|
+
writeSealedMemory: async (envelope, extras) => {
|
|
199
|
+
// Facts register in the fact hash index, so extraction and a wearable sync
|
|
200
|
+
// can commit the same new fact. Commit it under the window extraction uses
|
|
201
|
+
// and recheck the index there. Other categories keep the direct write: the
|
|
202
|
+
// index does not cover them, and the corpus scan stays outside the window.
|
|
203
|
+
if (envelope.category !== "fact") return storage.writeSealedMemory(envelope, extras);
|
|
204
|
+
const outcome = await persistUnderFactDedupWindow(
|
|
205
|
+
storage.dir,
|
|
206
|
+
() => storage.hasFactContentHash(extras.contentHashSource ?? envelope.content),
|
|
207
|
+
() => storage.writeSealedMemory(envelope, extras),
|
|
208
|
+
);
|
|
209
|
+
return outcome.suppressed ? { duplicate: true } : outcome.result;
|
|
210
|
+
},
|
|
198
211
|
findWearableMemoryByContent: async (content: string) =>
|
|
199
212
|
(await storage.findWearableMemoryByContent(content)) as
|
|
200
213
|
| { id: string; status: import("../types.js").MemoryStatus | undefined }
|
|
@@ -5,7 +5,10 @@ import { tmpdir } from "node:os";
|
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import { test } from "node:test";
|
|
7
7
|
|
|
8
|
+
import { persistUnderFactDedupWindow } from "../orchestration/fact-dedup-window.js";
|
|
8
9
|
import { StorageManager } from "../storage.js";
|
|
10
|
+
import { composeMemoryEnvelope } from "../write-envelope.js";
|
|
11
|
+
import { createWearableMemoryWriter } from "./service.js";
|
|
9
12
|
|
|
10
13
|
function makeStorage(): { storage: StorageManager; dir: string } {
|
|
11
14
|
const dir = mkdtempSync(path.join(tmpdir(), "remnic-wearable-io-"));
|
|
@@ -330,3 +333,25 @@ test("a symlinked _fusion dir that resolves INSIDE the memory dir is refused, no
|
|
|
330
333
|
rmSync(dir, { recursive: true, force: true });
|
|
331
334
|
}
|
|
332
335
|
});
|
|
336
|
+
|
|
337
|
+
test("a wearable fact write loses to a fact another writer committed first and writes nothing", async () => {
|
|
338
|
+
const { storage, dir } = makeStorage();
|
|
339
|
+
try {
|
|
340
|
+
await storage.ensureDirectories();
|
|
341
|
+
const fact = "Dana moved the review meeting to Thursday afternoon.";
|
|
342
|
+
const writer = createWearableMemoryWriter(storage);
|
|
343
|
+
const envelope = composeMemoryEnvelope(
|
|
344
|
+
{ content: fact, category: "fact", tags: ["wearable"] },
|
|
345
|
+
{ source: "wearable:limitless" },
|
|
346
|
+
);
|
|
347
|
+
await persistUnderFactDedupWindow(storage.dir, async () => false, () => storage.writeMemory("fact", fact));
|
|
348
|
+
|
|
349
|
+
const written = await writer.writeSealedMemory(envelope, { contentHashSource: fact, status: "active" });
|
|
350
|
+
|
|
351
|
+
assert.deepEqual(written, { duplicate: true });
|
|
352
|
+
const copies = (await storage.readAllMemories()).filter((memory) => memory.content.includes(fact));
|
|
353
|
+
assert.equal(copies.length, 1, "the corpus holds one copy of the fact");
|
|
354
|
+
} finally {
|
|
355
|
+
rmSync(dir, { recursive: true, force: true });
|
|
356
|
+
}
|
|
357
|
+
});
|
package/dist/chunk-2FGLEF7C.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
runLocationSync
|
|
3
|
-
} from "./chunk-SUSW74WU.js";
|
|
4
|
-
import {
|
|
5
|
-
log
|
|
6
|
-
} from "./chunk-3WCASNQZ.js";
|
|
7
|
-
|
|
8
|
-
// src/location/scheduler.ts
|
|
9
|
-
var LOCATION_SYNC_DEFAULT_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
10
|
-
function startLocationAutoSync(settings, deps = {}) {
|
|
11
|
-
const logger = deps.log ?? log;
|
|
12
|
-
const intervalMs = settings.intervalMs ?? LOCATION_SYNC_DEFAULT_INTERVAL_MS;
|
|
13
|
-
const setIntervalImpl = settings.setInterval ?? ((fn, ms) => setInterval(fn, ms));
|
|
14
|
-
const clearIntervalImpl = settings.clearInterval ?? ((handle) => clearInterval(handle));
|
|
15
|
-
const sync = deps.sync ?? ((deps2) => runLocationSync({ config: deps2.config, memoryDir: deps2.memoryDir }));
|
|
16
|
-
let inFlight = null;
|
|
17
|
-
let stopped = false;
|
|
18
|
-
const timer = setIntervalImpl(() => {
|
|
19
|
-
if (inFlight !== null || stopped) return;
|
|
20
|
-
inFlight = (async () => {
|
|
21
|
-
try {
|
|
22
|
-
const runs = await sync({ config: settings.config, memoryDir: settings.memoryDir });
|
|
23
|
-
const failed = runs.some((run) => run.results.some((result) => result.status === "failed"));
|
|
24
|
-
if (failed) {
|
|
25
|
-
logger.warn("location auto-sync: one or more sources failed \u2014 retrying on the next tick");
|
|
26
|
-
} else {
|
|
27
|
-
logger.info(
|
|
28
|
-
`location auto-sync: refreshed ${settings.config.syncDays}-day window across ${settings.config.sources.length} source(s)`
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
} catch (err) {
|
|
32
|
-
if (err instanceof Error && err.name === "AbortError") return;
|
|
33
|
-
logger.warn(
|
|
34
|
-
`location auto-sync failed: ${err instanceof Error ? err.name : "unknown error"} \u2014 retrying on the next tick`
|
|
35
|
-
);
|
|
36
|
-
} finally {
|
|
37
|
-
inFlight = null;
|
|
38
|
-
}
|
|
39
|
-
})();
|
|
40
|
-
}, intervalMs);
|
|
41
|
-
timer.unref?.();
|
|
42
|
-
return {
|
|
43
|
-
async tick() {
|
|
44
|
-
const runs = await sync({ config: settings.config, memoryDir: settings.memoryDir });
|
|
45
|
-
logger.info(
|
|
46
|
-
`location auto-sync tick: ${runs.length} day(s) across ${settings.config.sources.length} source(s)`
|
|
47
|
-
);
|
|
48
|
-
},
|
|
49
|
-
async stop() {
|
|
50
|
-
stopped = true;
|
|
51
|
-
clearIntervalImpl(timer);
|
|
52
|
-
await inFlight;
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export {
|
|
58
|
-
LOCATION_SYNC_DEFAULT_INTERVAL_MS,
|
|
59
|
-
startLocationAutoSync
|
|
60
|
-
};
|
|
61
|
-
//# sourceMappingURL=chunk-2FGLEF7C.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/location/scheduler.ts"],"sourcesContent":["/**\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":";;;;;;;;AAqBO,IAAM,oCAAoC,KAAK,KAAK,KAAK;AAwBzD,SAAS,sBACd,UACA,OAA6B,CAAC,GACN;AACxB,QAAM,SAAS,KAAK,OAAO;AAC3B,QAAM,aAAa,SAAS,cAAc;AAC1C,QAAM,kBAAkB,SAAS,gBAAgB,CAAC,IAAgB,OAAe,YAAY,IAAI,EAAE;AACnG,QAAM,oBAAoB,SAAS,kBAAkB,CAAC,WAAoB,cAAc,MAAwB;AAChH,QAAM,OACJ,KAAK,SACJ,CAACA,UACA,gBAAgB,EAAE,QAAQA,MAAK,QAAQ,WAAWA,MAAK,UAAU,CAAC;AACtE,MAAI,WAAiC;AACrC,MAAI,UAAU;AACd,QAAM,QAAQ,gBAAgB,MAAM;AAClC,QAAI,aAAa,QAAQ,QAAS;AAClC,gBAAY,YAAY;AACtB,UAAI;AACF,cAAM,OAAO,MAAM,KAAK,EAAE,QAAQ,SAAS,QAAQ,WAAW,SAAS,UAAU,CAAC;AAClF,cAAM,SAAS,KAAK,KAAK,CAAC,QAAQ,IAAI,QAAQ,KAAK,CAAC,WAAW,OAAO,WAAW,QAAQ,CAAC;AAC1F,YAAI,QAAQ;AACV,iBAAO,KAAK,iFAA4E;AAAA,QAC1F,OAAO;AACL,iBAAO;AAAA,YACL,iCAAiC,SAAS,OAAO,QAAQ,sBAAsB,SAAS,OAAO,QAAQ,MAAM;AAAA,UAC/G;AAAA,QACF;AAAA,MACF,SAAS,KAAK;AAEZ,YAAI,eAAe,SAAS,IAAI,SAAS,aAAc;AACvD,eAAO;AAAA,UACL,8BAA8B,eAAe,QAAQ,IAAI,OAAO,eAAe;AAAA,QACjF;AAAA,MACF,UAAE;AACA,mBAAW;AAAA,MACb;AAAA,IACF,GAAG;AAAA,EACL,GAAG,UAAU;AACb,QAAM,QAAQ;AACd,SAAO;AAAA,IACL,MAAM,OAAO;AACX,YAAM,OAAO,MAAM,KAAK,EAAE,QAAQ,SAAS,QAAQ,WAAW,SAAS,UAAU,CAAC;AAClF,aAAO;AAAA,QACL,4BAA4B,KAAK,MAAM,kBAAkB,SAAS,OAAO,QAAQ,MAAM;AAAA,MACzF;AAAA,IACF;AAAA,IACA,MAAM,OAAO;AACX,gBAAU;AACV,wBAAkB,KAAK;AACvB,YAAM;AAAA,IACR;AAAA,EACF;AACF;","names":["deps"]}
|