@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
package/dist/chunk-ID6JGIMD.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getOperation
|
|
3
|
-
} from "./chunk-ZTTCDQ4I.js";
|
|
4
|
-
import {
|
|
5
|
-
EngramAccessInputError
|
|
6
|
-
} from "./chunk-CJCTKL6Y.js";
|
|
7
|
-
|
|
8
|
-
// src/access-http-briefing.ts
|
|
9
|
-
async function respondBriefingHttp(options) {
|
|
10
|
-
const op = getOperation("briefing");
|
|
11
|
-
if (!op) {
|
|
12
|
-
throw new EngramAccessInputError("access-boundary: operation not registered: briefing");
|
|
13
|
-
}
|
|
14
|
-
const output = await op.run(options.gatedBody, {
|
|
15
|
-
service: options.service,
|
|
16
|
-
authenticatedPrincipal: options.principal,
|
|
17
|
-
abortSignal: options.abortSignal
|
|
18
|
-
});
|
|
19
|
-
options.respondJson(200, output.result);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export {
|
|
23
|
-
respondBriefingHttp
|
|
24
|
-
};
|
|
25
|
-
//# sourceMappingURL=chunk-ID6JGIMD.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/access-http-briefing.ts"],"sourcesContent":["import { getOperation } from \"./access-boundary.js\";\nimport { EngramAccessInputError, type EngramAccessService } from \"./access-service.js\";\n\ntype JsonResponder = (status: number, payload: unknown) => void;\n\nexport interface BriefingHttpOptions {\n /** Request body already run through the namespace gate (gatedBodyNamespace). */\n gatedBody: Record<string, unknown>;\n service: EngramAccessService;\n principal: string | undefined;\n respondJson: JsonResponder;\n /** HTTP request lifetime; aborting it stops the briefing (issue #3139). */\n abortSignal?: AbortSignal;\n}\n\n/**\n * POST /engram/v1/briefing — daily briefing over the access HTTP boundary.\n *\n * Dispatches the registered `briefing` boundary operation (the one the MCP\n * tool uses) so schema validation, namespace gating, and principal\n * propagation reach every HTTP briefing call — the same shape as the\n * coding/delta read dispatch. Pure read: nothing persists, so no\n * write-quota accounting. The operation result already carries both\n * renderings (markdown + json sections) in one response, letting thin\n * clients render without a CLI subprocess.\n */\nexport async function respondBriefingHttp(options: BriefingHttpOptions): Promise<void> {\n const op = getOperation(\"briefing\");\n if (!op) {\n throw new EngramAccessInputError(\"access-boundary: operation not registered: briefing\");\n }\n // SAFETY: the built-in briefing operation returns { result }; this adapter passes it through unchanged.\n const output = (await op.run(options.gatedBody, {\n service: options.service,\n authenticatedPrincipal: options.principal,\n abortSignal: options.abortSignal,\n })) as { result: unknown };\n options.respondJson(200, output.result);\n}\n"],"mappings":";;;;;;;;AA0BA,eAAsB,oBAAoB,SAA6C;AACrF,QAAM,KAAK,aAAa,UAAU;AAClC,MAAI,CAAC,IAAI;AACP,UAAM,IAAI,uBAAuB,qDAAqD;AAAA,EACxF;AAEA,QAAM,SAAU,MAAM,GAAG,IAAI,QAAQ,WAAW;AAAA,IAC9C,SAAS,QAAQ;AAAA,IACjB,wBAAwB,QAAQ;AAAA,IAChC,aAAa,QAAQ;AAAA,EACvB,CAAC;AACD,UAAQ,YAAY,KAAK,OAAO,MAAM;AACxC;","names":[]}
|
package/dist/chunk-IGLJNHOX.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
// src/access-mcp-frames.ts
|
|
2
|
-
var MCP_STDIO_MAX_FRAME_BYTES = 131072;
|
|
3
|
-
var TOO_LARGE = { kind: "too-large" };
|
|
4
|
-
var McpFrameReader = class {
|
|
5
|
-
buffer = Buffer.alloc(0);
|
|
6
|
-
/** Body bytes of a refused oversized frame that have not arrived yet. */
|
|
7
|
-
skipBytes = 0;
|
|
8
|
-
/** Buffer length when `next` last stopped on an incomplete frame. */
|
|
9
|
-
waitingBytes = 0;
|
|
10
|
-
push(chunk) {
|
|
11
|
-
const dropped = Math.min(this.skipBytes, chunk.length);
|
|
12
|
-
this.skipBytes -= dropped;
|
|
13
|
-
const kept = chunk.subarray(dropped);
|
|
14
|
-
if (kept.length > 0) this.buffer = Buffer.concat([this.buffer, kept]);
|
|
15
|
-
}
|
|
16
|
-
/** True when bytes arrived after `next` last examined the buffer. */
|
|
17
|
-
hasUnread() {
|
|
18
|
-
return this.buffer.length > this.waitingBytes;
|
|
19
|
-
}
|
|
20
|
-
next() {
|
|
21
|
-
this.waitingBytes = 0;
|
|
22
|
-
const headerEnd = this.buffer.indexOf("\r\n\r\n");
|
|
23
|
-
if (headerEnd < 0) {
|
|
24
|
-
if (this.buffer.length <= MCP_STDIO_MAX_FRAME_BYTES) return this.wait();
|
|
25
|
-
return this.discardBuffer();
|
|
26
|
-
}
|
|
27
|
-
if (headerEnd > MCP_STDIO_MAX_FRAME_BYTES) return this.discardBuffer();
|
|
28
|
-
const headers = this.buffer.subarray(0, headerEnd).toString("utf-8").split("\r\n");
|
|
29
|
-
const lengthHeader = headers.find((line) => line.toLowerCase().startsWith("content-length:"));
|
|
30
|
-
const contentLength = lengthHeader ? Number.parseInt(lengthHeader.split(":")[1]?.trim() ?? "0", 10) : NaN;
|
|
31
|
-
if (!Number.isFinite(contentLength) || contentLength < 0) {
|
|
32
|
-
this.buffer = Buffer.alloc(0);
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
const bodyStart = headerEnd + 4;
|
|
36
|
-
if (contentLength > MCP_STDIO_MAX_FRAME_BYTES) {
|
|
37
|
-
const rest = this.buffer.subarray(bodyStart);
|
|
38
|
-
this.skipBytes = Math.max(0, contentLength - rest.length);
|
|
39
|
-
this.buffer = rest.subarray(Math.min(contentLength, rest.length));
|
|
40
|
-
return TOO_LARGE;
|
|
41
|
-
}
|
|
42
|
-
const bodyEnd = bodyStart + contentLength;
|
|
43
|
-
if (this.buffer.length < bodyEnd) return this.wait();
|
|
44
|
-
const text = this.buffer.subarray(bodyStart, bodyEnd).toString("utf-8");
|
|
45
|
-
this.buffer = this.buffer.subarray(bodyEnd);
|
|
46
|
-
return { kind: "body", text };
|
|
47
|
-
}
|
|
48
|
-
wait() {
|
|
49
|
-
this.waitingBytes = this.buffer.length;
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
discardBuffer() {
|
|
53
|
-
this.buffer = Buffer.alloc(0);
|
|
54
|
-
return TOO_LARGE;
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export {
|
|
59
|
-
MCP_STDIO_MAX_FRAME_BYTES,
|
|
60
|
-
McpFrameReader
|
|
61
|
-
};
|
|
62
|
-
//# sourceMappingURL=chunk-IGLJNHOX.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/access-mcp-frames.ts"],"sourcesContent":["/**\n * Content-Length framing for the MCP stdio transport.\n *\n * `push` appends raw bytes. `next` returns one frame at a time and `null` when\n * the buffered bytes hold no complete frame yet. The header and the body are\n * each bounded to MCP_STDIO_MAX_FRAME_BYTES, which matches the HTTP access\n * default `maxBodyBytes`, so a peer cannot grow the buffer without limit.\n */\nexport const MCP_STDIO_MAX_FRAME_BYTES = 131072;\n\nexport type McpFrame = { kind: \"body\"; text: string } | { kind: \"too-large\" };\n\nconst TOO_LARGE: McpFrame = { kind: \"too-large\" };\n\nexport class McpFrameReader {\n private buffer = Buffer.alloc(0);\n /** Body bytes of a refused oversized frame that have not arrived yet. */\n private skipBytes = 0;\n /** Buffer length when `next` last stopped on an incomplete frame. */\n private waitingBytes = 0;\n\n push(chunk: Buffer): void {\n const dropped = Math.min(this.skipBytes, chunk.length);\n this.skipBytes -= dropped;\n const kept = chunk.subarray(dropped);\n if (kept.length > 0) this.buffer = Buffer.concat([this.buffer, kept]);\n }\n\n /** True when bytes arrived after `next` last examined the buffer. */\n hasUnread(): boolean {\n return this.buffer.length > this.waitingBytes;\n }\n\n next(): McpFrame | null {\n this.waitingBytes = 0;\n const headerEnd = this.buffer.indexOf(\"\\r\\n\\r\\n\");\n if (headerEnd < 0) {\n if (this.buffer.length <= MCP_STDIO_MAX_FRAME_BYTES) return this.wait();\n return this.discardBuffer();\n }\n if (headerEnd > MCP_STDIO_MAX_FRAME_BYTES) return this.discardBuffer();\n\n const headers = this.buffer.subarray(0, headerEnd).toString(\"utf-8\").split(\"\\r\\n\");\n const lengthHeader = headers.find((line) => line.toLowerCase().startsWith(\"content-length:\"));\n const contentLength = lengthHeader ? Number.parseInt(lengthHeader.split(\":\")[1]?.trim() ?? \"0\", 10) : NaN;\n if (!Number.isFinite(contentLength) || contentLength < 0) {\n this.buffer = Buffer.alloc(0);\n return null;\n }\n\n const bodyStart = headerEnd + 4;\n if (contentLength > MCP_STDIO_MAX_FRAME_BYTES) {\n const rest = this.buffer.subarray(bodyStart);\n this.skipBytes = Math.max(0, contentLength - rest.length);\n this.buffer = rest.subarray(Math.min(contentLength, rest.length));\n return TOO_LARGE;\n }\n const bodyEnd = bodyStart + contentLength;\n if (this.buffer.length < bodyEnd) return this.wait();\n const text = this.buffer.subarray(bodyStart, bodyEnd).toString(\"utf-8\");\n this.buffer = this.buffer.subarray(bodyEnd);\n return { kind: \"body\", text };\n }\n\n private wait(): null {\n this.waitingBytes = this.buffer.length;\n return null;\n }\n\n private discardBuffer(): McpFrame {\n this.buffer = Buffer.alloc(0);\n return TOO_LARGE;\n }\n}\n"],"mappings":";AAQO,IAAM,4BAA4B;AAIzC,IAAM,YAAsB,EAAE,MAAM,YAAY;AAEzC,IAAM,iBAAN,MAAqB;AAAA,EAClB,SAAS,OAAO,MAAM,CAAC;AAAA;AAAA,EAEvB,YAAY;AAAA;AAAA,EAEZ,eAAe;AAAA,EAEvB,KAAK,OAAqB;AACxB,UAAM,UAAU,KAAK,IAAI,KAAK,WAAW,MAAM,MAAM;AACrD,SAAK,aAAa;AAClB,UAAM,OAAO,MAAM,SAAS,OAAO;AACnC,QAAI,KAAK,SAAS,EAAG,MAAK,SAAS,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC;AAAA,EACtE;AAAA;AAAA,EAGA,YAAqB;AACnB,WAAO,KAAK,OAAO,SAAS,KAAK;AAAA,EACnC;AAAA,EAEA,OAAwB;AACtB,SAAK,eAAe;AACpB,UAAM,YAAY,KAAK,OAAO,QAAQ,UAAU;AAChD,QAAI,YAAY,GAAG;AACjB,UAAI,KAAK,OAAO,UAAU,0BAA2B,QAAO,KAAK,KAAK;AACtE,aAAO,KAAK,cAAc;AAAA,IAC5B;AACA,QAAI,YAAY,0BAA2B,QAAO,KAAK,cAAc;AAErE,UAAM,UAAU,KAAK,OAAO,SAAS,GAAG,SAAS,EAAE,SAAS,OAAO,EAAE,MAAM,MAAM;AACjF,UAAM,eAAe,QAAQ,KAAK,CAAC,SAAS,KAAK,YAAY,EAAE,WAAW,iBAAiB,CAAC;AAC5F,UAAM,gBAAgB,eAAe,OAAO,SAAS,aAAa,MAAM,GAAG,EAAE,CAAC,GAAG,KAAK,KAAK,KAAK,EAAE,IAAI;AACtG,QAAI,CAAC,OAAO,SAAS,aAAa,KAAK,gBAAgB,GAAG;AACxD,WAAK,SAAS,OAAO,MAAM,CAAC;AAC5B,aAAO;AAAA,IACT;AAEA,UAAM,YAAY,YAAY;AAC9B,QAAI,gBAAgB,2BAA2B;AAC7C,YAAM,OAAO,KAAK,OAAO,SAAS,SAAS;AAC3C,WAAK,YAAY,KAAK,IAAI,GAAG,gBAAgB,KAAK,MAAM;AACxD,WAAK,SAAS,KAAK,SAAS,KAAK,IAAI,eAAe,KAAK,MAAM,CAAC;AAChE,aAAO;AAAA,IACT;AACA,UAAM,UAAU,YAAY;AAC5B,QAAI,KAAK,OAAO,SAAS,QAAS,QAAO,KAAK,KAAK;AACnD,UAAM,OAAO,KAAK,OAAO,SAAS,WAAW,OAAO,EAAE,SAAS,OAAO;AACtE,SAAK,SAAS,KAAK,OAAO,SAAS,OAAO;AAC1C,WAAO,EAAE,MAAM,QAAQ,KAAK;AAAA,EAC9B;AAAA,EAEQ,OAAa;AACnB,SAAK,eAAe,KAAK,OAAO;AAChC,WAAO;AAAA,EACT;AAAA,EAEQ,gBAA0B;AAChC,SAAK,SAAS,OAAO,MAAM,CAAC;AAC5B,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/local-llm.ts"],"sourcesContent":["import { log } from \"./logger.js\";\nimport { isUnsupportedJsonSchemaError } from \"./fallback-llm-codex-subscription.js\";\nimport { estimateTokenCount } from \"./token-estimate.js\";\nimport type { PluginConfig } from \"./types.js\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport type { ModelRegistry } from \"./model-registry.js\";\nimport { launchProcessSync } from \"./runtime/child-process.js\";\nimport { mergeEnv, readEnvVar } from \"./runtime/env.js\";\nimport { resolveLocalLlmCapabilities } from \"./capabilities.js\";\nimport { resolvePipelineProcessingCapabilities } from \"./capabilities.js\";\nimport { ChatTransport } from \"./local-llm-transport.js\";\nimport { LocalLlmRateLimitSession, resolveRateLimitSettings } from \"./local-llm-rate-limit.js\";\nimport {\n extractNonRecoverableBackendReason,\n extractNonRecoverableBackendReasonFromErrorText,\n isAbortError,\n probeFetch,\n resolveUnavailableVerdict,\n type ProbeFetchResult,\n normalizeBackendTripReason,\n waitForRetryBackoff,\n SingleFlightProbe,\n type LocalLlmChatCompletionOptions,\n type LocalLlmChatCompletionResult,\n} from \"./local-llm-helpers.js\";\nimport { NativeSchemaGate, type LocalLlmChatRequestBody } from \"./local-llm-native-schema.js\";\nimport {\n isObjectRecord,\n orderedLocalServers,\n type LocalLlmType,\n} from \"./local-llm-servers.js\";\n\nexport type { LocalLlmType } from \"./local-llm-servers.js\";\n\n/** Trim trailing slash characters without backtracking regex. */\nfunction trimTrailingSlashes(s: string): string {\n let end = s.length;\n while (end > 0 && s[end - 1] === \"/\") end--;\n return s.substring(0, end);\n}\n\nfunction stripTrailingV1Path(s: string): string {\n return s.endsWith(\"/v1\") ? s.slice(0, -3) : s;\n}\n\nfunction isLlamaCppPropsResponse(value: unknown): boolean {\n return (\n isObjectRecord(value) &&\n isObjectRecord(value.default_generation_settings) &&\n typeof value.total_slots === \"number\" &&\n (\n typeof value.model_path === \"string\" ||\n typeof value.chat_template === \"string\" ||\n typeof value.build_info === \"string\"\n )\n );\n}\n\nfunction isLlamaCppModelsResponse(value: unknown): boolean {\n if (!isObjectRecord(value) || !Array.isArray(value.data)) {\n return false;\n }\n return value.data.some((entry) => {\n if (!isObjectRecord(entry)) return false;\n if (entry.owned_by === \"llamacpp\") return true;\n if (typeof entry.id === \"string\" && entry.id.endsWith(\".gguf\")) return true;\n const meta = entry.meta;\n return (\n isObjectRecord(meta) &&\n (\"n_ctx_train\" in meta || \"n_params\" in meta || \"vocab_type\" in meta)\n );\n });\n}\n\nfunction isLmStudioApiV1ModelsResponse(value: unknown): boolean {\n if (!isObjectRecord(value) || !Array.isArray(value.models)) {\n return false;\n }\n return value.models.some((entry) => {\n if (!isObjectRecord(entry)) return false;\n return (\n typeof entry.key === \"string\" &&\n typeof entry.display_name === \"string\" &&\n (\n typeof entry.format === \"string\" ||\n typeof entry.max_context_length === \"number\" ||\n Array.isArray(entry.loaded_instances)\n )\n );\n });\n}\n\nfunction isLmStudioApiV0ModelsResponse(value: unknown): boolean {\n if (!isObjectRecord(value) || !Array.isArray(value.data)) {\n return false;\n }\n return value.data.some((entry) => {\n if (!isObjectRecord(entry)) return false;\n return (\n typeof entry.id === \"string\" &&\n typeof entry.publisher === \"string\" &&\n (\n typeof entry.compatibility_type === \"string\" ||\n typeof entry.max_context_length === \"number\" ||\n typeof entry.state === \"string\"\n )\n );\n });\n}\n\nfunction isLmStudioNativeModelsResponse(value: unknown): boolean {\n return isLmStudioApiV1ModelsResponse(value) || isLmStudioApiV0ModelsResponse(value);\n}\n\n/**\n * Backends known to honor `chat_template_kwargs: { enable_thinking: false }`\n * on OpenAI-compatible `/v1/chat/completions`. LM Studio, vLLM, and\n * llama.cpp forward this field to the jinja chat template, where thinking-capable\n * models (Qwen 3.5, Gemma 4, DeepSeek) suppress reasoning tokens.\n *\n * Strict OpenAI-compatible backends (standard OpenAI, Azure OpenAI, some\n * proxies) reject unknown request fields with 400 — which trips the\n * `localLlm400*` cooldown path. `LocalLlmClient` therefore only injects\n * the kwarg when the detected backend is in this set; unknown / `generic`\n * / `ollama` / `mlx` fail open (no injection, no 400 risk). Issue #548.\n */\nconst THINKING_COMPATIBLE_BACKENDS: ReadonlySet<LocalLlmType> = new Set([\n \"lmstudio\",\n \"vllm\",\n \"llamacpp\",\n]);\n\nconst THINKING_SUPPRESSED_OPERATIONS: ReadonlySet<string> = new Set([\n \"extraction\",\n \"extraction-judge\",\n \"extraction-faithfulness\",\n \"contradiction-judge\",\n \"contradiction_verification\",\n \"link_suggestion\",\n \"memory_summarization\",\n \"proactive_extraction\",\n \"lcm-summarize\",\n \"day_summary\",\n \"hourly_summary\",\n \"hourly_summary_extended\",\n \"correction-classify\",\n]);\n\nexport interface LocalModelInfo {\n id: string;\n contextWindow?: number;\n maxTokens?: number;\n}\n\nexport type LocalLlmRequestPriority = \"recall-critical\" | \"background\";\n\n// LocalLlmChatCompletionOptions and LocalLlmChatCompletionResult live in\n// local-llm-helpers.ts so this file stays under its size ceiling (#1995).\ninterface LocalLlmQueuedRequest {\n messages: Array<{ role: string; content: string }>;\n options: LocalLlmChatCompletionOptions;\n priority: LocalLlmRequestPriority;\n enqueuedAtMs: number;\n resolve: (value: LocalLlmChatCompletionResult | null) => void;\n}\n\nconst LOCAL_LLM_GLOBAL_BACKEND_STATE = \"__openclawEngramLocalLlmBackendState\";\n\ntype LocalLlmBackendState = {\n untilMs: number;\n reason: string;\n};\n/**\n * Local LLM client for OpenAI-compatible endpoints (LM Studio, Ollama, MLX, etc.)\n *\n * Based on openclaw-tactician's provider detection patterns for consistency.\n * Provides privacy-preserving, cost-effective LLM operations with\n * graceful fallback to cloud providers when local LLM is unavailable.\n */\nexport class LocalLlmClient {\n private config: PluginConfig;\n private isAvailable: boolean | null = null;\n private lastHealthCheck: number = 0;\n private readonly availabilityProbe = new SingleFlightProbe();\n private detectedType: LocalLlmType | null = null;\n private cachedModelInfo: LocalModelInfo | null = null;\n private cachedLmsContext: number | null = null;\n private lastLmsCheck: number = 0;\n private consecutive400s: number = 0;\n private cooldownUntilMs: number = 0;\n private modelRegistry?: ModelRegistry;\n private readonly nativeSchemaGate = new NativeSchemaGate(() => this.modelRegistry);\n private _disableThinking: boolean = false;\n private readonly requestQueues: Record<LocalLlmRequestPriority, LocalLlmQueuedRequest[]> = {\n \"recall-critical\": [],\n background: [],\n };\n private readonly queueProcessing = new Set<LocalLlmRequestPriority>();\n private queueDrainScheduled: boolean = false;\n private static readonly HEALTH_CHECK_INTERVAL_MS = 60000; // 1 minute\n private static readonly LMS_CACHE_INTERVAL_MS = 30000; // 30 seconds\n private readonly chatTransport = new ChatTransport();\n\n constructor(config: PluginConfig, modelRegistry?: ModelRegistry) {\n this.config = config;\n this.modelRegistry = modelRegistry;\n }\n\n /**\n * Request thinking/reasoning suppression on the next chat completion.\n *\n * When `true`, the client will inject\n * `chat_template_kwargs: { enable_thinking: false }` into the request\n * body — **but only when the detected backend is known to support it**\n * (LM Studio, vLLM; see `THINKING_COMPATIBLE_BACKENDS`). Strict\n * OpenAI-compat backends reject unknown fields with 400; on those the\n * client fails open (thinking runs normally). This is the safe\n * default for Remnic extraction-style operations: measurable latency\n * win on thinking-capable backends, zero risk on others. Consolidation\n * operations keep thinking enabled unless explicitly added to\n * `THINKING_SUPPRESSED_OPERATIONS`. Issues #548 and #979.\n */\n set disableThinking(value: boolean) {\n this._disableThinking = value;\n }\n\n private resolveHomeDir(): string {\n return this.config.localLlmHomeDir || readEnvVar(\"HOME\") || os.homedir();\n }\n\n private buildRequestHeaders(base: Record<string, string> = {}): Record<string, string> {\n const headers: Record<string, string> = {\n ...base,\n ...(this.config.localLlmHeaders ?? {}),\n };\n if (this.config.localLlmApiKey && this.config.localLlmAuthHeader !== false) {\n headers.Authorization = `Bearer ${this.config.localLlmApiKey}`;\n }\n return headers;\n }\n\n\n /**\n * Set the ModelRegistry for caching detected capabilities\n */\n setModelRegistry(registry: ModelRegistry): void {\n this.modelRegistry = registry;\n }\n\n /**\n * Get the detected server type (null if not detected)\n */\n getDetectedType(): LocalLlmType | null {\n return this.detectedType;\n }\n\n private getBackendKey(): string {\n return trimTrailingSlashes(\n this.config.localLlmUrl.replace(\"localhost\", \"127.0.0.1\"),\n ).replace(/\\/v1$/, \"\");\n }\n\n private getGlobalBackendState(): Map<string, LocalLlmBackendState> {\n const globalAny = globalThis as typeof globalThis & {\n [LOCAL_LLM_GLOBAL_BACKEND_STATE]?: Map<string, LocalLlmBackendState>;\n };\n if (!globalAny[LOCAL_LLM_GLOBAL_BACKEND_STATE]) {\n globalAny[LOCAL_LLM_GLOBAL_BACKEND_STATE] = new Map();\n }\n return globalAny[LOCAL_LLM_GLOBAL_BACKEND_STATE];\n }\n\n private getTrippedBackendState(now: number): LocalLlmBackendState | null {\n const state = this.getGlobalBackendState().get(this.getBackendKey()) ?? null;\n if (!state) return null;\n if (state.untilMs <= now) {\n this.getGlobalBackendState().delete(this.getBackendKey());\n this.lastHealthCheck = 0;\n return null;\n }\n return state;\n }\n\n private markBackendUnavailable(reason: string, durationMs: number): void {\n const normalizedReason = normalizeBackendTripReason(reason);\n if (durationMs > 0) {\n const untilMs = Date.now() + durationMs;\n this.getGlobalBackendState().set(this.getBackendKey(), { untilMs, reason: normalizedReason });\n } else {\n this.getGlobalBackendState().delete(this.getBackendKey());\n }\n this.isAvailable = false;\n this.lastHealthCheck = 0;\n log.warn(\n `local LLM backend unavailable for ${durationMs}ms: model=${this.config.localLlmModel} reason=${normalizedReason}`,\n );\n }\n\n\n /**\n * Fetch with timeout for health checks. Body lives in the sibling helper so\n * this file stays under its size ceiling (issue #1995).\n */\n private async fetchWithTimeout(\n url: string,\n timeoutMs: number = 2000,\n headers?: Record<string, string>,\n signal?: AbortSignal,\n ): Promise<ProbeFetchResult> {\n return await probeFetch(url, {\n timeoutMs,\n headers: this.buildRequestHeaders({ Accept: \"application/json\", ...(headers ?? {}) }),\n signal,\n });\n }\n\n private async probeLmStudioNativeModels(\n probeBaseUrl: string,\n signal?: AbortSignal,\n ): Promise<{ matched: boolean; unauthorized: boolean }> {\n let unauthorized = false;\n for (const endpoint of [\"/api/v1/models\", \"/api/v0/models\"]) {\n if (signal?.aborted) return { matched: false, unauthorized };\n const probe = await this.fetchWithTimeout(`${probeBaseUrl}${endpoint}`, 2000, undefined, signal);\n if (signal?.aborted) return { matched: false, unauthorized };\n if (probe.ok && isLmStudioNativeModelsResponse(probe.data)) {\n return { matched: true, unauthorized };\n }\n if (probe.status === 401 || probe.status === 403) {\n unauthorized = true;\n }\n }\n return { matched: false, unauthorized };\n }\n\n /**\n * Check if local LLM is available\n * Uses 127.0.0.1 instead of localhost to avoid DNS issues (consistent with tactician)\n */\n async checkAvailability(signal?: AbortSignal): Promise<boolean> {\n if (signal?.aborted) return false;\n // Cache health check results for 1 minute\n const now = Date.now();\n const trippedState = this.getTrippedBackendState(now);\n if (trippedState) {\n this.isAvailable = false;\n this.lastHealthCheck = 0;\n log.info(\n `local LLM availability: backend circuit open for ${Math.max(0, trippedState.untilMs - now)}ms (${trippedState.reason})`,\n );\n return false;\n }\n if (this.isAvailable !== null && now - this.lastHealthCheck < LocalLlmClient.HEALTH_CHECK_INTERVAL_MS) {\n return this.isAvailable;\n }\n return await this.availabilityProbe.run((probeSignal) => this.probeAvailability(probeSignal), signal);\n }\n\n private async probeAvailability(signal?: AbortSignal): Promise<boolean> {\n const now = Date.now();\n // Probe server-native endpoints from the server root even when users configure\n // the OpenAI-compatible `/v1` base URL for chat completions.\n const configuredBaseUrl = trimTrailingSlashes(\n this.config.localLlmUrl.replace(\"localhost\", \"127.0.0.1\"),\n );\n const probeBaseUrl = stripTrailingV1Path(configuredBaseUrl);\n let sawUnauthorizedProbe = false;\n // A probe that TIMED OUT says the event loop was busy, not that the backend\n // is down (issue #2210). Tracked separately so a loaded daemon cannot cache\n // itself into a blackout.\n let sawAbortedProbe = false;\n // Several server shapes share a probe URL (LiteLLM and Ollama both use\n // `/`); fetch each URL once per pass.\n const probed = new Map<string, ProbeFetchResult>();\n const probeOnce = async (url: string): Promise<ProbeFetchResult> => {\n const cached = probed.get(url);\n if (cached) return cached;\n const result = await this.fetchWithTimeout(url, 2000, undefined, signal);\n probed.set(url, result);\n return result;\n };\n\n // Try to detect which server type is running\n if (signal?.aborted) return false;\n for (const serverConfig of orderedLocalServers(configuredBaseUrl)) {\n const healthUrl = `${probeBaseUrl}${serverConfig.healthEndpoint}`;\n log.debug(`checking ${serverConfig.type} at ${healthUrl}`);\n\n const result = await probeOnce(healthUrl);\n if (result.aborted) sawAbortedProbe = true;\n if (signal?.aborted) return false;\n if (result.ok && serverConfig.detectFn(result.data)) {\n if (serverConfig.type === \"mlx\") {\n const lmStudioProbe = await this.probeLmStudioNativeModels(probeBaseUrl, signal);\n if (signal?.aborted) return false;\n if (lmStudioProbe.unauthorized) {\n sawUnauthorizedProbe = true;\n }\n if (lmStudioProbe.matched) {\n this.isAvailable = true;\n this.detectedType = \"lmstudio\";\n this.lastHealthCheck = now;\n log.info(`detected lmstudio at ${configuredBaseUrl}`);\n return true;\n }\n }\n if (serverConfig.type === \"llamacpp\") {\n let sawLlamaCppSignal = false;\n const propsProbe = await this.fetchWithTimeout(`${probeBaseUrl}/props`, 2000, undefined, signal);\n if (propsProbe.aborted) sawAbortedProbe = true;\n if (signal?.aborted) return false;\n if (propsProbe.ok && isLlamaCppPropsResponse(propsProbe.data)) {\n sawLlamaCppSignal = true;\n }\n if (propsProbe.status === 401 || propsProbe.status === 403) {\n sawUnauthorizedProbe = true;\n }\n\n const modelsUrl = `${probeBaseUrl}${serverConfig.modelsEndpoint}`;\n const modelsProbe = await this.fetchWithTimeout(modelsUrl, 2000, undefined, signal);\n if (modelsProbe.aborted) sawAbortedProbe = true;\n if (signal?.aborted) return false;\n if (modelsProbe.ok && isLlamaCppModelsResponse(modelsProbe.data)) {\n sawLlamaCppSignal = true;\n }\n if (modelsProbe.status === 401 || modelsProbe.status === 403) {\n sawUnauthorizedProbe = true;\n continue;\n }\n\n const authConfigured =\n Boolean(this.config.localLlmApiKey) &&\n this.config.localLlmAuthHeader !== false;\n if (!sawLlamaCppSignal || (authConfigured && !modelsProbe.ok)) {\n continue;\n }\n }\n this.isAvailable = true;\n this.detectedType = serverConfig.type;\n this.lastHealthCheck = now;\n log.info(`detected ${serverConfig.type} at ${configuredBaseUrl}`);\n return true;\n }\n if (result.status === 401 || result.status === 403) {\n sawUnauthorizedProbe = true;\n }\n }\n if (signal?.aborted) return false;\n\n // Generic check if specific detection failed\n try {\n const modelsUrl = `${probeBaseUrl}/v1/models`;\n const result = await this.fetchWithTimeout(modelsUrl, 2000, undefined, signal);\n if (result.aborted) sawAbortedProbe = true;\n if (signal?.aborted) return false;\n if (result.ok) {\n this.isAvailable = true;\n this.detectedType = \"generic\";\n this.lastHealthCheck = now;\n log.info(`detected generic OpenAI-compatible server at ${configuredBaseUrl}`);\n return true;\n }\n if (result.status === 401 || result.status === 403) {\n sawUnauthorizedProbe = true;\n }\n } catch {\n // Fall through to unavailable\n }\n\n const wasAvailable = this.isAvailable;\n this.detectedType = null;\n if (sawUnauthorizedProbe) {\n log.warn(\n `local LLM availability probe was unauthorized at ${configuredBaseUrl}; verify localLlmApiKey and localLlmAuthHeader settings`,\n );\n }\n const verdict = resolveUnavailableVerdict({\n baseUrl: configuredBaseUrl,\n sawAbortedProbe,\n wasAvailable,\n });\n this.isAvailable = verdict.cacheVerdict ? false : null;\n this.lastHealthCheck = verdict.cacheVerdict ? now : 0;\n if (verdict.warning) log.warn(verdict.warning);\n log.debug(\"local LLM not available at\", configuredBaseUrl);\n return false;\n }\n\n /**\n * Try to get context window from LM Studio settings.json as fallback.\n * This reads the defaultContextLength setting which is what LM Studio uses\n * when loading models without explicit context configuration.\n */\n private getContextFromLmStudioSettings(): number | null {\n try {\n const homeDir = this.resolveHomeDir();\n const settingsPath = `${homeDir}/.cache/lm-studio/settings.json`;\n\n if (!fs.existsSync(settingsPath)) {\n log.debug(`LM Studio settings: file not found at ${settingsPath}`);\n return null;\n }\n\n const content = fs.readFileSync(settingsPath, \"utf-8\");\n const settings = JSON.parse(content) as {\n defaultContextLength?: {\n type?: string;\n value?: number;\n };\n };\n\n if (settings.defaultContextLength?.value) {\n const contextWindow = settings.defaultContextLength.value;\n log.debug(`LM Studio settings: found default context length: ${contextWindow}`);\n return contextWindow;\n }\n\n return null;\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n log.debug(`LM Studio settings: failed to read - ${errorMsg}`);\n return null;\n }\n }\n\n /**\n * Try to get context window from LMS CLI (LM Studio specific).\n * Uses --json flag for reliable parsing.\n * Returns null if LMS CLI is not available or model not found.\n */\n private getContextFromLmsCli(modelId: string): number | null {\n try {\n // Check if lms CLI exists in common locations.\n // HOME may be absent in launchd environments, so prefer the resolved helper.\n const homeDir = this.resolveHomeDir();\n const lmsPaths = [\n this.config.localLmsCliPath || \"\",\n `${homeDir}/.cache/lm-studio/bin/lms`,\n \"/usr/local/bin/lms\",\n \"/opt/homebrew/bin/lms\",\n ];\n\n const lmsPath = lmsPaths.find((p) => p.length > 0 && fs.existsSync(p));\n if (!lmsPath) {\n log.debug(`LMS CLI: not found in standard locations (checked: ${lmsPaths.join(\", \")})`);\n return null;\n }\n\n // Run lms ps --json to get loaded models with context\n // Use spawnSync with shell and explicit PATH to ensure lms can find its dependencies\n log.debug(`LMS CLI: running: ${lmsPath} ps --json`);\n const existingPath = readEnvVar(\"PATH\") || \"\";\n const result = launchProcessSync(lmsPath, [\"ps\", \"--json\"], {\n encoding: \"utf-8\",\n timeout: 5000,\n shell: false, // Don't use shell for JSON output - more reliable\n env: mergeEnv({\n PATH: `${this.config.localLmsBinDir || `${homeDir}/.cache/lm-studio/bin`}:/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:${existingPath}`,\n HOME: homeDir,\n }),\n });\n\n if (result.error) {\n log.debug(`LMS CLI: spawn error - ${result.error.message}`);\n return null;\n }\n\n if (result.stderr && result.stderr.trim()) {\n log.debug(`LMS CLI: stderr - ${result.stderr.slice(0, 200)}`);\n }\n\n const output = result.stdout || \"\";\n if (!output.trim()) {\n log.debug(\"LMS CLI: empty output - LM Studio may not be running or no models loaded\");\n return null;\n }\n\n // Parse JSON output\n let models: Array<{\n identifier?: string;\n modelKey?: string;\n contextLength?: number;\n maxContextLength?: number;\n }>;\n\n try {\n models = JSON.parse(output) as typeof models;\n } catch (parseErr) {\n log.debug(`LMS CLI: JSON parse error - ${parseErr}`);\n return null;\n }\n\n if (!Array.isArray(models) || models.length === 0) {\n log.debug(\"LMS CLI: no models loaded\");\n return null;\n }\n\n // Find the model matching our configured model ID\n const model = models.find((m) =>\n m.identifier === modelId ||\n m.modelKey === modelId ||\n (m.identifier?.includes(modelId.replace(/@\\d+bit$/, \"\")))\n );\n\n if (!model) {\n log.debug(`LMS CLI: model \"${modelId}\" not found in loaded models: ${models.map(m => m.identifier).join(\", \")}`);\n return null;\n }\n\n // Use contextLength (actual configured) or fall back to maxContextLength (model max)\n const contextWindow = model.contextLength || model.maxContextLength;\n\n if (contextWindow) {\n log.info(`LMS CLI detected context window: ${contextWindow} for ${modelId} (max: ${model.maxContextLength})`);\n return contextWindow;\n }\n\n return null;\n } catch (err) {\n // LMS CLI not available or failed\n const errorMsg = err instanceof Error ? err.message : String(err);\n log.debug(`LMS CLI: failed - ${errorMsg}`);\n return null;\n }\n }\n\n /**\n * Get full model info from LMS CLI including context length and max context length.\n * Returns null if LMS CLI is unavailable or model not found.\n */\n private getLmsModelInfo(modelId: string): { contextLength: number; maxContextLength: number; identifier: string } | null {\n try {\n const result = launchProcessSync(\"lms\", [\"ps\", \"--json\"], {\n encoding: \"utf-8\",\n timeout: 5000,\n shell: false,\n });\n\n if (result.error) {\n return null;\n }\n\n const output = result.stdout || \"\";\n if (!output.trim()) {\n return null;\n }\n\n let models: Array<{\n identifier?: string;\n modelKey?: string;\n contextLength?: number;\n maxContextLength?: number;\n }>;\n\n try {\n models = JSON.parse(output) as typeof models;\n } catch {\n return null;\n }\n\n if (!Array.isArray(models) || models.length === 0) {\n return null;\n }\n\n const model = models.find((m) =>\n m.identifier === modelId ||\n m.modelKey === modelId ||\n (m.identifier?.includes(modelId.replace(/@\\d+bit$/, \"\")))\n );\n\n if (!model || !model.contextLength) {\n return null;\n }\n\n return {\n contextLength: model.contextLength,\n maxContextLength: model.maxContextLength || model.contextLength,\n identifier: model.identifier || modelId,\n };\n } catch {\n return null;\n }\n }\n\n /**\n * Get context window for the configured model, using cache if available.\n * This method caches the result to avoid repeated LMS CLI calls.\n * Order: ModelRegistry (persistent) -> memory cache -> LMS CLI -> settings.json\n */\n getCachedContextWindow(modelId: string): number | null {\n const now = Date.now();\n\n // 1. Check ModelRegistry for persisted context window\n if (this.modelRegistry) {\n const caps = this.modelRegistry.getCapabilities(modelId);\n if (caps.source === \"lmstudio\" && caps.contextWindow) {\n log.debug(`ModelRegistry: using persisted LM Studio context: ${caps.contextWindow}`);\n // Also update memory cache\n this.cachedLmsContext = caps.contextWindow;\n this.lastLmsCheck = now;\n return caps.contextWindow;\n }\n }\n\n // 2. Return in-memory cached value if still valid\n if (this.cachedLmsContext && now - this.lastLmsCheck < LocalLlmClient.LMS_CACHE_INTERVAL_MS) {\n log.debug(`LMS CLI: returning in-memory cached context: ${this.cachedLmsContext}`);\n return this.cachedLmsContext;\n }\n\n // 3. Try LMS CLI (authoritative source)\n const lmsInfo = this.getLmsModelInfo(modelId);\n if (lmsInfo?.contextLength) {\n this.cachedLmsContext = lmsInfo.contextLength;\n this.lastLmsCheck = now;\n // Calculate appropriate output tokens based on context size\n // Use 12.5% of context window, capped at 16K (generous but safe)\n const calculatedOutputTokens = Math.min(Math.floor(lmsInfo.contextLength / 8), 16384);\n const outputTokens = Math.max(calculatedOutputTokens, 4096); // Minimum 4K\n // Persist to ModelRegistry with detected capabilities\n if (this.modelRegistry) {\n this.modelRegistry.setCapabilities(modelId, {\n maxPositionEmbeddings: lmsInfo.maxContextLength || lmsInfo.contextLength,\n contextWindow: lmsInfo.contextLength,\n supportsExtendedContext: (lmsInfo.maxContextLength || lmsInfo.contextLength) > 65536,\n typicalOutputTokens: outputTokens,\n source: \"lmstudio\",\n });\n log.info(`LMS CLI: Stored capabilities for ${modelId}: ${lmsInfo.contextLength} context, ${outputTokens} output tokens`);\n }\n return lmsInfo.contextLength;\n }\n\n // Legacy: Try LMS CLI context only (fallback)\n const legacyContext = this.getContextFromLmsCli(modelId);\n if (legacyContext) {\n this.cachedLmsContext = legacyContext;\n this.lastLmsCheck = now;\n // Persist to ModelRegistry with calculated output tokens\n if (this.modelRegistry) {\n const calculatedOutputTokens = Math.min(Math.floor(legacyContext / 8), 16384);\n const outputTokens = Math.max(calculatedOutputTokens, 4096);\n this.modelRegistry.setCapabilities(modelId, {\n maxPositionEmbeddings: legacyContext,\n contextWindow: legacyContext,\n supportsExtendedContext: false,\n typicalOutputTokens: outputTokens,\n source: \"lmstudio\",\n });\n }\n return legacyContext;\n }\n\n // 4. Fall back to LM Studio settings.json\n const settingsContext = this.getContextFromLmStudioSettings();\n if (settingsContext) {\n log.info(`LM Studio settings: using default context: ${settingsContext}`);\n this.cachedLmsContext = settingsContext;\n this.lastLmsCheck = now;\n return settingsContext;\n }\n\n return null;\n }\n\n /**\n * Clear the LMS context cache. Call this when the model changes.\n */\n clearContextCache(): void {\n this.cachedLmsContext = null;\n this.lastLmsCheck = 0;\n log.debug(\"LMS CLI: context cache cleared\");\n }\n\n private remainingCooldownMs(now: number = Date.now()): number {\n return Math.max(0, this.cooldownUntilMs - now);\n }\n\n private scheduleQueueDrain(): void {\n if (this.queueDrainScheduled) return;\n this.queueDrainScheduled = true;\n\n queueMicrotask(() => {\n this.queueDrainScheduled = false;\n this.startAvailableQueuedRequests();\n });\n }\n\n /** True when a background-priority request occupies or awaits this client's local lane (issue #2011). */\n isBackgroundLaneContended(): boolean {\n return this.queueProcessing.has(\"background\") || this.requestQueues.background.length > 0;\n }\n\n private dequeueQueuedRequest(priority: LocalLlmRequestPriority): LocalLlmQueuedRequest | null {\n const next = this.requestQueues[priority].shift();\n return next ?? null;\n }\n\n private failOpenQueuedRequestsForCooldown(): number {\n let dropped = 0;\n for (const priority of [\"recall-critical\", \"background\"] as const) {\n while (this.requestQueues[priority].length > 0) {\n const queued = this.requestQueues[priority].shift();\n queued?.resolve(null);\n dropped += 1;\n }\n }\n return dropped;\n }\n\n private startAvailableQueuedRequests(): void {\n if (!this.queueProcessing.has(\"recall-critical\")) {\n const nextCritical = this.dequeueQueuedRequest(\"recall-critical\");\n if (nextCritical) {\n this.queueProcessing.add(\"recall-critical\");\n void this.runQueuedRequest(nextCritical);\n }\n }\n\n if (!this.queueProcessing.has(\"background\")) {\n const nextBackground = this.dequeueQueuedRequest(\"background\");\n if (nextBackground) {\n this.queueProcessing.add(\"background\");\n void this.runQueuedRequest(nextBackground);\n }\n }\n }\n\n private async runQueuedRequest(next: LocalLlmQueuedRequest): Promise<void> {\n try {\n if (next.options.signal?.aborted) {\n next.resolve(null);\n return;\n }\n const remainingCooldownMs = this.remainingCooldownMs();\n if (remainingCooldownMs > 0) {\n const additionalDropped = this.failOpenQueuedRequestsForCooldown();\n log.warn(\n `local LLM: cooldown active (${remainingCooldownMs}ms remaining), dropping ${additionalDropped + 1} queued request(s) fail-open`,\n );\n next.resolve(null);\n return;\n }\n\n let result: LocalLlmChatCompletionResult | null = null;\n try {\n result = await this.runChatCompletionRequest(next.messages, next.options, {\n priority: next.priority,\n enqueuedAtMs: next.enqueuedAtMs,\n });\n } catch (err) {\n log.warn(`local LLM queue drain failed open: ${next.options.redactProviderErrors ? \"provider error details redacted\" : err instanceof Error ? err.message : String(err)}`);\n }\n next.resolve(result);\n } finally {\n this.queueProcessing.delete(next.priority);\n if (this.requestQueues[\"recall-critical\"].length > 0 || this.requestQueues.background.length > 0) {\n this.scheduleQueueDrain();\n }\n }\n }\n\n private async runChatCompletionRequest(\n messages: Array<{ role: string; content: string }>,\n options: LocalLlmChatCompletionOptions,\n queueMeta?: { priority: LocalLlmRequestPriority; enqueuedAtMs: number },\n ): Promise<LocalLlmChatCompletionResult | null> {\n log.debug(\n `local LLM chatCompletion: localLlmEnabled=${resolveLocalLlmCapabilities(this.config).localLlm}, model=${this.config.localLlmModel}`,\n );\n\n const operation = options.operation ?? \"unspecified\";\n const startedAtMs = Date.now();\n if (queueMeta) {\n log.debug(\n `local LLM queue start: priority=${queueMeta.priority} waitMs=${startedAtMs - queueMeta.enqueuedAtMs} op=${operation}`,\n );\n }\n\n try {\n if (options.signal?.aborted) return null;\n const isAvailable = await this.checkAvailability(options.signal);\n if (!isAvailable) {\n log.debug(\n `local LLM: checkAvailability returned false for ${this.config.localLlmUrl}`,\n );\n return null;\n }\n\n const promptChars = messages.reduce((sum, m) => sum + (m.content?.length ?? 0), 0);\n const requestModel = options.model ?? this.config.localLlmModel;\n const requestBody: LocalLlmChatRequestBody = {\n model: requestModel,\n messages,\n temperature: options.temperature ?? 0.7,\n // Use max_tokens consistent with cloud models\n max_tokens: options.maxTokens ?? 4096,\n stream: false,\n };\n this.nativeSchemaGate.attach(requestBody, options, requestModel);\n\n // Suppress thinking/reasoning for operations that benefit from terse,\n // structured output. Thinking-capable models (Qwen 3.5, Gemma 4,\n // DeepSeek) default to thinking-on via their chat template; sending\n // `chat_template_kwargs: { enable_thinking: false }` tells the template\n // to skip reasoning tokens. Consolidation operations on the main client\n // intentionally keep thinking enabled so entity resolution and\n // deduplication can use the model's reasoning path. Fast-tier callers can\n // still force suppression per request to preserve their low-latency\n // contract.\n //\n // Gate the injection on detected backend support (issue #548,\n // Codex P1 on PR #550): `chat_template_kwargs` is an LM Studio /\n // vLLM / llama.cpp extension, not part of standard OpenAI chat\n // completions. Strict OpenAI-compatible backends reject\n // unknown fields with 400, which trips the 400-cooldown path and\n // can effectively disable local extraction. Fail open when the\n // backend hasn't been positively identified as thinking-capable.\n const shouldSuppressThinking =\n options.forceDisableThinking === true ||\n ((options.disableThinking ?? this._disableThinking) &&\n THINKING_SUPPRESSED_OPERATIONS.has(operation));\n if (\n shouldSuppressThinking &&\n this.detectedType !== null &&\n THINKING_COMPATIBLE_BACKENDS.has(this.detectedType)\n ) {\n requestBody.chat_template_kwargs = { enable_thinking: false };\n } else if (shouldSuppressThinking && this.detectedType === \"ollama\") {\n // Ollama's /v1 endpoint silently drops `chat_template_kwargs` AND\n // `enable_thinking` — the ONLY field it maps to think-off is\n // `reasoning_effort` (\"none\" → ThinkValue{false} in openai.go;\n // verified against Ollama 0.32.0 source and live probes,\n // issue #1996). Without this, thinking-capable models (Gemma 4,\n // Qwen 3.5+) burn 1,700-2,000 reasoning tokens per extraction on\n // Ollama, exceed the client timeout, and retry forever. The effort\n // is configurable via `localLlmReasoningEffort` (default \"none\");\n // an empty value disables injection (pre-#1996 behavior).\n const effort = this.config.localLlmReasoningEffort;\n if (effort) {\n requestBody.reasoning_effort = effort;\n }\n }\n\n // Normalize URL (use 127.0.0.1 instead of localhost)\n const baseUrl = trimTrailingSlashes(\n this.config.localLlmUrl.replace(\"localhost\", \"127.0.0.1\"),\n );\n const chatUrl = baseUrl.endsWith(\"/v1\")\n ? `${baseUrl}/chat/completions`\n : `${baseUrl}/v1/chat/completions`;\n\n let requestBodyJson = JSON.stringify(requestBody);\n log.debug(\n `local LLM: sending request to ${chatUrl} with model ${this.config.localLlmModel}`,\n );\n // Avoid logging request bodies by default (can contain sensitive user content).\n log.debug(`local LLM: request body length=${requestBodyJson.length}`);\n\n const effectiveTimeoutMs =\n typeof options.timeoutMs === \"number\"\n ? Math.min(this.config.localLlmTimeoutMs, options.timeoutMs)\n : this.config.localLlmTimeoutMs;\n const maxAttempts = 1 + Math.max(0, this.config.localLlmRetry5xxCount);\n // COS-447: rate-limit responses (429/503/408, LiteLLM cooldown) get\n // their own retry budget with exponential full-jitter backoff, and a\n // process-wide semaphore caps concurrent HTTP attempts across clients.\n // Waits are bounded by the request timeout so a short caller budget\n // (e.g. rerankTimeoutMs) is never stretched by a long cooldown.\n const rateLimit = new LocalLlmRateLimitSession(chatUrl, resolveRateLimitSettings(this.config),\n (queueMeta?.priority ?? options.priority) === \"recall-critical\", effectiveTimeoutMs);\n let lastRateLimitDelayMs: number | null = null;\n let response: Response | null = null, responseBody = \"\";\n let lastAbortError: Error | null = null;\n for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {\n response = null;\n responseBody = \"\";\n // Wait out the shared endpoint backoff, then take a concurrency slot.\n const gate = await rateLimit.beforeAttempt(options.signal);\n if (gate.kind === \"skip\") {\n log.warn(gate.reason === \"queue\"\n ? `local LLM concurrency queue exceeded the ${effectiveTimeoutMs}ms request budget; skipping request (op=${operation})`\n : `local LLM rate-limit backoff active (${gate.remainingMs}ms remaining); skipping request (op=${operation})`);\n if (options.failureDiag)\n options.failureDiag.lastError = Object.assign(new Error(\"local LLM rate limited\"), { status: 429 });\n return null;\n }\n if (gate.kind === \"aborted\") return null;\n const attemptAbort = new AbortController();\n const onCallerAbort = (): void => {\n attemptAbort.abort(options.signal?.reason);\n };\n if (options.signal) {\n if (options.signal.aborted) {\n onCallerAbort();\n } else {\n options.signal.addEventListener(\"abort\", onCallerAbort, { once: true });\n }\n }\n const attemptTimeout = setTimeout(() => attemptAbort.abort(), effectiveTimeoutMs);\n try {\n response = await this.chatTransport.post({\n url: chatUrl,\n headers: this.buildRequestHeaders({ \"Content-Type\": \"application/json\" }),\n body: requestBodyJson,\n signal: attemptAbort.signal,\n budgetMs: this.config.localLlmTimeoutMs,\n });\n responseBody = await response.text();\n if (\n response.status === 400 &&\n requestBody.response_format &&\n isUnsupportedJsonSchemaError(`400 ${responseBody}`)\n ) {\n this.nativeSchemaGate.rememberRejection(requestModel);\n // Match gateway degradation without extending this attempt's deadline.\n delete requestBody.response_format;\n requestBodyJson = JSON.stringify(requestBody);\n log.debug(\"local LLM rejected native JSON schema; retrying without it\");\n response = null;\n response = await this.chatTransport.post({\n url: chatUrl,\n headers: this.buildRequestHeaders({ \"Content-Type\": \"application/json\" }),\n body: requestBodyJson,\n signal: attemptAbort.signal,\n budgetMs: this.config.localLlmTimeoutMs,\n });\n responseBody = await response.text();\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n if (options.signal?.aborted) {\n response = null;\n lastAbortError = error;\n break;\n }\n if (response && !response.ok) {\n log.debug(`local LLM failed to read ${response.status} response body: ${options.redactProviderErrors ? \"details redacted\" : error.message}`);\n } else {\n response = null;\n if (!isAbortError(err)) throw err;\n lastAbortError = error;\n if (attempt >= maxAttempts) break;\n const backoffMs = this.config.localLlmRetryBackoffMs * attempt;\n log.warn(\n `local LLM request aborted: op=${operation} attempt=${attempt}/${maxAttempts} timeoutMs=${effectiveTimeoutMs} model=${this.config.localLlmModel}; retrying after ${backoffMs}ms`,\n );\n if (!(await waitForRetryBackoff(backoffMs, options.signal))) return null;\n continue;\n }\n } finally {\n clearTimeout(attemptTimeout);\n options.signal?.removeEventListener(\"abort\", onCallerAbort);\n gate.release();\n }\n\n if (response.ok) {\n rateLimit.onSuccess();\n break;\n }\n if (response.status >= 500 && attempt < maxAttempts) {\n const nonRecoverableReason =\n extractNonRecoverableBackendReasonFromErrorText(responseBody);\n if (nonRecoverableReason) {\n this.markBackendUnavailable(\n nonRecoverableReason,\n this.config.localLlm400CooldownMs,\n );\n this.consecutive400s = 0;\n return null;\n }\n }\n const decision = rateLimit.onResponse(response.status, response.headers?.get(\"retry-after\"), responseBody);\n lastRateLimitDelayMs = decision.kind === \"not-rate-limited\" ? null : decision.delayMs;\n if (decision.kind === \"give-up\") break;\n if (decision.kind === \"retry\") {\n log.warn(`local LLM rate limited: ${response.status}; backing off ${decision.delayMs}ms before retry ${decision.retry}/${rateLimit.settings.retryCount} (op=${operation}, serverHintMs=${decision.hintMs ?? \"none\"}, consecutive=${decision.consecutive})`);\n attempt -= 1; // own retry budget; the loop top waits out the backoff\n continue;\n }\n if (response.status < 500 || attempt >= maxAttempts) break;\n\n const backoffMs = this.config.localLlmRetryBackoffMs * attempt;\n log.warn(\n `local LLM request got ${response.status}; retrying (attempt ${attempt + 1}/${maxAttempts}) after ${backoffMs}ms`,\n );\n if (!(await waitForRetryBackoff(backoffMs, options.signal))) return null;\n }\n log.debug(\n `local LLM: received response, status=${response?.status}, ok=${response?.ok}`,\n );\n if (!response) {\n if (lastAbortError) {\n log.warn(\n `local LLM request aborted after ${maxAttempts} attempt(s): op=${operation} timeoutMs=${effectiveTimeoutMs} model=${this.config.localLlmModel} promptChars=${promptChars} durationMs=${Date.now() - startedAtMs}${queueMeta ? ` waitMs=${startedAtMs - queueMeta.enqueuedAtMs}` : \"\"}`,\n );\n } else {\n log.warn(\n `local LLM request failed: no response object (op=${operation} model=${this.config.localLlmModel} durationMs=${Date.now() - startedAtMs})`,\n );\n }\n return null;\n }\n\n if (!response.ok) {\n if (lastRateLimitDelayMs !== null)\n log.warn(`local LLM rate-limit retries exhausted: op=${operation} retries=${rateLimit.retryCount} nextBackoffMs=${lastRateLimitDelayMs}`);\n let reason = \"\";\n try {\n const parsed = JSON.parse(responseBody) as { error?: { message?: string } };\n reason = parsed?.error?.message ? ` — ${parsed.error.message}` : \"\";\n } catch {\n if (!options.redactProviderErrors) log.debug(`local LLM error body: ${responseBody.slice(0, 500)}`);\n }\n log.warn(\n `local LLM request failed: ${response.status}${options.redactProviderErrors ? \"\" : ` ${response.statusText}${reason}`} ` +\n `(op=${operation}, model=${this.config.localLlmModel}, url=${chatUrl}, promptChars=${promptChars}, maxTokens=${requestBody.max_tokens as number})`,\n );\n const nonRecoverableReason =\n extractNonRecoverableBackendReason(reason) ??\n extractNonRecoverableBackendReasonFromErrorText(responseBody);\n if (nonRecoverableReason) {\n this.markBackendUnavailable(\n nonRecoverableReason,\n this.config.localLlm400CooldownMs,\n );\n this.consecutive400s = 0;\n return null;\n }\n if (response.status === 400) {\n this.consecutive400s += 1;\n if (this.consecutive400s >= this.config.localLlm400TripThreshold) {\n this.cooldownUntilMs = Date.now() + this.config.localLlm400CooldownMs;\n log.warn(\n `local LLM: entering cooldown for ${this.config.localLlm400CooldownMs}ms ` +\n `after ${this.consecutive400s} consecutive 400 responses`,\n );\n this.consecutive400s = 0;\n }\n } else {\n this.consecutive400s = 0;\n }\n if (options.failureDiag)\n options.failureDiag.lastError = Object.assign(new Error(\"local LLM http failure\"), {\n status: response.status,\n });\n return null;\n }\n this.consecutive400s = 0;\n this.nativeSchemaGate.recordSuccess(requestBody, requestModel);\n\n const data = JSON.parse(responseBody) as {\n choices?: Array<{\n message?: { content?: string; reasoning_content?: string };\n }>;\n usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n };\n };\n\n log.debug(\n `local LLM response: choices=${data.choices?.length}, usage=${JSON.stringify(data.usage)}`,\n );\n\n // Thinking models (e.g. Qwen 3.5) may put their response in\n // `reasoning_content` and leave `content` empty. Fall back to\n // reasoning_content so engram still gets a usable result.\n const msg = data.choices?.[0]?.message;\n const content = msg?.content || msg?.reasoning_content || \"\";\n if (!content) {\n log.warn(`local LLM returned empty content.${options.redactProviderErrors ? \"\" : ` choices=${JSON.stringify(data.choices)?.slice(0, 200)}`}`);\n return null;\n }\n\n // Estimate tokens if not provided by local LLM\n const usage = data.usage\n ? {\n promptTokens: data.usage.prompt_tokens ?? 0,\n completionTokens: data.usage.completion_tokens ?? 0,\n totalTokens: data.usage.total_tokens ?? 0,\n }\n : this.estimateTokens(messages, content);\n\n const durationMs = Date.now() - startedAtMs;\n if (resolvePipelineProcessingCapabilities(this.config).slowLog && durationMs >= this.config.slowLogThresholdMs) {\n const promptChars = messages.reduce((sum, m) => sum + (m.content?.length ?? 0), 0);\n const op = options.operation ? ` op=${options.operation}` : \"\";\n log.warn(\n `SLOW local LLM:${op} durationMs=${durationMs} model=${this.config.localLlmModel} url=${chatUrl} promptChars=${promptChars} outputTokens=${usage.completionTokens} totalTokens=${usage.totalTokens}`,\n );\n }\n\n log.debug(\"local LLM: request succeeded, tokens:\", usage.totalTokens);\n return { content, usage };\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n const durationMs = Date.now() - startedAtMs;\n if (isAbortError(err)) {\n log.warn(\n `local LLM request aborted: op=${operation} timeoutMs=${options.timeoutMs ?? this.config.localLlmTimeoutMs} model=${this.config.localLlmModel} durationMs=${durationMs} error=${options.redactProviderErrors ? \"provider error details redacted\" : errMsg}`,\n );\n return null;\n }\n log.warn(`local LLM request error: op=${operation} error=${options.redactProviderErrors ? \"provider error details redacted\" : errMsg}`);\n this.isAvailable = false; // Mark as unavailable on non-abort errors\n const nonRecoverableReason = extractNonRecoverableBackendReason(errMsg);\n if (nonRecoverableReason) {\n this.markBackendUnavailable(\n nonRecoverableReason,\n this.config.localLlm400CooldownMs,\n );\n }\n return null;\n } finally {\n if (queueMeta) {\n const finishedAtMs = Date.now();\n const waitMs = startedAtMs - queueMeta.enqueuedAtMs;\n log.debug(\n `local LLM queue finish: priority=${queueMeta.priority} waitMs=${waitMs} runMs=${finishedAtMs - startedAtMs} totalMs=${finishedAtMs - queueMeta.enqueuedAtMs} op=${operation}`,\n );\n }\n }\n }\n\n /**\n * Query the local LLM server for loaded model information.\n * Returns null if unavailable or if the model is not found.\n */\n async getLoadedModelInfo(): Promise<LocalModelInfo | null> {\n const baseUrl = trimTrailingSlashes(\n this.config.localLlmUrl.replace(\"localhost\", \"127.0.0.1\"),\n );\n\n // Handle URL construction - localLlmUrl may already include /v1\n const modelsUrl = baseUrl.endsWith(\"/v1\")\n ? `${baseUrl}/models`\n : `${baseUrl}/v1/models`;\n log.debug(`Fetching model info from ${modelsUrl}`);\n\n try {\n const result = await this.fetchWithTimeout(modelsUrl, 3000);\n if (!result.ok) {\n if (result.status === 401 || result.status === 403) {\n log.warn(\n `Local LLM: unauthorized while fetching models from ${modelsUrl}; verify localLlmApiKey and localLlmAuthHeader settings`,\n );\n }\n log.warn(`Local LLM: Failed to fetch models from ${modelsUrl} - server returned error`);\n return null;\n }\n if (!result.data) {\n log.warn(`Local LLM: No data returned from ${modelsUrl}`);\n return null;\n }\n\n const data = result.data as {\n data?: Array<{\n id?: string;\n object?: string;\n owned_by?: string;\n // LM Studio specific fields\n max_context_length?: number;\n max_tokens?: number;\n // Ollama specific\n name?: string;\n details?: {\n parameter_size?: string;\n family?: string;\n };\n }>;\n };\n\n if (!Array.isArray(data.data) || data.data.length === 0) {\n log.warn(\"Local LLM returned no models\");\n return null;\n }\n\n // Verbose model listings are noisy on every gateway restart. Keep it debug-only.\n const modelIds = data.data.map((m) => m.id).filter(Boolean);\n log.debug(\n `Local LLM: Found ${modelIds.length} model(s). First 10: ${modelIds.slice(0, 10).join(\", \")}`,\n );\n\n // Find the model matching our configured model ID\n const configuredModel = this.config.localLlmModel;\n let model = data.data.find((m) => m.id === configuredModel);\n\n // If not found by exact match, try partial match (handle suffixes like @4bit)\n if (!model) {\n model = data.data.find((m) =>\n configuredModel.includes(m.id || \"\") ||\n (m.id || \"\").includes(configuredModel.replace(/@\\d+bit$/, \"\"))\n );\n }\n\n // If still not found, use the first loaded model and warn\n if (!model) {\n model = data.data[0];\n const availablePreview = data.data\n .map((m) => m.id)\n .filter(Boolean)\n .slice(0, 10)\n .join(\", \");\n log.warn(\n `Configured model \"${configuredModel}\" not found in local LLM. ` +\n `Using \"${model.id}\" instead. Available (first 10): ${availablePreview}`\n );\n }\n\n // Extract context window - try multiple field names\n let contextWindow = model.max_context_length || model.max_tokens;\n\n // If API doesn't report context window, try LMS CLI (LM Studio specific)\n if (!contextWindow) {\n log.info(\"Local LLM: API did not report context window, trying LMS CLI...\");\n const lmsContext = this.getCachedContextWindow(model.id || \"\");\n if (lmsContext) {\n contextWindow = lmsContext;\n }\n }\n\n this.cachedModelInfo = {\n id: model.id || \"unknown\",\n contextWindow: contextWindow,\n maxTokens: model.max_tokens,\n };\n\n log.info(\n `Local LLM model detected: ${this.cachedModelInfo.id}, ` +\n `context window: ${contextWindow?.toLocaleString() || \"unknown (may use default)\"}`\n );\n\n return this.cachedModelInfo;\n } catch (err) {\n log.warn(`Failed to fetch model info: ${err}`);\n return null;\n }\n }\n\n /**\n * Check if the configured model is available and get its actual context window.\n * Warns if there's a mismatch between expected and actual context.\n */\n async validateModelConfig(expectedContextWindow?: number): Promise<{\n available: boolean;\n actualContextWindow?: number;\n warnings: string[];\n }> {\n const warnings: string[] = [];\n\n const modelInfo = await this.getLoadedModelInfo();\n if (!modelInfo) {\n return { available: false, warnings: [\"Could not query local LLM for model info\"] };\n }\n\n // If we have expected context and the server reports one, check for mismatch\n if (expectedContextWindow && modelInfo.contextWindow) {\n if (modelInfo.contextWindow < expectedContextWindow) {\n warnings.push(\n `Context window mismatch: Model ${modelInfo.id} supports ${modelInfo.contextWindow.toLocaleString()} tokens, ` +\n `but engram is configured for ${expectedContextWindow.toLocaleString()}. ` +\n `Set localLlmMaxContext: ${modelInfo.contextWindow} in config to avoid errors.`\n );\n }\n }\n\n // Warn if server doesn't report context window (common with some local LLM setups)\n if (!modelInfo.contextWindow) {\n warnings.push(\n `Local LLM server did not report context window for ${modelInfo.id}. ` +\n `If you get \"context length exceeded\" errors, set localLlmMaxContext in config.`\n );\n }\n\n return {\n available: true,\n actualContextWindow: modelInfo.contextWindow,\n warnings,\n };\n }\n\n /**\n * Make a chat completion request to local LLM\n */\n async chatCompletion(\n messages: Array<{ role: string; content: string }>,\n options: LocalLlmChatCompletionOptions = {},\n ): Promise<LocalLlmChatCompletionResult | null> {\n if (!resolveLocalLlmCapabilities(this.config).localLlm) {\n log.debug(\"local LLM: disabled, returning null\");\n return null;\n }\n\n const remainingMs = this.remainingCooldownMs();\n if (remainingMs > 0) {\n log.debug(`local LLM: cooldown active (${remainingMs}ms remaining), skipping request`);\n return null;\n }\n if (options.priority) {\n const priority = options.priority;\n return await new Promise<LocalLlmChatCompletionResult | null>((resolve) => {\n let settled = false;\n let onAbort: (() => void) | undefined;\n const finish = (value: LocalLlmChatCompletionResult | null): void => {\n if (settled) return;\n settled = true;\n if (onAbort) options.signal?.removeEventListener(\"abort\", onAbort);\n resolve(value);\n };\n const queued: LocalLlmQueuedRequest = {\n messages,\n options,\n priority,\n enqueuedAtMs: Date.now(),\n resolve: finish,\n };\n onAbort = () => {\n const queue = this.requestQueues[priority];\n const index = queue.indexOf(queued);\n if (index >= 0) queue.splice(index, 1);\n finish(null);\n };\n this.requestQueues[priority].push(queued);\n if (options.signal?.aborted) onAbort();\n else if (options.signal) options.signal.addEventListener(\"abort\", onAbort, { once: true });\n this.scheduleQueueDrain();\n });\n }\n\n return await this.runChatCompletionRequest(messages, options);\n }\n /**\n * Estimate tokens when local LLM does not return usage stats.\n */\n private estimateTokens(\n messages: Array<{ role: string; content: string }>,\n response: string\n ): { promptTokens: number; completionTokens: number; totalTokens: number } {\n const promptTokens = messages.reduce((sum, message) => sum + estimateTokenCount(message.content), 0);\n const completionTokens = estimateTokenCount(response);\n\n return {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n };\n }\n\n /**\n * Try local LLM first, fallback to cloud provider if configured\n */\n async withFallback<T>(\n localOperation: () => Promise<T | null>,\n fallbackOperation: () => Promise<T>,\n operationName: string\n ): Promise<T> {\n // Try local LLM first if enabled\n if (resolveLocalLlmCapabilities(this.config).localLlm) {\n const localResult = await localOperation();\n if (localResult !== null) {\n log.debug(`${operationName}: used local LLM`);\n return localResult;\n }\n\n // Local failed or unavailable\n if (this.config.localLlmFallback) {\n log.info(`${operationName}: local LLM unavailable, falling back to cloud`);\n } else {\n throw new Error(`${operationName}: local LLM unavailable and fallback disabled`);\n }\n }\n\n // Use fallback (cloud provider)\n return fallbackOperation();\n }\n}\n"],"mappings":"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","names":["backoffMs","promptChars"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/wearables/cleanup.ts","../src/wearables/sync-state.ts","../src/wearables/trust.ts","../src/wearables/memory-gen.ts","../src/wearables/pipeline.ts"],"sourcesContent":["/**\n * Wearable transcript cleanup — deterministic, zero-LLM normalization.\n *\n * ASR output from always-on wearables is noisy: fragmented utterances,\n * filler tokens, stuttered repeats, and occasional pure garbage. This\n * module cleans a conversation in place-order without changing meaning:\n * everything here is conservative and reversible by re-syncing.\n */\n\nimport { detectScriptHints, type ScriptHint } from \"./text-language.js\";\nimport type {\n WearableCleanupSettings,\n WearableConversation,\n WearableTranscriptSegment,\n} from \"./types.js\";\n\nexport interface CleanupResult {\n conversation: WearableConversation;\n /** Segments removed by the low-quality heuristic. */\n droppedSegments: number;\n /** Segments merged into a predecessor. */\n mergedSegments: number;\n}\n\n/** Merge consecutive same-speaker segments when gaps are below this. */\nconst MERGE_GAP_MS = 30_000;\n\n/**\n * Speaker labels that carry no real diarization signal. Bee (and other\n * providers that fall back to a placeholder) emit \"Unknown\"/\"unknown\"\n * or an empty string when diarization is unavailable; merging on such a\n * label would collapse an entire conversation into one segment (issue #1811).\n */\nconst GENERIC_SPEAKER_PATTERN = /^unknown$/i;\n\n/**\n * Standalone filler tokens stripped when `stripFillers` is on, for\n * scripts that separate words with spaces. Matched case-insensitively\n * as whole tokens — \"um\" inside \"umbrella\" is never touched.\n * Deliberately short and low-risk: meaning-bearing hedges (\"like\",\n * \"well\", Spanish \"este\", Arabic \"يعني\") are NOT stripped, and neither\n * is \"mm\", which is also a unit of length.\n */\nconst TOKEN_FILLERS: Readonly<Partial<Record<ScriptHint, readonly string[]>>> = {\n latin: [\n \"um\",\n \"uh\",\n \"uhm\",\n \"umm\",\n \"uhh\",\n \"erm\",\n \"hmm\",\n \"mhm\",\n \"ähm\",\n \"äh\",\n \"ehm\",\n \"euh\",\n \"mmm\",\n ],\n korean: [\"음\", \"으음\", \"엄\", \"어어\"],\n arabic: [\"امم\", \"اممم\", \"اه\"],\n cyrillic: [\"эм\", \"ээ\", \"ммм\"],\n};\n\n/**\n * Filler tokens for scripts without word spaces. These are removed\n * inline, so each entry must be unambiguous on its own: the elongated\n * Japanese hesitation forms and the two Chinese hesitation particles\n * qualify, while a bare 「あの」 (\"that\") does not (issue #2196).\n */\nconst INLINE_FILLERS: Readonly<Partial<Record<ScriptHint, readonly string[]>>> = {\n japanese: [\"えーと\", \"えっと\", \"ええと\", \"あのー\", \"あのう\", \"うーん\"],\n han: [\"呃\", \"嗯\"],\n};\n\n/**\n * Punctuation that trails a filler and collapses with it, so\n * 「えーと、では」 becomes 「では」 rather than 「、では」. Arabic sentence\n * punctuation is included: an Arabic filler is normally followed by\n * `،`, and without it the token survives the pass.\n */\nconst TRAILING_FILLER_PUNCTUATION = \"[,.、。،؛؟!?]?\";\n\ninterface FillerMatchers {\n token: RegExp | null;\n inline: RegExp | null;\n}\n\n/**\n * Compiled matchers are cached per (script hints + extra tokens) key.\n * A day of transcripts is thousands of segments over a handful of\n * scripts, so this compiles a few regexes instead of one per segment.\n */\nconst FILLER_MATCHER_CACHE = new Map<string, FillerMatchers>();\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Build one alternation from `tokens`, or `null` when none remain.\n *\n * Each token contributes BOTH its composed and its decomposed spelling,\n * so `ähm` also matches an ASR that emits `a\\u0308hm`. Matching both\n * forms keeps the surrounding transcript byte-identical, which\n * normalizing the text before stripping would not.\n *\n * JavaScript alternation is first-match, not longest-match, so the\n * tokens are sorted longest-first. Without that a built-in prefix such\n * as 「あのー」 would match ahead of a longer operator token that starts\n * with it and leave the tail in the transcript. Ties break on the token\n * itself so the pattern is stable across runs.\n */\nfunction buildAlternation(\n tokens: readonly string[],\n shape: (pattern: string) => { source: string; flags: string },\n): RegExp | null {\n const forms = new Set<string>();\n for (const token of tokens) {\n forms.add(token.normalize(\"NFC\"));\n forms.add(token.normalize(\"NFD\"));\n }\n const unique = [...forms].sort(\n (left, right) => right.length - left.length || (left < right ? -1 : left > right ? 1 : 0),\n );\n if (unique.length === 0) return null;\n const { source, flags } = shape(unique.map((token) => escapeRegExp(token)).join(\"|\"));\n return new RegExp(source, flags);\n}\n\nfunction buildFillerMatchers(\n hints: readonly ScriptHint[],\n extraTokens: readonly string[],\n): FillerMatchers {\n // Structural key: comma-joining collapses `[\"a,b\"]` and `[\"a\",\"b\"]`\n // onto one entry, and the first caller's regex would then clean the\n // second caller's transcripts.\n const key = JSON.stringify([hints, extraTokens]);\n const cached = FILLER_MATCHER_CACHE.get(key);\n if (cached) return cached;\n\n const tokens: string[] = [];\n const inline: string[] = [];\n for (const hint of hints) {\n tokens.push(...(TOKEN_FILLERS[hint] ?? []));\n inline.push(...(INLINE_FILLERS[hint] ?? []));\n }\n // Operator tokens apply to every script: the operator knows their own\n // language, and routing them by script hint would silently drop them.\n for (const extra of extraTokens) {\n const trimmed = extra.trim();\n if (trimmed.length === 0) continue;\n if (SPACE_DELIMITED_TOKEN.test(trimmed)) tokens.push(trimmed);\n else inline.push(trimmed);\n }\n\n const matchers: FillerMatchers = {\n token: buildAlternation(tokens, (pattern) => ({\n source: `(?:^|\\\\s)(?:${pattern})${TRAILING_FILLER_PUNCTUATION}(?=\\\\s|$)`,\n flags: \"giu\",\n })),\n inline: buildAlternation(inline, (pattern) => ({\n source: `(?:${pattern})${TRAILING_FILLER_PUNCTUATION}`,\n flags: \"gu\",\n })),\n };\n FILLER_MATCHER_CACHE.set(key, matchers);\n return matchers;\n}\n\n/**\n * A token whose first character belongs to a space-delimited script.\n *\n * Anything outside this set is removed INLINE, so a script that does\n * space its words must appear here. Hebrew `אה` routed to the inline\n * matcher would strip the suffix of `נראה` and corrupt the transcript.\n */\nconst SPACE_DELIMITED_TOKEN =\n /^[\\p{Script=Latin}\\p{Script=Cyrillic}\\p{Script=Greek}\\p{Script=Hangul}\\p{Script=Arabic}\\p{Script=Hebrew}\\p{N}]/u;\n\n/** Apply configured cleanup passes to one conversation. */\nexport function cleanConversation(\n conversation: WearableConversation,\n settings: WearableCleanupSettings,\n extraFillerTokens: readonly string[] = [],\n): CleanupResult {\n let segments = conversation.segments.map((segment) => ({ ...segment }));\n let droppedSegments = 0;\n let mergedSegments = 0;\n // Optional on the type so other connectors keep the legacy no-op\n // default; absent means \"off\" (unchanged merge behavior).\n const preserveBoundaries = settings.preserveUtteranceBoundaries === true;\n\n if (settings.stripFillers) {\n for (const segment of segments) {\n segment.text = stripFillerTokens(segment.text, extraFillerTokens);\n }\n }\n\n if (settings.collapseRepeats) {\n for (const segment of segments) {\n segment.text = collapseImmediateRepeats(segment.text);\n }\n }\n\n for (const segment of segments) {\n segment.text = normalizeWhitespace(segment.text);\n }\n\n if (settings.dropLowQuality) {\n const kept: WearableTranscriptSegment[] = [];\n for (const segment of segments) {\n if (isLowQualitySegment(segment.text)) {\n droppedSegments += 1;\n } else {\n kept.push(segment);\n }\n }\n segments = kept;\n } else {\n // Even without the quality heuristic, segments whose text became\n // empty after filler stripping carry no information.\n const kept = segments.filter((segment) => segment.text.length > 0);\n droppedSegments += segments.length - kept.length;\n segments = kept;\n }\n\n if (settings.mergeSameSpeaker) {\n const merged: WearableTranscriptSegment[] = [];\n for (const segment of segments) {\n const previous = merged[merged.length - 1];\n if (previous && canMerge(previous, segment, preserveBoundaries)) {\n previous.text = `${previous.text} ${segment.text}`.trim();\n if (segment.endIso) previous.endIso = segment.endIso;\n mergedSegments += 1;\n } else {\n merged.push(segment);\n }\n }\n segments = merged;\n }\n\n return {\n conversation: { ...conversation, segments },\n droppedSegments,\n mergedSegments,\n };\n}\n\nfunction canMerge(\n previous: WearableTranscriptSegment,\n next: WearableTranscriptSegment,\n preserveUtteranceBoundaries: boolean,\n): boolean {\n if (previous.speakerKey !== next.speakerKey) return false;\n // A generic/low-confidence speaker label (\"Unknown\", empty) carries no\n // real diarization: merging on it collapses unrelated utterances. When\n // the source opts into boundary preservation, refuse to merge across\n // such a label (issue #1811). The keys are equal here, so checking one\n // suffices; diarized labels still merge normally.\n if (preserveUtteranceBoundaries) {\n const label = previous.speakerKey.trim();\n if (label.length === 0 || GENERIC_SPEAKER_PATTERN.test(label)) return false;\n }\n const previousEnd = previous.endIso\n ? Date.parse(previous.endIso)\n : Number.NaN;\n const nextStart = next.startIso ? Date.parse(next.startIso) : Number.NaN;\n // Without timestamps, adjacency is the only signal — still merge.\n if (Number.isNaN(previousEnd) || Number.isNaN(nextStart)) return true;\n return nextStart - previousEnd <= MERGE_GAP_MS;\n}\n\n/**\n * Remove filler tokens from `text`. The built-in token set is chosen\n * from the scripts present in the text, so a Japanese or Korean segment\n * is cleaned by its own list instead of silently keeping every filler\n * (issue #2196). Operator tokens apply to every script.\n */\nexport function stripFillerTokens(\n text: string,\n extraTokens: readonly string[] = [],\n): string {\n const matchers = buildFillerMatchers(detectScriptHints(text), extraTokens);\n let result = text;\n if (matchers.token) result = result.replace(matchers.token, \" \");\n if (matchers.inline) result = result.replace(matchers.inline, \"\");\n return normalizeWhitespace(result);\n}\n\n/**\n * Collapse immediate word/phrase stutters: \"I I I think\" -> \"I think\",\n * \"we should we should go\" -> \"we should go\". Only collapses *adjacent*\n * repeats (up to 4-word phrases) so intentional repetition across a\n * sentence is preserved.\n */\nexport function collapseImmediateRepeats(text: string): string {\n const words = text.split(/\\s+/).filter((word) => word.length > 0);\n if (words.length < 2) return text.trim();\n const out: string[] = [];\n let index = 0;\n while (index < words.length) {\n out.push(words[index]);\n index += 1;\n // Greedily consume every adjacent repeat of the phrase that just\n // ended at the output tail (largest phrase first, then re-check so\n // \"I I I think\" fully collapses to \"I think\").\n let matched = true;\n while (matched) {\n matched = false;\n for (let size = 4; size >= 1; size--) {\n if (out.length < size || index + size > words.length) continue;\n const tail = out.slice(-size).join(\" \").toLowerCase();\n // Spoken digit sequences legitimately repeat (\"555 555 1234\");\n // never collapse a phrase that carries no letters.\n if (!/\\p{L}/u.test(tail)) continue;\n const ahead = words.slice(index, index + size).join(\" \").toLowerCase();\n if (tail === ahead) {\n index += size;\n matched = true;\n break;\n }\n }\n }\n }\n return out.join(\" \");\n}\n\n/**\n * Heuristic ASR-garbage detector. Intentionally conservative: it only\n * drops segments that carry no plausible information.\n */\nexport function isLowQualitySegment(text: string): boolean {\n const trimmed = text.trim();\n if (trimmed.length === 0) return true;\n // Single repeated character runs (\"aaaaaa\", \"######\").\n if (/^(.)\\1{4,}$/.test(trimmed)) return true;\n // Mostly non-letter content with no digits (timestamps/amounts are\n // information; \"%$#@!\" is not).\n const letters = trimmed.replace(/[^\\p{L}\\p{N}]/gu, \"\");\n if (letters.length === 0) return true;\n if (trimmed.length >= 12 && letters.length / trimmed.length < 0.3) {\n return true;\n }\n // One identical token repeated many times (\"yeah yeah yeah yeah yeah\").\n const words = trimmed.toLowerCase().split(/\\s+/);\n if (words.length >= 5) {\n const unique = new Set(words);\n if (unique.size === 1) return true;\n }\n return false;\n}\n\nexport function normalizeWhitespace(text: string): string {\n return text.replace(/\\s+/g, \" \").trim();\n}\n","/**\n * Wearable sync-state ledger — per-source bookkeeping for incremental\n * syncs, stored at `state/wearables/sync.json`.\n *\n * State is written only AFTER a source's transcripts (and any memories)\n * were persisted successfully, so a failed sync never advances the\n * watermark past data that didn't land.\n */\n\nimport { promises as fsPromises } from \"node:fs\";\nimport * as path from \"node:path\";\n\nexport interface WearableSourceSyncState {\n /** ISO timestamp of the last successful sync run. */\n lastSyncAt: string;\n /** Most recent day (YYYY-MM-DD) that was synced. */\n lastDateSynced: string;\n /** Body hashes of the last-written day files, keyed by date. */\n dayHashes: Record<string, string>;\n /**\n * Body hashes of days whose memory-extraction pass completed without\n * warnings, keyed by date. A day whose entry is missing or stale\n * re-extracts on the next sync even when its transcript is unchanged\n * — this is how a memory pass that failed mid-run (transcript stored,\n * memories incomplete) self-heals. Absent on records written before\n * this field existed.\n */\n memoryDayHashes?: Record<string, string>;\n /** Native-memory ids already imported (bounded, newest kept). */\n importedNativeMemoryIds: string[];\n}\n\nexport interface WearableSyncStateFile {\n version: 1;\n sources: Record<string, WearableSourceSyncState>;\n}\n\n/** Cap on remembered native-memory ids per source. */\nconst MAX_TRACKED_NATIVE_IDS = 5_000;\n/** Cap on remembered day hashes per source (~2 years of days). */\nconst MAX_TRACKED_DAY_HASHES = 800;\n\nexport function syncStateFilePath(memoryDir: string): string {\n return path.join(memoryDir, \"state\", \"wearables\", \"sync.json\");\n}\n\nexport function emptySyncState(): WearableSyncStateFile {\n return { version: 1, sources: {} };\n}\n\nexport async function loadSyncState(\n memoryDir: string,\n): Promise<WearableSyncStateFile> {\n const filePath = syncStateFilePath(memoryDir);\n let raw: string;\n try {\n raw = await fsPromises.readFile(filePath, \"utf-8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return emptySyncState();\n }\n throw err;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n // A corrupt state file should not brick syncing forever; treat it\n // as a cold start (the worst case is re-syncing days we already\n // have, which the day-hash skip makes cheap and idempotent).\n return emptySyncState();\n }\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n Array.isArray(parsed) ||\n typeof (parsed as WearableSyncStateFile).sources !== \"object\" ||\n (parsed as WearableSyncStateFile).sources === null\n ) {\n return emptySyncState();\n }\n return { version: 1, sources: (parsed as WearableSyncStateFile).sources };\n}\n\nexport async function saveSyncState(\n memoryDir: string,\n state: WearableSyncStateFile,\n): Promise<void> {\n const filePath = syncStateFilePath(memoryDir);\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now().toString(36)}`;\n await fsPromises.writeFile(\n tmpPath,\n `${JSON.stringify(state, null, 2)}\\n`,\n \"utf-8\",\n );\n try {\n await fsPromises.rename(tmpPath, filePath);\n } catch (err) {\n await fsPromises.unlink(tmpPath).catch(() => undefined);\n throw err;\n }\n}\n\n/** Merge a completed source sync into the state file shape. */\nexport function updateSourceSyncState(\n state: WearableSyncStateFile,\n sourceId: string,\n update: {\n syncedAt: string;\n days: string[];\n dayHashes: Record<string, string>;\n /** Days whose memory pass completed cleanly this run. */\n memoryDayHashes?: Record<string, string>;\n /**\n * Days whose memory pass ran this sync but did NOT complete\n * cleanly. Their previous completion records are removed so a\n * stale hash from an earlier clean pass can never mask the\n * failure on the next sync.\n */\n clearMemoryDays?: string[];\n importedNativeMemoryIds?: string[];\n },\n): WearableSyncStateFile {\n const previous = state.sources[sourceId];\n const mergedHashes: Record<string, string> = {\n ...(previous?.dayHashes ?? {}),\n ...update.dayHashes,\n };\n // Bound the hash map: keep the lexicographically-largest (most\n // recent) dates, which sort naturally for YYYY-MM-DD keys.\n const hashKeys = Object.keys(mergedHashes).sort();\n while (hashKeys.length > MAX_TRACKED_DAY_HASHES) {\n const oldest = hashKeys.shift();\n if (oldest === undefined) break;\n delete mergedHashes[oldest];\n }\n\n const mergedMemoryHashes: Record<string, string> = {\n ...(previous?.memoryDayHashes ?? {}),\n ...(update.memoryDayHashes ?? {}),\n };\n for (const day of update.clearMemoryDays ?? []) {\n if (!(day in (update.memoryDayHashes ?? {}))) {\n delete mergedMemoryHashes[day];\n }\n }\n const memoryHashKeys = Object.keys(mergedMemoryHashes).sort();\n while (memoryHashKeys.length > MAX_TRACKED_DAY_HASHES) {\n const oldest = memoryHashKeys.shift();\n if (oldest === undefined) break;\n delete mergedMemoryHashes[oldest];\n }\n\n const mergedNativeIds = [\n ...(previous?.importedNativeMemoryIds ?? []),\n ...(update.importedNativeMemoryIds ?? []),\n ];\n const dedupedNativeIds = [...new Set(mergedNativeIds)];\n const boundedNativeIds =\n dedupedNativeIds.length > MAX_TRACKED_NATIVE_IDS\n ? dedupedNativeIds.slice(dedupedNativeIds.length - MAX_TRACKED_NATIVE_IDS)\n : dedupedNativeIds;\n\n const sortedDays = [...update.days].sort();\n const latestDay = sortedDays[sortedDays.length - 1];\n const lastDateSynced =\n latestDay !== undefined &&\n (!previous || previous.lastDateSynced < latestDay)\n ? latestDay\n : previous?.lastDateSynced ?? latestDay ?? \"\";\n\n return {\n version: 1,\n sources: {\n ...state.sources,\n [sourceId]: {\n lastSyncAt: update.syncedAt,\n lastDateSynced,\n dayHashes: mergedHashes,\n memoryDayHashes: mergedMemoryHashes,\n importedNativeMemoryIds: boundedNativeIds,\n },\n },\n };\n}\n","/**\n * Wearable memory trust scoring — the \"smart\" memoryMode engine.\n *\n * Grounded in how production memory systems handle noisy ingest:\n *\n * - **LLM-as-judge gating** (Remnic's own extraction judge,\n * `judgeFactDurability`): an LLM verdict on durability decides\n * accept / defer / reject before anything persists.\n * - **Provenance priors**: each source carries a configurable trust\n * prior (`sourceTrust`) — a noisy ASR channel contributes less\n * confidence than a clean one (provenance-weighted fusion).\n * - **Corroboration**: independent agreement raises trust. A fact\n * supported by a second wearable that recorded the same day, or by\n * an existing active memory, is far less likely to be an ASR\n * artifact (ensemble agreement / self-consistency).\n *\n * The combined score maps to a three-way decision:\n *\n * trust >= autoApproveTrust -> written ACTIVE\n * trust >= reviewTrust -> written pending_review\n * below -> dropped\n *\n * with the judge able to short-circuit (reject -> drop,\n * defer -> pending_review regardless of score). The score, verdict,\n * and corroboration evidence persist on the memory (confidence +\n * structuredAttributes + verificationState), so Remnic's existing\n * trust machinery — memory-worth outcome counters, pattern\n * reinforcement, temporal supersession, contradiction scans — keeps\n * calibrating these memories after they land.\n */\n\nimport {\n countRecallTokenOverlap,\n normalizeRecallTokens,\n} from \"../recall-tokenization.js\";\n\n/** Judge adjustments and corroboration boosts (documented, fixed). */\nexport const TRUST_JUDGE_ACCEPT_BOOST = 0.15;\nexport const TRUST_CROSS_SOURCE_BOOST = 0.15;\nexport const TRUST_SUPPORTING_MEMORY_BOOST = 0.1;\n\n/** Minimum distinct tokens before similarity is meaningful. */\nconst MIN_FACT_TOKENS = 4;\n/** Fact-token coverage required to call a day-text corroborating. */\nconst CROSS_SOURCE_COVERAGE = 0.6;\n/** Fact-token coverage required to call an existing memory supporting. */\nconst MEMORY_SUPPORT_COVERAGE = 0.7;\n/** Bound on existing memories scanned per fact batch. */\nconst MAX_MEMORIES_SCANNED = 5_000;\n\nexport interface TrustEvidence {\n /** Other-source ids whose same-day content corroborates the fact. */\n corroboratedBySources: string[];\n /** Id of an existing active memory whose content supports the fact. */\n supportingMemoryId?: string;\n}\n\nexport interface TrustScoreInput {\n /** Extraction confidence for the fact (defaults to 0.7 when absent). */\n extractionConfidence: number | undefined;\n /** Per-source trust prior from config (0..1). */\n sourceTrust: number;\n /** Judge verdict kind when a judge ran; undefined when unavailable. */\n judgeVerdict?: \"accept\" | \"reject\" | \"defer\";\n evidence: TrustEvidence;\n}\n\nexport function computeTrustScore(input: TrustScoreInput): number {\n const confidence =\n typeof input.extractionConfidence === \"number\" &&\n Number.isFinite(input.extractionConfidence)\n ? Math.min(1, Math.max(0, input.extractionConfidence))\n : 0.7;\n let trust = confidence * Math.min(1, Math.max(0, input.sourceTrust));\n if (input.judgeVerdict === \"accept\") trust += TRUST_JUDGE_ACCEPT_BOOST;\n if (input.evidence.corroboratedBySources.length > 0) {\n trust += TRUST_CROSS_SOURCE_BOOST;\n }\n if (input.evidence.supportingMemoryId !== undefined) {\n trust += TRUST_SUPPORTING_MEMORY_BOOST;\n }\n return Math.min(1, Math.max(0, trust));\n}\n\nexport interface CorroborationContext {\n /**\n * Same-day transcript bodies from OTHER sources, keyed by source id.\n * Pre-tokenized once per sync (day bodies are large).\n */\n otherSourceDayTokens: Map<string, Set<string>>;\n /** Existing active memories: id + content. */\n existingMemories: Array<{ id: string; content: string }>;\n}\n\n/** Tokenize a day body once for repeated per-fact coverage checks. */\nexport function tokenizeDayBody(body: string): Set<string> {\n return new Set(normalizeRecallTokens(body));\n}\n\n/**\n * Find corroborating evidence for one fact. Deterministic and local:\n * token-coverage similarity via the shared recall tokenizer — no LLM\n * cost on the corroboration path.\n */\nexport function findCorroboration(\n factText: string,\n context: CorroborationContext,\n): TrustEvidence {\n const factTokens = normalizeRecallTokens(factText);\n const evidence: TrustEvidence = { corroboratedBySources: [] };\n if (factTokens.length < MIN_FACT_TOKENS) return evidence;\n const factTokenSet = new Set(factTokens);\n\n for (const [sourceId, dayTokens] of context.otherSourceDayTokens) {\n let matches = 0;\n for (const token of factTokenSet) {\n if (dayTokens.has(token)) matches += 1;\n }\n if (matches / factTokenSet.size >= CROSS_SOURCE_COVERAGE) {\n evidence.corroboratedBySources.push(sourceId);\n }\n }\n evidence.corroboratedBySources.sort();\n\n let scanned = 0;\n for (const memory of context.existingMemories) {\n if (scanned >= MAX_MEMORIES_SCANNED) break;\n scanned += 1;\n const matches = countRecallTokenOverlap(factTokenSet, memory.content);\n if (matches / factTokenSet.size >= MEMORY_SUPPORT_COVERAGE) {\n evidence.supportingMemoryId = memory.id;\n break;\n }\n }\n return evidence;\n}\n\nexport interface SmartDecision {\n outcome: \"active\" | \"review\" | \"drop\";\n reason:\n | \"judge-rejected\"\n | \"judge-deferred\"\n | \"auto-approved\"\n | \"queued-for-review\"\n | \"below-trust\"\n | \"ambient-high-impact\";\n trust: number;\n}\n\nexport interface SmartDecisionOptions {\n /**\n * Deny auto-approval regardless of score (issue #2294). Set for high-impact\n * personal facts derived from ambient capture: clamping extraction\n * confidence to the speculative ceiling is not enough on its own, because\n * judge-accept plus cross-source and existing-memory boosts sum past\n * `autoApproveTrust` — and two devices in one room recording the same\n * television program corroborate each other perfectly. Such a candidate\n * tops out in the review queue.\n */\n capAtReview?: boolean;\n}\n\n/** Map judge verdict + trust score to the smart-mode decision. */\nexport function decideSmart(\n trust: number,\n judgeVerdict: \"accept\" | \"reject\" | \"defer\" | undefined,\n thresholds: { autoApproveTrust: number; reviewTrust: number },\n options?: SmartDecisionOptions,\n): SmartDecision {\n if (judgeVerdict === \"reject\") {\n return { outcome: \"drop\", reason: \"judge-rejected\", trust };\n }\n if (judgeVerdict === \"defer\") {\n return { outcome: \"review\", reason: \"judge-deferred\", trust };\n }\n if (trust >= thresholds.autoApproveTrust) {\n return options?.capAtReview === true\n ? { outcome: \"review\", reason: \"ambient-high-impact\", trust }\n : { outcome: \"active\", reason: \"auto-approved\", trust };\n }\n if (trust >= thresholds.reviewTrust) {\n return { outcome: \"review\", reason: \"queued-for-review\", trust };\n }\n return { outcome: \"drop\", reason: \"below-trust\", trust };\n}\n","/**\n * Wearable memory generation — trust-gated extraction from cleaned day\n * transcripts.\n *\n * Wearable ASR quality varies wildly between providers and rooms, so\n * unlike live-session extraction this path is gated per source:\n *\n * - memoryMode \"off\" -> never runs\n * - memoryMode \"review\" -> candidates land with status\n * \"pending_review\" (operator approves via\n * the existing review-queue surfaces)\n * - memoryMode \"auto\" -> candidates that pass every gate land\n * active\n *\n * Deterministic gates (applied in order, all modes except \"off\"):\n * 1. category gate — procedure / reasoning_trace candidates are\n * skipped (they need richer persistence than\n * this path provides)\n * 2. confidence floor — `minConfidence`\n * 3. importance floor — local `scoreImportance` >= `minImportance`\n * 4. dedup — storage content-hash index + intra-run set\n * 5. day cap — top `maxMemoriesPerDay` by importance score\n * (0 disables the cap)\n *\n * The extraction engine itself is injected so this module stays free of\n * LLM-client construction; callers hand in `orchestrator`-owned or\n * standalone engines alike.\n */\n\nimport { scoreImportance } from \"../importance.js\";\nimport { collectPersonEntityRefs, isHighImpactPersonalFact } from \"../ambient-provenance.js\";\nimport type { MemoryWriteResult } from \"../storage.js\";\nimport {\n composeMemoryEnvelope,\n type MemoryWriteInput,\n type SealedMemoryEnvelope,\n type WriteContext,\n} from \"../write-envelope.js\";\nimport { log } from \"../logger.js\";\nimport type { JudgeBatchResult, JudgeCandidate } from \"../extraction-judge.js\";\nimport { getVerdictKind } from \"../extraction-judge.js\";\nimport { describeErrorForOperator } from \"./errors.js\";\nimport {\n computeTrustScore,\n decideSmart,\n findCorroboration,\n type CorroborationContext,\n type TrustEvidence,\n} from \"./trust.js\";\nimport type {\n BufferTurn,\n ExtractedFact,\n ExtractionResult,\n ImportanceLevel,\n ImportanceScore,\n MemoryCategory,\n MemoryStatus,\n} from \"../types.js\";\nimport { resolveSpeaker, type SpeakerRegistry } from \"./speakers.js\";\nimport type {\n WearableConversation,\n WearableMemoryMode,\n WearableNativeMemory,\n WearableSourceSettings,\n} from \"./types.js\";\n\n/**\n * Memory status used for a given memory mode. Least-privilege default:\n * anything that is not explicitly \"auto\" lands in the review queue\n * rather than active recall.\n */\n/**\n * Compose a wearable-side envelope in salvage mode (issue #1989 PR4):\n * wearable transcripts and provider-native memories are MACHINE input —\n * one malformed field must not abort a whole day's sync. Drops are\n * warn-logged, never silent.\n */\nfunction composeSalvagedWearableEnvelope(\n input: MemoryWriteInput,\n ctx: WriteContext,\n): SealedMemoryEnvelope {\n const envelope = composeMemoryEnvelope(input, ctx, { salvage: true });\n if (envelope.salvageNotes.length > 0) {\n log.warn(`wearable write salvaged invalid fields: ${envelope.salvageNotes.join(\"; \")}`);\n }\n return envelope;\n}\n\nexport function memoryStatusForMode(\n mode: WearableMemoryMode,\n): Extract<MemoryStatus, \"active\" | \"pending_review\"> {\n return mode === \"auto\" || mode === \"smart\" ? \"active\" : \"pending_review\";\n}\n\n/** Narrow writer interface satisfied by `StorageManager`. */\nexport interface WearableMemoryWriter {\n /**\n * Sealed-envelope write entry point (issue #1989 PR4): cross-cutting\n * fields ride the composed envelope; per-write extras stay explicit.\n */\n writeSealedMemory(\n envelope: SealedMemoryEnvelope,\n extras: {\n importance?: ImportanceScore;\n contentHashSource?: string;\n status?: MemoryStatus;\n memoryKind?: \"episode\" | \"note\" | \"box\" | \"dream\" | \"procedural\";\n },\n ): Promise<MemoryWriteResult>;\n hasFactContentHash(content: string): Promise<boolean>;\n /**\n * Locate an earlier wearable write of the same content (any status).\n * Optional: enables in-place promotion when re-scoring with stronger\n * evidence.\n */\n findWearableMemoryByContent?(\n content: string,\n ): Promise<{ id: string; status: MemoryStatus | undefined } | null>;\n /**\n * Promote a pending_review wearable memory to active, merging trust\n * evidence. Returns false when missing or no longer pending.\n */\n promoteWearableMemory?(\n id: string,\n attributeUpdates: Record<string, string>,\n confidence?: number,\n ): Promise<boolean>;\n /**\n * Demote a pending_review wearable memory to rejected on an explicit\n * judge-reject re-verdict. Returns false when missing or no longer\n * pending. Active rows are never auto-demoted.\n */\n demoteWearableMemory?(\n id: string,\n attributeUpdates: Record<string, string>,\n ): Promise<boolean>;\n}\n\nexport interface WearableMemoryGenDeps {\n extract(turns: BufferTurn[]): Promise<ExtractionResult>;\n writer: WearableMemoryWriter;\n /**\n * LLM-as-judge batch evaluation (the existing extraction judge).\n * Absent when no judge is wired (degraded smart mode: trust scoring\n * runs on confidence x sourceTrust + corroboration alone).\n */\n judgeFacts?(candidates: JudgeCandidate[]): Promise<JudgeBatchResult>;\n /**\n * Corroboration evidence for smart mode: other sources' same-day\n * transcript tokens + existing active memories. Absent disables\n * corroboration boosts.\n */\n corroboration?: CorroborationContext;\n}\n\nexport interface WearableMemoryGenResult {\n created: number;\n /**\n * Writes the #1579 tombstone chokepoint downgraded to pending_review (no\n * active copy). Surfaced distinctly from `created` so sync summaries and\n * callers don't count blocked content as successfully created (#1645).\n */\n tombstoneBlocked: number;\n /** Earlier borderline writes promoted to active by new evidence. */\n promoted: number;\n /** Earlier pending writes retired by a fresh judge-reject verdict. */\n demoted: number;\n skipped: number;\n skippedByReason: Record<string, number>;\n /** Non-fatal problems (e.g. the extraction engine erroring). */\n warnings: string[];\n /**\n * True when every conversation was extracted (the pass may still\n * carry degraded-mode warnings, e.g. judge unavailable). False only\n * when extraction itself aborted mid-day — the signal callers use to\n * re-run the pass on the next sync. A degraded-but-complete pass\n * must NOT re-run forever: its facts are already written and dedup\n * would suppress improvements anyway.\n */\n completed: boolean;\n}\n\nexport const WEARABLE_SOURCE_PREFIX = \"wearable\";\n\nexport function wearableSourceLabel(sourceId: string): string {\n return `${WEARABLE_SOURCE_PREFIX}:${sourceId}`;\n}\n\nexport function wearableDayTag(date: string): string {\n return `wearable-day:${date}`;\n}\n\nconst IMPORTANCE_RANK: Record<ImportanceLevel, number> = {\n trivial: 0,\n low: 1,\n normal: 2,\n high: 3,\n critical: 4,\n};\n\n/** Max characters of transcript per extraction turn. */\nconst MAX_EXTRACTION_CHUNK_CHARS = 6_000;\n/** Skip extraction for conversations with less substance than this. */\nconst MIN_CONVERSATION_CHARS = 80;\n\n/**\n * Render a cleaned conversation into extraction-ready turns. Each turn\n * carries a labeled multi-speaker transcript block; the wearer is\n * marked \"(you)\" so first-person facts attribute correctly.\n */\nexport function buildExtractionTurns(\n sourceId: string,\n date: string,\n conversation: WearableConversation,\n registry: SpeakerRegistry,\n): BufferTurn[] {\n const headerParts = [\n `Wearable transcript (${sourceId}) — ${date}`,\n conversation.title ? `\"${conversation.title}\"` : undefined,\n conversation.location ? `at ${conversation.location}` : undefined,\n ].filter((part): part is string => typeof part === \"string\");\n const header = `[${headerParts.join(\" — \")}]`;\n\n const lines: string[] = [];\n for (const segment of conversation.segments) {\n const { label } = resolveSpeaker(sourceId, segment, registry);\n lines.push(`${label}: ${segment.text}`);\n }\n const transcript = lines.join(\"\\n\");\n if (transcript.trim().length < MIN_CONVERSATION_CHARS) return [];\n\n const sessionKey = `wearables:${sourceId}:${date}:${conversation.id}`;\n const timestamp = conversation.startIso;\n const turns: BufferTurn[] = [];\n let chunkLines: string[] = [];\n let chunkChars = 0;\n const flush = () => {\n if (chunkLines.length === 0) return;\n turns.push({\n role: \"user\",\n content: `${header}\\n${chunkLines.join(\"\\n\")}`,\n timestamp,\n sourceValidAt: timestamp,\n sessionKey,\n // Always-on capture: this transcript may carry TV, podcast, or\n // overheard speech the wearer never authored (issue #2294).\n ambientCapture: true,\n });\n chunkLines = [];\n chunkChars = 0;\n };\n for (const line of transcript.split(\"\\n\")) {\n if (chunkChars + line.length + 1 > MAX_EXTRACTION_CHUNK_CHARS) flush();\n chunkLines.push(line);\n chunkChars += line.length + 1;\n }\n flush();\n return turns;\n}\n\ninterface GatedCandidate {\n fact: ExtractedFact;\n importance: ImportanceScore;\n conversation: WearableConversation;\n /**\n * Ambient high-impact verdict, decided where the extraction's entity list is\n * still in scope so personhood comes from the extractor's own `type:\n * \"person\"` metadata rather than the spelling of `entityRef` (#2294).\n */\n highImpact: boolean;\n}\n\ninterface ScoredCandidate {\n trust: number;\n verdict?: \"accept\" | \"reject\" | \"defer\";\n evidence: TrustEvidence;\n}\n\n/**\n * Smart-mode scoring: one judge batch call for the whole day, then\n * per-fact trust = confidence x sourceTrust + judge/corroboration\n * boosts. A judge failure degrades gracefully (warned once; scoring\n * continues on confidence + corroboration alone).\n */\nasync function scoreCandidates(\n novel: GatedCandidate[],\n settings: WearableSourceSettings,\n deps: WearableMemoryGenDeps,\n result: WearableMemoryGenResult,\n): Promise<Map<number, ScoredCandidate>> {\n const scored = new Map<number, ScoredCandidate>();\n if (novel.length === 0) return scored;\n\n let verdicts: Map<number, \"accept\" | \"reject\" | \"defer\"> | undefined;\n if (deps.judgeFacts) {\n const judgeCandidates: JudgeCandidate[] = novel.map((candidate) => ({\n text: candidate.fact.content,\n category: candidate.fact.category,\n confidence:\n typeof candidate.fact.confidence === \"number\"\n ? candidate.fact.confidence\n : 0.7,\n tags: candidate.fact.tags ?? [],\n importanceLevel: candidate.importance.level,\n }));\n try {\n const judgeResult = await deps.judgeFacts(judgeCandidates);\n verdicts = new Map();\n for (const [index, verdict] of judgeResult.verdicts) {\n verdicts.set(index, getVerdictKind(verdict));\n }\n } catch (err) {\n result.warnings.push(\n `extraction judge unavailable for this pass: ${describeErrorForOperator(err)} — trust scoring continued without judge verdicts`,\n );\n }\n }\n\n const corroboration: CorroborationContext = deps.corroboration ?? {\n otherSourceDayTokens: new Map(),\n existingMemories: [],\n };\n novel.forEach((candidate, index) => {\n const evidence = findCorroboration(candidate.fact.content, corroboration);\n const verdict = verdicts?.get(index);\n const trust = computeTrustScore({\n extractionConfidence: candidate.fact.confidence,\n sourceTrust: settings.sourceTrust,\n judgeVerdict: verdict,\n evidence,\n });\n scored.set(index, { trust, ...(verdict !== undefined ? { verdict } : {}), evidence });\n });\n return scored;\n}\n\n/**\n * Run extraction + gates over a day's conversations and persist the\n * survivors. Returns counts for the sync summary.\n */\nexport async function generateWearableMemories(\n sourceId: string,\n date: string,\n conversations: WearableConversation[],\n settings: WearableSourceSettings,\n registry: SpeakerRegistry,\n deps: WearableMemoryGenDeps,\n): Promise<WearableMemoryGenResult> {\n const result: WearableMemoryGenResult = {\n created: 0,\n tombstoneBlocked: 0,\n promoted: 0,\n demoted: 0,\n skipped: 0,\n skippedByReason: {},\n warnings: [],\n completed: true,\n };\n if (settings.memoryMode === \"off\") return result;\n\n const skip = (reason: string, count = 1): void => {\n result.skipped += count;\n result.skippedByReason[reason] =\n (result.skippedByReason[reason] ?? 0) + count;\n };\n\n const candidates: GatedCandidate[] = [];\n const seenContent = new Set<string>();\n\n for (const conversation of conversations) {\n const turns = buildExtractionTurns(sourceId, date, conversation, registry);\n if (turns.length === 0) continue;\n let extraction: ExtractionResult;\n try {\n extraction = await deps.extract(turns);\n } catch (err) {\n // One failing extraction call almost always means every call will\n // fail (missing key, provider outage) — stop hammering the engine\n // and surface a single actionable warning instead of one per\n // conversation. Candidates gathered before the failure still\n // persist below.\n result.warnings.push(\n `extraction failed for ${sourceId}/${date} (conversation ${conversation.id}): ${describeErrorForOperator(err)} — the memory pass for this day retries on the next sync`,\n );\n result.completed = false;\n break;\n }\n const personRefs = collectPersonEntityRefs(extraction);\n for (const fact of extraction.facts) {\n const content = fact.content?.trim();\n if (!content) {\n skip(\"empty\");\n continue;\n }\n if (fact.category === \"procedure\" || fact.category === \"reasoning_trace\") {\n skip(\"unsupported-category\");\n continue;\n }\n const highImpact = isHighImpactPersonalFact({ ...fact, content }, personRefs);\n // In smart mode the trust bands subsume the hard confidence\n // floor — a borderline fact belongs in the review band, not on\n // the floor. The pre-filter applies to review/auto modes only.\n //\n // High-impact ambient claims are exempt: the extraction clamp just\n // pinned them to 0.39, which is below the 0.6 default floor, so the\n // pre-filter would silently drop the very candidates `review` mode\n // promises to queue and would make the auto-mode cap unreachable\n // (#2294). They skip the floor and land pending_review via the cap.\n if (\n highImpact === false &&\n settings.memoryMode !== \"smart\" &&\n typeof fact.confidence === \"number\" &&\n fact.confidence < settings.minConfidence\n ) {\n skip(\"below-confidence\");\n continue;\n }\n const importance = scoreImportance(content, fact.category, fact.tags ?? []);\n if (\n IMPORTANCE_RANK[importance.level] <\n IMPORTANCE_RANK[settings.minImportance]\n ) {\n skip(\"below-importance\");\n continue;\n }\n const dedupKey = content.toLowerCase();\n if (seenContent.has(dedupKey)) {\n skip(\"duplicate-in-run\");\n continue;\n }\n seenContent.add(dedupKey);\n candidates.push({\n fact: { ...fact, content },\n importance,\n conversation,\n highImpact,\n });\n }\n }\n\n // Drop candidates that already exist in storage BEFORE applying the\n // day cap so duplicates never consume cap slots that novel,\n // lower-scoring candidates should get (Codex P2 on PR #1458). In\n // smart mode a duplicate of a PENDING_REVIEW write is kept aside as\n // a promotion candidate — corroboration that arrives after the\n // original borderline write (another device syncing the same day)\n // must be able to promote it in place (Cursor review on PR #1462).\n const novel: GatedCandidate[] = [];\n const promotable: GatedCandidate[] = [];\n for (const candidate of candidates) {\n if (await deps.writer.hasFactContentHash(candidate.fact.content)) {\n if (\n settings.memoryMode === \"smart\" &&\n deps.writer.findWearableMemoryByContent !== undefined &&\n deps.writer.promoteWearableMemory !== undefined\n ) {\n promotable.push(candidate);\n } else {\n skip(\"duplicate-existing\");\n }\n continue;\n }\n novel.push(candidate);\n }\n\n // Re-score promotion candidates with TODAY'S evidence; promote the\n // ones that now clear the auto threshold. Never consumes day-cap\n // slots (no new memory is written).\n if (promotable.length > 0) {\n const promoteScores = await scoreCandidates(promotable, settings, deps, result);\n for (const [index, candidate] of promotable.entries()) {\n const scored = promoteScores.get(index);\n // Every wearable candidate is ambient by construction, so a high-impact\n // personal claim can only ever reach the review queue — late-arriving\n // corroboration must not promote a clamped TV line to active (#2294).\n const decision = scored\n ? decideSmart(scored.trust, scored.verdict, settings, {\n capAtReview: candidate.highImpact,\n })\n : undefined;\n if (!scored || !decision) {\n skip(\"duplicate-existing\");\n continue;\n }\n // A fresh judge-REJECT retires the stored row — but only a\n // pending_review one. Active rows are never auto-demoted: an\n // operator approval or accrued recall signals must not be\n // overturned by one later LLM verdict; contradiction scans and\n // supersession own active-row retirement (Cursor review on PR\n // #1462, round 7).\n if (scored.verdict === \"reject\") {\n if (deps.writer.demoteWearableMemory !== undefined) {\n const existingForDemote = await deps.writer.findWearableMemoryByContent!(\n candidate.fact.content,\n );\n if (\n existingForDemote &&\n existingForDemote.status === \"pending_review\" &&\n (await deps.writer.demoteWearableMemory(existingForDemote.id, {\n trustScore: scored.trust.toFixed(3),\n trustDecision: \"demoted-by-rejection\",\n judgeVerdict: \"reject\",\n }))\n ) {\n result.demoted += 1;\n continue;\n }\n }\n skip(\"duplicate-existing\");\n continue;\n }\n if (decision.outcome !== \"active\") {\n skip(\"duplicate-existing\");\n continue;\n }\n const existing = await deps.writer.findWearableMemoryByContent!(\n candidate.fact.content,\n );\n if (!existing || existing.status !== \"pending_review\") {\n skip(\"duplicate-existing\");\n continue;\n }\n const promoted = await deps.writer.promoteWearableMemory!(\n existing.id,\n {\n trustScore: scored.trust.toFixed(3),\n trustDecision: \"promoted-by-corroboration\",\n ...(scored.verdict !== undefined ? { judgeVerdict: scored.verdict } : {}),\n ...(scored.evidence.corroboratedBySources.length > 0\n ? { corroboratedBySources: scored.evidence.corroboratedBySources.join(\",\") }\n : {}),\n ...(scored.evidence.supportingMemoryId !== undefined\n ? { supportingMemoryId: scored.evidence.supportingMemoryId }\n : {}),\n },\n scored.trust,\n );\n if (promoted) {\n result.promoted += 1;\n } else {\n skip(\"duplicate-existing\");\n }\n }\n }\n\n // Smart mode: judge + trust scoring decide active/review/drop per\n // fact. The judge runs ONE batch call for the whole day; trust\n // combines extraction confidence x sourceTrust with corroboration\n // boosts (cross-device agreement, existing-memory support).\n let trustById = new Map<number, ScoredCandidate>();\n if (settings.memoryMode === \"smart\") {\n trustById = await scoreCandidates(novel, settings, deps, result);\n }\n\n // Smart decisions run BEFORE the day cap so dropped facts (judge\n // rejections, below-trust) never consume cap slots that surviving\n // candidates ranked past position N should get (Cursor review on PR\n // #1462).\n interface Writable {\n candidate: GatedCandidate;\n index: number;\n status: MemoryStatus;\n trustAttributes: Record<string, string>;\n }\n const modeStatus = memoryStatusForMode(settings.memoryMode);\n const writable: Writable[] = [];\n novel.forEach((candidate, index) => {\n // Every wearable candidate is ambient, so a high-impact personal claim can\n // never be written active — on ANY mode. `auto` has no trust scoring and an\n // operator may set minConfidence at or below the speculative ceiling, which\n // would otherwise let a clamped TV line straight into recall (#2294).\n const capAtReview = candidate.highImpact;\n if (settings.memoryMode !== \"smart\") {\n const status = capAtReview ? \"pending_review\" : modeStatus;\n writable.push({\n candidate,\n index,\n status,\n trustAttributes: capAtReview ? { trustDecision: \"ambient-high-impact\" } : {},\n });\n return;\n }\n const scored = trustById.get(index);\n if (!scored) return;\n const decision = decideSmart(scored.trust, scored.verdict, settings, { capAtReview });\n if (decision.outcome === \"drop\") {\n skip(decision.reason);\n return;\n }\n writable.push({\n candidate,\n index,\n status: decision.outcome === \"active\" ? \"active\" : \"pending_review\",\n trustAttributes: {\n trustScore: scored.trust.toFixed(3),\n trustDecision: decision.reason,\n ...(scored.verdict !== undefined ? { judgeVerdict: scored.verdict } : {}),\n ...(scored.evidence.corroboratedBySources.length > 0\n ? { corroboratedBySources: scored.evidence.corroboratedBySources.join(\",\") }\n : {}),\n ...(scored.evidence.supportingMemoryId !== undefined\n ? { supportingMemoryId: scored.evidence.supportingMemoryId }\n : {}),\n },\n });\n });\n\n // Day cap over the SURVIVORS: strongest by trust in smart mode, by\n // importance otherwise. Stable ordering with a content tiebreak.\n const strength = (entry: Writable): number =>\n settings.memoryMode === \"smart\"\n ? trustById.get(entry.index)?.trust ?? 0\n : entry.candidate.importance.score;\n writable.sort((a, b) => {\n const sa = strength(a);\n const sb = strength(b);\n if (sa > sb) return -1;\n if (sa < sb) return 1;\n if (a.candidate.fact.content < b.candidate.fact.content) return -1;\n if (a.candidate.fact.content > b.candidate.fact.content) return 1;\n return 0;\n });\n const cap = settings.maxMemoriesPerDay;\n const kept = cap > 0 ? writable.slice(0, cap) : writable;\n if (writable.length > kept.length) {\n skip(\"over-day-cap\", writable.length - kept.length);\n }\n\n for (const { candidate, index, status, trustAttributes } of kept) {\n const tags = [\n ...new Set([\n ...(candidate.fact.tags ?? []),\n WEARABLE_SOURCE_PREFIX,\n wearableSourceLabel(sourceId),\n wearableDayTag(date),\n ]),\n ];\n const factEnvelope = composeSalvagedWearableEnvelope(\n {\n content: candidate.fact.content,\n category: candidate.fact.category,\n confidence:\n settings.memoryMode === \"smart\"\n ? trustById.get(index)?.trust\n : candidate.fact.confidence,\n tags,\n validAt: candidate.conversation.startIso,\n structuredAttributes: {\n ...(candidate.fact.structuredAttributes ?? {}),\n wearableSource: sourceId,\n wearableDate: date,\n wearableConversationId: candidate.conversation.id,\n ...trustAttributes,\n },\n },\n { source: wearableSourceLabel(sourceId) },\n );\n const writeResult = await deps.writer.writeSealedMemory(factEnvelope, {\n importance: candidate.importance,\n contentHashSource: candidate.fact.content,\n status,\n });\n // #1645: a tombstone-blocked wearable write lands pending_review (no active\n // copy) — count it as blocked, NOT created, so the sync summary and callers\n // don't report blocked content as successfully created active memories.\n if (writeResult.tombstoneBlocked) {\n result.tombstoneBlocked += 1;\n } else {\n result.created += 1;\n }\n }\n return result;\n}\n\n/**\n * Write one compact daily-digest memory summarizing the day's recorded\n * conversations. Deterministic (no LLM): titles, time ranges, speaker\n * counts. Gated by `wearables.digestEnabled`.\n */\nexport async function writeDailyDigestMemory(\n sourceId: string,\n date: string,\n conversations: WearableConversation[],\n settings: WearableSourceSettings,\n registry: SpeakerRegistry,\n writer: WearableMemoryWriter,\n): Promise<boolean> {\n if (settings.memoryMode === \"off\") return false;\n if (conversations.length === 0) return false;\n const lines = conversations.map((conversation) => {\n const title = conversation.title?.trim() || \"Untitled conversation\";\n const speakers = new Set(\n conversation.segments.map(\n (segment) => resolveSpeaker(sourceId, segment, registry).label,\n ),\n );\n return `- ${title} (${speakers.size} speaker${speakers.size === 1 ? \"\" : \"s\"})`;\n });\n const content =\n `Wearable day digest — ${sourceId}, ${date}: ` +\n `${conversations.length} recorded conversation${conversations.length === 1 ? \"\" : \"s\"}.\\n` +\n lines.join(\"\\n\");\n if (await writer.hasFactContentHash(content)) return false;\n const digestEnvelope = composeSalvagedWearableEnvelope(\n {\n content,\n category: \"moment\",\n confidence: 0.9,\n tags: [\n WEARABLE_SOURCE_PREFIX,\n wearableSourceLabel(sourceId),\n wearableDayTag(date),\n \"daily-digest\",\n ],\n validAt: `${date}T00:00:00.000Z`,\n structuredAttributes: {\n wearableSource: sourceId,\n wearableDate: date,\n },\n },\n { source: wearableSourceLabel(sourceId) },\n );\n // A provider derives conversation.title from the same captured audio, so a\n // title lifted from a television scene (\"Dana's cancer diagnosis\") would\n // otherwise ride into recall on the digest, bypassing the extraction prompt\n // and its clamp entirely. Digest content is `moment`, which the classifier\n // already treats as personal-claim-shaped, so this reads the titles alone\n // and holds only a contaminated digest for review (#2294).\n const titleIsHighImpact = lines.some((line) =>\n isHighImpactPersonalFact({ category: \"fact\", content: line }),\n );\n await writer.writeSealedMemory(digestEnvelope, {\n importance: scoreImportance(content, \"moment\", [\"daily-digest\"]),\n contentHashSource: content,\n status: titleIsHighImpact ? \"pending_review\" : memoryStatusForMode(settings.memoryMode),\n memoryKind: \"episode\",\n });\n return true;\n}\n\n/**\n * Import provider-extracted memories (Bee facts, Omi memories) into the\n * review queue. Always `pending_review` regardless of memoryMode — the\n * provider's extraction quality is outside Remnic's control.\n */\n/** Native-source trust prior reduction (provider extraction quality). */\nconst NATIVE_TRUST_FACTOR = 0.9;\n\nexport async function importNativeMemories(\n sourceId: string,\n memories: WearableNativeMemory[],\n alreadyImportedIds: ReadonlySet<string>,\n settings: WearableSourceSettings,\n deps: WearableMemoryGenDeps,\n): Promise<{ imported: number; tombstoneBlocked: number; importedIds: string[]; warnings: string[] }> {\n let imported = 0;\n let tombstoneBlocked = 0;\n const importedIds: string[] = [];\n const warnings: string[] = [];\n const seenContent = new Set<string>();\n const smart = settings.importNativeMemories === \"smart\";\n\n // Smart path: one judge batch over the novel items, like transcript\n // facts, with a reduced source prior — provider extraction quality is\n // outside Remnic's control.\n const novel: WearableNativeMemory[] = [];\n for (const memory of memories) {\n const content = memory.content?.trim();\n if (!content) continue;\n if (alreadyImportedIds.has(memory.id)) continue;\n // Intra-run + cross-run dedup: the storage hash index only learns a\n // fact after its write lands, so same-content items within one page\n // batch need the local set.\n if (seenContent.has(content) || (await deps.writer.hasFactContentHash(content))) {\n importedIds.push(memory.id);\n continue;\n }\n seenContent.add(content);\n novel.push({ ...memory, content });\n }\n\n let verdicts: Map<number, \"accept\" | \"reject\" | \"defer\"> | undefined;\n if (smart && deps.judgeFacts && novel.length > 0) {\n try {\n const judgeResult = await deps.judgeFacts(\n novel.map((memory) => ({\n text: memory.content,\n category: \"fact\",\n confidence: 0.7,\n tags: memory.tags ?? [],\n })),\n );\n verdicts = new Map();\n for (const [index, verdict] of judgeResult.verdicts) {\n verdicts.set(index, getVerdictKind(verdict));\n }\n } catch (err) {\n warnings.push(\n `extraction judge unavailable for native import: ${describeErrorForOperator(err)} — trust scoring continued without judge verdicts`,\n );\n }\n }\n const corroboration: CorroborationContext = deps.corroboration ?? {\n otherSourceDayTokens: new Map(),\n existingMemories: [],\n };\n\n for (const [index, memory] of novel.entries()) {\n const content = memory.content;\n let status: MemoryStatus = \"pending_review\";\n let trustAttributes: Record<string, string> = {};\n let confidence = 0.6;\n if (smart) {\n const evidence = findCorroboration(content, corroboration);\n const verdict = verdicts?.get(index);\n const trust = computeTrustScore({\n extractionConfidence: undefined,\n sourceTrust: settings.sourceTrust * NATIVE_TRUST_FACTOR,\n judgeVerdict: verdict,\n evidence,\n });\n const decision = decideSmart(trust, verdict, settings);\n if (decision.outcome === \"drop\") {\n // Deliberately NOT recorded in importedIds: a dropped native\n // fact re-fetches and re-scores on later syncs, so corpus or\n // corroboration support that arrives later can still admit it.\n // The judge verdict cache keeps repeated rejections cheap\n // (Cursor review on PR #1462).\n continue;\n }\n status = decision.outcome === \"active\" ? \"active\" : \"pending_review\";\n confidence = trust;\n trustAttributes = {\n trustScore: trust.toFixed(3),\n trustDecision: decision.reason,\n ...(verdict !== undefined ? { judgeVerdict: verdict } : {}),\n ...(evidence.corroboratedBySources.length > 0\n ? { corroboratedBySources: evidence.corroboratedBySources.join(\",\") }\n : {}),\n ...(evidence.supportingMemoryId !== undefined\n ? { supportingMemoryId: evidence.supportingMemoryId }\n : {}),\n };\n }\n const nativeEnvelope = composeSalvagedWearableEnvelope(\n {\n content,\n category: \"fact\",\n confidence,\n tags: [\n ...new Set([\n ...(memory.tags ?? []),\n WEARABLE_SOURCE_PREFIX,\n wearableSourceLabel(sourceId),\n \"native-import\",\n ]),\n ],\n validAt: memory.createdIso,\n structuredAttributes: {\n wearableSource: sourceId,\n wearableNativeId: memory.id,\n ...trustAttributes,\n },\n },\n { source: `${wearableSourceLabel(sourceId)}:native` },\n );\n const nativeWriteResult = await deps.writer.writeSealedMemory(nativeEnvelope, {\n importance: scoreImportance(content, \"fact\", memory.tags ?? []),\n contentHashSource: content,\n status,\n });\n // #1645: a tombstone-blocked native import lands pending_review (no active\n // copy) — count it as blocked, NOT imported. Still record the provider id in\n // importedIds so the next sync doesn't re-import and duplicate the pending\n // row; the block is permanent for this content.\n if (nativeWriteResult.tombstoneBlocked) {\n tombstoneBlocked += 1;\n } else {\n imported += 1;\n }\n importedIds.push(memory.id);\n }\n return { imported, tombstoneBlocked, importedIds, warnings };\n}\n","/**\n * Wearable sync pipeline — pull → clean → label → correct → store →\n * (optionally) remember, for one source.\n *\n * Stage order per conversation is deliberate:\n * 1. off-the-record elision (before anything can persist the span)\n * 2. cleanup (merging first lets redaction see numbers\n * that ASR split across segments)\n * 3. redaction (built-in + user patterns)\n * 4. corrections (user-specific word fixes)\n *\n * Day files are rebuilt idempotently; the per-day body hash recorded in\n * sync state lets unchanged days skip both the rewrite and the\n * (expensive) memory extraction. Sync state advances only after every\n * write for the run has succeeded.\n */\n\nimport { fillWearableConversationLocations } from \"../location/tagging.js\";\nimport { cleanConversation } from \"./cleanup.js\";\nimport { describeErrorForOperator, WearablesInputError } from \"./errors.js\";\nimport {\n applyCorrections,\n compileCorrectionRules,\n loadCorrectionsFile,\n type CompiledCorrectionRule,\n} from \"./corrections.js\";\nimport {\n composeDayTranscriptBody,\n composeDayTranscriptMeta,\n hashTranscriptBody,\n isValidTranscriptDate,\n serializeDayTranscript,\n} from \"./day-store.js\";\nimport {\n generateWearableMemories,\n importNativeMemories,\n writeDailyDigestMemory,\n type WearableMemoryGenDeps,\n} from \"./memory-gen.js\";\nimport { tokenizeDayBody, type CorroborationContext } from \"./trust.js\";\nimport {\n applyOffTheRecord,\n compileOffTheRecordMarkers,\n compileRedactionPatterns,\n redactText,\n type CompiledOffTheRecordMarkers,\n} from \"./redaction.js\";\nimport { loadSpeakerRegistry } from \"./speakers.js\";\nimport {\n loadSyncState,\n saveSyncState,\n updateSourceSyncState,\n} from \"./sync-state.js\";\nimport type { LocationConfig } from \"../location/types.js\";\nimport type {\n WearableConversation,\n WearableSourceConnector,\n WearableSourceSettings,\n WearableSyncSummary,\n WearablesConfig,\n} from \"./types.js\";\n\n/**\n * Pathological-provider backstop on pagination loops. Day fetches are\n * intentionally UNLIMITED for real data — a full day must never be\n * truncated — so runaway protection comes from cursor-cycle detection\n * plus this far-out-of-band ceiling, not from a content-sized cap.\n */\nconst PAGE_SAFETY_CEILING = 10_000;\n/** Default lookback window (today + yesterday) for unscoped syncs. */\nconst DEFAULT_SYNC_DAYS = 2;\nconst MAX_SYNC_DAYS = 90;\n\nexport interface WearableSyncOptions {\n /** Sync exactly this day (YYYY-MM-DD). Overrides `days`. */\n date?: string;\n /** Lookback window in days ending today (default 2, max 90). */\n days?: number;\n /** Re-run memory extraction even for unchanged days. */\n forceMemories?: boolean;\n signal?: AbortSignal;\n}\n\nexport interface WearableSyncDeps {\n /** Memory dir for state files (speakers, corrections, sync ledger). */\n memoryDir: string;\n /** Read the stored content hash for a day file, if present. */\n readDayContentHash(sourceId: string, date: string): Promise<string | null>;\n /** Persist a serialized day-transcript file (atomic). */\n writeDayTranscript(\n sourceId: string,\n date: string,\n serialized: string,\n ): Promise<void>;\n /**\n * Optional hook fired once after the sync wrote ANYTHING the search\n * index should see: day transcripts, created memories, in-place\n * promotions, or native imports. (A cross-source invalidation can\n * promote memories on a run with zero transcript writes — the index\n * must still refresh.)\n */\n afterWrites?(): Promise<void>;\n /**\n * Memory-generation dependencies, or null when no extraction engine\n * is available in this context (transcripts still sync; memory\n * creation is skipped with a warning when the mode wanted it).\n */\n memoryGen: WearableMemoryGenDeps | null;\n /**\n * Same-day transcript bodies from OTHER sources, for cross-device\n * corroboration in smart mode. Absent disables the boost.\n */\n readOtherSourceDayBodies?(\n date: string,\n excludeSource: string,\n ): Promise<Map<string, string>>;\n /**\n * Existing memories usable as support evidence: status \"active\" or\n * \"pending_review\" (explicit allow-list — a borderline fact observed\n * again on a later day is repetition signal, and the +0.10 boost is\n * how it earns promotion). Rejected/quarantined/superseded/archived/\n * forgotten rows are never support evidence.\n */\n listSupportMemories?(): Promise<Array<{ id: string; content: string }>>;\n /** Clock injection for tests. */\n now?: () => Date;\n /**\n * Location config for the missing-only conversation location fill\n * (issue #2925). Absent, or location tagging disabled, changes nothing.\n */\n locationConfig?: LocationConfig;\n}\n\n/** Format a Date as YYYY-MM-DD in the given IANA timezone. */\nexport function dateInTimezone(date: Date, timezone: string): string {\n try {\n const parts = new Intl.DateTimeFormat(\"en-CA\", {\n timeZone: timezone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n }).formatToParts(date);\n const get = (type: string) =>\n parts.find((part) => part.type === type)?.value ?? \"\";\n return `${get(\"year\")}-${get(\"month\")}-${get(\"day\")}`;\n } catch {\n return date.toISOString().slice(0, 10);\n }\n}\n\n/** Resolve the list of days to sync, oldest first. */\nexport function resolveSyncDates(\n options: WearableSyncOptions,\n timezone: string,\n now: Date,\n): string[] {\n if (options.date !== undefined) {\n if (!isValidTranscriptDate(options.date)) {\n throw new WearablesInputError(\n `wearables sync: invalid date '${options.date}' — expected YYYY-MM-DD`,\n );\n }\n return [options.date];\n }\n let days = DEFAULT_SYNC_DAYS;\n if (options.days !== undefined) {\n if (\n !Number.isFinite(options.days) ||\n !Number.isInteger(options.days) ||\n options.days < 1 ||\n options.days > MAX_SYNC_DAYS\n ) {\n throw new WearablesInputError(\n `wearables sync: invalid days '${options.days}' — expected an integer between 1 and ${MAX_SYNC_DAYS}`,\n );\n }\n days = options.days;\n }\n // Walk back by CALENDAR days from today's local date — subtracting\n // fixed 24h intervals from the wall clock can skip a local day\n // around DST transitions (Codex P2 on PR #1458).\n const dates: string[] = [];\n let cursor = dateInTimezone(now, timezone);\n for (let count = 0; count < days; count++) {\n dates.unshift(cursor);\n cursor = previousIsoDate(cursor);\n }\n return dates;\n}\n\n/** Previous calendar date in pure date arithmetic (no DST exposure). */\nfunction previousIsoDate(date: string): string {\n const parsed = new Date(`${date}T00:00:00Z`);\n parsed.setUTCDate(parsed.getUTCDate() - 1);\n return parsed.toISOString().slice(0, 10);\n}\n\nasync function fetchAllConversationsForDate(\n connector: WearableSourceConnector,\n date: string,\n timezone: string,\n signal: AbortSignal | undefined,\n warnings: string[],\n): Promise<{ conversations: WearableConversation[]; partial: boolean }> {\n // Keyed by conversation id: a looping or overlapping provider can\n // re-serve rows it already returned, and appending blindly would\n // store the same conversation twice in the day file (Cursor review\n // on PR #1464). Map keeps first-seen order; a re-served id replaces\n // its entry in place, so the provider's LATEST version of a\n // conversation wins — exactly what the current-day refresh wants.\n const byId = new Map<string, WearableConversation>();\n let cursor: string | null | undefined = undefined;\n const seenCursors = new Set<string>();\n const collect = () => [...byId.values()];\n for (let page = 0; page < PAGE_SAFETY_CEILING; page++) {\n const result = await connector.fetchConversations({\n date,\n timezone,\n cursor,\n signal,\n });\n for (const conversation of result.conversations) {\n byId.set(conversation.id, conversation);\n }\n if (!result.nextCursor) return { conversations: collect(), partial: false };\n // A repeated cursor means the provider's pagination is looping —\n // following it again would refetch the same page forever.\n if (seenCursors.has(result.nextCursor)) {\n warnings.push(\n `${connector.id}: provider pagination repeated cursor on ${date} — stopped to avoid an infinite loop; day may be partially synced (every sync refetches and re-warns while the provider misbehaves)`,\n );\n return { conversations: collect(), partial: true };\n }\n seenCursors.add(result.nextCursor);\n cursor = result.nextCursor;\n }\n warnings.push(\n `${connector.id}: stopped paginating ${date} after the ${PAGE_SAFETY_CEILING}-page safety ceiling — day may be partially synced (every sync refetches and re-warns while this persists)`,\n );\n return { conversations: collect(), partial: true };\n}\n\n/** Visible marker appended to day files whose fetch hit the page cap. */\nconst PARTIAL_DAY_MARKER =\n \"\\n*Note: pagination safety cap reached during sync — this day may be incomplete.*\\n\";\n\n/**\n * Explicit replacement body for a day whose provider data exists but\n * whose every segment was elided or dropped (all off-the-record, all\n * ASR garbage). Written so previously-stored content for the day stops\n * being searchable instead of lingering as a stale transcript.\n */\nfunction emptyDayBody(sourceId: string, date: string): string {\n return (\n `# ${sourceId} transcript — ${date}\\n\\n` +\n \"_No storable conversation content for this day (all segments were elided or dropped)._\\n\"\n );\n}\n\ninterface CleanedDay {\n conversations: WearableConversation[];\n segmentsKept: number;\n segmentsDropped: number;\n redactions: number;\n correctionsApplied: number;\n}\n\nfunction cleanDay(\n raw: WearableConversation[],\n sourceId: string,\n settings: WearableSourceSettings,\n config: WearablesConfig,\n userRedaction: RegExp[],\n correctionRules: CompiledCorrectionRule[],\n offTheRecordMarkers: CompiledOffTheRecordMarkers,\n): CleanedDay {\n const { offTheRecordEnabled, redactionEnabled } = config;\n const out: CleanedDay = {\n conversations: [],\n segmentsKept: 0,\n segmentsDropped: 0,\n redactions: 0,\n correctionsApplied: 0,\n };\n for (const conversation of raw) {\n let current = conversation;\n if (offTheRecordEnabled) {\n const otr = applyOffTheRecord(current, offTheRecordMarkers);\n current = otr.conversation;\n out.segmentsDropped += otr.droppedSegments;\n }\n const cleaned = cleanConversation(\n current,\n settings.cleanup,\n config.fillerTokens,\n );\n current = cleaned.conversation;\n out.segmentsDropped += cleaned.droppedSegments;\n\n const segments = current.segments.map((segment) => {\n let text = segment.text;\n if (redactionEnabled) {\n const redacted = redactText(text, userRedaction);\n text = redacted.text;\n out.redactions += redacted.redactions;\n }\n const corrected = applyCorrections(text, correctionRules, sourceId);\n out.correctionsApplied += corrected.applied;\n return { ...segment, text: corrected.text };\n });\n current = { ...current, segments };\n\n if (current.segments.length > 0) {\n out.conversations.push(current);\n out.segmentsKept += current.segments.length;\n }\n }\n return out;\n}\n\n/** Sync one source. */\nexport async function syncWearableSource(\n connector: WearableSourceConnector,\n settings: WearableSourceSettings,\n config: WearablesConfig,\n options: WearableSyncOptions,\n deps: WearableSyncDeps,\n): Promise<WearableSyncSummary> {\n const now = deps.now ? deps.now() : new Date();\n const timezone = config.timezone ?? defaultTimezone();\n const dates = resolveSyncDates(options, timezone, now);\n\n const summary: WearableSyncSummary = {\n source: connector.id,\n days: dates,\n conversations: 0,\n segmentsKept: 0,\n segmentsDropped: 0,\n redactions: 0,\n correctionsApplied: 0,\n transcriptsWritten: [],\n memoriesCreated: 0,\n memoriesPromoted: 0,\n memoriesDemoted: 0,\n memoriesSkipped: 0,\n memoriesBlocked: 0,\n nativeMemoriesImported: 0,\n warnings: [],\n };\n\n const registry = await loadSpeakerRegistry(deps.memoryDir);\n const stateRules = await loadCorrectionsFile(deps.memoryDir);\n const correctionRules = [\n ...compileCorrectionRules(config.corrections, \"wearables.corrections\"),\n ...compileCorrectionRules(stateRules, \"state corrections\"),\n ];\n const userRedaction = compileRedactionPatterns(config.redactionPatterns);\n const offTheRecordMarkers = compileOffTheRecordMarkers(\n config.offTheRecordMarkers,\n );\n\n let syncState = await loadSyncState(deps.memoryDir);\n const previousState = syncState.sources[connector.id];\n const dayHashes: Record<string, string> = {};\n const memoryDayHashes: Record<string, string> = {};\n const failedMemoryDays: string[] = [];\n const importedNativeIds: string[] = [];\n\n for (const date of dates) {\n const fetched = await fetchAllConversationsForDate(\n connector,\n date,\n timezone,\n options.signal,\n summary.warnings,\n );\n const cleaned = cleanDay(\n fetched.conversations,\n connector.id,\n settings,\n config,\n userRedaction,\n correctionRules,\n offTheRecordMarkers,\n );\n summary.conversations += cleaned.conversations.length;\n summary.segmentsKept += cleaned.segmentsKept;\n summary.segmentsDropped += cleaned.segmentsDropped;\n summary.redactions += cleaned.redactions;\n summary.correctionsApplied += cleaned.correctionsApplied;\n\n // Missing-only location fill (issue #2925): run before the body is\n // composed so a qualifying dominant-overlap place reaches the day\n // transcript and memory headers exactly like a provider-supplied\n // location would. Source-provided values are never overwritten, and\n // the helper no-ops unless both location gates are on.\n if (deps.locationConfig) {\n await fillWearableConversationLocations(cleaned.conversations, {\n memoryDir: deps.memoryDir,\n config: deps.locationConfig,\n });\n }\n\n if (fetched.conversations.length === 0) {\n // No provider data at all. A transient provider hiccup can\n // legitimately produce an empty result, so an existing stored\n // transcript is never auto-deleted here — surface it instead.\n const existing = await deps.readDayContentHash(connector.id, date);\n if (existing !== null) {\n summary.warnings.push(\n `${connector.id}: provider returned no conversations for ${date} but a stored transcript exists — leaving it in place; delete the day file manually if the recordings were intentionally removed upstream`,\n );\n }\n continue;\n }\n\n // Provider data exists but cleanup/off-the-record elided all of it:\n // replace any stored transcript with an explicit empty-day file so\n // elided content stops being stored and searchable (Codex P2 on PR\n // #1458).\n const allElided = cleaned.conversations.length === 0;\n let body = allElided\n ? emptyDayBody(connector.id, date)\n : composeDayTranscriptBody(\n connector.id,\n date,\n timezone,\n cleaned.conversations,\n registry,\n );\n if (fetched.partial && !allElided) {\n body += PARTIAL_DAY_MARKER;\n }\n const bodyHash = hashTranscriptBody(body);\n // The on-disk file is the authority for the skip decision — a hash\n // remembered in sync state must never suppress recreating a day\n // file that was deleted or lost (Cursor review on PR #1458). The\n // state's dayHashes remain as bookkeeping only.\n const existingHash = await deps.readDayContentHash(connector.id, date);\n const changed = existingHash !== bodyHash;\n // An all-elided day only writes a replacement over an existing\n // file; it never creates an empty-day file from nothing.\n const shouldWrite = changed && (!allElided || existingHash !== null);\n\n if (shouldWrite) {\n const meta = composeDayTranscriptMeta(\n connector.id,\n date,\n timezone,\n cleaned.conversations,\n registry,\n body,\n now.toISOString(),\n );\n await deps.writeDayTranscript(\n connector.id,\n date,\n serializeDayTranscript(meta, body),\n );\n summary.transcriptsWritten.push(date);\n }\n dayHashes[date] = bodyHash;\n\n if (allElided) continue;\n\n const needsSmartContext =\n settings.memoryMode === \"smart\" || settings.importNativeMemories === \"smart\";\n\n // The memory pass runs when the day changed, when forced, or when\n // the last pass for this exact content didn't complete cleanly —\n // a sync that stored the transcript but failed mid-memory-write\n // self-heals on the next run instead of being frozen out by the\n // unchanged-day skip (Cursor review on PR #1458).\n const memoryPassComplete =\n previousState?.memoryDayHashes?.[date] === bodyHash;\n if (\n settings.memoryMode !== \"off\" &&\n (changed || options.forceMemories === true || !memoryPassComplete)\n ) {\n if (!deps.memoryGen) {\n summary.warnings.push(\n `${connector.id}: memoryMode is '${settings.memoryMode}' but no extraction engine is available in this context — transcripts synced, memories skipped`,\n );\n } else {\n // The whole memory pass (extraction, fact writes, digest) is\n // warn-and-retry rather than abort: the transcript is already\n // stored, and aborting here would leave any stale completion\n // record from an earlier clean run in place to mask the\n // failure (Kilo review on PR #1458 for the digest case; fact\n // writes share the same failure class). A clean pass records\n // completion; anything else clears it so the next sync\n // re-runs the day.\n let passClean = false;\n try {\n const corroboration = needsSmartContext\n ? await buildCorroborationContext(connector.id, date, deps)\n : undefined;\n const dayMemoryGen: WearableMemoryGenDeps = {\n ...deps.memoryGen,\n ...(corroboration !== undefined ? { corroboration } : {}),\n };\n const generated = await generateWearableMemories(\n connector.id,\n date,\n cleaned.conversations,\n settings,\n registry,\n dayMemoryGen,\n );\n summary.memoriesCreated += generated.created;\n summary.memoriesPromoted += generated.promoted;\n summary.memoriesDemoted += generated.demoted;\n summary.memoriesSkipped += generated.skipped;\n summary.memoriesBlocked += generated.tombstoneBlocked;\n summary.warnings.push(...generated.warnings);\n // Degraded-but-complete passes (e.g. judge unavailable) still\n // record completion — only an aborted extraction should force\n // the day to re-run on the next sync (Cursor review on PR\n // #1462).\n passClean = generated.completed;\n const { digestEnabled } = config;\n if (digestEnabled) {\n const wrote = await writeDailyDigestMemory(\n connector.id,\n date,\n cleaned.conversations,\n settings,\n registry,\n deps.memoryGen.writer,\n );\n if (wrote) summary.memoriesCreated += 1;\n }\n } catch (err) {\n passClean = false;\n summary.warnings.push(\n `${connector.id}: memory pass failed for ${date}: ${describeErrorForOperator(err)} — retries on the next sync`,\n );\n }\n if (passClean) {\n memoryDayHashes[date] = bodyHash;\n } else {\n failedMemoryDays.push(date);\n }\n }\n } else if (settings.memoryMode !== \"off\" && memoryPassComplete) {\n // Carry the completion record forward for unchanged days.\n memoryDayHashes[date] = bodyHash;\n }\n }\n\n if (\n settings.importNativeMemories !== \"off\" &&\n typeof connector.fetchNativeMemories === \"function\"\n ) {\n if (!deps.memoryGen) {\n summary.warnings.push(\n `${connector.id}: importNativeMemories is enabled but no memory writer is available in this context`,\n );\n } else {\n const alreadyImported = new Set(\n previousState?.importedNativeMemoryIds ?? [],\n );\n // Native memories carry no day, so same-day cross-device\n // corroboration does not apply to them — scoring a provider fact\n // against an arbitrary day's tokens would be wrong-day evidence\n // (Cursor review on PR #1462). They keep only the day-independent\n // existing-memory support boost.\n const nativeCorroboration =\n settings.importNativeMemories === \"smart\"\n ? {\n otherSourceDayTokens: new Map<string, Set<string>>(),\n existingMemories: deps.listSupportMemories\n ? await deps.listSupportMemories()\n : [],\n }\n : undefined;\n const nativeMemoryGen: WearableMemoryGenDeps = {\n ...deps.memoryGen,\n ...(nativeCorroboration !== undefined\n ? { corroboration: nativeCorroboration }\n : {}),\n };\n let cursor: string | null | undefined = undefined;\n const seenNativeCursors = new Set<string>();\n for (let page = 0; page < PAGE_SAFETY_CEILING; page++) {\n const result = await connector.fetchNativeMemories({\n cursor,\n signal: options.signal,\n });\n const imported = await importNativeMemories(\n connector.id,\n result.memories,\n alreadyImported,\n settings,\n nativeMemoryGen,\n );\n summary.warnings.push(...imported.warnings);\n summary.nativeMemoriesImported += imported.imported;\n summary.memoriesBlocked += imported.tombstoneBlocked;\n importedNativeIds.push(...imported.importedIds);\n for (const id of imported.importedIds) alreadyImported.add(id);\n if (!result.nextCursor) break;\n if (seenNativeCursors.has(result.nextCursor)) {\n summary.warnings.push(\n `${connector.id}: provider pagination repeated cursor during native-memory import — stopped to avoid an infinite loop; remaining items import on the next sync`,\n );\n break;\n }\n seenNativeCursors.add(result.nextCursor);\n cursor = result.nextCursor;\n if (page === PAGE_SAFETY_CEILING - 1) {\n summary.warnings.push(\n `${connector.id}: stopped native-memory import at the ${PAGE_SAFETY_CEILING}-page safety ceiling — remaining items import on the next sync`,\n );\n }\n }\n }\n }\n\n const wroteAnything =\n summary.transcriptsWritten.length > 0 ||\n summary.memoriesCreated > 0 ||\n summary.memoriesPromoted > 0 ||\n summary.memoriesDemoted > 0 ||\n summary.memoriesBlocked > 0 ||\n summary.nativeMemoriesImported > 0;\n if (wroteAnything && deps.afterWrites) {\n try {\n await deps.afterWrites();\n } catch (err) {\n summary.warnings.push(\n `search reindex failed (writes are stored and will index on the next update): ${describeErrorForOperator(err)}`,\n );\n }\n }\n\n // Watermark advances only now — after transcript writes, memory\n // writes, and native imports all succeeded.\n syncState = updateSourceSyncState(syncState, connector.id, {\n syncedAt: now.toISOString(),\n days: dates,\n dayHashes,\n memoryDayHashes,\n clearMemoryDays: failedMemoryDays,\n importedNativeMemoryIds: importedNativeIds,\n });\n // New same-day evidence invalidates OTHER sources' memory-pass\n // completion for the days this source just (re)wrote: their next\n // sync re-scores with this transcript available, and the promotion\n // path upgrades earlier borderline writes in place (Cursor review on\n // PR #1462).\n if (summary.transcriptsWritten.length > 0) {\n const cleared: typeof syncState.sources = {};\n for (const [otherId, otherState] of Object.entries(syncState.sources)) {\n if (otherId === connector.id || otherState.memoryDayHashes === undefined) {\n cleared[otherId] = otherState;\n continue;\n }\n const memoryDays = { ...otherState.memoryDayHashes };\n let touched = false;\n for (const date of summary.transcriptsWritten) {\n if (date in memoryDays) {\n delete memoryDays[date];\n touched = true;\n }\n }\n cleared[otherId] = touched\n ? { ...otherState, memoryDayHashes: memoryDays }\n : otherState;\n }\n syncState = { version: 1, sources: cleared };\n }\n await saveSyncState(deps.memoryDir, syncState);\n\n return summary;\n}\n\n/**\n * Assemble smart-mode corroboration evidence: other sources' same-day\n * transcript tokens + existing active memories. The memory list loads\n * fresh per day (not per run) so facts written on earlier days of a\n * multi-day backfill are visible as support evidence on later days —\n * the underlying readAllMemories is cached in storage and invalidated\n * by writes, so the per-day refresh is cheap (Cursor review on PR\n * #1462).\n */\nasync function buildCorroborationContext(\n sourceId: string,\n date: string,\n deps: WearableSyncDeps,\n): Promise<CorroborationContext> {\n const otherSourceDayTokens = new Map<string, Set<string>>();\n if (deps.readOtherSourceDayBodies) {\n const bodies = await deps.readOtherSourceDayBodies(date, sourceId);\n for (const [otherSource, body] of bodies) {\n otherSourceDayTokens.set(otherSource, tokenizeDayBody(body));\n }\n }\n const existingMemories = deps.listSupportMemories\n ? await deps.listSupportMemories()\n : [];\n return { otherSourceDayTokens, existingMemories };\n}\n\nexport function defaultTimezone(): string {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || \"UTC\";\n } catch {\n return \"UTC\";\n }\n}\n"],"mappings":"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","names":[]}
|