@remnic/core 9.69.39 → 9.69.40
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.d.ts +16 -14
- package/dist/access-admin-ops-surface.js +37 -37
- package/dist/access-authorization-probe.d.ts +16 -14
- package/dist/access-authorization-probe.js +38 -38
- package/dist/access-boundary.d.ts +16 -14
- package/dist/access-boundary.js +37 -37
- package/dist/access-cli.js +98 -97
- package/dist/access-cli.js.map +1 -1
- package/dist/access-coding-context-resolution.d.ts +1 -1
- package/dist/access-extraction-force-flush.d.ts +16 -14
- package/dist/access-extraction-force-flush.js +37 -37
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-briefing.d.ts +16 -14
- package/dist/access-http-briefing.js +38 -38
- package/dist/access-http-lcm-compaction.d.ts +16 -14
- package/dist/access-http-lifecycle-flush.d.ts +16 -14
- package/dist/access-http-offline-stream.d.ts +16 -14
- package/dist/access-http-query.d.ts +2 -2
- package/dist/access-http-query.js +39 -39
- package/dist/access-http.d.ts +17 -15
- package/dist/access-http.js +53 -53
- package/dist/access-identity-continuity-surface.d.ts +13 -12
- package/dist/access-identity-continuity-surface.js +37 -37
- package/dist/access-lcm-surface.d.ts +16 -14
- package/dist/access-lcm-surface.js +37 -37
- package/dist/access-mcp.d.ts +16 -14
- package/dist/access-mcp.js +45 -45
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-DY4z6L5X.d.ts → access-namespace-preflight-CKBumJ8W.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +37 -37
- package/dist/access-observe-transcript.d.ts +13 -12
- package/dist/access-observe-write-category.d.ts +1 -1
- package/dist/access-observe-write-category.js +2 -2
- package/dist/access-observe-write-surface.d.ts +16 -14
- package/dist/access-observe-write-surface.js +37 -37
- package/dist/access-offline-manifest.d.ts +37 -13
- package/dist/access-offline-manifest.js +7 -3
- package/dist/access-operations-batch.js +39 -39
- package/dist/access-operations.d.ts +16 -14
- package/dist/access-operations.js +44 -44
- package/dist/access-recall-concurrency.d.ts +16 -14
- package/dist/access-recall-concurrency.js +37 -37
- package/dist/access-recall-response.d.ts +16 -14
- package/dist/access-recall-response.js +37 -37
- package/dist/access-recall-surface.d.ts +16 -14
- package/dist/access-recall-surface.js +37 -37
- package/dist/access-schema.d.ts +5 -3
- package/dist/access-schema.js +1 -1
- package/dist/{access-service-MY4bsr_Q.d.ts → access-service-DJ3qogNt.d.ts} +8 -8
- package/dist/access-service-helpers.d.ts +16 -14
- package/dist/access-service.d.ts +16 -14
- package/dist/access-service.js +37 -37
- package/dist/access-surface-catalog.d.ts +16 -14
- package/dist/access-surface-catalog.js +39 -39
- package/dist/access-wearables-meetings-surface.d.ts +6 -5
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +6 -5
- package/dist/active-recall.js.map +1 -1
- package/dist/activity/digest.d.ts +1 -1
- package/dist/ambient-provenance.d.ts +1 -1
- package/dist/artifact-search.d.ts +1 -1
- package/dist/{auto-sync-RP4C6ZPF.js → auto-sync-D6BAPIJV.js} +5 -5
- package/dist/background-generation-config.d.ts +1 -1
- package/dist/background-generation-config.js +3 -1
- package/dist/background-generation.d.ts +1 -1
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +13 -12
- package/dist/briefing-window.d.ts +2 -2
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +15 -15
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer-turn-helpers.d.ts +1 -1
- package/dist/buffer.d.ts +2 -2
- package/dist/buffer.js +5 -5
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +3 -2
- package/dist/calibration.js +2 -2
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-CW87xGx8.d.ts → catalog-BHXkBVhg.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +19 -18
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-B36GUU6T.js → chunk-23HFXQ5J.js} +36 -3
- package/dist/chunk-23HFXQ5J.js.map +1 -0
- package/dist/{chunk-YFTNZMGR.js → chunk-27NO2KVB.js} +31 -8
- package/dist/chunk-27NO2KVB.js.map +1 -0
- package/dist/{chunk-JTQR6L2N.js → chunk-2SKMZUOW.js} +2 -2
- package/dist/{chunk-GG5FHZJJ.js → chunk-2SNCQHJZ.js} +2 -2
- package/dist/{chunk-O76F5QPP.js → chunk-3QEVOQF5.js} +2 -2
- package/dist/{chunk-3N3PBBEH.js → chunk-3XPN7AVY.js} +2 -2
- package/dist/{chunk-X4TD4O2F.js → chunk-46Z3VGWF.js} +2 -2
- package/dist/{chunk-2ZTDRNCS.js → chunk-4CMXI55L.js} +2 -2
- package/dist/{chunk-FWWWUPOE.js → chunk-5LYSEZOF.js} +18 -9
- package/dist/chunk-5LYSEZOF.js.map +1 -0
- package/dist/{chunk-IQEJIOEU.js → chunk-62HFZNCJ.js} +17 -10
- package/dist/chunk-62HFZNCJ.js.map +1 -0
- package/dist/{chunk-YKREX63Z.js → chunk-6L2II3KS.js} +5 -3
- package/dist/chunk-6L2II3KS.js.map +1 -0
- package/dist/{chunk-MUTVRKKY.js → chunk-7D6SER6A.js} +8 -8
- package/dist/{chunk-WBM2MVBK.js → chunk-7Z54NXVS.js} +2 -2
- package/dist/{chunk-ACY4ZPF7.js → chunk-AEOOGXMQ.js} +10 -5
- package/dist/chunk-AEOOGXMQ.js.map +1 -0
- package/dist/{chunk-UHRZ324M.js → chunk-BGRTZOJB.js} +3 -3
- package/dist/{chunk-ZEGL7YCC.js → chunk-BXIJSPCL.js} +2 -2
- package/dist/{chunk-JKKII6GX.js → chunk-BYQX4IOG.js} +2 -2
- package/dist/{chunk-WIWLX65T.js → chunk-C5R73BON.js} +2 -2
- package/dist/{chunk-BD7JTBYP.js → chunk-D7MAYFQP.js} +2 -2
- package/dist/{chunk-TNZNSMVJ.js → chunk-DAZANIYU.js} +249 -295
- package/dist/chunk-DAZANIYU.js.map +1 -0
- package/dist/{chunk-LMLGBG6H.js → chunk-DFVSJFHK.js} +2 -2
- package/dist/{chunk-3NPNBVFR.js → chunk-ES5BUPYB.js} +2 -2
- package/dist/{chunk-MVAQ2WM3.js → chunk-G2XZFGYN.js} +6 -2
- package/dist/chunk-G2XZFGYN.js.map +1 -0
- package/dist/{chunk-TNSCIPBQ.js → chunk-G6NQ3L4T.js} +2 -2
- package/dist/{chunk-5N4MZLEH.js → chunk-GO5LOHCD.js} +2 -2
- package/dist/{chunk-AECJY3GJ.js → chunk-GSSDHAYH.js} +2 -2
- package/dist/{chunk-TSP5WH72.js → chunk-HJUP3K6L.js} +2 -2
- package/dist/{chunk-AAZEKXUP.js → chunk-ICNJ6J46.js} +2 -2
- package/dist/{chunk-6EHM2AV7.js → chunk-IMBQFHRV.js} +2 -2
- package/dist/{chunk-4RZLTZVM.js → chunk-IOPIGX7Y.js} +1 -1
- package/dist/{chunk-EYKIDK33.js → chunk-IS2IDFJP.js} +13 -13
- package/dist/{chunk-VJIE4D2I.js → chunk-J4WGTN2N.js} +2 -2
- package/dist/{chunk-NXGSONWM.js → chunk-J7FVMLBR.js} +2 -2
- package/dist/{chunk-VSL63CFJ.js → chunk-JAJIMTFB.js} +6 -6
- package/dist/{chunk-4JVQRFZD.js → chunk-JOP7W24J.js} +9 -9
- package/dist/{chunk-YUW66UAH.js → chunk-JUDT33RX.js} +2 -2
- package/dist/{chunk-QHT7MOUF.js → chunk-K4A6UVPB.js} +22 -11
- package/dist/chunk-K4A6UVPB.js.map +1 -0
- package/dist/{chunk-D545Z7UH.js → chunk-K4JKAXMO.js} +2 -2
- package/dist/{chunk-BV4ZKIZH.js → chunk-K75A5XWU.js} +3 -2
- package/dist/{chunk-3CBI2ZSA.js → chunk-KG2OOAFF.js} +20 -12
- package/dist/chunk-KG2OOAFF.js.map +1 -0
- package/dist/{chunk-M6HBPLBA.js → chunk-KOTG3RXE.js} +2 -2
- package/dist/{chunk-CWTCMZGE.js → chunk-KTOXQMW3.js} +119 -3
- package/dist/chunk-KTOXQMW3.js.map +1 -0
- package/dist/{chunk-OB3XMWX5.js → chunk-KVO2BTA4.js} +2 -2
- package/dist/{chunk-GI4ZBWPQ.js → chunk-KY236SP5.js} +3 -3
- package/dist/{chunk-4BCHFSC7.js → chunk-LFVZD5EC.js} +3 -3
- package/dist/{chunk-O4M4BDIJ.js → chunk-LKFVVN7R.js} +2 -2
- package/dist/{chunk-IYKZJVL5.js → chunk-MDIEES23.js} +1 -1
- package/dist/chunk-MDIEES23.js.map +1 -0
- package/dist/{chunk-4RX5O34R.js → chunk-MJC7UKH7.js} +2 -2
- package/dist/{chunk-CZTPU65V.js → chunk-MVG3KXON.js} +63 -16
- package/dist/chunk-MVG3KXON.js.map +1 -0
- package/dist/{chunk-YDVAM7S4.js → chunk-NETQXOPY.js} +2 -2
- package/dist/chunk-NETQXOPY.js.map +1 -0
- package/dist/{chunk-UZZ7SQT3.js → chunk-NVVZPBKI.js} +2 -2
- package/dist/{chunk-BLDBT6HZ.js → chunk-PJRKLB4Z.js} +18 -2
- package/dist/chunk-PJRKLB4Z.js.map +1 -0
- package/dist/{chunk-VBSNUH4J.js → chunk-PKUJNICO.js} +2 -2
- package/dist/{chunk-OPBFP4TF.js → chunk-QE6YVNP5.js} +7 -7
- package/dist/{chunk-WKDFJXW5.js → chunk-QM2SPLKQ.js} +1 -1
- package/dist/chunk-QM2SPLKQ.js.map +1 -0
- package/dist/{chunk-ENBPQEVU.js → chunk-QPVNK2VE.js} +2 -2
- package/dist/{chunk-RLJHJQUY.js → chunk-QS5Q5RBV.js} +2 -2
- package/dist/{chunk-3EKVN2OP.js → chunk-RDLPFM7I.js} +2 -2
- package/dist/{chunk-F4EHXVP7.js → chunk-RJQAP333.js} +2 -2
- package/dist/{chunk-QTQ6XKGT.js → chunk-RLU2Y5GV.js} +4 -4
- package/dist/chunk-RLU2Y5GV.js.map +1 -0
- package/dist/{chunk-5SPCMAUC.js → chunk-ROEFLU37.js} +4 -4
- package/dist/{chunk-UZEHHXYC.js → chunk-SFHLQPBF.js} +22 -22
- package/dist/chunk-SK7D34OL.js +68 -0
- package/dist/chunk-SK7D34OL.js.map +1 -0
- package/dist/{chunk-ZBW7A667.js → chunk-SUEOG2S4.js} +2 -2
- package/dist/{chunk-Q7CLGFA7.js → chunk-TEMKBSV6.js} +2 -2
- package/dist/{chunk-LQNRVPTV.js → chunk-THTF2W7M.js} +151 -118
- package/dist/chunk-THTF2W7M.js.map +1 -0
- package/dist/{chunk-GMIQV6DD.js → chunk-TO36VPZI.js} +2 -2
- package/dist/{chunk-6YAIN5XJ.js → chunk-TZ44E732.js} +44 -6
- package/dist/chunk-TZ44E732.js.map +1 -0
- package/dist/{chunk-JXN77SGO.js → chunk-U2VDCFXW.js} +1 -1
- package/dist/chunk-U2VDCFXW.js.map +1 -0
- package/dist/{chunk-LPYYY4AP.js → chunk-UZ6JYPJ6.js} +500 -218
- package/dist/chunk-UZ6JYPJ6.js.map +1 -0
- package/dist/{chunk-FXHOZGSE.js → chunk-V2ZA4YWE.js} +2 -2
- package/dist/{chunk-XH3HGI2N.js → chunk-VHKXRYQN.js} +2 -2
- package/dist/{chunk-GAS7KYC7.js → chunk-VKWFNPBH.js} +2 -2
- package/dist/{chunk-6H2G7RFT.js → chunk-VNQRRUNS.js} +11 -10
- package/dist/chunk-VNQRRUNS.js.map +1 -0
- package/dist/{chunk-IL6ODG6M.js → chunk-VQWBUPSJ.js} +2 -2
- package/dist/{chunk-5YCNDC3C.js → chunk-VQYWKCDR.js} +2 -2
- package/dist/{chunk-54NX5EG4.js → chunk-W23JARAQ.js} +2 -2
- package/dist/{chunk-YQNNHLCG.js → chunk-WPGR3RW4.js} +2 -2
- package/dist/{chunk-FNHLI2RE.js → chunk-WZYHPAG5.js} +3 -3
- package/dist/{chunk-5HYUUFMQ.js → chunk-XCPNH2MG.js} +259 -10
- package/dist/chunk-XCPNH2MG.js.map +1 -0
- package/dist/{chunk-TA4C3XB7.js → chunk-XM46Y66S.js} +2 -2
- package/dist/{chunk-PCOVEDPK.js → chunk-YRIFXXMI.js} +3 -3
- package/dist/{chunk-TRLOFIQC.js → chunk-YTK5MVNF.js} +2 -2
- package/dist/{chunk-Q432REND.js → chunk-YTZN2UZO.js} +2 -2
- package/dist/{chunk-6AGWBKQU.js → chunk-Z4GUX2M6.js} +2 -2
- package/dist/{chunk-AENM6GKU.js → chunk-ZAEPD4C2.js} +2 -2
- package/dist/{chunk-KQW3YGEN.js → chunk-ZRMMXWYI.js} +2 -2
- package/dist/{chunk-P4ZAEYKG.js → chunk-ZX3AYNNV.js} +6 -3
- package/dist/chunk-ZX3AYNNV.js.map +1 -0
- package/dist/{chunk-MCRVQ5YQ.js → chunk-ZZCR2QYL.js} +2 -2
- package/dist/{cli-DGq0MNpO.d.ts → cli-BWri5-am.d.ts} +5 -5
- package/dist/cli.d.ts +18 -16
- package/dist/cli.js +75 -74
- package/dist/coding/export-okf-codegraph.d.ts +1 -1
- package/dist/coding/export-okf-codegraph.js +14 -14
- package/dist/coding/pre-action-gate.d.ts +1 -1
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +15 -15
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +6 -5
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +15 -15
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +15 -15
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/contradiction/index.d.ts +6 -5
- package/dist/converge-config.d.ts +1 -1
- package/dist/convergence-refresh.d.ts +2 -2
- package/dist/convergence-refresh.js +17 -17
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/corpus-watermark.d.ts +1 -1
- package/dist/corpus-watermark.js +17 -17
- package/dist/cross-lingual-recall.d.ts +1 -1
- package/dist/day-summary.d.ts +1 -1
- package/dist/deep-recall-http-glue.d.ts +16 -14
- package/dist/deep-recall-http-glue.js +2 -2
- package/dist/deep-recall-mcp-tools.d.ts +16 -14
- package/dist/deep-recall-policy-llm.d.ts +4 -3
- package/dist/deep-recall-policy-llm.js +3 -3
- package/dist/deep-recall-seeds.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/dependency-propagation-config.d.ts +1 -1
- package/dist/{dependency-propagation-delivery-Cq1ekOwV.d.ts → dependency-propagation-delivery-ueZlKTQB.d.ts} +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-origin-fields.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +2 -2
- package/dist/entity-retrieval.js +15 -15
- package/dist/entity-schema.d.ts +1 -1
- package/dist/episodic-context.d.ts +1 -1
- package/dist/eval-trajectory-contracts.d.ts +1 -1
- package/dist/eval-trajectory-store.d.ts +1 -1
- package/dist/eval-trajectory.d.ts +1 -1
- package/dist/explicit-capture.d.ts +13 -12
- package/dist/external-wiki-access.d.ts +16 -14
- package/dist/external-wiki-access.js +38 -38
- package/dist/external-wiki-collection-registration.d.ts +1 -1
- package/dist/external-wiki-collection.d.ts +1 -1
- package/dist/external-wiki-mcp-tools.d.ts +16 -14
- package/dist/extraction-error-classification.d.ts +14 -3
- package/dist/extraction-error-classification.js +5 -3
- package/dist/extraction-faithfulness.d.ts +4 -3
- package/dist/extraction-faithfulness.js +3 -3
- package/dist/extraction-judge-telemetry.d.ts +4 -3
- package/dist/extraction-judge-training.d.ts +4 -3
- package/dist/extraction-judge.d.ts +4 -3
- package/dist/extraction-judge.js +3 -3
- package/dist/extraction-liveness.d.ts +1 -1
- package/dist/extraction-normalization.d.ts +1 -1
- package/dist/extraction-prompt.d.ts +1 -1
- package/dist/extraction-source-grounding-rules.d.ts +1 -1
- package/dist/extraction-source-grounding.d.ts +1 -1
- package/dist/extraction-span-config.d.ts +1 -1
- package/dist/extraction-span-materialize.d.ts +1 -1
- package/dist/extraction.d.ts +4 -4
- package/dist/extraction.js +25 -23
- package/dist/fallback-llm-codex-subscription.d.ts +52 -7
- package/dist/fallback-llm-codex-subscription.js +17 -1
- package/dist/fallback-llm.d.ts +25 -28
- package/dist/fallback-llm.js +4 -3
- package/dist/{generic-recall-paths-BcySh_f9.d.ts → generic-recall-paths-CpyVmA88.d.ts} +1 -1
- package/dist/graph-append-error.d.ts +1 -1
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-jsonl.d.ts +1 -1
- package/dist/graph-path-reconstruction.d.ts +1 -1
- package/dist/graph-path-scoring.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/importers/index.d.ts +1 -1
- package/dist/importers/test-utils.d.ts +1 -1
- package/dist/in-flight-reads.d.ts +1 -1
- package/dist/{index-DWOb4wz9.d.ts → index-BvUA4S7M.d.ts} +3 -3
- package/dist/index.d.ts +79 -39
- package/dist/index.js +119 -103
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/index.js +3 -3
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm-BsJGulur.d.ts +247 -0
- package/dist/local-llm-config.d.ts +4 -1
- package/dist/local-llm-config.js +4 -2
- package/dist/local-llm-helpers.d.ts +18 -69
- package/dist/local-llm-helpers.js +1 -1
- package/dist/local-llm.d.ts +3 -170
- package/dist/local-llm.js +2 -2
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/location/index.d.ts +3 -3
- package/dist/location/index.js +17 -17
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +15 -15
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +15 -15
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +16 -16
- package/dist/{maintenance-CW9wCzMP.d.ts → maintenance-CYubtIRr.d.ts} +9 -3
- package/dist/mcp-memory-inspector-app.d.ts +16 -14
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-controller.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-snapshot.d.ts +1 -1
- package/dist/memory-subject-cli.d.ts +16 -14
- package/dist/memory-subject.d.ts +16 -14
- package/dist/memory-worth-outcomes.d.ts +2 -2
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +3 -3
- package/dist/namespaces/migrate.js +16 -16
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +12 -1
- package/dist/namespaces/search.js +9 -9
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +15 -15
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/operator-doctor-corpus.d.ts +1 -1
- package/dist/operator-doctor-corpus.js +18 -18
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +29 -28
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -4
- package/dist/orchestration/maintenance.js +26 -26
- package/dist/{orchestrator-ComHLm49.d.ts → orchestrator-BWDRJ_0d.d.ts} +24 -11
- package/dist/orchestrator.d.ts +13 -12
- package/dist/orchestrator.js +101 -98
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-CiYcqqhl.d.ts → pipeline-7JMn2yMH.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/proactive-contention.d.ts +2 -2
- package/dist/provenance.d.ts +1 -1
- package/dist/{public-http-CuK4_Cb1.d.ts → public-http-CJWs3mo9.d.ts} +1 -1
- package/dist/{qmd-qXD4vatU.d.ts → qmd-CxCH9G9m.d.ts} +2 -2
- package/dist/qmd-preflight.d.ts +2 -2
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +2 -2
- package/dist/qmd.js +2 -2
- package/dist/recall-concurrency-config.d.ts +1 -1
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-memory-map.d.ts +1 -1
- package/dist/recall-navigate-access.d.ts +1 -1
- package/dist/recall-navigate-access.js +2 -2
- package/dist/recall-navigate-disclosure.d.ts +1 -1
- package/dist/recall-navigate-disclosure.js +1 -1
- package/dist/recall-navigate.d.ts +1 -1
- package/dist/recall-navigate.js +2 -2
- package/dist/recall-planner-llm.d.ts +3 -2
- package/dist/recall-planner-llm.js +4 -3
- package/dist/recall-planner-llm.js.map +1 -1
- package/dist/recall-state-view-admission.d.ts +13 -3
- package/dist/recall-state-view-admission.js +8 -8
- package/dist/recall-state-view-anchors.d.ts +1 -1
- package/dist/recall-state-view-anchors.js +2 -2
- package/dist/recall-state-view-inject.js +2 -2
- package/dist/recall-state-view-render.js +2 -2
- package/dist/recall-state-view-widen.js +2 -2
- package/dist/recall-state-view-wire.js +3 -3
- package/dist/recall-state-view.d.ts +1 -0
- package/dist/recall-state-view.js +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-timings.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +2 -2
- package/dist/reconcile/cursor.d.ts +1 -1
- package/dist/reconcile/manifest.d.ts +1 -1
- package/dist/reconcile/plan.d.ts +1 -1
- package/dist/replay/normalizers/chatgpt.d.ts +1 -1
- package/dist/replay/normalizers/claude.d.ts +1 -1
- package/dist/replay/normalizers/openclaw.d.ts +1 -1
- package/dist/replay/normalizers/shared.d.ts +1 -1
- package/dist/replay/runner.d.ts +1 -1
- package/dist/replay/types.d.ts +1 -1
- package/dist/replica-divergence.d.ts +1 -1
- package/dist/replica-peers-config.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +7 -6
- package/dist/retrieval-agents.d.ts +2 -2
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/salvage-envelope.d.ts +1 -1
- package/dist/{scope-profiles-BT9rqC3w.d.ts → scope-profiles-BGAUqhBR.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +8 -8
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +10 -10
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/lancedb-backend.js +2 -2
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.js +2 -2
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/orama-backend.js +2 -2
- package/dist/search/port.d.ts +2 -2
- package/dist/search/port.js +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/search/remote-backend.js +2 -2
- package/dist/{semantic-consolidation-CmZi7thY.d.ts → semantic-consolidation-CCeRD9pV.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +16 -16
- package/dist/semantic-rule-promotion.js +15 -15
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +15 -15
- package/dist/{service-CLkA7AQB.d.ts → service-BwpzRduk.d.ts} +2 -2
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/source-agent-qualifier.d.ts +1 -1
- package/dist/source-agent-qualifier.js +18 -18
- package/dist/standup/build.js +15 -15
- package/dist/{storage-wvX01lht.d.ts → storage-BprAGBG0.d.ts} +2 -1
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +14 -14
- package/dist/subject-config.d.ts +1 -1
- package/dist/summarizer.d.ts +1 -1
- package/dist/summarizer.js +5 -5
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/support-passport/index.d.ts +18 -17
- package/dist/support-passport/index.js +40 -40
- package/dist/task-llm-config.d.ts +16 -0
- package/dist/task-llm-config.js +12 -0
- package/dist/task-llm-config.js.map +1 -0
- package/dist/temporal-supersession.d.ts +2 -2
- package/dist/temporal-timeline-recall.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +2 -2
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/export-okf.d.ts +1 -1
- package/dist/transfer/export-okf.js +15 -15
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-Cyr32ABj.d.ts → types-nJV23pa3.d.ts} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +15 -15
- package/dist/who-knows-cli.d.ts +16 -14
- package/dist/who-knows-cli.js +16 -16
- package/dist/who-knows.d.ts +16 -14
- package/dist/who-knows.js +15 -15
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +2 -2
- package/src/access-category-alias.test.ts +93 -0
- package/src/access-offline-manifest.ts +52 -2
- package/src/access-schema.ts +9 -4
- package/src/activity/config.test.ts +61 -0
- package/src/activity/config.ts +40 -1
- package/src/activity/timeline/analysis-provider.ts +8 -2
- package/src/activity/timeline/analysis-run.test.ts +82 -0
- package/src/activity/timeline/publish-cli.test.ts +30 -0
- package/src/activity/timeline/publish-cli.ts +7 -0
- package/src/activity/timeline/query.ts +8 -1
- package/src/activity/timeline/regenerate.test.ts +82 -2
- package/src/activity/timeline/regenerate.ts +7 -1
- package/src/activity/vault-frontmatter.test.ts +99 -1
- package/src/activity/vault-frontmatter.ts +131 -0
- package/src/activity/vault-publish.ts +6 -0
- package/src/activity/vault-publisher.test.ts +172 -0
- package/src/activity/vault-publisher.ts +69 -15
- package/src/activity/vault-region.test.ts +7 -2
- package/src/activity/vault-region.ts +7 -3
- package/src/background-generation-config.ts +32 -6
- package/src/background-generation.test.ts +10 -1
- package/src/cli/timeline-publish-commands.ts +9 -0
- package/src/config.test.ts +170 -43
- package/src/config.ts +17 -5
- package/src/extraction-error-classification.ts +36 -1
- package/src/extraction-timeout.test.ts +110 -1
- package/src/extraction.test.ts +19 -1
- package/src/extraction.ts +23 -21
- package/src/fallback-llm-codex-subscription.ts +193 -7
- package/src/fallback-llm-parse-failure.test.ts +156 -0
- package/src/fallback-llm.test.ts +58 -0
- package/src/fallback-llm.ts +187 -186
- package/src/index.ts +1 -0
- package/src/local-llm-config.ts +11 -10
- package/src/local-llm-headers-timeout.test.ts +30 -0
- package/src/local-llm-helpers.ts +28 -0
- package/src/local-llm.ts +8 -19
- package/src/namespaces/search.test.ts +51 -1
- package/src/namespaces/search.ts +48 -3
- package/src/orchestration/extraction-persist-promotion.test.ts +228 -2
- package/src/orchestration/extraction-persist-promotion.ts +17 -14
- package/src/orchestration/orchestrator-helpers.test.ts +104 -0
- package/src/orchestration/orchestrator-init.ts +366 -106
- package/src/orchestration/recall-internal.ts +5 -11
- package/src/orchestration/semantic-merge-promotion-payload.ts +62 -6
- package/src/orchestration/startup-collection-checks.ts +85 -0
- package/src/orchestrator-flush.test.ts +7 -0
- package/src/orchestrator-init.test.ts +1539 -0
- package/src/orchestrator.ts +7 -0
- package/src/qmd.ts +21 -10
- package/src/recall-state-view-admission.ts +11 -1
- package/src/recall-state-view-cron-intent.test.ts +268 -0
- package/src/recall-state-view-widen.test.ts +14 -5
- package/src/recall-state-view-widen.ts +8 -1
- package/src/recall-state-view.ts +5 -2
- package/src/reconcile-identity-cache.test.ts +82 -1
- package/src/search/port.ts +1 -1
- package/src/storage/cas-revision-store.test.ts +4 -0
- package/src/storage/cas-revision-store.ts +104 -12
- package/src/storage-frontmatter-cas.test.ts +19 -17
- package/src/summarizer.ts +5 -1
- package/src/task-llm-config.test.ts +124 -0
- package/src/task-llm-config.ts +88 -0
- package/src/testing/subjects/startup-discovery-lifecycle.test.ts +273 -0
- package/src/types.ts +1 -0
- package/dist/chunk-3CBI2ZSA.js.map +0 -1
- package/dist/chunk-5HYUUFMQ.js.map +0 -1
- package/dist/chunk-6H2G7RFT.js.map +0 -1
- package/dist/chunk-6YAIN5XJ.js.map +0 -1
- package/dist/chunk-ACY4ZPF7.js.map +0 -1
- package/dist/chunk-B36GUU6T.js.map +0 -1
- package/dist/chunk-BLDBT6HZ.js.map +0 -1
- package/dist/chunk-CWTCMZGE.js.map +0 -1
- package/dist/chunk-CZTPU65V.js.map +0 -1
- package/dist/chunk-FWWWUPOE.js.map +0 -1
- package/dist/chunk-IQEJIOEU.js.map +0 -1
- package/dist/chunk-IYKZJVL5.js.map +0 -1
- package/dist/chunk-JXN77SGO.js.map +0 -1
- package/dist/chunk-LPYYY4AP.js.map +0 -1
- package/dist/chunk-LQNRVPTV.js.map +0 -1
- package/dist/chunk-MVAQ2WM3.js.map +0 -1
- package/dist/chunk-P4ZAEYKG.js.map +0 -1
- package/dist/chunk-QHT7MOUF.js.map +0 -1
- package/dist/chunk-QTQ6XKGT.js.map +0 -1
- package/dist/chunk-TNZNSMVJ.js.map +0 -1
- package/dist/chunk-WKDFJXW5.js.map +0 -1
- package/dist/chunk-YDVAM7S4.js.map +0 -1
- package/dist/chunk-YFTNZMGR.js.map +0 -1
- package/dist/chunk-YKREX63Z.js.map +0 -1
- /package/dist/{auto-sync-RP4C6ZPF.js.map → auto-sync-D6BAPIJV.js.map} +0 -0
- /package/dist/{chunk-JTQR6L2N.js.map → chunk-2SKMZUOW.js.map} +0 -0
- /package/dist/{chunk-GG5FHZJJ.js.map → chunk-2SNCQHJZ.js.map} +0 -0
- /package/dist/{chunk-O76F5QPP.js.map → chunk-3QEVOQF5.js.map} +0 -0
- /package/dist/{chunk-3N3PBBEH.js.map → chunk-3XPN7AVY.js.map} +0 -0
- /package/dist/{chunk-X4TD4O2F.js.map → chunk-46Z3VGWF.js.map} +0 -0
- /package/dist/{chunk-2ZTDRNCS.js.map → chunk-4CMXI55L.js.map} +0 -0
- /package/dist/{chunk-MUTVRKKY.js.map → chunk-7D6SER6A.js.map} +0 -0
- /package/dist/{chunk-WBM2MVBK.js.map → chunk-7Z54NXVS.js.map} +0 -0
- /package/dist/{chunk-UHRZ324M.js.map → chunk-BGRTZOJB.js.map} +0 -0
- /package/dist/{chunk-ZEGL7YCC.js.map → chunk-BXIJSPCL.js.map} +0 -0
- /package/dist/{chunk-JKKII6GX.js.map → chunk-BYQX4IOG.js.map} +0 -0
- /package/dist/{chunk-WIWLX65T.js.map → chunk-C5R73BON.js.map} +0 -0
- /package/dist/{chunk-BD7JTBYP.js.map → chunk-D7MAYFQP.js.map} +0 -0
- /package/dist/{chunk-LMLGBG6H.js.map → chunk-DFVSJFHK.js.map} +0 -0
- /package/dist/{chunk-3NPNBVFR.js.map → chunk-ES5BUPYB.js.map} +0 -0
- /package/dist/{chunk-TNSCIPBQ.js.map → chunk-G6NQ3L4T.js.map} +0 -0
- /package/dist/{chunk-5N4MZLEH.js.map → chunk-GO5LOHCD.js.map} +0 -0
- /package/dist/{chunk-AECJY3GJ.js.map → chunk-GSSDHAYH.js.map} +0 -0
- /package/dist/{chunk-TSP5WH72.js.map → chunk-HJUP3K6L.js.map} +0 -0
- /package/dist/{chunk-AAZEKXUP.js.map → chunk-ICNJ6J46.js.map} +0 -0
- /package/dist/{chunk-6EHM2AV7.js.map → chunk-IMBQFHRV.js.map} +0 -0
- /package/dist/{chunk-4RZLTZVM.js.map → chunk-IOPIGX7Y.js.map} +0 -0
- /package/dist/{chunk-EYKIDK33.js.map → chunk-IS2IDFJP.js.map} +0 -0
- /package/dist/{chunk-VJIE4D2I.js.map → chunk-J4WGTN2N.js.map} +0 -0
- /package/dist/{chunk-NXGSONWM.js.map → chunk-J7FVMLBR.js.map} +0 -0
- /package/dist/{chunk-VSL63CFJ.js.map → chunk-JAJIMTFB.js.map} +0 -0
- /package/dist/{chunk-4JVQRFZD.js.map → chunk-JOP7W24J.js.map} +0 -0
- /package/dist/{chunk-YUW66UAH.js.map → chunk-JUDT33RX.js.map} +0 -0
- /package/dist/{chunk-D545Z7UH.js.map → chunk-K4JKAXMO.js.map} +0 -0
- /package/dist/{chunk-BV4ZKIZH.js.map → chunk-K75A5XWU.js.map} +0 -0
- /package/dist/{chunk-M6HBPLBA.js.map → chunk-KOTG3RXE.js.map} +0 -0
- /package/dist/{chunk-OB3XMWX5.js.map → chunk-KVO2BTA4.js.map} +0 -0
- /package/dist/{chunk-GI4ZBWPQ.js.map → chunk-KY236SP5.js.map} +0 -0
- /package/dist/{chunk-4BCHFSC7.js.map → chunk-LFVZD5EC.js.map} +0 -0
- /package/dist/{chunk-O4M4BDIJ.js.map → chunk-LKFVVN7R.js.map} +0 -0
- /package/dist/{chunk-4RX5O34R.js.map → chunk-MJC7UKH7.js.map} +0 -0
- /package/dist/{chunk-UZZ7SQT3.js.map → chunk-NVVZPBKI.js.map} +0 -0
- /package/dist/{chunk-VBSNUH4J.js.map → chunk-PKUJNICO.js.map} +0 -0
- /package/dist/{chunk-OPBFP4TF.js.map → chunk-QE6YVNP5.js.map} +0 -0
- /package/dist/{chunk-ENBPQEVU.js.map → chunk-QPVNK2VE.js.map} +0 -0
- /package/dist/{chunk-RLJHJQUY.js.map → chunk-QS5Q5RBV.js.map} +0 -0
- /package/dist/{chunk-3EKVN2OP.js.map → chunk-RDLPFM7I.js.map} +0 -0
- /package/dist/{chunk-F4EHXVP7.js.map → chunk-RJQAP333.js.map} +0 -0
- /package/dist/{chunk-5SPCMAUC.js.map → chunk-ROEFLU37.js.map} +0 -0
- /package/dist/{chunk-UZEHHXYC.js.map → chunk-SFHLQPBF.js.map} +0 -0
- /package/dist/{chunk-ZBW7A667.js.map → chunk-SUEOG2S4.js.map} +0 -0
- /package/dist/{chunk-Q7CLGFA7.js.map → chunk-TEMKBSV6.js.map} +0 -0
- /package/dist/{chunk-GMIQV6DD.js.map → chunk-TO36VPZI.js.map} +0 -0
- /package/dist/{chunk-FXHOZGSE.js.map → chunk-V2ZA4YWE.js.map} +0 -0
- /package/dist/{chunk-XH3HGI2N.js.map → chunk-VHKXRYQN.js.map} +0 -0
- /package/dist/{chunk-GAS7KYC7.js.map → chunk-VKWFNPBH.js.map} +0 -0
- /package/dist/{chunk-IL6ODG6M.js.map → chunk-VQWBUPSJ.js.map} +0 -0
- /package/dist/{chunk-5YCNDC3C.js.map → chunk-VQYWKCDR.js.map} +0 -0
- /package/dist/{chunk-54NX5EG4.js.map → chunk-W23JARAQ.js.map} +0 -0
- /package/dist/{chunk-YQNNHLCG.js.map → chunk-WPGR3RW4.js.map} +0 -0
- /package/dist/{chunk-FNHLI2RE.js.map → chunk-WZYHPAG5.js.map} +0 -0
- /package/dist/{chunk-TA4C3XB7.js.map → chunk-XM46Y66S.js.map} +0 -0
- /package/dist/{chunk-PCOVEDPK.js.map → chunk-YRIFXXMI.js.map} +0 -0
- /package/dist/{chunk-TRLOFIQC.js.map → chunk-YTK5MVNF.js.map} +0 -0
- /package/dist/{chunk-Q432REND.js.map → chunk-YTZN2UZO.js.map} +0 -0
- /package/dist/{chunk-6AGWBKQU.js.map → chunk-Z4GUX2M6.js.map} +0 -0
- /package/dist/{chunk-AENM6GKU.js.map → chunk-ZAEPD4C2.js.map} +0 -0
- /package/dist/{chunk-KQW3YGEN.js.map → chunk-ZRMMXWYI.js.map} +0 -0
- /package/dist/{chunk-MCRVQ5YQ.js.map → chunk-ZZCR2QYL.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/activity/journal-vault-prereq.ts","../src/activity/vault-path.ts","../src/activity/vault-region.ts","../src/activity/timeline/analysis-failure.ts","../src/activity/timeline/analysis-provider.ts","../src/activity/timeline/categories.ts","../src/activity/timeline/analysis.ts","../src/activity/timeline/analysis-metadata.ts","../src/activity/config.ts","../src/activity/journal-heading.ts","../src/activity/journal-source.ts"],"sourcesContent":["/**\n * Vault journal read-back prerequisites (issue #1987).\n *\n * Pure. `journal.source: \"vault\"` requires all three prerequisites; the\n * caller checks `source` first via the journal-source parser and calls this\n * only for vault mode. Reports EVERY unmet prerequisite at once so the user\n * gets one actionable error, not the first hit.\n */\n\nexport const VAULT_JOURNAL_PREREQUISITES = [\n \"vault.enabled\",\n \"vault.dailyNotePath\",\n \"vault.readback.journalSection\",\n] as const;\nexport type VaultJournalPrerequisite = (typeof VAULT_JOURNAL_PREREQUISITES)[number];\n\nexport type VaultJournalPrereqResult =\n | { ok: true }\n | { ok: false; missing: VaultJournalPrerequisite[]; message: string };\n\nexport function checkVaultJournalPrerequisites(input: {\n vaultEnabled?: unknown;\n dailyNotePath?: unknown;\n journalSection?: unknown;\n}): VaultJournalPrereqResult {\n const missing: VaultJournalPrerequisite[] = [];\n if (input.vaultEnabled !== true) missing.push(\"vault.enabled\");\n // Exact values are validated: trim() only detects blankness, never rewrites.\n if (typeof input.dailyNotePath !== \"string\" || input.dailyNotePath.trim().length === 0) {\n missing.push(\"vault.dailyNotePath\");\n }\n if (typeof input.journalSection !== \"string\" || input.journalSection.trim().length === 0) {\n missing.push(\"vault.readback.journalSection\");\n }\n if (missing.length === 0) return { ok: true };\n return {\n ok: false,\n missing,\n message: `vault journal read-back prerequisites unmet: ${missing.join(\", \")}`,\n };\n}\n","/**\n * Vault note path templates (issue #1985).\n *\n * Expands date tokens on a YYYY-MM-DD day. Returns a vault-relative path.\n * Rejects `..` segments, absolute templates, and unknown `{...}` tokens —\n * naming the token and the full valid set (never a silent pass-through).\n */\nimport path from \"node:path\";\n\nimport { assertValidTimezone, isValidActivityDate } from \"./digest.js\";\n\n/** Every token a vault template accepts (English names in v1). */\nexport const VAULT_TEMPLATE_TOKENS = [\n \"{yyyy}\",\n \"{yy}\",\n \"{M}\",\n \"{MM}\",\n \"{d}\",\n \"{dd}\",\n \"{ww}\",\n \"{MMM}\",\n \"{MMMM}\",\n \"{ddd}\",\n \"{dddd}\",\n] as const;\n\nexport type VaultTemplateToken = (typeof VAULT_TEMPLATE_TOKENS)[number];\n\nconst TOKEN_LOOKUP: Record<string, true> = Object.fromEntries(VAULT_TEMPLATE_TOKENS.map((t) => [t, true]));\n\nconst ANY_BRACED_RE = /\\{[^{}]*\\}/g;\n\nconst MONTH_NAMES = [\n \"January\",\n \"February\",\n \"March\",\n \"April\",\n \"May\",\n \"June\",\n \"July\",\n \"August\",\n \"September\",\n \"October\",\n \"November\",\n \"December\",\n] as const;\n\nconst WEEKDAY_NAMES = [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"] as const;\n\nfunction templateValues(date: string): Record<string, string> {\n const [yyyy, mm, dd] = date.split(\"-\");\n const monthIndex = Number(mm) - 1;\n const weekday = new Date(Date.UTC(Number(yyyy), monthIndex, Number(dd))).getUTCDay();\n return {\n \"{yyyy}\": yyyy,\n \"{yy}\": yyyy.slice(2),\n \"{MM}\": mm,\n \"{dd}\": dd,\n \"{M}\": String(Number(mm)),\n \"{d}\": String(Number(dd)),\n \"{ww}\": isoWeekNumber(date),\n \"{MMM}\": MONTH_NAMES[monthIndex]!.slice(0, 3),\n \"{MMMM}\": MONTH_NAMES[monthIndex]!,\n \"{ddd}\": WEEKDAY_NAMES[weekday]!.slice(0, 3),\n \"{dddd}\": WEEKDAY_NAMES[weekday]!,\n };\n}\n\n/**\n * Expand vault date tokens in any template text (a note path or a note\n * template body). Unknown `{...}` tokens are rejected naming the token and\n * the full valid set; an unreplaced token in a path would silently publish\n * to the wrong file.\n */\nexport function expandVaultTemplateTokens(text: string, date: string): string {\n if (!isValidActivityDate(date)) {\n throw new RangeError(`Invalid vault date \"${date}\"; expected YYYY-MM-DD.`);\n }\n if (typeof text !== \"string\" || text.length === 0) {\n throw new RangeError(\"Vault template must be a non-empty string.\");\n }\n const values = templateValues(date);\n return text.replace(ANY_BRACED_RE, (token) => {\n if (TOKEN_LOOKUP[token] === true) return values[token]!;\n throw new RangeError(\n `Unknown vault template token ${token}; valid tokens are ${VAULT_TEMPLATE_TOKENS.join(\" \")}.`,\n );\n });\n}\n\n/**\n * Validate a vault note path template without resolving it to a date:\n * token allow-list plus the relative/no-`..` shape checks shared with\n * `resolveVaultNotePath`.\n */\nexport function validateVaultNoteTemplate(template: string): void {\n if (typeof template !== \"string\" || template.length === 0) {\n throw new RangeError(\"Vault note path template must be a non-empty relative path.\");\n }\n if (path.posix.isAbsolute(template) || path.win32.isAbsolute(template)) {\n throw new RangeError(\"Vault note path template must be relative; absolute templates are rejected.\");\n }\n if (template.split(/[\\\\/]/).some((segment) => segment === \"..\")) {\n throw new RangeError(\"Vault note path template must not contain `..` segments.\");\n }\n expandVaultTemplateTokens(template, \"2026-01-04\");\n}\n\nexport function resolveVaultNotePath(\n template: string,\n date: string,\n options?: { timezone?: string },\n): string {\n if (!isValidActivityDate(date)) {\n throw new RangeError(`Invalid vault date \"${date}\"; expected YYYY-MM-DD.`);\n }\n if (options?.timezone !== undefined) {\n assertValidTimezone(options.timezone);\n }\n validateVaultNoteTemplate(template);\n\n const expanded = expandVaultTemplateTokens(template, date);\n if (path.posix.isAbsolute(expanded) || path.win32.isAbsolute(expanded)) {\n throw new RangeError(\"Vault note path template must be relative; absolute templates are rejected.\");\n }\n return expanded;\n}\n\nfunction isoWeekNumber(date: string): string {\n const [year, month, day] = date.split(\"-\").map(Number);\n const utc = new Date(Date.UTC(year, month - 1, day));\n const dayNum = utc.getUTCDay() || 7;\n utc.setUTCDate(utc.getUTCDate() + 4 - dayNum);\n const yearStart = new Date(Date.UTC(utc.getUTCFullYear(), 0, 1));\n const weekNo = Math.ceil(((utc.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);\n return String(weekNo).padStart(2, \"0\");\n}\n","/**\n * Validate a managed-region name for vault markers (issue #1985).\n *\n * Empty, whitespace-only, newline, and `-->` names are rejected so they\n * cannot break `<!-- remnic:<name>:start -->` markers. `:` is rejected\n * too: the marker grammar is colon-delimited, so a colon-bearing name\n * makes a marker line ambiguous (`remnic:A:B:start` reads as name `A:B`\n * or as name `A` with a `B:start` suffix) and a publisher that resolved\n * that ambiguity differently from the note's own markers would pair a\n * start with a later end and delete the user bytes in between.\n */\n\nexport type ValidateRegionNameResult =\n | { ok: true; name: string }\n | { ok: false; error: \"invalid_name\" };\n\nexport function validateRegionName(name: string): ValidateRegionNameResult {\n if (typeof name !== \"string\") {\n return { ok: false, error: \"invalid_name\" };\n }\n if (name.includes(\"\\n\") || name.includes(\"\\r\") || name.includes(\"-->\") || name.includes(\":\")) {\n return { ok: false, error: \"invalid_name\" };\n }\n const trimmed = name.trim();\n if (trimmed.length === 0) {\n return { ok: false, error: \"invalid_name\" };\n }\n return { ok: true, name: trimmed };\n}\n","/**\n * Typed failure classification for timeline analysis (issue #2050).\n *\n * Unknown kinds fail closed with a TypeError. Pure: no I/O, no clock, no randomness.\n */\n\nexport const ANALYSIS_FAILURE_KINDS = [\n \"provider_unavailable\",\n \"timeout\",\n \"aborted\",\n \"rate_limited\",\n \"malformed_json\",\n \"invalid_schema\",\n \"partial_output\",\n \"invalid_config\",\n] as const;\nexport type AnalysisFailureKind = (typeof ANALYSIS_FAILURE_KINDS)[number];\n\nexport interface AnalysisFailure {\n kind: AnalysisFailureKind;\n /** True only when retrying the same request could plausibly succeed. */\n retryable: boolean;\n /** True when the deterministic cards must be preserved unchanged. */\n preservesDeterministic: true;\n}\n\nconst RETRYABLE_BY_KIND: Readonly<Record<AnalysisFailureKind, boolean>> = {\n provider_unavailable: true,\n timeout: true,\n rate_limited: true,\n aborted: false,\n malformed_json: false,\n invalid_schema: false,\n partial_output: false,\n invalid_config: false,\n};\n\n/** True when the value is one of the known failure kinds. */\nexport function isAnalysisFailureKind(\n value: unknown,\n): value is AnalysisFailureKind {\n return (\n typeof value === \"string\" &&\n (ANALYSIS_FAILURE_KINDS as readonly string[]).includes(value)\n );\n}\n\n/** Classify a failure kind. Unknown kinds throw; there is no permissive default. */\nexport function classifyAnalysisFailure(kind: string): AnalysisFailure {\n if (!isAnalysisFailureKind(kind)) {\n throw new TypeError(\n `unknown analysis failure kind: ${String(kind)}; allowed kinds: ${ANALYSIS_FAILURE_KINDS.join(\", \")}`,\n );\n }\n return {\n kind,\n retryable: RETRYABLE_BY_KIND[kind],\n preservesDeterministic: true,\n };\n}\n","/**\n * Provider-neutral completion adapter for timeline analysis (issue #2050).\n *\n * Routes the analysis prompt through the existing LLM contracts — the local\n * client (`provider: \"local\"`) or the gateway fallback client pinned to the\n * ONE explicitly configured remote provider/model. An invalid or missing\n * explicit provider surfaces as a typed failure; there is no chain fallback,\n * so a bad config can never silently answer from a different provider.\n *\n * Structural seams (not concrete classes) so hosts and tests inject fakes.\n * Never logs prompt or response content.\n */\nimport type { AnalysisFailureKind } from \"./analysis-failure.js\";\nimport type { TimelineAnalysisComplete } from \"./analysis.js\";\n\n/** Structural slice of LocalLlmClient.chatCompletion (local seam). */\nexport interface TimelineAnalysisLocalLlm {\n chatCompletion(\n messages: Array<{ role: \"system\" | \"user\"; content: string }>,\n options: {\n temperature?: number;\n maxTokens?: number;\n responseFormat?: { type: string };\n signal?: AbortSignal;\n operation?: string;\n redactProviderErrors?: boolean;\n model?: string;\n },\n ): Promise<{ content: string } | null>;\n}\n\n/** Structural slice of FallbackLlmClient.chatCompletion (remote seam). */\nexport interface TimelineAnalysisRemoteLlm {\n chatCompletion(\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options: {\n temperature?: number;\n maxTokens?: number;\n signal?: AbortSignal;\n modelChain?: { primary?: string; fallbacks?: string[] };\n includeDefaultModelFallback?: boolean;\n redactProviderErrors?: boolean;\n },\n ): Promise<{ content: string } | null>;\n}\n\n/** Marker base for provider errors the analysis run classifies by kind. */\nexport class TimelineAnalysisProviderError extends Error {\n override readonly name = \"TimelineAnalysisProviderError\";\n constructor(\n readonly kind: AnalysisFailureKind,\n message: string,\n ) {\n super(message);\n }\n}\n\n/** The provider id that routes to the local LLM client. */\nexport const TIMELINE_ANALYSIS_LOCAL_PROVIDER_ID = \"local\";\n\nconst RATE_LIMIT_MESSAGE = /rate limit|too many requests/i;\n\n/**\n * Map a thrown provider error onto the typed failure kinds. Name- and\n * status-based only: the message is matched, never logged, so provider error\n * text cannot become a log line.\n */\nexport function classifyAnalysisProviderError(error: unknown): AnalysisFailureKind {\n if (error instanceof TimelineAnalysisProviderError) return error.kind;\n if (error instanceof Error) {\n if (error.name === \"AbortError\" || (\"code\" in error && error.code === \"ABORT_ERR\")) {\n return \"aborted\";\n }\n if (error.name === \"TimeoutError\") return \"timeout\";\n const status = \"status\" in error ? error.status : \"statusCode\" in error ? error.statusCode : undefined;\n if (status === 429 || RATE_LIMIT_MESSAGE.test(error.message)) return \"rate_limited\";\n }\n return \"provider_unavailable\";\n}\n\n/**\n * Build the `complete` function `analyzeTimelineCards` calls, from the local\n * and remote client seams. Selection is by the explicit provider id carried\n * on each call: `\"local\"` uses the local client; anything else is a remote\n * provider id pinned as the single-model chain (no default-chain fallback is\n * ever appended).\n */\nexport function timelineAnalysisCompleteFromClients(input: {\n localLlm: TimelineAnalysisLocalLlm | null;\n remoteLlm: TimelineAnalysisRemoteLlm | null;\n}): TimelineAnalysisComplete {\n const { localLlm, remoteLlm } = input;\n return async ({ prompt, provider, model, signal }) => {\n const messages = [{ role: \"user\" as const, content: prompt }];\n if (provider === TIMELINE_ANALYSIS_LOCAL_PROVIDER_ID) {\n if (!localLlm) {\n throw new TimelineAnalysisProviderError(\n \"provider_unavailable\",\n \"timeline analysis local provider is not configured\",\n );\n }\n const result = await localLlm.chatCompletion(messages, {\n temperature: 0,\n maxTokens: 2048,\n signal,\n operation: \"timeline-analysis\",\n redactProviderErrors: true,\n model,\n });\n if (result === null) {\n throw new TimelineAnalysisProviderError(\n \"provider_unavailable\",\n \"timeline analysis local provider returned no completion\",\n );\n }\n return result.content;\n }\n if (!remoteLlm) {\n throw new TimelineAnalysisProviderError(\n \"provider_unavailable\",\n \"timeline analysis remote provider is not configured\",\n );\n }\n const result = await remoteLlm.chatCompletion(messages, {\n temperature: 0,\n maxTokens: 2048,\n signal,\n modelChain: { primary: `${provider}/${model}` },\n includeDefaultModelFallback: false,\n redactProviderErrors: true,\n });\n if (result === null) {\n throw new TimelineAnalysisProviderError(\n \"provider_unavailable\",\n `timeline analysis provider \"${provider}\" is not configured`,\n );\n }\n return result.content;\n };\n}\n","/**\n * Timeline category registry (issue #2049): ordered definitions with stable\n * IDs, names, colors, descriptions, and system/idle flags. Reserved system\n * categories (`system.*`) must always be present; unknown activity stays\n * visible in `system.unknown` rather than being silently reassigned.\n */\n\nimport type { TimelineCategory } from \"./types.js\";\n\nexport const TIMELINE_RESERVED_UNKNOWN = \"system.unknown\";\nexport const TIMELINE_RESERVED_IDLE = \"system.idle\";\nexport const TIMELINE_RESERVED_PAUSE = \"system.pause\";\n\nconst COLOR_PATTERN = /^#[0-9a-fA-F]{6}$/;\n\nfunction isTimelineCategoryId(id: string): boolean {\n const parts = id.split(\".\");\n if (parts.length === 0) return false;\n for (const part of parts) {\n if (part.length === 0) return false;\n const first = part.charCodeAt(0);\n if (first < 97 || first > 122) return false;\n for (let i = 1; i < part.length; i += 1) {\n const code = part.charCodeAt(i);\n const ok = (code >= 97 && code <= 122) || (code >= 48 && code <= 57) || code === 45;\n if (!ok) return false;\n }\n }\n return true;\n}\n\n\n/**\n * Default registry. User category orders step by 10 so a corrected registry\n * can insert between without renumbering; system categories sort last.\n */\nexport const DEFAULT_TIMELINE_CATEGORIES: readonly TimelineCategory[] = [\n { id: \"development\", name: \"Development\", color: \"#3b82f6\", description: \"Editors, terminals, IDEs, and code forges.\", order: 10 },\n { id: \"communication\", name: \"Communication\", color: \"#f59e0b\", description: \"Mail, chat, and meetings.\", order: 20 },\n { id: \"browsing\", name: \"Browsing\", color: \"#6366f1\", description: \"Web browsing without a more specific domain match.\", order: 30 },\n { id: \"documents\", name: \"Documents\", color: \"#10b981\", description: \"Writing, notes, and PDFs.\", order: 40 },\n { id: \"design\", name: \"Design & Media\", color: \"#ec4899\", description: \"Design, audio, and video tools.\", order: 50 },\n { id: \"data\", name: \"Data & Analysis\", color: \"#84cc16\", description: \"Spreadsheets, dashboards, and databases.\", order: 60 },\n { id: \"admin\", name: \"Admin\", color: \"#64748b\", description: \"File managers, settings, and system utilities.\", order: 70 },\n { id: \"entertainment\", name: \"Entertainment\", color: \"#ef4444\", description: \"Video, music, and games.\", order: 80 },\n { id: TIMELINE_RESERVED_UNKNOWN, name: \"Uncategorized\", color: \"#9ca3af\", description: \"Activity with no matching classification signal. Kept visible, never silently reassigned.\", order: 990, system: true },\n { id: TIMELINE_RESERVED_IDLE, name: \"Idle\", color: \"#d1d5db\", description: \"Derived gap between cards where no activity was observed.\", order: 991, system: true, idle: true },\n { id: TIMELINE_RESERVED_PAUSE, name: \"Paused\", color: \"#a78bfa\", description: \"User-declared pause interval.\", order: 992, system: true },\n];\n\n/**\n * Validate a registry: unique well-formed ids, non-empty names, hex colors,\n * integer orders, reserved categories present and correctly flagged, and the\n * `system.` namespace reserved. Throws with the offending key path.\n */\nexport function validateTimelineCategories(categories: readonly TimelineCategory[]): void {\n if (!Array.isArray(categories) || categories.length === 0) {\n throw new RangeError(\"timeline categories must be a non-empty array\");\n }\n const seen = new Set<string>();\n for (const category of categories) {\n if (typeof category.id !== \"string\" || !isTimelineCategoryId(category.id)) {\n throw new RangeError(`timeline category id must be dotted kebab-case: ${String(category.id)}`);\n }\n if (seen.has(category.id)) {\n throw new RangeError(`timeline category id must be unique: ${category.id}`);\n }\n seen.add(category.id);\n if (typeof category.name !== \"string\" || category.name.length === 0) {\n throw new RangeError(`timeline category ${category.id}: name must be a non-empty string`);\n }\n if (typeof category.color !== \"string\" || !COLOR_PATTERN.test(category.color)) {\n throw new RangeError(`timeline category ${category.id}: color must be #RRGGBB`);\n }\n if (typeof category.description !== \"string\" || category.description.length === 0) {\n throw new RangeError(`timeline category ${category.id}: description must be a non-empty string`);\n }\n if (!Number.isInteger(category.order)) {\n throw new RangeError(`timeline category ${category.id}: order must be an integer`);\n }\n const isReserved = category.id === TIMELINE_RESERVED_UNKNOWN || category.id === TIMELINE_RESERVED_IDLE || category.id === TIMELINE_RESERVED_PAUSE;\n if (isReserved && category.system !== true) {\n throw new RangeError(`timeline category ${category.id} is reserved and must set system: true`);\n }\n if (!isReserved) {\n if (category.id.startsWith(\"system.\")) {\n throw new RangeError(`timeline category id namespace \"system.\" is reserved: ${category.id}`);\n }\n if (category.system === true || category.idle === true) {\n throw new RangeError(`timeline category ${category.id}: only reserved categories may set system/idle flags`);\n }\n }\n }\n const idle = categories.find((category) => category.id === TIMELINE_RESERVED_IDLE);\n if (idle !== undefined && idle.idle !== true) {\n throw new RangeError(`timeline category ${TIMELINE_RESERVED_IDLE} must set idle: true`);\n }\n for (const reserved of [TIMELINE_RESERVED_UNKNOWN, TIMELINE_RESERVED_IDLE, TIMELINE_RESERVED_PAUSE]) {\n if (!categories.some((category) => category.id === reserved)) {\n throw new RangeError(`timeline categories must include the reserved \"${reserved}\" category`);\n }\n }\n}\n\n/**\n * Deterministic presentation order: by `order`, then by id as the equal-order\n * tie-breaker, so two categories sharing an order never swap between runs.\n */\nexport function sortTimelineCategories(categories: readonly TimelineCategory[]): TimelineCategory[] {\n return [...categories].sort((a, b) => a.order - b.order || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));\n}\n","/**\n * Optional evidence-bound AI analysis over deterministic timeline cards\n * (issue #2050). Provider calls arrive through the injected `complete`\n * function; hosts build it from the local/remote seams in analysis-provider.js\n * and the parsed `activity.timeline.analysis` config. Failures leave the\n * cards unchanged and carry a typed failure kind. Disabled makes zero\n * provider calls.\n */\nimport { classifyAnalysisFailure, type AnalysisFailure, type AnalysisFailureKind } from \"./analysis-failure.js\";\nimport { classifyAnalysisProviderError } from \"./analysis-provider.js\";\nimport { extractJsonCandidates } from \"../../json-extract.js\";\nimport { log } from \"../../logger.js\";\nimport { DEFAULT_TIMELINE_CATEGORIES } from \"./categories.js\";\nimport type { TimelineCard, TimelineCategory, TimelineCorrection, TimelineEvidenceRange, TimelineObservation } from \"./types.js\";\n\nexport const TIMELINE_ANALYSIS_PROMPT_VERSION = 1;\nexport const TIMELINE_ANALYSIS_BATCH_SIZE = 40;\nexport const TIMELINE_ANALYSIS_BATCH_OVERLAP = 2;\nexport const TIMELINE_ANALYSIS_DEFAULT_TIMEOUT_MS = 15_000;\n\nexport class TimelineAnalysisConfigError extends Error {\n override readonly name = \"TimelineAnalysisConfigError\";\n readonly code = \"invalid_config\" as const;\n constructor(message: string) {\n super(message);\n }\n}\n\nexport type TimelineAnalysisStatus = \"ok\" | \"disabled\" | \"provider_failed\" | \"invalid_output\";\n\nexport interface TimelineAnalysisCompleteInput {\n prompt: string;\n provider: string;\n model: string;\n signal: AbortSignal;\n}\n\nexport type TimelineAnalysisComplete = (input: TimelineAnalysisCompleteInput) => Promise<string>;\n\nexport interface TimelineAnalysisInput {\n enabled: boolean;\n cards: readonly TimelineCard[];\n observations: readonly TimelineObservation[];\n provider?: string;\n model?: string;\n complete?: TimelineAnalysisComplete;\n timeoutMs?: number;\n date?: string;\n timezone?: string;\n categories?: readonly TimelineCategory[];\n preferences?: readonly string[];\n priorEdits?: readonly TimelineCorrection[];\n signal?: AbortSignal;\n}\n\nexport interface TimelineAnalysisResult {\n status: TimelineAnalysisStatus;\n cards: TimelineCard[];\n provider?: string;\n model?: string;\n promptVersion?: number;\n /** Typed failure detail; present only when status is a failure. */\n failure?: AnalysisFailure;\n}\n\ninterface AnalysisOp {\n cardId: string;\n title?: string;\n summary?: string;\n categoryId?: string;\n confidence?: number;\n uncertainty?: string;\n evidenceRange: TimelineEvidenceRange;\n}\n\nconst INSTRUCTIONS = [\n \"Use only supplied evidence.\",\n \"Preserve chronology.\",\n \"Do not invent people, places, or tasks.\",\n \"Avoid productivity or emotional claims.\",\n \"Merge adjacent activity only when the evidence justifies it.\",\n \"Return strict JSON: {\\\"ops\\\":[...]} with evidenceRange, title, summary, categoryId, confidence, uncertainty.\",\n \"Cite evidence ranges for every op. Empty ops is a valid no-op.\",\n].join(\" \");\n\nfunction fail(\n status: Exclude<TimelineAnalysisStatus, \"ok\" | \"disabled\">,\n cards: readonly TimelineCard[],\n provider: string,\n model: string,\n kind: AnalysisFailureKind,\n): TimelineAnalysisResult {\n log.info(`timeline analysis status=${status} provider=${provider} model=${model} promptVersion=${TIMELINE_ANALYSIS_PROMPT_VERSION}`);\n return {\n status,\n cards: cards as TimelineCard[],\n provider,\n model,\n promptVersion: TIMELINE_ANALYSIS_PROMPT_VERSION,\n failure: classifyAnalysisFailure(kind),\n };\n}\n\nfunction requiredToken(value: string | undefined, name: string): string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TimelineAnalysisConfigError(`${name} is required`);\n }\n return value.trim();\n}\n\nfunction evidenceKeyOf(observation: TimelineObservation): string {\n const parsed = Date.parse(observation.capturedAtUtc);\n const instant = Number.isFinite(parsed) ? new Date(parsed).toISOString() : observation.capturedAtUtc;\n return `${observation.machine}|${instant}|${observation.contentHash}`;\n}\n\nfunction sortObservations(observations: readonly TimelineObservation[]): TimelineObservation[] {\n return [...observations].sort((a, b) => {\n const time = Date.parse(a.capturedAtUtc) - Date.parse(b.capturedAtUtc);\n if (time !== 0) return time;\n if (a.contentHash !== b.contentHash) return a.contentHash < b.contentHash ? -1 : 1;\n return a.id - b.id;\n });\n}\n\nfunction batchObservations(observations: readonly TimelineObservation[]): TimelineObservation[][] {\n if (observations.length === 0) return [[]];\n const size = TIMELINE_ANALYSIS_BATCH_SIZE;\n const overlap = TIMELINE_ANALYSIS_BATCH_OVERLAP;\n const step = size - overlap;\n const batches: TimelineObservation[][] = [];\n for (let start = 0; start < observations.length; start += step) {\n batches.push(observations.slice(start, start + size));\n if (start + size >= observations.length) break;\n }\n return batches;\n}\n\nfunction safeObservation(observation: TimelineObservation) {\n return {\n id: observation.id,\n machine: observation.machine,\n capturedAtUtc: observation.capturedAtUtc,\n app: observation.app,\n windowTitle: observation.windowTitle,\n ...(observation.browserUrl ? { browserUrl: observation.browserUrl } : {}),\n contentHash: observation.contentHash,\n };\n}\n\nfunction safeCard(card: TimelineCard) {\n return {\n id: card.id,\n kind: card.kind,\n title: card.title,\n summary: card.summary,\n categoryId: card.categoryId,\n confidence: card.confidence,\n startUtc: card.startUtc,\n endUtc: card.endUtc,\n machine: card.machine,\n evidenceIds: card.evidenceIds,\n evidenceRange: card.evidenceRange,\n ...(card.manualEdit ? { manualEdit: card.manualEdit } : {}),\n };\n}\n\nexport function buildTimelineAnalysisPrompt(input: {\n date?: string;\n timezone?: string;\n cards: readonly TimelineCard[];\n observations: readonly TimelineObservation[];\n categories: readonly TimelineCategory[];\n preferences?: readonly string[];\n priorEdits?: readonly TimelineCorrection[];\n}): string {\n const payload = {\n date: input.date ?? null,\n timezone: input.timezone ?? null,\n categories: input.categories.map((category) => ({ id: category.id, name: category.name })),\n preferences: input.preferences ?? [],\n priorEdits: input.priorEdits ?? [],\n cards: input.cards.map(safeCard),\n observations: input.observations.map(safeObservation),\n };\n return `${INSTRUCTIONS}\\n${JSON.stringify(payload)}`;\n}\n\nasync function callComplete(\n complete: TimelineAnalysisComplete,\n prompt: string,\n provider: string,\n model: string,\n timeoutMs: number,\n parent?: AbortSignal,\n): Promise<string> {\n const { promise: timeoutPromise, reject } = Promise.withResolvers<string>();\n const failOnce = (message: string, name: string) => {\n reject(Object.assign(new Error(message), { name }));\n };\n const timer = setTimeout(() => failOnce(\"timeline analysis timed out\", \"TimeoutError\"), timeoutMs);\n if (parent?.aborted) {\n clearTimeout(timer);\n throw Object.assign(new Error(\"timeline analysis aborted\"), { name: \"AbortError\" });\n }\n const onAbort = () => failOnce(\"timeline analysis aborted\", \"AbortError\");\n parent?.addEventListener(\"abort\", onAbort, { once: true });\n const signal = parent ? AbortSignal.any([parent, AbortSignal.timeout(timeoutMs)]) : AbortSignal.timeout(timeoutMs);\n try {\n return await Promise.race([complete({ prompt, provider, model, signal }), timeoutPromise]);\n } finally {\n clearTimeout(timer);\n parent?.removeEventListener(\"abort\", onAbort);\n }\n}\n\nfunction asOp(value: unknown): AnalysisOp | null {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return null;\n if (!(\"cardId\" in value) || typeof value.cardId !== \"string\" || value.cardId.length === 0) return null;\n if (!(\"evidenceRange\" in value)) return null;\n const range = value.evidenceRange;\n if (!range || typeof range !== \"object\" || Array.isArray(range)) return null;\n if (!(\"firstKey\" in range) || typeof range.firstKey !== \"string\" || range.firstKey.length === 0) return null;\n if (!(\"lastKey\" in range) || typeof range.lastKey !== \"string\" || range.lastKey.length === 0) return null;\n if (\"title\" in value && value.title !== undefined && typeof value.title !== \"string\") return null;\n if (\"summary\" in value && value.summary !== undefined && typeof value.summary !== \"string\") return null;\n if (\"categoryId\" in value && value.categoryId !== undefined && typeof value.categoryId !== \"string\") return null;\n if (\"uncertainty\" in value && value.uncertainty !== undefined && typeof value.uncertainty !== \"string\") return null;\n if (\n \"confidence\" in value &&\n value.confidence !== undefined &&\n (typeof value.confidence !== \"number\" || value.confidence < 0 || value.confidence > 1)\n ) {\n return null;\n }\n return {\n cardId: value.cardId,\n title: \"title\" in value && typeof value.title === \"string\" ? value.title : undefined,\n summary: \"summary\" in value && typeof value.summary === \"string\" ? value.summary : undefined,\n categoryId: \"categoryId\" in value && typeof value.categoryId === \"string\" ? value.categoryId : undefined,\n confidence: \"confidence\" in value && typeof value.confidence === \"number\" ? value.confidence : undefined,\n uncertainty: \"uncertainty\" in value && typeof value.uncertainty === \"string\" ? value.uncertainty : undefined,\n evidenceRange: { firstKey: range.firstKey, lastKey: range.lastKey },\n };\n}\n\ntype ParseOpsResult = { ok: true; ops: AnalysisOp[] } | { ok: false; kind: AnalysisFailureKind };\n\n/**\n * Parse the provider response. An empty/whitespace body is `partial_output`\n * (nothing came back); a body whose JSON candidates never parse is\n * `malformed_json`; parseable JSON in the wrong shape — missing `ops`, or an\n * op that fails validation — is `invalid_schema`.\n */\nfunction parseOps(raw: string): ParseOpsResult {\n if (typeof raw !== \"string\" || raw.trim().length === 0) return { ok: false, kind: \"partial_output\" };\n let sawParsableShape = false;\n for (const candidate of extractJsonCandidates(raw)) {\n try {\n const parsed: unknown = JSON.parse(candidate);\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) continue;\n if (!(\"ops\" in parsed) || !Array.isArray(parsed.ops)) {\n sawParsableShape = true;\n continue;\n }\n const out: AnalysisOp[] = [];\n let valid = true;\n for (const item of parsed.ops) {\n const op = asOp(item);\n if (!op) {\n valid = false;\n break;\n }\n out.push(op);\n }\n if (valid) return { ok: true, ops: out };\n sawParsableShape = true;\n } catch {\n continue;\n }\n }\n if (sawParsableShape) return { ok: false, kind: \"invalid_schema\" };\n return { ok: false, kind: \"malformed_json\" };\n}\n\nfunction knownEvidenceKeys(cards: readonly TimelineCard[], observations: readonly TimelineObservation[]): Set<string> {\n const keys = new Set<string>();\n for (const observation of observations) keys.add(evidenceKeyOf(observation));\n for (const card of cards) {\n if (card.evidenceRange) {\n keys.add(card.evidenceRange.firstKey);\n keys.add(card.evidenceRange.lastKey);\n }\n }\n return keys;\n}\n\nfunction applyOps(\n cards: readonly TimelineCard[],\n ops: readonly AnalysisOp[],\n categories: readonly TimelineCategory[],\n evidenceKeys: ReadonlySet<string>,\n): TimelineCard[] | null {\n if (ops.length === 0) return cards as TimelineCard[];\n const byId = new Map(cards.map((card) => [card.id, card]));\n const allowedCategories = new Set(categories.map((category) => category.id));\n const next = new Map<string, TimelineCard>();\n for (const op of ops) {\n const card = byId.get(op.cardId);\n if (!card) return null;\n if (!evidenceKeys.has(op.evidenceRange.firstKey) || !evidenceKeys.has(op.evidenceRange.lastKey)) return null;\n if (op.categoryId !== undefined && !allowedCategories.has(op.categoryId)) return null;\n if (card.manualEdit) continue;\n next.set(card.id, {\n ...card,\n ...(op.title !== undefined ? { title: op.title } : {}),\n ...(op.summary !== undefined ? { summary: op.summary } : {}),\n ...(op.categoryId !== undefined ? { categoryId: op.categoryId } : {}),\n ...(op.confidence !== undefined ? { confidence: op.confidence } : {}),\n });\n }\n if (next.size === 0) return cards as TimelineCard[];\n return cards.map((card) => next.get(card.id) ?? card);\n}\n\n/** Analyze one day of cards. Failures return the input cards unchanged. */\nexport async function analyzeTimelineCards(input: TimelineAnalysisInput): Promise<TimelineAnalysisResult> {\n const cards = input.cards as TimelineCard[];\n if (!input.enabled) {\n return { status: \"disabled\", cards };\n }\n\n const provider = requiredToken(input.provider, \"provider\");\n const model = requiredToken(input.model, \"model\");\n const complete = input.complete;\n if (typeof complete !== \"function\") {\n throw new TimelineAnalysisConfigError(\"complete is required\");\n }\n const timeoutMs = input.timeoutMs ?? TIMELINE_ANALYSIS_DEFAULT_TIMEOUT_MS;\n if (!Number.isInteger(timeoutMs) || timeoutMs <= 0) {\n throw new TimelineAnalysisConfigError(\"timeoutMs must be a positive integer\");\n }\n if (input.cards.length === 0 && input.observations.length === 0) {\n // Caller-side guard, not a provider failure: nothing to analyze and zero\n // calls must be made, so no typed response failure is attached.\n return {\n status: \"invalid_output\",\n cards,\n provider,\n model,\n promptVersion: TIMELINE_ANALYSIS_PROMPT_VERSION,\n };\n }\n\n const categories = input.categories ?? DEFAULT_TIMELINE_CATEGORIES;\n const ordered = sortObservations(input.observations);\n const batches = batchObservations(ordered);\n const merged = new Map<string, AnalysisOp>();\n\n try {\n for (const batch of batches) {\n const prompt = buildTimelineAnalysisPrompt({\n date: input.date,\n timezone: input.timezone,\n cards: input.cards,\n observations: batch,\n categories,\n preferences: input.preferences,\n priorEdits: input.priorEdits,\n });\n const raw = await callComplete(complete, prompt, provider, model, timeoutMs, input.signal);\n const parsed = parseOps(raw);\n if (!parsed.ok) return fail(\"invalid_output\", cards, provider, model, parsed.kind);\n for (const op of parsed.ops) merged.set(op.cardId, op);\n }\n } catch (error) {\n return fail(\"provider_failed\", cards, provider, model, classifyAnalysisProviderError(error));\n }\n\n const applied = applyOps(cards, [...merged.values()], categories, knownEvidenceKeys(cards, ordered));\n if (!applied) return fail(\"invalid_output\", cards, provider, model, \"invalid_schema\");\n\n log.info(`timeline analysis status=ok provider=${provider} model=${model} promptVersion=${TIMELINE_ANALYSIS_PROMPT_VERSION}`);\n return {\n status: \"ok\",\n cards: applied,\n provider,\n model,\n promptVersion: TIMELINE_ANALYSIS_PROMPT_VERSION,\n };\n}\n","/**\n * Analysis run metadata (issue #2050).\n *\n * Pure builder for the provider/model/prompt-version record attached to\n * stored analysis results and telemetry.\n *\n * The no-content guarantee is enforced by SHAPE, not by length: each field\n * must match an identifier/version syntax, so prose cannot be stored as a\n * \"model name\". A 200-character single-line string is happily prose\n * (\"Summarize this user\\u0027s activity\"), which is why a length cap alone was\n * not the boundary this record needs. Errors never echo a rejected value,\n * so a mis-passed prompt cannot reach logs through the failure path either.\n */\nexport interface AnalysisRunMetadata {\n provider: string;\n model: string;\n promptVersion: string;\n /** Observation ids the run consumed, for evidence tracing. */\n observationCount: number;\n}\n\nexport const ANALYSIS_METADATA_MAX_FIELD_LENGTH = 120;\n/** True when value is an identifier-safe provider/model/promptVersion string. */\nexport const isAnalysisIdentifier = (value: string): boolean => IDENTIFIER.test(value);\n\n/**\n * Identifier syntax for provider and model: letters, digits, and the\n * separators real provider/model slugs use (`openai`, `gpt-5.2`,\n * `anthropic/claude-4_1`, `llama3:8b`). No spaces, so a sentence cannot pass.\n */\nconst IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:\\-\\/]*$/;\n\n/**\n * Prompt versions are identifiers or dates (`v3`, `2026-08-01`,\n * `timeline-analysis.v2`), so the same syntax applies.\n */\nconst FIELD_PATTERNS: Readonly<Record<string, RegExp>> = {\n provider: IDENTIFIER,\n model: IDENTIFIER,\n promptVersion: IDENTIFIER,\n};\n\nfunction validateMetadataField(field: string, value: unknown): string {\n // Never interpolate the value itself: an error string is a log line, and a\n // caller that mis-passes prompt text must not have it echoed there.\n if (typeof value !== \"string\") {\n throw new RangeError(\n `analysis run metadata ${field} must be a string; received ${typeof value}`,\n );\n }\n if (value.length === 0) {\n throw new RangeError(`analysis run metadata ${field} must not be empty`);\n }\n if (value.length > ANALYSIS_METADATA_MAX_FIELD_LENGTH) {\n throw new RangeError(\n `analysis run metadata ${field} is too long (max ${ANALYSIS_METADATA_MAX_FIELD_LENGTH} characters)`,\n );\n }\n // Report a line break as a line break even though the pattern would also\n // reject it. U+2028 and U+2029 are line separators too: they break\n // line-oriented log consumers exactly like CR and LF.\n if (/[\\n\\r\\u2028\\u2029]/.test(value)) {\n throw new RangeError(`analysis run metadata ${field} must not contain a line break`);\n }\n const pattern = FIELD_PATTERNS[field];\n if (pattern === undefined) {\n throw new RangeError(`analysis run metadata has no pattern for field ${field}`);\n }\n if (!pattern.test(value)) {\n throw new RangeError(\n `analysis run metadata ${field} must be an identifier (letters, digits, and ._:-/ only)`,\n );\n }\n return value;\n}\n\nexport function buildAnalysisRunMetadata(input: {\n provider: string;\n model: string;\n promptVersion: string;\n observationCount: number;\n}): AnalysisRunMetadata {\n const provider = validateMetadataField(\"provider\", input.provider);\n const model = validateMetadataField(\"model\", input.model);\n const promptVersion = validateMetadataField(\"promptVersion\", input.promptVersion);\n const observationCount = input.observationCount;\n if (\n typeof observationCount !== \"number\" ||\n !Number.isInteger(observationCount) ||\n observationCount < 0\n ) {\n // Report the type only. JSON.stringify would echo mis-passed content into\n // the log, and it throws outright on a bigint or a circular object.\n throw new RangeError(\n `analysis run metadata observationCount must be a non-negative integer; received ${typeof observationCount}`,\n );\n }\n return { provider, model, promptVersion, observationCount };\n}\n","import path from \"node:path\";\n\nimport { coerceBooleanLike, coerceNumber } from \"../connectors/coerce.js\";\nimport { assertValidTimezone } from \"./digest.js\";\nimport { applyLegacyJournalHeading } from \"./journal-heading.js\";\nimport { checkVaultJournalPrerequisites } from \"./journal-vault-prereq.js\";\nimport { resolveJournalSource } from \"./journal-source.js\";\nimport { validateVaultNoteTemplate } from \"./vault-path.js\";\nimport { validateRegionName } from \"./vault-region.js\";\nimport { TIMELINE_ANALYSIS_DEFAULT_TIMEOUT_MS } from \"./timeline/analysis.js\";\nimport { ANALYSIS_METADATA_MAX_FIELD_LENGTH, isAnalysisIdentifier } from \"./timeline/analysis-metadata.js\";\nimport type {\n ActivityConfig,\n ActivityExtractionMode,\n ActivitySourceConfig,\n ActivityTimelineAnalysisConfig,\n ActivityTimelineConfig,\n ActivityTimelineJournalConfig,\n ActivityTimelineQaConfig,\n ActivityTimelineVaultConfig,\n ActivityTimelineVaultTargetConfig,\n} from \"./types.js\";\nimport type { ImportanceLevel } from \"../types.js\";\n\nconst EXTRACTION_MODES: readonly ActivityExtractionMode[] = [\"off\", \"smart\"];\nconst IMPORTANCE_LEVELS: readonly ImportanceLevel[] = [\"critical\", \"high\", \"normal\", \"low\", \"trivial\"];\nconst LOOPBACK_HOSTS: Record<string, true> = { localhost: true, \"127.0.0.1\": true, \"::1\": true };\n\n/** The inert default: disabled, search-only, no sources, no extraction. */\nexport function defaultActivityConfig(): ActivityConfig {\n return {\n enabled: false,\n timezone: \"UTC\",\n syncDays: 1,\n autoSyncIntervalMinutes: 15,\n sources: [],\n extractionMode: \"off\",\n sourceTrust: 0.6,\n autoApproveTrust: 0.8,\n reviewTrust: 0.5,\n minConfidence: 0.7,\n minImportance: \"normal\",\n maxMemoriesPerDay: 0,\n timeline: {\n enabled: false,\n analysis: parseTimelineAnalysisConfig(undefined),\n journal: { enabled: false, source: \"memoryDir\", extractionMode: \"off\" },\n qa: { enabled: false, maxRangeDays: 31 },\n vault: parseTimelineVaultConfig(undefined),\n },\n };\n}\n\nfunction asRecord(value: unknown, name: string): Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new TypeError(`${name} must be an object`);\n }\n return value as Record<string, unknown>;\n}\n\nfunction optionalString(value: unknown, name: string): string | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== \"string\" || value.length === 0) throw new TypeError(`${name} must be a non-empty string`);\n return value;\n}\n\nfunction parseUnitInterval(value: unknown, key: string, fallback: number): number {\n if (value === undefined) return fallback;\n const parsed = coerceNumber(value);\n if (parsed === undefined || !Number.isFinite(parsed) || parsed < 0 || parsed > 1) {\n throw new RangeError(`activity.${key} must be a number in [0, 1]`);\n }\n return parsed;\n}\n\n/**\n * Parse, protocol-check, and confine an activity source base URL to a local\n * loopback host. Shared with ActivityHttpSourceClient so config-load and client\n * construction reject the exact same shapes with the same prefixed message. The\n * bearer token travels in an Authorization header, so a non-loopback baseUrl\n * would exfiltrate it; the subsystem contract is local capture daemons only.\n */\nexport function validateActivityBaseUrl(baseUrl: string): URL {\n let parsed: URL;\n try {\n parsed = new URL(baseUrl);\n } catch {\n throw new RangeError(`activity source baseUrl must be a valid URL: ${baseUrl}`);\n }\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n throw new RangeError(\"activity source baseUrl must use HTTP or HTTPS\");\n }\n // URL keeps IPv6 hosts bracketed (e.g. \"[::1]\"); normalize before the lookup.\n const host = parsed.hostname.replace(/^\\[|\\]$/g, \"\");\n if (LOOPBACK_HOSTS[host] !== true && !/^127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(host)) {\n throw new RangeError(`activity source baseUrl must target a loopback host (got ${parsed.hostname})`);\n }\n return parsed;\n}\n\nfunction parseSource(value: unknown): ActivitySourceConfig {\n const raw = asRecord(value, \"activity source\");\n const machineLabel = optionalString(raw.machineLabel, \"activity source machineLabel\");\n const baseUrl = optionalString(raw.baseUrl, \"activity source baseUrl\");\n if (machineLabel === undefined || baseUrl === undefined) {\n throw new TypeError(\"activity source requires machineLabel and baseUrl\");\n }\n // Reject whitespace-only labels here so config-load fails loudly rather than\n // parsing clean and then failing on the first sync (the client and pipeline\n // both reject a blank label via trim()).\n if (machineLabel.trim().length === 0) {\n throw new RangeError(\"activity source machineLabel must not be blank\");\n }\n validateActivityBaseUrl(baseUrl);\n const token = optionalString(raw.token, \"activity source token\");\n return { machineLabel, baseUrl, ...(token === undefined ? {} : { token }) };\n}\n\n/**\n * Parse the unified `activity.*` block. `enabled` gates source ingestion (and\n * requires at least one source); `extractionMode` independently gates durable\n * trust-gated memory extraction (default `off`, i.e. search-only).\n */\nexport function parseActivityConfig(raw: unknown): ActivityConfig {\n const defaults = defaultActivityConfig();\n if (raw === undefined || raw === null) {\n return { ...defaults };\n }\n const config = asRecord(raw, \"activity\");\n const enabledValue = coerceBooleanLike(config.enabled, \"activity.enabled\");\n if (config.enabled !== undefined && enabledValue === undefined) {\n throw new TypeError(\"activity.enabled must be a boolean\");\n }\n const timezone = optionalString(config.timezone, \"activity.timezone\") ?? \"UTC\";\n // Validate the IANA zone at parse so a typo fails before any daemon is\n // contacted; otherwise activityDayWindow() throws mid-sync after snapshot\n // pages may already be persisted, and every retry replays the same window.\n assertValidTimezone(timezone);\n const syncDaysValue = coerceNumber(config.syncDays, \"activity.syncDays\");\n if (config.syncDays !== undefined && syncDaysValue === undefined) {\n throw new TypeError(\"activity.syncDays must be a finite number\");\n }\n const syncDays = syncDaysValue ?? 1;\n if (!Number.isInteger(syncDays) || syncDays < 1 || syncDays > 90) {\n throw new RangeError(\"activity.syncDays must be an integer from 1 to 90\");\n }\n const intervalValue = coerceNumber(config.autoSyncIntervalMinutes, \"activity.autoSyncIntervalMinutes\");\n if (config.autoSyncIntervalMinutes !== undefined && intervalValue === undefined) {\n throw new TypeError(\"activity.autoSyncIntervalMinutes must be a finite number\");\n }\n const autoSyncIntervalMinutes = intervalValue ?? 15;\n if (!Number.isInteger(autoSyncIntervalMinutes) || autoSyncIntervalMinutes < 1 || autoSyncIntervalMinutes > 1440) {\n throw new RangeError(\"activity.autoSyncIntervalMinutes must be an integer from 1 to 1440\");\n }\n if (config.sources !== undefined && !Array.isArray(config.sources)) {\n throw new TypeError(\"activity.sources must be an array\");\n }\n const sources = (config.sources ?? []).map(parseSource);\n // Cursors and row dedup are keyed on machineLabel alone; two sources sharing\n // a label would share a cursor and dedup namespace, silently skipping or\n // conflating snapshots. Reject the collision at parse.\n const labels = new Set<string>();\n for (const source of sources) {\n if (labels.has(source.machineLabel)) {\n throw new RangeError(`activity source machineLabel must be unique: ${source.machineLabel}`);\n }\n labels.add(source.machineLabel);\n }\n const enabled = enabledValue ?? false;\n if (enabled && sources.length === 0) throw new RangeError(\"activity.enabled requires at least one source\");\n\n // Extraction gate (independent of `enabled` ingestion). Default `off`.\n // Default only for a missing key; an explicit null/non-string is a malformed\n // enum and must fail rather than silently disabling extraction.\n const extractionMode = config.extractionMode === undefined ? defaults.extractionMode : config.extractionMode;\n if (typeof extractionMode !== \"string\" || !EXTRACTION_MODES.includes(extractionMode as ActivityExtractionMode)) {\n throw new RangeError(`activity.extractionMode must be one of: ${EXTRACTION_MODES.join(\", \")}`);\n }\n const minImportance = config.minImportance === undefined ? defaults.minImportance : config.minImportance;\n if (typeof minImportance !== \"string\" || !IMPORTANCE_LEVELS.includes(minImportance as ImportanceLevel)) {\n throw new RangeError(`activity.minImportance must be one of: ${IMPORTANCE_LEVELS.join(\", \")}`);\n }\n // Default only for a missing key; an explicit null/unparseable value must fail\n // rather than silently uncapping (parity with the enum/unit-interval fields).\n const rawMaxMemoriesPerDay =\n config.maxMemoriesPerDay === undefined ? defaults.maxMemoriesPerDay : config.maxMemoriesPerDay;\n const maxMemoriesPerDay = coerceNumber(rawMaxMemoriesPerDay);\n if (maxMemoriesPerDay === undefined || !Number.isInteger(maxMemoriesPerDay) || maxMemoriesPerDay < 0) {\n throw new RangeError(\"activity.maxMemoriesPerDay must be a non-negative integer\");\n }\n const autoApproveTrust = parseUnitInterval(config.autoApproveTrust, \"autoApproveTrust\", defaults.autoApproveTrust);\n const reviewTrust = parseUnitInterval(config.reviewTrust, \"reviewTrust\", defaults.reviewTrust);\n if (reviewTrust >= autoApproveTrust) {\n throw new RangeError(\n `activity.reviewTrust (${reviewTrust}) must be below autoApproveTrust (${autoApproveTrust})`,\n );\n }\n return {\n enabled,\n timezone,\n syncDays,\n autoSyncIntervalMinutes,\n sources,\n extractionMode: extractionMode as ActivityExtractionMode,\n sourceTrust: parseUnitInterval(config.sourceTrust, \"sourceTrust\", defaults.sourceTrust),\n autoApproveTrust,\n reviewTrust,\n minConfidence: parseUnitInterval(config.minConfidence, \"minConfidence\", defaults.minConfidence),\n minImportance: minImportance as ImportanceLevel,\n maxMemoriesPerDay,\n timeline: parseTimelineConfig(config.timeline),\n };\n}\n\n/**\n * Parse the `activity.timeline.*` block. `enabled` is the master gate for\n * timeline-card derivation (issue #2049); default false. A non-object block\n * is malformed and must fail rather than silently disabling the layer.\n */\nfunction parseTimelineConfig(raw: unknown): ActivityTimelineConfig {\n if (raw === undefined) {\n return {\n enabled: false,\n analysis: parseTimelineAnalysisConfig(undefined),\n journal: { enabled: false, source: \"memoryDir\", extractionMode: \"off\" },\n qa: parseTimelineQaConfig(undefined),\n vault: parseTimelineVaultConfig(undefined),\n };\n }\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n throw new TypeError(\"activity.timeline must be an object\");\n }\n const timeline = raw as Record<string, unknown>;\n const enabledValue = coerceBooleanLike(timeline.enabled, \"activity.timeline.enabled\");\n if (timeline.enabled !== undefined && enabledValue === undefined) {\n throw new TypeError(\"activity.timeline.enabled must be a boolean\");\n }\n const journal = parseTimelineJournalConfig(timeline.journal);\n let vault = parseTimelineVaultConfig(timeline.vault);\n const journalRaw = timeline.journal;\n const headingRaw =\n typeof journalRaw === \"object\" && journalRaw !== null && !Array.isArray(journalRaw)\n ? (journalRaw as Record<string, unknown>).heading\n : undefined;\n if (headingRaw !== undefined) {\n const aliased = applyLegacyJournalHeading({\n journalSection: vault.readback.journalSection,\n heading: headingRaw,\n });\n process.emitWarning(\n aliased.ignoredLegacyHeading\n ? 'activity.timeline.journal.heading is deprecated and ignored because activity.timeline.vault.readback.journalSection is set; use vault.readback.journalSection.'\n : 'activity.timeline.journal.heading is deprecated; use activity.timeline.vault.readback.journalSection.',\n { type: \"DeprecationWarning\", code: \"REMNIC_DEP_JOURNAL_HEADING\" },\n );\n if (aliased.usedLegacyHeading) {\n vault = { ...vault, readback: { ...vault.readback, journalSection: aliased.journalSection } };\n }\n }\n if (journal.source === \"vault\") {\n // Parse-time prerequisite gate (issue #1987): the error names EVERY\n // missing prerequisite, never just the first (§1/§39).\n const prereq = checkVaultJournalPrerequisites({\n vaultEnabled: vault.enabled,\n dailyNotePath: vault.dailyNotePath,\n journalSection: vault.readback.journalSection,\n });\n if (!prereq.ok) {\n throw new RangeError(\n `activity.timeline.journal.source \"vault\" requires ${prereq.message}`,\n );\n }\n }\n return {\n enabled: enabledValue ?? false,\n analysis: parseTimelineAnalysisConfig(timeline.analysis),\n journal,\n qa: parseTimelineQaConfig(timeline.qa),\n vault,\n };\n}\n\n/**\n * Parse the `activity.timeline.analysis.*` block (issue #2050). Independent\n * gate, default off: disabled means zero provider calls and zero analysis\n * artifacts. Enabling requires an explicit provider and model; every invalid\n * value is rejected at parse time — never silently defaulted or fallen back.\n */\nfunction parseTimelineAnalysisConfig(raw: unknown): ActivityTimelineAnalysisConfig {\n if (raw === undefined) return { enabled: false };\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n throw new TypeError(\"activity.timeline.analysis must be an object\");\n }\n const analysis = raw as Record<string, unknown>;\n const enabled = requireBool(analysis.enabled, \"activity.timeline.analysis.enabled\", false);\n const provider = optionalNonEmptyString(analysis.provider, \"activity.timeline.analysis.provider\")?.trim();\n const model = optionalNonEmptyString(analysis.model, \"activity.timeline.analysis.model\")?.trim();\n for (const [key, value] of [\n [\"provider\", provider],\n [\"model\", model],\n ] as const) {\n if (value === undefined) continue;\n if (key === \"provider\" && value.includes(\"/\")) {\n throw new RangeError(\n \"activity.timeline.analysis.provider must be a single provider segment (no '/')\",\n );\n }\n if (!isAnalysisIdentifier(value)) {\n throw new RangeError(\n `activity.timeline.analysis.${key} must be an identifier (letters, digits, and ._:-/ only)`,\n );\n }\n if (value.length > ANALYSIS_METADATA_MAX_FIELD_LENGTH) {\n throw new RangeError(\n `activity.timeline.analysis.${key} must be at most ${ANALYSIS_METADATA_MAX_FIELD_LENGTH} characters`,\n );\n }\n }\n if (enabled && (provider === undefined || model === undefined)) {\n throw new RangeError(\n \"activity.timeline.analysis.provider and model are required when analysis is enabled\",\n );\n }\n const timeoutMsValue = coerceNumber(analysis.timeoutMs, \"activity.timeline.analysis.timeoutMs\");\n if (analysis.timeoutMs !== undefined && timeoutMsValue === undefined) {\n throw new TypeError(\"activity.timeline.analysis.timeoutMs must be a finite number\");\n }\n const timeoutMs = timeoutMsValue ?? TIMELINE_ANALYSIS_DEFAULT_TIMEOUT_MS;\n if (!Number.isInteger(timeoutMs) || timeoutMs < 1_000 || timeoutMs > 120_000) {\n throw new RangeError(\"activity.timeline.analysis.timeoutMs must be an integer from 1000 to 120000\");\n }\n if (analysis.preferences !== undefined) {\n if (!Array.isArray(analysis.preferences)) {\n throw new TypeError(\"activity.timeline.analysis.preferences must be an array of strings\");\n }\n if (analysis.preferences.length > 16) {\n throw new RangeError(\"activity.timeline.analysis.preferences supports at most 16 entries\");\n }\n for (const entry of analysis.preferences) {\n if (typeof entry !== \"string\" || entry.trim().length === 0) {\n throw new TypeError(\"activity.timeline.analysis.preferences entries must be non-empty strings\");\n }\n if (entry.length > 200) {\n throw new RangeError(\"activity.timeline.analysis.preferences entries must be at most 200 characters\");\n }\n }\n }\n const preferences = analysis.preferences as string[] | undefined;\n return {\n enabled,\n ...(provider === undefined ? {} : { provider }),\n ...(model === undefined ? {} : { model }),\n ...(analysis.timeoutMs !== undefined || timeoutMsValue !== undefined ? { timeoutMs } : {}),\n ...(preferences === undefined ? {} : { preferences }),\n };\n}\n\n/**\n * Parse the `activity.timeline.vault.*` block (issue #1985). Default off.\n * Invalid values are rejected at parse time naming the missing or invalid\n * prerequisite — never silently defaulted.\n */\nexport function parseTimelineVaultConfig(raw: unknown): ActivityTimelineVaultConfig {\n if (raw === undefined) return defaultVaultConfig();\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n throw new TypeError(\"activity.timeline.vault must be an object\");\n }\n const vault = raw as Record<string, unknown>;\n const enabled = requireBool(vault.enabled, \"activity.timeline.vault.enabled\", false);\n const createMissingNotes = requireBool(vault.createMissingNotes, \"activity.timeline.vault.createMissingNotes\", false);\n const autoPublish = requireBool(vault.autoPublish, \"activity.timeline.vault.autoPublish\", true);\n const vaultPath = optionalNonEmptyString(vault.vaultPath, \"activity.timeline.vault.vaultPath\") ?? \"\";\n const dailyNotePath =\n optionalNonEmptyString(vault.dailyNotePath, \"activity.timeline.vault.dailyNotePath\") ?? \"{yyyy}-{MM}-{dd}.md\";\n const weeklyNotePath = optionalNonEmptyString(vault.weeklyNotePath, \"activity.timeline.vault.weeklyNotePath\") ?? \"\";\n const noteTemplate = optionalNonEmptyString(vault.noteTemplate, \"activity.timeline.vault.noteTemplate\") ?? \"\";\n const insertUnderHeading =\n optionalNonEmptyString(vault.insertUnderHeading, \"activity.timeline.vault.insertUnderHeading\") ?? \"\";\n\n if (typeof vault.sectionStrategy !== \"undefined\" && typeof vault.sectionStrategy !== \"string\") {\n throw new TypeError(\"activity.timeline.vault.sectionStrategy must be a string\");\n }\n const sectionStrategy = (vault.sectionStrategy as string | undefined) ?? \"markers\";\n if (sectionStrategy !== \"markers\" && sectionStrategy !== \"heading\") {\n throw new RangeError('activity.timeline.vault.sectionStrategy must be one of \"markers\", \"heading\"');\n }\n\n // The documented `vaultPath` contract: absolute on the host platform, or\n // `~` / `~`-rooted for `expandTildePath`. Everything else — `.`, `vault`,\n // `../notes`, whitespace — is relative and would resolve against whatever\n // directory the daemon or CLI happened to launch from, so an enabled vault\n // rejects it at parse time.\n const tildeRooted = vaultPath === \"~\" || vaultPath.startsWith(\"~/\") || vaultPath.startsWith(\"~\\\\\");\n if (enabled && (vaultPath.trim().length === 0 || (!tildeRooted && !path.isAbsolute(vaultPath)))) {\n throw new RangeError(\n \"activity.timeline.vault.vaultPath must be an absolute or `~`-rooted path when activity.timeline.vault.enabled is true; \" +\n `a relative path resolves against the process working directory (got ${JSON.stringify(vaultPath)})`,\n );\n }\n try {\n validateVaultNoteTemplate(dailyNotePath);\n } catch (err) {\n throw new RangeError(`activity.timeline.vault.dailyNotePath: ${(err as Error).message}`);\n }\n if (weeklyNotePath.length > 0) {\n try {\n validateVaultNoteTemplate(weeklyNotePath);\n } catch (err) {\n throw new RangeError(`activity.timeline.vault.weeklyNotePath: ${(err as Error).message}`);\n }\n }\n if (createMissingNotes) {\n if (noteTemplate.length === 0) {\n throw new RangeError(\n \"activity.timeline.vault.noteTemplate must name a vault-relative template file when activity.timeline.vault.createMissingNotes is true\",\n );\n }\n try {\n validateVaultNoteTemplate(noteTemplate);\n } catch (err) {\n throw new RangeError(`activity.timeline.vault.noteTemplate: ${(err as Error).message}`);\n }\n }\n\n const publish = parseVaultPublishBlock(vault.publish, weeklyNotePath);\n const wikilinksRaw = vault.wikilinks;\n const wikilinks =\n wikilinksRaw === undefined\n ? { places: false, placesFolder: \"Places\" }\n : parseVaultWikilinks(wikilinksRaw);\n const propertiesRaw = vault.properties;\n const properties: ActivityTimelineVaultConfig[\"properties\"] =\n propertiesRaw === undefined ? { mode: \"off\", prefix: \"remnic_\" } : parseVaultProperties(propertiesRaw);\n const readbackRaw = vault.readback;\n if (readbackRaw !== undefined && (typeof readbackRaw !== \"object\" || readbackRaw === null || Array.isArray(readbackRaw))) {\n throw new TypeError(\"activity.timeline.vault.readback must be an object\");\n }\n const readbackJournalSection = optionalNonEmptyString(\n (readbackRaw as Record<string, unknown> | undefined)?.journalSection,\n \"activity.timeline.vault.readback.journalSection\",\n );\n const readback = {\n journalSection: readbackJournalSection ?? \"\",\n };\n\n return {\n enabled,\n vaultPath,\n dailyNotePath,\n weeklyNotePath,\n createMissingNotes,\n noteTemplate,\n sectionStrategy,\n publish,\n insertUnderHeading,\n readback,\n wikilinks,\n properties,\n autoPublish,\n };\n}\n\nfunction defaultVaultConfig(): ActivityTimelineVaultConfig {\n return {\n enabled: false,\n vaultPath: \"\",\n dailyNotePath: \"{yyyy}-{MM}-{dd}.md\",\n weeklyNotePath: \"\",\n createMissingNotes: false,\n noteTemplate: \"\",\n sectionStrategy: \"markers\",\n publish: {\n timeline: { enabled: true, target: \"daily\", section: \"Timeline\" },\n standup: { enabled: false, target: \"daily\", section: \"Standup\" },\n weekly: { enabled: false, target: \"weekly\", section: \"Weekly Review\" },\n locations: { enabled: false, target: \"daily\", section: \"Locations\" },\n },\n insertUnderHeading: \"\",\n wikilinks: { places: false, placesFolder: \"Places\" },\n properties: { mode: \"off\", prefix: \"remnic_\" },\n autoPublish: true,\n readback: { journalSection: \"\" },\n };\n}\n\nfunction requireBool(value: unknown, key: string, fallback: boolean): boolean {\n const coerced = coerceBooleanLike(value, key);\n if (value === undefined) return fallback;\n if (coerced === undefined) {\n throw new TypeError(`${key} must be a boolean`);\n }\n return coerced;\n}\n\nfunction optionalNonEmptyString(value: unknown, key: string): string | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== \"string\") {\n throw new TypeError(`${key} must be a string`);\n }\n return value;\n}\n\nfunction parseVaultPublishBlock(\n raw: unknown,\n weeklyNotePath: string,\n): ActivityTimelineVaultConfig[\"publish\"] {\n const defaults = defaultVaultConfig().publish;\n if (raw === undefined) return defaults;\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n throw new TypeError(\"activity.timeline.vault.publish must be an object\");\n }\n const block = raw as Record<string, unknown>;\n // Each kind is read at its literal path so the config-contract extractor\n // can attribute publish.<kind>.<leaf> to real parse sites (§40).\n const publish = {\n timeline: parseVaultTarget(block.timeline, \"timeline\", defaults.timeline, weeklyNotePath),\n standup: parseVaultTarget(block.standup, \"standup\", defaults.standup, weeklyNotePath),\n weekly: parseVaultTarget(block.weekly, \"weekly\", defaults.weekly, weeklyNotePath),\n locations: parseVaultTarget(block.locations, \"locations\", defaults.locations, weeklyNotePath),\n };\n for (const targetFile of [\"daily\", \"weekly\"] as const) {\n const sections = ([\"timeline\", \"standup\", \"weekly\", \"locations\"] as const)\n .filter((kind) => publish[kind].target === targetFile && publish[kind].enabled)\n .map((kind) => publish[kind].section);\n const duplicates = sections.filter((section, index) => sections.indexOf(section) !== index);\n if (duplicates.length > 0) {\n throw new RangeError(\n `activity.timeline.vault.publish section names must be unique per target file; duplicate ${targetFile} section(s): ${[...new Set(duplicates)].join(\", \")}`,\n );\n }\n }\n return publish;\n}\n\nfunction parseVaultTarget(\n raw: unknown,\n kind: string,\n fallback: ActivityTimelineVaultTargetConfig,\n weeklyNotePath: string,\n): ActivityTimelineVaultTargetConfig {\n if (raw === undefined) return fallback;\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n throw new TypeError(`activity.timeline.vault.publish.${kind} must be an object`);\n }\n const entry = raw as Record<string, unknown>;\n const enabled = requireBool(entry.enabled, `activity.timeline.vault.publish.${kind}.enabled`, fallback.enabled);\n const target = entry.target === undefined ? fallback.target : entry.target;\n if (target !== \"daily\" && target !== \"weekly\") {\n throw new RangeError(`activity.timeline.vault.publish.${kind}.target must be one of \"daily\", \"weekly\"`);\n }\n const section = entry.section === undefined ? fallback.section : entry.section;\n if (typeof section !== \"string\" || section.trim().length === 0 || section !== section.trim()) {\n throw new RangeError(`activity.timeline.vault.publish.${kind}.section must be a non-empty trimmed string`);\n }\n // The accepted config domain must equal what the publisher accepts: a name\n // containing `-->` or a line break passes the trimmed-string check above\n // but is rejected downstream by `publishVaultNote`, which would turn an\n // apparently valid config into a runtime failure.\n if (!validateRegionName(section).ok) {\n throw new RangeError(\n `activity.timeline.vault.publish.${kind}.section must not contain a line break or \"-->\"`,\n );\n }\n // The path is required only by an entry that will actually publish to the\n // weekly file; a disabled entry must load the same whether its object is\n // explicit (serialized schema default) or omitted entirely.\n if (enabled && target === \"weekly\" && weeklyNotePath.length === 0) {\n throw new RangeError(\n `activity.timeline.vault.publish.${kind}.target is \"weekly\" but activity.timeline.vault.weeklyNotePath is empty; configure weeklyNotePath first`,\n );\n }\n return { enabled, target, section };\n}\n\nfunction parseVaultWikilinks(raw: unknown): ActivityTimelineVaultConfig[\"wikilinks\"] {\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n throw new TypeError(\"activity.timeline.vault.wikilinks must be an object\");\n }\n const block = raw as Record<string, unknown>;\n const places = requireBool(block.places, \"activity.timeline.vault.wikilinks.places\", false);\n const placesFolder =\n optionalNonEmptyString(block.placesFolder, \"activity.timeline.vault.wikilinks.placesFolder\") ?? \"Places\";\n return { places, placesFolder };\n}\n\nfunction parseVaultProperties(raw: unknown): ActivityTimelineVaultConfig[\"properties\"] {\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n throw new TypeError(\"activity.timeline.vault.properties must be an object\");\n }\n const block = raw as Record<string, unknown>;\n const mode = block.mode === undefined ? \"off\" : block.mode;\n if (mode !== \"off\" && mode !== \"frontmatter\" && mode !== \"dataview-inline\") {\n throw new RangeError(\n 'activity.timeline.vault.properties.mode must be one of \"off\", \"frontmatter\", \"dataview-inline\"',\n );\n }\n const prefix = optionalNonEmptyString(block.prefix, \"activity.timeline.vault.properties.prefix\") ?? \"remnic_\";\n if (prefix.trim().length === 0 || prefix !== prefix.trim()) {\n throw new RangeError(\"activity.timeline.vault.properties.prefix must be non-empty and trimmed\");\n }\n return { mode, prefix };\n}\n\nfunction parseTimelineJournalConfig(raw: unknown): ActivityTimelineJournalConfig {\n if (raw === undefined) return { enabled: false, source: \"memoryDir\", extractionMode: \"off\" };\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n throw new TypeError(\"activity.timeline.journal must be an object\");\n }\n const journal = raw as Record<string, unknown>;\n const enabledValue = coerceBooleanLike(journal.enabled, \"activity.timeline.journal.enabled\");\n if (journal.enabled !== undefined && enabledValue === undefined) {\n throw new TypeError(\"activity.timeline.journal.enabled must be a boolean\");\n }\n const source = journal.source === undefined ? \"memoryDir\" : typeof journal.source === \"string\" ? journal.source : \"\";\n const resolved = resolveJournalSource({ source });\n if (!resolved.ok) {\n throw new RangeError('activity.timeline.journal.source must be one of \"memoryDir\", \"vault\"');\n }\n if (resolved.deprecatedAlias === \"file\") {\n process.emitWarning(\n 'activity.timeline.journal.source \"file\" is deprecated; use \"memoryDir\".',\n { type: \"DeprecationWarning\", code: \"REMNIC_DEP_JOURNAL_SOURCE_FILE\" },\n );\n }\n const extractionMode = journal.extractionMode === undefined ? \"off\" : journal.extractionMode;\n if (extractionMode !== \"off\" && extractionMode !== \"review\") {\n throw new RangeError('activity.timeline.journal.extractionMode must be one of \"off\", \"review\"');\n }\n return { enabled: enabledValue ?? false, source: resolved.mode, extractionMode };\n}\n\nfunction parseTimelineQaConfig(raw: unknown): ActivityTimelineQaConfig {\n if (raw === undefined) return { enabled: false, maxRangeDays: 31 };\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n throw new TypeError(\"activity.timeline.qa must be an object\");\n }\n const qa = raw as Record<string, unknown>;\n const enabledValue = coerceBooleanLike(qa.enabled, \"activity.timeline.qa.enabled\");\n if (qa.enabled !== undefined && enabledValue === undefined) {\n throw new TypeError(\"activity.timeline.qa.enabled must be a boolean\");\n }\n const maxRangeDaysValue = coerceNumber(qa.maxRangeDays, \"activity.timeline.qa.maxRangeDays\");\n if (qa.maxRangeDays !== undefined && maxRangeDaysValue === undefined) {\n throw new TypeError(\"activity.timeline.qa.maxRangeDays must be an integer from 1 to 366\");\n }\n const maxRangeDays = maxRangeDaysValue ?? 31;\n if (!Number.isInteger(maxRangeDays) || maxRangeDays < 1 || maxRangeDays > 366) {\n throw new RangeError(\"activity.timeline.qa.maxRangeDays must be an integer from 1 to 366\");\n }\n return { enabled: enabledValue ?? false, maxRangeDays };\n}\n\n","/**\n * Normalize a journal section heading (issue #1987 leftover).\n *\n * Trim. Empty is empty_heading. Newline is invalid_heading.\n */\n\nexport type NormalizeJournalHeadingResult =\n | { ok: true; heading: string }\n | { ok: false; error: \"empty_heading\" | \"invalid_heading\" };\n\nexport function normalizeJournalHeading(value: string): NormalizeJournalHeadingResult {\n const heading = value.trim();\n if (heading.length === 0) return { ok: false, error: \"empty_heading\" };\n if (heading.includes(\"\\n\") || heading.includes(\"\\r\")) {\n return { ok: false, error: \"invalid_heading\" };\n }\n return { ok: true, heading };\n}\n\n/**\n * Legacy `activity.timeline.journal.heading` → `vault.readback.journalSection`.\n * The new key wins when both are present; the legacy key is still validated.\n */\nexport function applyLegacyJournalHeading(input: {\n journalSection: string;\n heading: unknown;\n}): { journalSection: string; usedLegacyHeading: boolean; ignoredLegacyHeading: boolean } {\n if (input.heading === undefined) {\n return { journalSection: input.journalSection, usedLegacyHeading: false, ignoredLegacyHeading: false };\n }\n if (typeof input.heading !== \"string\") {\n throw new TypeError(\"activity.timeline.journal.heading must be a string\");\n }\n const normalized = normalizeJournalHeading(input.heading);\n if (!normalized.ok) {\n throw new RangeError(\n normalized.error === \"empty_heading\"\n ? \"activity.timeline.journal.heading must be a non-empty string\"\n : \"activity.timeline.journal.heading must not contain a newline\",\n );\n }\n if (input.journalSection.trim().length > 0) {\n return {\n journalSection: input.journalSection,\n usedLegacyHeading: false,\n ignoredLegacyHeading: true,\n };\n }\n return {\n journalSection: normalized.heading,\n usedLegacyHeading: true,\n ignoredLegacyHeading: false,\n };\n}\n","/**\n * Resolve journal source mode (issue #1987).\n *\n * Pure. `memoryDir` mode reads `<memoryDir>/journal/<date>.md` (#1984\n * behavior, the default); `vault` mode reads the configured section of\n * the vault daily note. The heading is NOT resolved here — vault\n * prerequisites (vault.enabled, dailyNotePath, readback.journalSection)\n * are checked by journal-vault-prereq.ts at config parse time.\n *\n * Legacy `source: \"file\"` is accepted as an alias of `memoryDir` and\n * flagged so config parse can emit a deprecation warning.\n */\n\nexport type JournalSource = \"memoryDir\" | \"vault\";\n\nconst JOURNAL_SOURCES: readonly JournalSource[] = [\"memoryDir\", \"vault\"];\n\nexport type JournalSourceResult =\n | { ok: true; mode: JournalSource; deprecatedAlias?: \"file\" }\n | { ok: false; error: \"unknown_source\" };\n\nexport function resolveJournalSource(input: { source: string }): JournalSourceResult {\n if (input.source === \"file\") {\n return { ok: true, mode: \"memoryDir\", deprecatedAlias: \"file\" };\n }\n if ((JOURNAL_SOURCES as readonly string[]).includes(input.source)) {\n return { ok: true, mode: input.source as JournalSource };\n }\n return { ok: false, error: \"unknown_source\" };\n}\n"],"mappings":"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","names":["result","path","path"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/local-llm-config.ts"],"sourcesContent":["import { coerceNumber } from \"./connectors/coerce.js\";\nimport { parseBackgroundGeneration } from \"./background-generation-config.js\";\nimport type { BackgroundGenerationConfig } from \"./background-generation-config.js\";\nimport { readEnvVar } from \"./runtime/env.js\";\n\nexport interface LocalLlmConfig {\n // Local LLM Provider (v2.1)\n localLlmEnabled: boolean;\n localLlmUrl: string;\n localLlmModel: string;\n /** Optional API key for authenticated OpenAI-compatible endpoints. */\n localLlmApiKey?: string;\n /** Optional environment-variable name that supplies localLlmApiKey at runtime. */\n localLlmApiKeyEnv?: string;\n /** Additional headers for local/compatible endpoint requests. */\n localLlmHeaders?: Record<string, string>;\n /** If false, do not send Authorization header even when localLlmApiKey is set. */\n localLlmAuthHeader: boolean;\n localLlmFallback: boolean;\n /** Optional home directory override for local LLM helpers (LM Studio settings, CLI PATH). */\n localLlmHomeDir?: string;\n /** Optional absolute path to LMS CLI binary (preferred over auto-detection). */\n localLmsCliPath?: string;\n /** Optional bin directory prepended to PATH for LMS CLI execution. */\n localLmsBinDir?: string;\n /** Hard timeout for local LLM and gateway fallback requests (ms). */\n localLlmTimeoutMs: number;\n /** Max context window for local LLM (override auto-detection). Set lower if your LLM server defaults to smaller contexts. */\n localLlmMaxContext?: number;\n}\n\nexport type LocalLlmParseResult = LocalLlmConfig & {\n backgroundGeneration?: BackgroundGenerationConfig;\n};\n\nfunction parseLocalLlmTimeoutMs(value: unknown): number {\n const coerced = coerceNumber(value);\n if (coerced === undefined) return 180_000;\n return Math.min(86_400_000, Math.max(1, Math.floor(coerced)));\n}\n\nfunction parseLocalLlmMaxContext(value: unknown): number | undefined {\n if (value === undefined || value === null) return undefined;\n const coerced = coerceNumber(value);\n if (\n coerced === undefined ||\n !Number.isFinite(coerced) ||\n !Number.isInteger(coerced) ||\n coerced < 1024\n ) {\n throw new Error(\n `localLlmMaxContext must be an integer greater than or equal to 1024; got ${JSON.stringify(value)}`,\n );\n }\n return coerced;\n}\n\nfunction copyStringRecord(value: unknown): Record<string, string> | undefined {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined;\n const headers: Record<string, string> = {};\n for (const key of Object.getOwnPropertyNames(value)) {\n const headerValue = Reflect.get(value, key);\n if (typeof headerValue === \"string\") headers[key] = headerValue;\n }\n return headers;\n}\n\nexport function parseLocalLlmConfig(\n cfg: Record<string, unknown>,\n localLlmApiKeyEnv: string | undefined,\n resolveEnvVars: (value: string) => string,\n): LocalLlmParseResult {\n return {\n backgroundGeneration: parseBackgroundGeneration(cfg),\n localLlmEnabled: cfg.localLlmEnabled === true || cfg.localLlmEnabled === \"true\",\n localLlmUrl:\n typeof cfg.localLlmUrl === \"string\" && cfg.localLlmUrl.length > 0\n ? cfg.localLlmUrl\n : \"http://localhost:1234/v1\",\n localLlmModel:\n typeof cfg.localLlmModel === \"string\" && cfg.localLlmModel.length > 0\n ? cfg.localLlmModel\n : \"local-model\",\n localLlmApiKey:\n typeof cfg.localLlmApiKey === \"string\" && cfg.localLlmApiKey.length > 0\n ? resolveEnvVars(cfg.localLlmApiKey)\n : localLlmApiKeyEnv === undefined\n ? undefined\n : readEnvVar(localLlmApiKeyEnv),\n localLlmApiKeyEnv,\n localLlmHeaders: copyStringRecord(cfg.localLlmHeaders),\n localLlmAuthHeader: cfg.localLlmAuthHeader !== false,\n localLlmFallback: cfg.localLlmFallback !== false,\n localLlmHomeDir:\n typeof cfg.localLlmHomeDir === \"string\" && cfg.localLlmHomeDir.length > 0\n ? cfg.localLlmHomeDir\n : undefined,\n localLmsCliPath:\n typeof cfg.localLmsCliPath === \"string\" && cfg.localLmsCliPath.length > 0\n ? cfg.localLmsCliPath\n : undefined,\n localLmsBinDir:\n typeof cfg.localLmsBinDir === \"string\" && cfg.localLmsBinDir.length > 0\n ? cfg.localLmsBinDir\n : undefined,\n localLlmTimeoutMs: parseLocalLlmTimeoutMs(cfg.localLlmTimeoutMs),\n localLlmMaxContext: parseLocalLlmMaxContext(cfg.localLlmMaxContext),\n };\n}\n"],"mappings":";;;;;;;;;;;AAmCA,SAAS,uBAAuB,OAAwB;AACtD,QAAM,UAAU,aAAa,KAAK;AAClC,MAAI,YAAY,OAAW,QAAO;AAClC,SAAO,KAAK,IAAI,OAAY,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,CAAC,CAAC;AAC9D;AAEA,SAAS,wBAAwB,OAAoC;AACnE,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,QAAM,UAAU,aAAa,KAAK;AAClC,MACE,YAAY,UACZ,CAAC,OAAO,SAAS,OAAO,KACxB,CAAC,OAAO,UAAU,OAAO,KACzB,UAAU,MACV;AACA,UAAM,IAAI;AAAA,MACR,4EAA4E,KAAK,UAAU,KAAK,CAAC;AAAA,IACnG;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,OAAoD;AAC5E,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,EAAG,QAAO;AACxE,QAAM,UAAkC,CAAC;AACzC,aAAW,OAAO,OAAO,oBAAoB,KAAK,GAAG;AACnD,UAAM,cAAc,QAAQ,IAAI,OAAO,GAAG;AAC1C,QAAI,OAAO,gBAAgB,SAAU,SAAQ,GAAG,IAAI;AAAA,EACtD;AACA,SAAO;AACT;AAEO,SAAS,oBACd,KACA,mBACA,gBACqB;AACrB,SAAO;AAAA,IACL,sBAAsB,0BAA0B,GAAG;AAAA,IACnD,iBAAiB,IAAI,oBAAoB,QAAQ,IAAI,oBAAoB;AAAA,IACzE,aACE,OAAO,IAAI,gBAAgB,YAAY,IAAI,YAAY,SAAS,IAC5D,IAAI,cACJ;AAAA,IACN,eACE,OAAO,IAAI,kBAAkB,YAAY,IAAI,cAAc,SAAS,IAChE,IAAI,gBACJ;AAAA,IACN,gBACE,OAAO,IAAI,mBAAmB,YAAY,IAAI,eAAe,SAAS,IAClE,eAAe,IAAI,cAAc,IACjC,sBAAsB,SACpB,SACA,WAAW,iBAAiB;AAAA,IACpC;AAAA,IACA,iBAAiB,iBAAiB,IAAI,eAAe;AAAA,IACrD,oBAAoB,IAAI,uBAAuB;AAAA,IAC/C,kBAAkB,IAAI,qBAAqB;AAAA,IAC3C,iBACE,OAAO,IAAI,oBAAoB,YAAY,IAAI,gBAAgB,SAAS,IACpE,IAAI,kBACJ;AAAA,IACN,iBACE,OAAO,IAAI,oBAAoB,YAAY,IAAI,gBAAgB,SAAS,IACpE,IAAI,kBACJ;AAAA,IACN,gBACE,OAAO,IAAI,mBAAmB,YAAY,IAAI,eAAe,SAAS,IAClE,IAAI,iBACJ;AAAA,IACN,mBAAmB,uBAAuB,IAAI,iBAAiB;AAAA,IAC/D,oBAAoB,wBAAwB,IAAI,kBAAkB;AAAA,EACpE;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/namespaces/search.ts"],"sourcesContent":["import { resolveNamespaceCapabilities } from \"../capabilities.js\";\nimport path from \"node:path\";\nimport { realpathSync } from \"node:fs\";\nimport type { PluginConfig, QmdSearchResult } from \"../types.js\";\nimport type {\n SearchBackend,\n SearchExecutionOptions,\n SearchQueryOptions,\n} from \"../search/port.js\";\nimport { createSearchBackend } from \"../search/factory.js\";\nimport { namespaceIdentityToken, normalizeNamespaceIdentity } from \"./identity.js\";\nimport { ALL_CATEGORY_DIRS } from \"../utils/category-dir.js\";\n\nconst NESTED_NAMESPACE_FILTER_OVERFETCH_FACTOR = 4;\nconst NESTED_NAMESPACE_FILTER_OVERFETCH_MIN = 50;\n\nexport function namespaceCollectionName(\n baseCollection: string,\n namespace: string,\n options?: {\n defaultNamespace?: string;\n useLegacyDefaultCollection?: boolean;\n },\n): string {\n const trimmed = normalizeNamespaceIdentity(namespace);\n const defaultNamespace = normalizeNamespaceIdentity(options?.defaultNamespace ?? \"\") || \"default\";\n if (\n options?.useLegacyDefaultCollection === true &&\n trimmed === defaultNamespace\n ) {\n return baseCollection;\n }\n\n return `${baseCollection}--${namespaceIdentityToken(trimmed || defaultNamespace)}`;\n}\n\ntype StorageRouterLike = {\n storageFor(namespace: string): Promise<{ dir: string }>;\n};\n\ntype NamespaceBackendRecord = {\n backend: SearchBackend;\n collection: string;\n memoryDir: string;\n available: boolean;\n collectionState: CollectionState;\n filtersNestedNamespaces: boolean;\n};\n\nexport interface NamespaceUpdateResult {\n backendCount: number;\n eligibleNamespaces: string[];\n}\n\nexport type CollectionState = \"present\" | \"missing\" | \"unknown\" | \"skipped\";\n\nexport interface NamespaceSearchHealth {\n collection: string;\n memoryDir: string;\n available: boolean;\n collectionState: CollectionState;\n debugStatus: string;\n installedVersion: string | null;\n supportedVersion: string | null;\n supported: boolean | null;\n upgradeAvailable: boolean | null;\n doctorAvailable: boolean | null;\n daemonMode: boolean | null;\n pendingEmbeddings: number | null;\n oldestPendingAgeMs: number | null;\n embeddingBacklogThreshold: number;\n}\n\ntype NamespaceScopedSearchConfig = PluginConfig & {\n hostEmbeddingProviderScope?: string;\n};\n\ntype BackendRecordOptions = {\n autoCreateCollection: boolean;\n abortAsUnavailable: boolean;\n failOpenMissingGuardedCollection: boolean;\n};\n\nexport class NamespaceSearchRouter {\n private readonly cache = new Map<string, Promise<NamespaceBackendRecord>>();\n\n constructor(\n private readonly config: PluginConfig,\n private readonly storageRouter: StorageRouterLike,\n private readonly createBackend: (config: PluginConfig) => SearchBackend = createSearchBackend,\n ) {}\n\n async collectionForNamespace(namespace: string): Promise<string> {\n return (await this.backendRecordFor(namespace)).collection;\n }\n\n /**\n * Resolve the search backend for a namespace (#2019). Returns `unknown` so\n * callers (e.g. the prioritized-embed path) duck-type for capability like\n * `embedFiles` rather than depending on a concrete QMD shape. Reuses the\n * cached backend record, so this never re-creates a backend. Returns `null`\n * when the namespace backend is unavailable.\n */\n async backendForNamespace(namespace: string): Promise<unknown> {\n const record = await this.backendRecordFor(namespace);\n return record.available ? record.backend : null;\n }\n\n async searchAcrossNamespaces(options: {\n query: string;\n namespaces: string[];\n maxResults?: number;\n mode?: \"search\" | \"hybrid\" | \"bm25\" | \"vector\";\n searchOptions?: SearchQueryOptions;\n execution?: SearchExecutionOptions;\n }): Promise<QmdSearchResult[]> {\n const query = options.query.trim();\n if (!query) return [];\n const maxResults = Math.max(0, Math.floor(options.maxResults ?? this.config.qmdMaxResults));\n if (maxResults === 0) return [];\n\n const method = options.mode ?? \"search\";\n const namespaces = Array.from(new Set(options.namespaces.map((value) => value.trim()).filter(Boolean)));\n if (namespaces.length === 0) return [];\n\n const resultsByNamespace = await Promise.all(\n namespaces.map(async (namespace) => {\n const record = await this.backendRecordFor(namespace);\n if (!record.available || record.collectionState === \"missing\") {\n // A per-namespace backend that is unavailable (or missing its\n // collection) must report like any other QMD skip (#1536): its\n // empty contribution is otherwise indistinguishable from a true\n // no-matches for that namespace (codex round-5 review on #1544).\n try {\n options.execution?.onDegradation?.({\n backend: \"qmd\",\n code: \"backend_unavailable\",\n detail:\n record.collectionState === \"missing\"\n ? `namespace collection missing: ${namespace}`\n : `namespace backend unavailable: ${namespace}`,\n });\n } catch {\n // Observers must never break search.\n }\n return { namespace, results: [] as QmdSearchResult[] };\n }\n const backendLimit = backendSearchLimit(record, maxResults);\n let results: QmdSearchResult[];\n switch (method) {\n case \"hybrid\":\n results = await record.backend.hybridSearch(\n query,\n record.collection,\n backendLimit,\n options.execution,\n );\n break;\n case \"bm25\":\n results = await record.backend.bm25Search(\n query,\n record.collection,\n backendLimit,\n options.execution,\n );\n break;\n case \"vector\":\n results = await record.backend.vectorSearch(\n query,\n record.collection,\n backendLimit,\n options.execution,\n );\n break;\n default:\n results = await record.backend.search(\n query,\n record.collection,\n backendLimit,\n options.searchOptions,\n options.execution,\n );\n break;\n }\n results = filterNamespaceSubtreeResults(record, results)\n .map((result) => ({\n ...result,\n namespace,\n // Resolve to an absolute path so the (namespace, path) identity is\n // globally unique — same-relative-path hits from different namespaces\n // stay distinct across every downstream consumer with no special\n // handling. Display/citation surfaces relativize for portability (#2020).\n path: resolveNamespaceResultPath(record.memoryDir, record.collection, result.path),\n }))\n // Reject hits whose resolved path escapes the queried namespace's\n // storage root (#2077). A stale/corrupt per-namespace collection can\n // return a path owned by another namespace; stamping it would let\n // downstream scope checks (which trust the stamped namespace) and\n // absolute-path resolution surface a foreign-namespace memory.\n .filter((result) => resolvedPathIsInsideNamespaceRoot(record.memoryDir, result.path));\n return { namespace, results };\n }),\n );\n\n return mergeNamespaceSearchResults(resultsByNamespace, maxResults);\n }\n\n /**\n * Update all namespace backends.\n * Returns the number of backends for which an update was attempted\n * (i.e., available and collection present). Callers can treat 0 as a\n * signal that no backend was eligible — useful for success-verification in\n * startup-sync when namespacesEnabled is true.\n */\n async updateNamespaces(\n namespaces: string[],\n execution?: SearchExecutionOptions,\n options?: { strict?: boolean },\n ): Promise<number> {\n return (await this.updateNamespacesDetailed(namespaces, execution, options)).backendCount;\n }\n\n async updateNamespacesDetailed(\n namespaces: string[],\n execution?: SearchExecutionOptions,\n options?: { strict?: boolean },\n ): Promise<NamespaceUpdateResult> {\n const unique = Array.from(new Set(namespaces.map((value) => value.trim()).filter(Boolean)));\n const eligible = (await Promise.all(\n unique.map(async (namespace) => {\n const record = await this.backendRecordFor(namespace);\n return record.available && record.collectionState !== \"missing\"\n ? { namespace, record }\n : null;\n }),\n )).filter((entry): entry is { namespace: string; record: NamespaceBackendRecord } => entry !== null);\n\n const globalEntry = eligible.find(({ record }) => record.backend.updatesAllCollections?.() === true);\n const scopedEntries = globalEntry\n ? eligible.filter(({ record }) => record.backend.updatesAllCollections?.() !== true)\n : eligible;\n\n await Promise.all([\n globalEntry\n ? updateBackendRecord(globalEntry.record, execution, options)\n : Promise.resolve(),\n ...scopedEntries.map(({ record }) => updateBackendRecord(record, execution, options)),\n ]);\n\n return {\n backendCount: (globalEntry ? 1 : 0) + scopedEntries.length,\n eligibleNamespaces: eligible.map(({ namespace }) => namespace),\n };\n }\n\n async embedNamespaces(namespaces: string[], options?: { strict?: boolean }): Promise<void> {\n const unique = Array.from(new Set(namespaces.map((value) => value.trim()).filter(Boolean)));\n await Promise.all(\n unique.map(async (namespace) => {\n const record = await this.backendRecordFor(namespace);\n if (!record.available || record.collectionState === \"missing\") return;\n await embedBackendRecord(record, options);\n }),\n );\n }\n\n async ensureNamespaceCollection(\n namespace: string,\n execution?: SearchExecutionOptions,\n ): Promise<\"present\" | \"missing\" | \"unknown\" | \"skipped\"> {\n const record = await this.backendRecordFor(namespace, execution);\n return record.collectionState;\n }\n\n async healthForNamespace(\n namespace: string,\n execution?: SearchExecutionOptions,\n ): Promise<NamespaceSearchHealth> {\n const key = namespace.trim() || this.config.defaultNamespace;\n const record = await this.createBackendRecordFor(\n key,\n execution,\n {\n autoCreateCollection: false,\n abortAsUnavailable: true,\n failOpenMissingGuardedCollection: false,\n },\n );\n try {\n const liveRecord = await this.liveCachedRecordForHealth(key, record, execution);\n const diagnosticBackend = liveRecord?.backend ?? record.backend;\n const versionStatus =\n \"getVersionStatus\" in diagnosticBackend &&\n typeof diagnosticBackend.getVersionStatus === \"function\"\n ? diagnosticBackend.getVersionStatus()\n : null;\n const daemonMode = daemonModeForBackend(diagnosticBackend);\n const backendStatus =\n \"status\" in diagnosticBackend &&\n typeof diagnosticBackend.status === \"function\"\n ? await Promise.race([\n diagnosticBackend.status().catch(() => null),\n new Promise<null>((resolve) => setTimeout(() => resolve(null), 5_000).unref?.()),\n ])\n : null;\n const collectionState =\n liveRecord?.collectionState === \"missing\"\n ? \"missing\"\n : record.collectionState;\n\n return {\n collection: record.collection,\n memoryDir: record.memoryDir,\n available: liveRecord?.available ?? record.available,\n collectionState,\n debugStatus: diagnosticBackend.debugStatus(),\n installedVersion: versionStatus?.installedVersion ?? null,\n supportedVersion: versionStatus?.supportedVersion ?? null,\n supported: versionStatus?.supported ?? null,\n upgradeAvailable: versionStatus?.upgradeAvailable ?? null,\n doctorAvailable: versionStatus?.capabilities?.doctor ?? null,\n daemonMode,\n pendingEmbeddings: backendStatus?.pendingEmbeddings ?? null,\n oldestPendingAgeMs: backendStatus?.oldestPendingAgeMs ?? null,\n embeddingBacklogThreshold: this.config.qmdEmbeddingBacklogThreshold,\n };\n } finally {\n const dispose = (record.backend as { dispose?: () => void | Promise<void> }).dispose;\n await dispose?.call(record.backend);\n }\n }\n\n private async liveCachedRecordForHealth(\n key: string,\n disposableRecord: NamespaceBackendRecord,\n execution?: SearchExecutionOptions,\n ): Promise<NamespaceBackendRecord | null> {\n const pending = this.cache.get(key);\n if (!pending) return null;\n const cachedRecord = await awaitWithAbort(pending, execution?.signal).catch(() => null);\n if (!cachedRecord) return null;\n if (cachedRecord.collection !== disposableRecord.collection) return null;\n if (cachedRecord.memoryDir !== disposableRecord.memoryDir) return null;\n return cachedRecord;\n }\n\n /** Clear cached backend records so the next access re-probes availability. */\n clearCache(): void {\n this.cache.clear();\n }\n\n /** Release any per-namespace backend handles held by cached records. */\n async dispose(): Promise<void> {\n const pendingRecords = Array.from(this.cache.values());\n this.cache.clear();\n const settled = await Promise.allSettled(pendingRecords);\n await Promise.allSettled(\n settled.flatMap((entry) => {\n if (entry.status !== \"fulfilled\") return [];\n const dispose = (entry.value.backend as { dispose?: () => void | Promise<void> }).dispose;\n return dispose ? [dispose.call(entry.value.backend)] : [];\n }),\n );\n }\n\n private async backendRecordFor(\n namespace: string,\n execution?: SearchExecutionOptions,\n ): Promise<NamespaceBackendRecord> {\n const key = namespace.trim() || this.config.defaultNamespace;\n const existing = this.cache.get(key);\n if (existing) return await existing;\n\n const pending = this.createBackendRecordFor(key, execution, {\n autoCreateCollection: true,\n abortAsUnavailable: false,\n failOpenMissingGuardedCollection: true,\n }).catch((error) => {\n this.cache.delete(key);\n throw error;\n });\n\n this.cache.set(key, pending);\n return await pending;\n }\n\n private async createBackendRecordFor(\n namespace: string,\n execution: SearchExecutionOptions | undefined,\n options: BackendRecordOptions,\n ): Promise<NamespaceBackendRecord> {\n const key = namespace.trim() || this.config.defaultNamespace;\n const storage = await this.storageRouter.storageFor(key);\n const useLegacyDefaultCollection =\n key === this.config.defaultNamespace && storage.dir === this.config.memoryDir;\n const filtersNestedNamespaces =\n resolveNamespaceCapabilities(this.config).namespaces === true && useLegacyDefaultCollection;\n const rootHostEmbeddingScope =\n (this.config as NamespaceScopedSearchConfig).hostEmbeddingProviderScope ??\n this.config.memoryDir;\n const scopedConfig: NamespaceScopedSearchConfig = {\n ...this.config,\n memoryDir: storage.dir,\n hostEmbeddingProviderScope: rootHostEmbeddingScope,\n qmdCollection: namespaceCollectionName(this.config.qmdCollection, key, {\n defaultNamespace: this.config.defaultNamespace,\n useLegacyDefaultCollection,\n }),\n };\n\n const backend = this.createBackend(scopedConfig);\n try {\n const availabilityProbe =\n options.autoCreateCollection || typeof backend.checkAvailability !== \"function\"\n ? backend.probe()\n : backend.checkAvailability({ signal: execution?.signal });\n const available = await awaitWithAbort(availabilityProbe, execution?.signal).catch((error) => {\n if (error instanceof NamespaceSearchAbortError && !options.abortAsUnavailable) {\n throw error;\n }\n return false;\n });\n const collectionState = available\n ? await awaitWithAbort(\n // The legacy default namespace at the flat root (`filtersNestedNamespaces`)\n // shares its `memoryDir` with the `namespaces/` container, so its base\n // collection is a \"broad root\" that also indexes nested-namespace files.\n // We STILL auto-create it (issue #1929): the search side already targets\n // this exact base collection and `filterNamespaceSubtreeResults` strips\n // the nested `namespaces/` subtree out of its results, so index and\n // search stay symmetric. Skipping creation left a configured default\n // namespace (e.g. `geek`) with maintenance \"ran\" but no collection and 0\n // recall results. `ensureCollection` is a no-op when the collection is\n // already present, so legacy installs that pre-created the broad root are\n // unaffected.\n this.collectionStateForBackend(backend, storage.dir, scopedConfig.qmdCollection, {\n autoCreate: options.autoCreateCollection,\n failOpenMissingGuardedCollection: options.failOpenMissingGuardedCollection,\n execution,\n }),\n execution?.signal,\n ).catch((error) => {\n if (error instanceof NamespaceSearchAbortError && !options.abortAsUnavailable) {\n throw error;\n }\n return \"unknown\" as const;\n })\n : \"unknown\";\n return {\n backend,\n collection: scopedConfig.qmdCollection,\n memoryDir: storage.dir,\n available,\n collectionState,\n filtersNestedNamespaces,\n };\n } catch (error) {\n const dispose = (backend as { dispose?: () => void | Promise<void> }).dispose;\n if (dispose) {\n await Promise.resolve(dispose.call(backend)).catch(() => {});\n }\n throw error;\n }\n }\n\n private async collectionStateForBackend(\n backend: SearchBackend,\n memoryDir: string,\n collection: string,\n options: {\n autoCreate: boolean;\n failOpenMissingGuardedCollection: boolean;\n execution?: SearchExecutionOptions;\n },\n ): Promise<CollectionState> {\n if (!options.autoCreate) {\n if (!backend.checkCollection) return \"unknown\";\n const collectionState = await backend\n .checkCollection(collection, options.execution)\n .catch(() => \"unknown\" as const);\n return options.failOpenMissingGuardedCollection && collectionState === \"missing\"\n ? \"unknown\"\n : collectionState;\n }\n return await backend.ensureCollection(memoryDir, collection, options.execution).catch(() => \"unknown\" as const);\n }\n}\n\nclass NamespaceSearchAbortError extends Error {\n constructor() {\n super(\"operation aborted\");\n }\n}\n\nfunction awaitWithAbort<T>(operation: Promise<T>, signal?: AbortSignal): Promise<T> {\n if (!signal) return operation;\n if (signal.aborted) return Promise.reject(new NamespaceSearchAbortError());\n\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => {\n signal.removeEventListener(\"abort\", onAbort);\n reject(new NamespaceSearchAbortError());\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n operation.then(resolve, reject).finally(() => {\n signal.removeEventListener(\"abort\", onAbort);\n });\n });\n}\n\nfunction filterNamespaceSubtreeResults(\n record: NamespaceBackendRecord,\n results: QmdSearchResult[],\n): QmdSearchResult[] {\n if (!record.filtersNestedNamespaces) return results;\n return results.filter((result) =>\n !pathIsInsideNamespaceSubtree(record.memoryDir, record.collection, result.path)\n );\n}\n\nfunction backendSearchLimit(\n record: NamespaceBackendRecord,\n maxResults: number,\n): number {\n if (!record.filtersNestedNamespaces) return maxResults;\n return Math.max(\n maxResults,\n maxResults * NESTED_NAMESPACE_FILTER_OVERFETCH_FACTOR,\n NESTED_NAMESPACE_FILTER_OVERFETCH_MIN,\n );\n}\n\nasync function updateBackendRecord(\n record: NamespaceBackendRecord,\n execution?: SearchExecutionOptions,\n options?: { strict?: boolean },\n): Promise<void> {\n if (options?.strict === true) {\n if (\n record.backend.updatesAllCollections?.() === true &&\n typeof record.backend.updateStrict === \"function\"\n ) {\n await record.backend.updateStrict(execution);\n return;\n }\n if (typeof record.backend.updateCollectionStrict === \"function\") {\n await record.backend.updateCollectionStrict(record.collection, execution);\n return;\n }\n }\n await record.backend.update(execution);\n}\n\nasync function embedBackendRecord(\n record: NamespaceBackendRecord,\n options?: { strict?: boolean },\n): Promise<void> {\n if (options?.strict === true) {\n if (typeof record.backend.embedCollectionStrict === \"function\") {\n await record.backend.embedCollectionStrict(record.collection);\n return;\n }\n if (typeof record.backend.embedStrict === \"function\") {\n await record.backend.embedStrict();\n return;\n }\n }\n if (typeof record.backend.embedCollection === \"function\") {\n await record.backend.embedCollection(record.collection);\n return;\n }\n await record.backend.embed();\n}\n\nfunction daemonModeForBackend(backend: SearchBackend): boolean | null {\n return \"isDaemonMode\" in backend && typeof backend.isDaemonMode === \"function\"\n ? backend.isDaemonMode() === true\n : null;\n}\n\nfunction isContainedPath(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return (\n relative === \"\" ||\n (relative !== \"..\" && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative))\n );\n}\n\nfunction pathIsInsideNamespaceSubtree(\n memoryDir: string,\n collection: string,\n resultPath: string | undefined,\n): boolean {\n if (!resultPath) return false;\n const normalizedResultPath = normalizeQmdResultPath(resultPath, collection);\n const memoryRoot = path.resolve(memoryDir);\n const namespacesRoot = path.join(memoryRoot, \"namespaces\");\n const candidate = path.isAbsolute(normalizedResultPath)\n ? path.normalize(normalizedResultPath)\n : path.resolve(memoryRoot, normalizedResultPath);\n return isContainedPath(namespacesRoot, candidate);\n}\n\n/**\n * Reject a QMD hit whose resolved path escapes the queried namespace's storage\n * root (#2077). `resolvedPath` is the already-resolved value produced by\n * `resolveNamespaceResultPath`; a stale/corrupt per-namespace collection can\n * yield an absolute path owned by another namespace, and stamping it with the\n * queried namespace would surface a foreign-namespace memory downstream.\n *\n * Lexical containment runs first — it is cheap and the only check possible when\n * the file is not yet on disk (a stale collection may cite a since-deleted\n * path). When both root and candidate exist, their filesystem realpaths are\n * canonicalized and re-checked so a symlink planted inside the namespace root\n * cannot resolve into another namespace or outside storage. An unresolved path\n * (ENOENT) keeps the lexical result rather than rejecting the stale-collection\n * case this guard targets.\n */\nfunction resolvedPathIsInsideNamespaceRoot(memoryDir: string, resolvedPath: string): boolean {\n const root = path.resolve(memoryDir);\n const candidate = path.isAbsolute(resolvedPath)\n ? path.normalize(resolvedPath)\n : path.resolve(root, resolvedPath);\n if (!isContainedPath(root, candidate)) return false;\n try {\n return isContainedPath(realpathSync.native(root), realpathSync.native(candidate));\n } catch {\n return true;\n }\n}\nexport function normalizeQmdResultPath(resultPath: string, collection: string): string {\n let value = resultPath.trim();\n if (value.startsWith(\"qmd://\")) {\n try {\n const parsed = new URL(value);\n if (parsed.protocol === \"qmd:\" && parsed.hostname === collection) {\n value = decodeURIComponent(parsed.pathname.replace(/^\\/+/, \"\"));\n }\n } catch {\n const remainder = value.slice(\"qmd://\".length);\n const slashIndex = remainder.indexOf(\"/\");\n if (slashIndex !== -1) {\n value = remainder.slice(slashIndex + 1);\n }\n }\n }\n\n // Only strip a genuine QMD collection prefix — never when the collection name\n // collides with a memory category dir (e.g. qmdCollection == \"facts\"), or a\n // bare \"facts/<id>.md\" hit would be corrupted to \"<id>.md\" and fail namespace\n // reads/access tracking (#2020).\n const collectionPrefix = `${collection}/`;\n if (value.startsWith(collectionPrefix) && !ALL_CATEGORY_DIRS.includes(collection)) {\n value = value.slice(collectionPrefix.length);\n }\n return value;\n}\nfunction resolveNamespaceResultPath(\n memoryDir: string,\n collection: string,\n resultPath: string,\n): string {\n const normalized = normalizeQmdResultPath(resultPath, collection);\n if (path.isAbsolute(normalized)) return normalized;\n const root = path.resolve(memoryDir);\n const resolved = path.resolve(root, normalized);\n const relative = path.relative(root, resolved);\n if (relative === \"\" || relative.startsWith(\"..\") || path.isAbsolute(relative)) {\n return resultPath;\n }\n return resolved;\n}\nfunction mergeNamespaceSearchResults(\n lists: Array<{ namespace: string; results: QmdSearchResult[] }>,\n maxResults: number,\n): QmdSearchResult[] {\n const merged = new Map<string, QmdSearchResult>();\n\n for (const { namespace, results } of lists) {\n for (const result of results) {\n const key = `${namespace}\\0${result.path || result.docid}`;\n const existing = merged.get(key);\n if (!existing) {\n merged.set(key, { ...result, namespace });\n continue;\n }\n if (result.score > existing.score) {\n merged.set(key, {\n ...result,\n namespace,\n snippet: existing.snippet || result.snippet || \"\",\n });\n }\n }\n }\n\n return [...merged.values()]\n .sort((a, b) => b.score - a.score)\n .slice(0, maxResults);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/summarizer.ts"],"sourcesContent":["import { mkdir, readFile, writeFile, readdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport { log } from \"./logger.js\";\nimport { LocalLlmClient } from \"./local-llm.js\";\nimport { FallbackLlmClient, fallbackLlmRuntimeContextFromConfig, gatewayTaskChainOptions } from \"./fallback-llm.js\";\nimport { ModelRegistry } from \"./model-registry.js\";\nimport { extractJsonCandidates } from \"./json-extract.js\";\nimport {\n completeBackgroundGeneration,\n parseBackgroundGenerationJson,\n} from \"./background-generation.js\";\nimport type { HourlySummary, TranscriptEntry, PluginConfig, GatewayConfig } from \"./types.js\";\nimport type { TranscriptManager } from \"./transcript.js\";\nimport { readSummarySnapshot, upsertSummarySnapshot, writeSummarySnapshot } from \"./summary-snapshot.js\";\nimport { encodeStoragePathSegment, resolveSafeStoragePath } from \"./storage-paths.js\";\nimport { sessionStoragePaths } from \"./session-identity.js\";\nimport { resolveLocalLlmCapabilities } from \"./capabilities.js\";\nimport { resolvePipelineProcessingCapabilities } from \"./capabilities.js\";\n\n// Schema for LLM summary output\nconst HourlySummarySchema = z.object({\n bullets: z.array(z.string().trim().min(1)).min(1).describe(\"3-5 bullet points summarizing the hour's activity\"),\n});\n\ntype HourlySummaryResult = z.infer<typeof HourlySummarySchema>;\n\nconst HourlySummaryExtendedSchema = z.object({\n topics: z.array(z.string()).default([]),\n decisions: z.array(z.string()).default([]),\n actionItems: z.array(z.string()).default([]),\n rejected: z.array(z.string()).default([]),\n});\n\ntype HourlySummaryExtendedResult = z.infer<typeof HourlySummaryExtendedSchema>;\n\ntype HourlySummaryExtendedMeta = {\n userTurns: number;\n assistantTurns: number;\n toolCalls: number;\n toolCounts: Record<string, number>;\n};\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Outcome stats of one `runHourly` pass (issue #2783). The summarizer must\n * not report success on empty work: an empty or stale transcript store is\n * the delegate-mode starvation signature, and callers surface these counts\n * and the staleness flag as a distinct warning instead of a bare ok.\n */\nexport interface HourlySummarizerRunStats {\n sessionsConsidered: number;\n sessionsWithEntries: number;\n summariesWritten: number;\n /** True when the newest transcript entry across the store is older than STALE_STORE_MS. */\n staleStore: boolean;\n /** ISO timestamp of the newest transcript entry seen by the scan, when any exist. */\n newestEntryTimestamp: string | null;\n /** True when the transcript scan itself FAILED (unreadable store) — distinct from an empty store. */\n scanFailed: boolean;\n}\n\n/** A store whose newest entry is older than this is considered stale (starvation signal, issue #2783). */\nconst STALE_STORE_MS = 2 * 60 * 60 * 1000;\n\n/**\n * Interpret one {@link HourlySummarizerRunStats} into the operator-facing\n * message/warning (issue #2783). Every degraded shape — failed scan, empty\n * store, backend-down, stale store — is DISTINCT from success; monitoring\n * alerts on `warning`, never on `ok` (ok only means \"the run completed\").\n */\nexport function summarizeHourlyStatus(stats: HourlySummarizerRunStats): {\n message: string;\n warning?: string;\n} {\n if (stats.scanFailed) {\n return {\n message:\n \"Hourly summarization completed, but the transcript store scan FAILED (unreadable directory or I/O error). Investigate the daemon's memoryDir.\",\n warning: \"transcript store scan failed\",\n };\n }\n if (stats.sessionsConsidered === 0) {\n return {\n message:\n \"Hourly summarization completed, but the transcript store is empty — no sessions found. If turns are ingested via observe, transcript persistence may be disabled or failing.\",\n warning: \"transcript store is empty\",\n };\n }\n if (stats.sessionsWithEntries > 0 && stats.summariesWritten === 0) {\n return {\n message: `Hourly summarization found ${stats.sessionsWithEntries} session(s) with transcript entries but wrote no summaries — the summarizer backend may be down.`,\n warning: \"sessions with entries produced no summaries\",\n };\n }\n if (stats.sessionsWithEntries === 0 && stats.staleStore) {\n return {\n message: `Hourly summarization completed with no transcript entries for the target hour, and the store looks stale (newest entry ${stats.newestEntryTimestamp ?? \"unknown\"}).`,\n warning: \"no transcript entries for the target hour and no new entries recently\",\n };\n }\n return {\n message: `Hourly summarization completed: ${stats.summariesWritten} summar${stats.summariesWritten === 1 ? \"y\" : \"ies\"} written across ${stats.sessionsWithEntries} session${stats.sessionsWithEntries === 1 ? \"\" : \"s\"} with transcript entries.`,\n };\n}\n\nexport class HourlySummarizer {\n private summariesDir: string;\n private config: PluginConfig;\n private localLlm: LocalLlmClient;\n private fallbackLlm: FallbackLlmClient;\n private modelRegistry: ModelRegistry;\n private transcript?: TranscriptManager;\n\n constructor(\n config: PluginConfig,\n gatewayConfig?: GatewayConfig,\n modelRegistry?: ModelRegistry,\n transcript?: TranscriptManager\n ) {\n this.config = config;\n this.summariesDir = path.join(config.memoryDir, \"summaries\", \"hourly\");\n this.modelRegistry = modelRegistry ?? new ModelRegistry(config.memoryDir);\n this.transcript = transcript;\n\n // Initialize local LLM client with shared model registry\n this.localLlm = new LocalLlmClient(config, this.modelRegistry);\n\n // Initialize fallback client with gateway config\n this.fallbackLlm = new FallbackLlmClient(gatewayConfig, fallbackLlmRuntimeContextFromConfig(config));\n\n if (\n !gatewayConfig?.agents?.defaults?.model?.primary &&\n !resolveLocalLlmCapabilities(config).localLlm &&\n config.modelSource !== \"gateway\"\n ) {\n log.warn(\"no gateway default AI and local LLM disabled — hourly summarization disabled\");\n }\n }\n\n private get useGatewayModelSource(): boolean {\n return this.config.modelSource === \"gateway\";\n }\n\n private get shouldUseLocalLlm(): boolean {\n return resolveLocalLlmCapabilities(this.config).localLlm && !this.useGatewayModelSource;\n }\n\n private withGatewayAgent(\n options: import(\"./fallback-llm.js\").FallbackLlmOptions\n ): import(\"./fallback-llm.js\").FallbackLlmOptions {\n if (!this.useGatewayModelSource) return options;\n // Shared resolution (taskModelChain > gatewayAgentId) — gotcha #22. Issue #1365.\n return { ...options, ...gatewayTaskChainOptions(this.config) };\n }\n\n async initialize(): Promise<void> {\n await mkdir(this.summariesDir, { recursive: true });\n log.info(\"hourly summarizer initialized\");\n }\n\n private async summarySessionDir(sessionKey: string): Promise<string> {\n return resolveSafeStoragePath(this.summariesDir, encodeStoragePathSegment(sessionKey, \"session\"));\n }\n\n private async legacySummarySessionDir(sessionKey: string): Promise<string | null> {\n if (sessionKey.includes(\"\\0\")) return null;\n try {\n return await resolveSafeStoragePath(this.summariesDir, sessionKey);\n } catch {\n return null;\n }\n }\n\n private summaryDateString(hour: string): string {\n const dateStr = hour.slice(0, 10);\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(dateStr)) {\n throw new Error(`invalid hourly summary timestamp: ${hour}`);\n }\n return dateStr;\n }\n\n // Generate summary for a specific hour and session\n async generateSummary(\n sessionKey: string,\n hourStart: Date,\n entries: TranscriptEntry[]\n ): Promise<HourlySummary | null> {\n if (entries.length === 0) return null;\n\n // Format entries for the LLM\n const conversation = entries.map((e) => `[${e.role}] ${e.content}`).join(\"\\n\\n\");\n\n if (resolvePipelineProcessingCapabilities(this.config).hourlySummariesExtended) {\n const extended = await this.generateExtended(sessionKey, hourStart, conversation, entries);\n if (!extended) return null;\n const meta: HourlySummaryExtendedMeta = {\n userTurns: entries.filter((e) => e.role === \"user\").length,\n assistantTurns: entries.filter((e) => e.role === \"assistant\").length,\n toolCalls: extended._meta.toolCalls,\n toolCounts: extended._meta.toolCounts,\n };\n // Keep HourlySummary surface stable; encode \"topics\" as bullets for recall injection.\n const base: HourlySummary = {\n hour: hourStart.toISOString(),\n sessionKey,\n bullets: extended.topics.length > 0 ? extended.topics.slice(0, 5) : [\"(summary generated)\"],\n turnCount: entries.length,\n generatedAt: new Date().toISOString(),\n };\n const withExtras = base as any;\n withExtras._extended = extended;\n withExtras._extendedMeta = meta;\n return base;\n }\n\n const hourIso = hourStart.toISOString();\n const startTime = Date.now();\n\n if (this.config.backgroundGeneration) {\n try {\n const text = await completeBackgroundGeneration(this.config, [\n { role: \"system\", content: \"You are a conversation summarization system. Output valid JSON only.\" },\n { role: \"user\", content: `Summarize this conversation:\\n\\n${conversation}` },\n ]);\n const parsed = parseBackgroundGenerationJson(text, (value) => HourlySummarySchema.parse(value));\n if (parsed) {\n return {\n hour: hourIso,\n sessionKey,\n bullets: parsed.bullets,\n turnCount: entries.length,\n generatedAt: new Date().toISOString(),\n };\n }\n } catch {\n log.info(\"summary generation: background generation unavailable, continuing\");\n }\n }\n\n // Try local LLM first if enabled\n if (this.shouldUseLocalLlm) {\n try {\n const localResult = await this.generateWithLocalLlm(conversation);\n if (localResult) {\n const durationMs = Date.now() - startTime;\n log.debug(`generated hourly summary for ${sessionKey} at ${hourIso} in ${durationMs}ms using local LLM`);\n return {\n hour: hourIso,\n sessionKey,\n bullets: localResult.bullets,\n turnCount: entries.length,\n generatedAt: new Date().toISOString(),\n };\n }\n // Local failed, fall back if allowed\n if (!this.config.localLlmFallback) {\n log.warn(\"summary generation: local LLM failed and fallback disabled\");\n return null;\n }\n log.info(\"summary generation: local LLM unavailable, falling back to gateway default AI\");\n } catch (err) {\n if (!this.config.localLlmFallback) {\n log.warn(\"summary generation: local LLM error and fallback disabled:\", err);\n return null;\n }\n log.info(\"summary generation: local LLM error, falling back to gateway default AI:\", err);\n }\n }\n\n // Fall back to gateway's default AI\n log.info(\"summary generation: falling back to gateway default AI\");\n\n try {\n const messages = [\n {\n role: \"system\" as const,\n content: `You are a conversation summarization system. Summarize the following conversation transcript into 3-5 concise bullet points.\n\nGuidelines:\n- Focus on what was accomplished, decided, or discussed\n- Include specific topics, projects, or entities mentioned\n- Note any significant user requests or agent actions\n- Keep bullets brief but informative (1-2 sentences each)\n- Skip trivial greetings or meta-conversation\n- Use present tense for ongoing work, past for completed items\n\nRespond with valid JSON matching this schema:\n{\n \"bullets\": [\"bullet 1\", \"bullet 2\", \"bullet 3\"]\n}`,\n },\n { role: \"user\" as const, content: `Summarize this conversation:\\n\\n${conversation}` },\n ];\n\n const result = await this.fallbackLlm.parseWithSchema(\n messages,\n HourlySummarySchema,\n this.withGatewayAgent({ temperature: 0.3, maxTokens: 8192 })\n );\n\n const durationMs = Date.now() - startTime;\n\n if (result) {\n log.debug(`generated hourly summary for ${sessionKey} at ${hourIso} in ${durationMs}ms via fallback`);\n return {\n hour: hourIso,\n sessionKey,\n bullets: result.bullets,\n turnCount: entries.length,\n generatedAt: new Date().toISOString(),\n };\n }\n\n log.warn(\"summary generation fallback returned no parsed output\");\n return null;\n } catch (err) {\n log.error(\"summary generation fallback failed\", err);\n return null;\n }\n }\n\n private async generateExtended(\n sessionKey: string,\n hourStart: Date,\n conversation: string,\n entries: TranscriptEntry[]\n ): Promise<(HourlySummaryExtendedResult & { _meta: HourlySummaryExtendedMeta }) | null> {\n const hourIso = hourStart.toISOString();\n const startTime = Date.now();\n\n const hourEnd = new Date(hourStart.getTime() + 60 * 60 * 1000);\n let toolCounts: Record<string, number> = {};\n if (this.config.hourlySummariesIncludeToolStats && this.transcript) {\n const uses = await this.transcript.readToolUse(sessionKey, hourStart, hourEnd);\n for (const u of uses) toolCounts[u.tool] = (toolCounts[u.tool] ?? 0) + 1;\n }\n\n const sys = `You are a conversation summarization system.\\n\\nSummarize the hour into structured sections.\\n\\nReturn valid JSON matching:\\n{\\n \\\"topics\\\": [\\\"...\\\"],\\n \\\"decisions\\\": [\\\"...\\\"],\\n \\\"actionItems\\\": [\\\"...\\\"],\\n \\\"rejected\\\": [\\\"...\\\"]\\n}\\n\\nGuidelines:\\n- Prefer concrete topics and decisions.\\n- Action items should be imperative.\\n- Rejected ideas are things that were explicitly discarded or reversed.\\n- If there are none for a section, return an empty array.\\n`;\n\n const toolStatsLine =\n Object.keys(toolCounts).length > 0\n ? `Tools used (counts): ${Object.entries(toolCounts)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10)\n .map(([k, v]) => `${k}=${v}`)\n .join(\", \")}\\n\\n`\n : \"\";\n const userTurns = entries.filter((e) => e.role === \"user\").length;\n const assistantTurns = entries.filter((e) => e.role === \"assistant\").length;\n const toolCalls = Object.values(toolCounts).reduce((a, b) => a + b, 0);\n const statsLine = `Stats: userTurns=${userTurns}, assistantTurns=${assistantTurns}, toolCalls=${toolCalls}\\n\\n`;\n\n const user = `Hour: ${hourIso}\\nSession: ${sessionKey}\\n\\n${statsLine}${toolStatsLine}Conversation:\\n${conversation}\\n`;\n\n // Try local LLM first if enabled\n if (this.shouldUseLocalLlm) {\n try {\n const contextSizes = this.modelRegistry.calculateContextSizes(\n this.config.localLlmModel,\n this.config.localLlmMaxContext\n );\n const truncated =\n user.length > contextSizes.maxInputChars\n ? user.slice(0, contextSizes.maxInputChars) + \"\\n\\n[truncated]\"\n : user;\n const response = await this.localLlm.chatCompletion(\n [\n { role: \"system\", content: \"Output valid JSON only.\" },\n { role: \"user\", content: sys + \"\\n\\n\" + truncated },\n ],\n {\n temperature: 0.2,\n maxTokens: contextSizes.maxOutputTokens,\n operation: \"hourly_summary_extended\",\n priority: \"background\",\n }\n );\n if (response?.content) {\n const content = response.content.trim();\n for (const candidate of extractJsonCandidates(content)) {\n try {\n const parsed = JSON.parse(candidate);\n const result = HourlySummaryExtendedSchema.parse(parsed);\n log.debug(\n `generated extended hourly summary for ${sessionKey} at ${hourIso} in ${Date.now() - startTime}ms (local)`\n );\n return { ...result, _meta: { userTurns, assistantTurns, toolCalls, toolCounts } };\n } catch {\n // keep trying candidates\n }\n }\n }\n } catch (err) {\n if (!this.config.localLlmFallback) return null;\n log.info(\"extended summary: local failed, falling back:\", err);\n }\n }\n\n try {\n const result = await this.fallbackLlm.parseWithSchema(\n [\n { role: \"system\" as const, content: sys },\n { role: \"user\" as const, content: user },\n ],\n HourlySummaryExtendedSchema,\n this.withGatewayAgent({ temperature: 0.2, maxTokens: 2048 })\n );\n if (result) {\n log.debug(\n `generated extended hourly summary for ${sessionKey} at ${hourIso} in ${Date.now() - startTime}ms (fallback)`\n );\n return { ...result, _meta: { userTurns, assistantTurns, toolCalls, toolCounts } };\n }\n return null;\n } catch (err) {\n log.error(\"extended summary generation failed\", err);\n return null;\n }\n }\n\n /**\n * Generate summary using local LLM with JSON mode.\n * Uses dynamic context sizing based on model capabilities.\n */\n private async generateWithLocalLlm(conversation: string): Promise<HourlySummaryResult | null> {\n // Get dynamic context sizes based on model capabilities\n const contextSizes = this.modelRegistry.calculateContextSizes(\n this.config.localLlmModel,\n this.config.localLlmMaxContext\n );\n log.debug(`Summarizer model context: ${contextSizes.description}`);\n\n const maxConversationChars = contextSizes.maxInputChars;\n const truncatedConversation =\n conversation.length > maxConversationChars\n ? conversation.slice(0, maxConversationChars) + \"\\n\\n[truncated]\"\n : conversation;\n\n const instructions = `You are a conversation summarization system. Summarize the following conversation transcript into 3-5 concise bullet points.\n\nGuidelines:\n- Focus on what was accomplished, decided, or discussed\n- Include specific topics, projects, or entities mentioned\n- Note any significant user requests or agent actions\n- Keep bullets brief but informative (1-2 sentences each)\n- Skip trivial greetings or meta-conversation\n- Use present tense for ongoing work, past for completed items\n\nRespond with valid JSON matching this schema:\n{\n \"bullets\": [\"bullet 1\", \"bullet 2\", \"bullet 3\"]\n}`;\n\n const fullPrompt = `${instructions}\\n\\nConversation to summarize:\\n${truncatedConversation}`;\n\n const response = await this.localLlm.chatCompletion(\n [\n { role: \"system\", content: \"You are a conversation summarization system. Output valid JSON only.\" },\n { role: \"user\", content: fullPrompt },\n ],\n {\n temperature: 0.3,\n maxTokens: contextSizes.maxOutputTokens,\n operation: \"hourly_summary\",\n priority: \"background\",\n }\n );\n\n if (!response?.content) {\n return null;\n }\n\n try {\n // Parse JSON response\n const content = response.content.trim();\n for (const candidate of extractJsonCandidates(content)) {\n try {\n const parsed = JSON.parse(candidate);\n return HourlySummarySchema.parse(parsed);\n } catch {\n // keep trying candidates\n }\n }\n return null;\n } catch (err) {\n log.warn(\"local LLM summary: failed to parse JSON response:\", err);\n return null;\n }\n }\n\n // Save summary to file\n async saveSummary(summary: HourlySummary): Promise<void> {\n const sessionDir = await this.summarySessionDir(summary.sessionKey);\n await mkdir(sessionDir, { recursive: true });\n\n // Format date as YYYY-MM-DD for the filename\n const dateStr = this.summaryDateString(summary.hour);\n const filePath = await resolveSafeStoragePath(sessionDir, `${dateStr}.md`);\n\n // Format hour as HH:00 for display\n const hourStr = summary.hour.slice(11, 13);\n\n // Build markdown content\n const lines: string[] = [];\n\n // Check if file exists to append or create\n let existingContent = \"\";\n try {\n existingContent = await readFile(filePath, \"utf-8\");\n } catch {\n // File doesn't exist yet, will create new\n }\n\n // Check if this hour already exists (idempotent)\n const hourHeader = `## ${hourStr}:00`;\n if (existingContent.includes(hourHeader)) {\n // Replace existing hour section\n const headerMatch = new RegExp(`(^|\\\\n)${escapeRegExp(hourHeader)}\\\\n`).exec(existingContent);\n const sectionStart = headerMatch\n ? headerMatch.index + headerMatch[1].length\n : existingContent.indexOf(hourHeader);\n const nextHeaderPattern = /\\n## \\d{2}:00\\n/g;\n nextHeaderPattern.lastIndex = sectionStart + hourHeader.length;\n const nextHeader = nextHeaderPattern.exec(existingContent);\n const beforeHour = existingContent.slice(0, sectionStart);\n const afterHour = nextHeader ? existingContent.slice(nextHeader.index + 1) : \"\";\n const newSection = this.formatHourSection(summary, hourHeader);\n existingContent = beforeHour + newSection.trimEnd() + (afterHour ? \"\\n\\n\" + afterHour : \"\\n\");\n\n await writeFile(filePath, existingContent, \"utf-8\");\n log.debug(`updated hourly summary for ${summary.sessionKey} at ${hourStr}:00`);\n } else {\n // Append new hour section\n const newSection = this.formatHourSection(summary, hourHeader);\n\n if (existingContent) {\n // Add to existing file\n await writeFile(filePath, existingContent.trimEnd() + \"\\n\\n\" + newSection, \"utf-8\");\n } else {\n // Create new file with header\n const header = `# Hourly Summaries — ${dateStr}\\n\\n*Session: ${summary.sessionKey}*\\n`;\n await writeFile(filePath, header + \"\\n\" + newSection, \"utf-8\");\n }\n log.debug(`saved hourly summary for ${summary.sessionKey} at ${hourStr}:00`);\n }\n try {\n await upsertSummarySnapshot(this.config.memoryDir, summary);\n } catch (error) {\n log.warn(\n `hourly summarizer: failed to update summary snapshot for ${summary.sessionKey} (fail-open): ${String(error)}`\n );\n }\n }\n\n private formatHourSection(summary: HourlySummary, hourHeader: string): string {\n const ext = (summary as any)._extended as\n | (HourlySummaryExtendedResult & { _meta?: HourlySummaryExtendedMeta })\n | undefined;\n const meta = (summary as any)._extendedMeta as HourlySummaryExtendedMeta | undefined;\n const lines: string[] = [hourHeader, \"\"];\n\n if (resolvePipelineProcessingCapabilities(this.config).hourlySummariesExtended && ext) {\n lines.push(\"### Topics Discussed\");\n for (const t of ext.topics) lines.push(`- ${t}`);\n lines.push(\"\");\n lines.push(\"### Decisions Made\");\n for (const d of ext.decisions) lines.push(`- ${d}`);\n lines.push(\"\");\n lines.push(\"### Action Items\");\n for (const a of ext.actionItems) lines.push(`- ${a}`);\n lines.push(\"\");\n lines.push(\"### Rejected Ideas / Reversals\");\n for (const r of ext.rejected) lines.push(`- ${r}`);\n lines.push(\"\");\n if (meta && Object.keys(meta.toolCounts).length > 0) {\n lines.push(\"### Tools Used\");\n const top = Object.entries(meta.toolCounts)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 12);\n for (const [name, count] of top) lines.push(`- ${name}: ${count}`);\n lines.push(\"\");\n }\n lines.push(\"### Stats\");\n lines.push(`- Turns: ${summary.turnCount}`);\n if (meta) {\n lines.push(`- User turns: ${meta.userTurns}`);\n lines.push(`- Assistant turns: ${meta.assistantTurns}`);\n lines.push(`- Tool calls: ${meta.toolCalls}`);\n }\n lines.push(\"\");\n return lines.join(\"\\n\");\n }\n\n for (const bullet of summary.bullets) lines.push(`- ${bullet}`);\n lines.push(` *(${summary.turnCount} turns)*`);\n lines.push(\"\");\n return lines.join(\"\\n\");\n }\n\n // Read recent summaries for recall injection\n async readRecent(sessionKey: string, hours: number): Promise<HourlySummary[]> {\n try {\n const cutoffTime = Date.now() - hours * 60 * 60 * 1000;\n\n const snapshot = await readSummarySnapshot(this.config.memoryDir, sessionKey);\n if (snapshot) {\n return snapshot\n .filter((s) => new Date(s.hour).getTime() >= cutoffTime)\n .sort((a, b) => new Date(b.hour).getTime() - new Date(a.hour).getTime());\n }\n\n const summaries: HourlySummary[] = [];\n const encodedDir = await this.summarySessionDir(sessionKey);\n const legacyDir = await this.legacySummarySessionDir(sessionKey);\n const seenDirs = new Set<string>();\n\n for (const sessionDir of [encodedDir, legacyDir]) {\n if (!sessionDir || seenDirs.has(sessionDir)) continue;\n seenDirs.add(sessionDir);\n const parsed = await this.readSummaryDir(sessionDir, sessionKey);\n summaries.push(...parsed);\n }\n\n const byHour = new Map<string, HourlySummary>();\n for (const summary of summaries) {\n if (!byHour.has(summary.hour)) byHour.set(summary.hour, summary);\n }\n\n const sortedSummaries = Array.from(byHour.values()).sort(\n (a, b) => new Date(b.hour).getTime() - new Date(a.hour).getTime()\n );\n\n // Filter to recent hours while materializing the full parsed history.\n const recent = sortedSummaries.filter((s) => new Date(s.hour).getTime() >= cutoffTime);\n\n if (sortedSummaries.length > 0) {\n try {\n await writeSummarySnapshot(this.config.memoryDir, sessionKey, sortedSummaries);\n } catch (error) {\n log.warn(\n `hourly summarizer: failed to materialize summary snapshot for ${sessionKey} (fail-open): ${String(error)}`\n );\n }\n }\n\n return recent;\n } catch {\n // Directory doesn't exist or error reading\n return [];\n }\n }\n\n private async readSummaryDir(sessionDir: string, sessionKey: string): Promise<HourlySummary[]> {\n let files: string[];\n try {\n files = await readdir(sessionDir);\n } catch {\n return [];\n }\n\n const summaries: HourlySummary[] = [];\n const mdFiles = files.filter((f) => f.endsWith(\".md\"));\n for (const file of mdFiles) {\n const filePath = await resolveSafeStoragePath(sessionDir, file).catch(() => null);\n if (filePath === null) continue;\n\n try {\n const content = await readFile(filePath, \"utf-8\");\n summaries.push(...this.parseSummaryFile(content, sessionKey, file));\n } catch {\n // Skip unreadable summary files.\n }\n }\n\n return summaries;\n }\n\n private parseSummaryFile(content: string, sessionKey: string, filename: string): HourlySummary[] {\n const summaries: HourlySummary[] = [];\n\n // Extract date from filename (YYYY-MM-DD.md)\n const dateMatch = filename.match(/^(\\d{4}-\\d{2}-\\d{2})\\.md$/);\n if (!dateMatch) return summaries;\n const dateStr = dateMatch[1];\n\n // Split by hour sections\n const hourSections = content.split(/\\n## (\\d{2}):00\\n/);\n\n // First element is the header, skip it\n for (let i = 1; i < hourSections.length; i += 2) {\n const hourStr = hourSections[i];\n const sectionContent = hourSections[i + 1] || \"\";\n\n // Parse bullets\n const bullets: string[] = [];\n const lines = sectionContent.split(\"\\n\");\n let turnCount = 0;\n\n let inTopics = false;\n let sawExtendedTopicsHeader = false;\n for (const line of lines) {\n if (line.startsWith(\"### Topics\")) {\n inTopics = true;\n sawExtendedTopicsHeader = true;\n continue;\n }\n if (line.startsWith(\"### \") && !line.startsWith(\"### Topics\")) {\n inTopics = false;\n }\n const bulletMatch = line.match(/^- (.+)$/);\n if (bulletMatch) {\n // For extended format, only treat topic bullets as recall bullets.\n if (!sawExtendedTopicsHeader || inTopics) bullets.push(bulletMatch[1]);\n }\n const turnMatch = line.match(/\\((\\d+) turns?\\)/);\n if (turnMatch) turnCount = parseInt(turnMatch[1], 10);\n }\n\n if (bullets.length > 0) {\n summaries.push({\n hour: `${dateStr}T${hourStr}:00:00.000Z`,\n sessionKey,\n bullets,\n turnCount,\n generatedAt: \"\", // Not stored in file, not needed for recall\n });\n }\n }\n\n return summaries;\n }\n\n // Format summaries for recall injection\n formatForRecall(summaries: HourlySummary[], maxCount: number): string {\n if (summaries.length === 0) return \"\";\n\n const limited = summaries.slice(0, maxCount);\n const lines: string[] = [`## Recent Activity (last ${limited.length} hours)`];\n\n for (const summary of limited) {\n const hourStr = summary.hour.slice(11, 16); // HH:MM\n for (const bullet of summary.bullets) {\n lines.push(`- ${hourStr}: ${bullet}`);\n }\n }\n\n return lines.join(\"\\n\");\n }\n\n // Main entry point for cron job\n async runHourly(): Promise<HourlySummarizerRunStats> {\n log.debug(\"running hourly summary generation\");\n\n // Get active sessions from transcript. The scan outcome is kept LOCAL to\n // this invocation (review round 2): shared instance state would let two\n // concurrent runHourly calls cross-report each other's timestamps.\n const scan = await this.getActiveSessions();\n const sessions = scan.sessionKeys;\n let sessionsWithEntries = 0;\n let summariesWritten = 0;\n\n for (const sessionKey of sessions) {\n // Calculate the hour we want to summarize (previous hour)\n const now = new Date();\n const hourStart = new Date(now.getTime() - 60 * 60 * 1000);\n hourStart.setMinutes(0, 0, 0);\n const hourEnd = new Date(hourStart.getTime() + 60 * 60 * 1000);\n\n // Get entries for this session in the target hour\n const entries = await this.getTranscriptEntries(sessionKey, hourStart, hourEnd);\n\n if (entries.length === 0) {\n log.debug(`no transcript entries for ${sessionKey} at ${hourStart.toISOString()}`);\n continue;\n }\n sessionsWithEntries += 1;\n\n // Generate and save summary\n const summary = await this.generateSummary(sessionKey, hourStart, entries);\n if (summary) {\n await this.saveSummary(summary);\n summariesWritten += 1;\n log.info(`generated hourly summary for ${sessionKey} (${entries.length} turns)`);\n }\n }\n\n return {\n sessionsConsidered: sessions.length,\n sessionsWithEntries,\n summariesWritten,\n // Staleness signal (issue #2783): newest entry across the whole\n // store, from the same scan getActiveSessions already performs.\n staleStore:\n scan.newestEntryTimestamp !== null &&\n Date.now() - new Date(scan.newestEntryTimestamp).getTime() > STALE_STORE_MS,\n newestEntryTimestamp: scan.newestEntryTimestamp,\n scanFailed: scan.scanFailed,\n };\n }\n\n // Get list of active sessions from the transcript directory, plus the\n // scan bookkeeping runHourly reports: newest entry timestamp (staleness\n // signal) and whether the scan itself FAILED. A failed scan must not be\n // reported as an empty store — those are different incidents (review\n // round 2).\n private async getActiveSessions(): Promise<{\n sessionKeys: string[];\n newestEntryTimestamp: string | null;\n scanFailed: boolean;\n }> {\n const transcriptDir = await resolveSafeStoragePath(this.config.memoryDir, \"transcripts\").catch(() => null);\n if (transcriptDir === null) {\n return { sessionKeys: [], newestEntryTimestamp: null, scanFailed: false };\n }\n\n const sessionKeys = new Set<string>();\n let newestEntryTimestamp: string | null = null;\n let newestEntryTimeMs = Number.NaN;\n let fileReadFailed = false;\n const collect = async (transcriptPath: string): Promise<void> => {\n try {\n const raw = await readFile(transcriptPath, \"utf-8\");\n for (const line of raw.split(\"\\n\")) {\n if (!line.trim()) continue;\n try {\n const entry = JSON.parse(line) as TranscriptEntry;\n if (typeof entry.sessionKey === \"string\" && entry.sessionKey.length > 0) {\n sessionKeys.add(entry.sessionKey);\n }\n // Compare parsed epoch values, never strings: a malformed\n // timestamp in a hand-edited file must not poison the staleness\n // signal with a lexically-maximal garbage value (review).\n if (typeof entry.timestamp === \"string\") {\n const timeMs = new Date(entry.timestamp).getTime();\n if (Number.isFinite(timeMs) && (Number.isNaN(newestEntryTimeMs) || timeMs > newestEntryTimeMs)) {\n newestEntryTimeMs = timeMs;\n newestEntryTimestamp = entry.timestamp;\n }\n }\n } catch {\n // ignore malformed transcript lines\n }\n }\n } catch (err) {\n // ENOENT is a concurrently-deleted file — ignore. Any other read\n // failure (permissions, I/O) means the store is partially UNREADABLE:\n // mark the scan failed so the caller warns instead of reporting an\n // empty store (review round 3).\n if ((err as NodeJS.ErrnoException)?.code !== \"ENOENT\") {\n fileReadFailed = true;\n }\n }\n };\n\n try {\n const typeEntries = await readdir(transcriptDir, { withFileTypes: true });\n for (const typeEnt of typeEntries) {\n if (!typeEnt.isDirectory()) continue;\n const typeDir = await resolveSafeStoragePath(transcriptDir, typeEnt.name).catch(() => null);\n if (typeDir === null) continue;\n const idEntries = await readdir(typeDir, { withFileTypes: true });\n for (const idEnt of idEntries) {\n if (!idEnt.isDirectory()) continue;\n const chanDir = await resolveSafeStoragePath(typeDir, idEnt.name).catch(() => null);\n if (chanDir === null) continue;\n const files = (await readdir(chanDir)).filter((f) => f.endsWith(\".jsonl\")).sort();\n for (const file of files) {\n const transcriptPath = await resolveSafeStoragePath(chanDir, file).catch(() => null);\n if (transcriptPath === null) continue;\n await collect(transcriptPath);\n }\n }\n }\n return {\n sessionKeys: Array.from(sessionKeys),\n newestEntryTimestamp,\n scanFailed: fileReadFailed,\n };\n } catch (err) {\n // ENOENT on the root is a never-written store — an EMPTY store, not a\n // scan failure (those are different incidents).\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code === \"ENOENT\") {\n return { sessionKeys: [], newestEntryTimestamp: null, scanFailed: false };\n }\n log.warn(`transcript scan failed: ${err}`);\n return { sessionKeys: [], newestEntryTimestamp: null, scanFailed: true };\n }\n }\n\n // Get transcript entries for a session within a time range\n private async getTranscriptEntries(sessionKey: string, startTime: Date, endTime: Date): Promise<TranscriptEntry[]> {\n // Shared session-identity layer (issue #1496, rule #22). Arbitrary keys\n // route to `session/<hash>`; legacy `agent:<id>:...` keep their paths. The\n // shared helper also supplies the read-back-only candidate dirs (mirrors\n // TranscriptManager) so legacy `other/default` AND old `parts.length >= 3`\n // data stays discoverable here too.\n const paths = sessionStoragePaths(sessionKey);\n\n try {\n const transcriptRoot = path.join(this.config.memoryDir, \"transcripts\");\n const candidateDirs = new Set(\n [paths.dir, paths.alternateDir, paths.legacyDir, ...paths.readbackDirs].filter(\n (dir): dir is string => typeof dir === \"string\" && dir.length > 0\n )\n );\n const entries: TranscriptEntry[] = [];\n // Dedup identical raw rows that a partially-applied migration may have\n // left in both the primary dir and a read-back dir (issue #1496, cursor\n // review). Exact raw JSONL line is the stable identity.\n const seenRawLines = new Set<string>();\n\n // Read all daily transcript files in the directory\n for (const candidateDir of candidateDirs) {\n const transcriptDir = await resolveSafeStoragePath(transcriptRoot, candidateDir).catch(() => null);\n if (transcriptDir === null) continue;\n\n const files = await readdir(transcriptDir).catch(() => []);\n for (const file of files) {\n if (!file.endsWith(\".jsonl\")) continue;\n\n const transcriptPath = await resolveSafeStoragePath(transcriptDir, file).catch(() => null);\n if (transcriptPath === null) continue;\n\n try {\n const content = await readFile(transcriptPath, \"utf-8\");\n const lines = content.trim().split(\"\\n\");\n\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const entry = JSON.parse(line) as TranscriptEntry;\n const entryTime = new Date(entry.timestamp).getTime();\n\n if (\n entry.sessionKey === sessionKey &&\n entryTime >= startTime.getTime() &&\n entryTime < endTime.getTime() &&\n !seenRawLines.has(line)\n ) {\n seenRawLines.add(line);\n entries.push(entry);\n }\n } catch {\n // Skip malformed lines\n }\n }\n } catch {\n // Skip unreadable files\n }\n }\n }\n\n return entries;\n } catch {\n return [];\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/access-offline-manifest.ts"],"sourcesContent":["import * as nodePath from \"node:path\";\nimport { offlineSyncStorageForSnapshot } from \"./offline-sync-impression-drain.js\";\nimport { iterateOfflineSyncSnapshotFileRecords } from \"./offline-sync.js\";\nimport type { Orchestrator } from \"./orchestrator.js\";\nimport {\n CONTENT_HASH_NORMALIZER_VERSION,\n IDENTITY_RESOLUTION_VERSION,\n RECONCILE_MANIFEST_FORMAT,\n RECONCILE_MANIFEST_SCHEMA_VERSION,\n type ReconcileManifest,\n type ReconcileManifestFile,\n type ReconcileMemoryIdentity,\n type ReconcileMemoryParser,\n buildReconcileManifestFile,\n citationTemplateFingerprint,\n isCachedIdentityReusable,\n isReconcileMemoryIdentity,\n} from \"./reconcile/manifest.js\";\nimport { createPersistedSupportPassportPrivateFileExclusion } from \"./support-passport/card-projection.js\";\n\nexport interface OfflineSyncManifestRequest {\n includeTranscripts?: boolean;\n signal?: AbortSignal;\n}\n\nexport interface OfflineSyncManifestStreamResponse extends Omit<ReconcileManifest, \"files\"> {\n namespace: string;\n files: AsyncIterable<ReconcileManifestFile>;\n}\n\nexport interface ServerIdentityCacheEntry {\n path: string;\n sha256: string;\n memory?: ReconcileMemoryIdentity;\n /** Stamped on sha-only entries so an upgrade re-parses them. */\n normalizerVersion?: number;\n identityResolutionVersion?: number;\n /** Stat identity the exclusion classification was computed against. */\n statIdentity?: string;\n /** Persisted support-passport private-memory classification for statIdentity. */\n excluded?: boolean;\n}\n\n/**\n * Decide what the manifest stream should persist, and whether a write is\n * needed at all.\n *\n * A write is required whenever the persisted set changed — an identity was\n * rebuilt, or an entry was dropped because its file lost its identity — and\n * also when a completed walk leaves entries for paths it never saw, which are\n * files that have since been deleted. Skipping the write in that last case\n * would strand those entries on disk forever.\n *\n * Pruning is gated on the walk completing: an aborted or partially consumed\n * stream has simply not observed the remaining paths, so it may only add.\n */\nexport function planServerIdentityCacheWrite(input: {\n persisted: ReadonlyMap<string, ServerIdentityCacheEntry>;\n yieldedPaths: ReadonlySet<string>;\n streamCompleted: boolean;\n cacheDirty: boolean;\n}): { shouldWrite: boolean; entries: ServerIdentityCacheEntry[] } {\n const entries = input.streamCompleted\n ? [...input.persisted.values()].filter((entry) => input.yieldedPaths.has(entry.path))\n : [...input.persisted.values()];\n return { shouldWrite: input.cacheDirty || entries.length !== input.persisted.size, entries };\n}\n\n/**\n * The cache lives under `.remnic/` rather than `state/`: snapshot enumeration\n * excludes internal `.remnic/` paths structurally (`isInternalRemnicStatePath`),\n * whereas `state/` is only filtered by an allowlist of known node-local\n * artifacts. Under `state/` this node-local cache would be advertised as a peer\n * file, so convergence would transfer the cache itself — and the stream rewrites\n * it during finalization, so the advertised digest could go stale mid-transfer.\n */\nfunction serverIdentityCachePath(storageDir: string): string {\n return nodePath.join(storageDir, \".remnic\", \"state\", \"converge-identity-cache.json\");\n}\n\nasync function loadServerIdentityCache(\n storageDir: string,\n citationTemplate?: string\n): Promise<Map<string, ServerIdentityCacheEntry>> {\n const cache = new Map<string, ServerIdentityCacheEntry>();\n try {\n const raw = JSON.parse(\n await import(\"node:fs/promises\").then((fs) => fs.readFile(serverIdentityCachePath(storageDir), \"utf8\"))\n ) as {\n citationTemplate?: string;\n files?: unknown[];\n };\n // Identities are derived after stripping inline attribution for a specific\n // citation template; entries written under a different one would produce\n // wrong contentHash values for unchanged files.\n if (raw.citationTemplate !== citationTemplateFingerprint(citationTemplate)) return cache;\n for (const candidate of raw.files ?? []) {\n const entry = candidate as Partial<ServerIdentityCacheEntry> | null;\n if (typeof entry?.path !== \"string\" || typeof entry.sha256 !== \"string\") continue;\n if (entry.memory !== undefined && !isReconcileMemoryIdentity(entry.memory)) continue;\n if (entry.statIdentity !== undefined && typeof entry.statIdentity !== \"string\") continue;\n if (entry.excluded !== undefined && typeof entry.excluded !== \"boolean\") continue;\n if (entry.normalizerVersion !== undefined && typeof entry.normalizerVersion !== \"number\") continue;\n if (entry.identityResolutionVersion !== undefined && typeof entry.identityResolutionVersion !== \"number\") {\n continue;\n }\n cache.set(entry.path, {\n path: entry.path,\n sha256: entry.sha256,\n ...(entry.memory ? { memory: entry.memory } : {}),\n ...(entry.memory\n ? {}\n : {\n ...(typeof entry.normalizerVersion === \"number\" ? { normalizerVersion: entry.normalizerVersion } : {}),\n ...(typeof entry.identityResolutionVersion === \"number\"\n ? { identityResolutionVersion: entry.identityResolutionVersion }\n : {}),\n }),\n ...(entry.statIdentity !== undefined && entry.excluded !== undefined\n ? { statIdentity: entry.statIdentity, excluded: entry.excluded }\n : {}),\n });\n }\n } catch {\n // missing or corrupt cache: cold build\n }\n return cache;\n}\n\nconst serverIdentityCacheWriteLocks = new Map<string, Promise<void>>();\n\n/** Serialize cache replacements per path: overlapping manifest streams for one\n * namespace would otherwise each publish a whole-file snapshot, and the slower\n * stream built from older filesystem state would overwrite the newer one. */\nasync function withServerIdentityCacheWriteLock(cachePath: string, write: () => Promise<void>): Promise<void> {\n const previous = serverIdentityCacheWriteLocks.get(cachePath) ?? Promise.resolve();\n const next = previous.then(write, write);\n serverIdentityCacheWriteLocks.set(cachePath, next);\n try {\n await next;\n } finally {\n if (serverIdentityCacheWriteLocks.get(cachePath) === next) {\n serverIdentityCacheWriteLocks.delete(cachePath);\n }\n }\n}\n\n/** Union of the entries being written and the current on-disk set; on conflict\n * the write wins (it reflects the newer run). Callers hold the write lock. */\nasync function mergeServerIdentityCacheEntries(\n fs: typeof import(\"node:fs/promises\"),\n cachePath: string,\n entries: readonly ServerIdentityCacheEntry[],\n citationTemplate: string | undefined,\n dropped: ReadonlySet<string> = new Set()\n): Promise<ServerIdentityCacheEntry[]> {\n const merged = new Map(entries.map((entry) => [entry.path, entry]));\n try {\n const raw = JSON.parse(await fs.readFile(cachePath, \"utf8\")) as {\n citationTemplate?: string;\n files?: unknown[];\n };\n if (raw.citationTemplate === citationTemplateFingerprint(citationTemplate)) {\n for (const candidate of raw.files ?? []) {\n const entry = candidate as Partial<ServerIdentityCacheEntry> | null;\n if (typeof entry?.path !== \"string\" || typeof entry.sha256 !== \"string\") continue;\n if (entry.memory !== undefined && !isReconcileMemoryIdentity(entry.memory)) continue;\n if (dropped.has(entry.path) || merged.has(entry.path)) continue;\n merged.set(entry.path, {\n path: entry.path,\n sha256: entry.sha256,\n ...(entry.memory ? { memory: entry.memory } : {}),\n ...(entry.memory\n ? {}\n : {\n ...(typeof entry.normalizerVersion === \"number\" ? { normalizerVersion: entry.normalizerVersion } : {}),\n ...(typeof entry.identityResolutionVersion === \"number\"\n ? { identityResolutionVersion: entry.identityResolutionVersion }\n : {}),\n }),\n });\n }\n }\n } catch {\n // missing or unreadable current cache: the write set stands alone\n }\n return [...merged.values()];\n}\n\nexport async function createOfflineSyncManifestStream(\n orchestrator: Orchestrator,\n namespace: string,\n userExcludeRegexps: RegExp[],\n options: OfflineSyncManifestRequest,\n parseMemory: ReconcileMemoryParser\n): Promise<OfflineSyncManifestStreamResponse> {\n const storage = await offlineSyncStorageForSnapshot(orchestrator, namespace);\n const citationTemplate = orchestrator.config.inlineSourceAttributionFormat;\n const identityCache = await loadServerIdentityCache(storage.dir, citationTemplate);\n // The exclusion callback runs before every yield; without the persisted\n // classification it would read and parse every candidate file, defeating the\n // warm-cache skip entirely.\n const classificationUpdates = new Map<string, { statIdentity: string; excluded: boolean }>();\n const snapshotFiles = iterateOfflineSyncSnapshotFileRecords({\n root: storage.dir,\n includeContent: false,\n includeTranscripts: options.includeTranscripts !== false,\n readFileDigest: async ({ filePath }) => storage.digestOfflineSyncFile(filePath),\n signal: options.signal,\n userExcludeRegexps,\n excludeFile: createPersistedSupportPassportPrivateFileExclusion(storage, identityCache, classificationUpdates),\n });\n return {\n namespace,\n format: RECONCILE_MANIFEST_FORMAT,\n schemaVersion: RECONCILE_MANIFEST_SCHEMA_VERSION,\n files: (async function* () {\n // Any change to the persisted set, not just a rebuilt identity: a file\n // that lost its identity, or a completed walk that must prune vanished\n // paths, also has to reach disk or the removal is lost forever.\n let cacheDirty = false;\n let streamCompleted = false;\n // Seeded with every previously cached entry so a partially consumed\n // stream (client disconnect, abort) can only add to the cache. Writing\n // just this run's rebuilds would drop every cache hit, so one edit to a\n // large corpus would shrink the cache to that single entry and force a\n // near-cold rebuild on the next request.\n const persistedEntries = new Map(identityCache);\n const yieldedPaths = new Set<string>();\n const flush = async (): Promise<void> => {\n const { shouldWrite, entries } = planServerIdentityCacheWrite({\n persisted: persistedEntries,\n yieldedPaths,\n streamCompleted,\n cacheDirty,\n });\n if (!shouldWrite) return;\n try {\n const fs = await import(\"node:fs/promises\");\n const cachePath = serverIdentityCachePath(storage.dir);\n await withServerIdentityCacheWriteLock(cachePath, async () => {\n // Merge with whatever is on disk NOW: a concurrent stream for the\n // same namespace may have published newer entries after this\n // request loaded its snapshot, and a blind whole-file replace\n // would discard them.\n const written = new Set(entries.map((entry) => entry.path));\n const dropped = streamCompleted\n ? new Set([...identityCache.keys()].filter((pathName) => !written.has(pathName)))\n : new Set<string>();\n const merged = await mergeServerIdentityCacheEntries(\n fs,\n cachePath,\n entries,\n citationTemplate,\n dropped\n );\n await fs.mkdir(nodePath.dirname(cachePath), { recursive: true });\n const tmp = `${cachePath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;\n await fs.writeFile(\n tmp,\n JSON.stringify({ citationTemplate: citationTemplateFingerprint(citationTemplate), files: merged })\n );\n await fs.rename(tmp, cachePath);\n });\n } catch {\n // cache persistence is best-effort\n }\n };\n try {\n const applyClassification = (path: string, sha256: string): void => {\n const classification = classificationUpdates.get(path);\n if (classification === undefined) return;\n const entry = persistedEntries.get(path) ?? { path, sha256 };\n if (entry.statIdentity === classification.statIdentity && entry.excluded === classification.excluded) {\n return;\n }\n persistedEntries.set(path, {\n ...entry,\n ...(entry.memory ? { memory: entry.memory } : {}),\n statIdentity: classification.statIdentity,\n excluded: classification.excluded,\n });\n cacheDirty = true;\n };\n for await (const file of snapshotFiles) {\n yieldedPaths.add(file.path);\n const cached = identityCache.get(file.path);\n if (\n cached !== undefined &&\n cached.sha256.toLowerCase() === file.sha256.toLowerCase() &&\n isCachedIdentityReusable(cached)\n ) {\n applyClassification(file.path, file.sha256);\n yield {\n ...file,\n ...(cached.memory\n ? { memory: cached.memory }\n : {\n normalizerVersion: cached.normalizerVersion,\n identityResolutionVersion: cached.identityResolutionVersion,\n }),\n };\n continue;\n }\n const built = await buildReconcileManifestFile(\n file,\n async ({ path }) => storage.readOfflineSyncFile(nodePath.join(storage.dir, path)),\n parseMemory,\n citationTemplate\n );\n // Negative results persist too: a memory-shaped file that parses\n // without an identity would otherwise be re-read on every warm run.\n const previous = persistedEntries.get(built.path);\n if (previous?.sha256 !== built.sha256 || (built.memory !== undefined && previous?.memory !== built.memory)) {\n cacheDirty = true;\n }\n if (built.memory === undefined && built.normalizerVersion === undefined) {\n // Read failed: do not persist a negative identity for this SHA.\n yield built;\n continue;\n }\n persistedEntries.set(built.path, {\n path: built.path,\n sha256: built.sha256,\n ...(built.memory !== undefined\n ? { memory: built.memory }\n : {\n normalizerVersion: built.normalizerVersion,\n identityResolutionVersion: built.identityResolutionVersion,\n }),\n });\n applyClassification(built.path, built.sha256);\n yield built;\n }\n streamCompleted = true;\n } finally {\n await flush();\n }\n })(),\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/extraction-error-classification.ts"],"sourcesContent":["import type { ExtractionFailureClass } from \"./types.js\";\nimport { isTransientHttpError } from \"./connectors/live/transient-errors.js\";\n\n/**\n * Read a numeric HTTP status off a thrown error without re-implementing the\n * shared transient classifier. Uses `in`/typeof narrowing (no inline casts).\n */\nfunction httpStatusFromError(err: unknown): number | undefined {\n if (!err || typeof err !== \"object\") return undefined;\n if (\"status\" in err && typeof err.status === \"number\" && Number.isFinite(err.status)) return err.status;\n if (\"statusCode\" in err && typeof err.statusCode === \"number\" && Number.isFinite(err.statusCode)) {\n return err.statusCode;\n }\n return undefined;\n}\n\n/**\n * Map a thrown extraction error to a coarse failure class for the retry/breaker\n * layer. 401/403 → `auth_config` (misconfigured provider, open the breaker);\n * transient (429/5xx/network, per the shared `isTransientHttpError`) → back off;\n * anything else defaults to `provider_retryable` (fail-open toward retry, but\n * capped by the per-fingerprint attempt budget). Never throws.\n */\nexport function classifyExtractionThrownError(err: unknown): ExtractionFailureClass {\n try {\n const status = httpStatusFromError(err);\n if (status === 401 || status === 403) return \"auth_config\";\n if (isTransientHttpError(err)) return \"provider_retryable\";\n return \"provider_retryable\";\n } catch {\n return \"provider_retryable\";\n }\n}\n\n/**\n * Map the gateway fallback's discriminated parse-failure reason to a coarse\n * extraction failure class. \"no models configured\" is an auth/config problem;\n * an HTTP/transport failure backs off; an unparseable-but-present response is a\n * genuine empty-parse.\n */\nexport function classifyFallbackParseFailure(\n reason: \"no_models\" | \"empty\" | \"http_error\",\n): ExtractionFailureClass {\n switch (reason) {\n case \"no_models\":\n return \"auth_config\";\n case \"http_error\":\n return \"provider_retryable\";\n case \"empty\":\n return \"parse_empty\";\n }\n}\n"],"mappings":";;;;;AAOA,SAAS,oBAAoB,KAAkC;AAC7D,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,MAAI,YAAY,OAAO,OAAO,IAAI,WAAW,YAAY,OAAO,SAAS,IAAI,MAAM,EAAG,QAAO,IAAI;AACjG,MAAI,gBAAgB,OAAO,OAAO,IAAI,eAAe,YAAY,OAAO,SAAS,IAAI,UAAU,GAAG;AAChG,WAAO,IAAI;AAAA,EACb;AACA,SAAO;AACT;AASO,SAAS,8BAA8B,KAAsC;AAClF,MAAI;AACF,UAAM,SAAS,oBAAoB,GAAG;AACtC,QAAI,WAAW,OAAO,WAAW,IAAK,QAAO;AAC7C,QAAI,qBAAqB,GAAG,EAAG,QAAO;AACtC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQO,SAAS,6BACd,QACwB;AACxB,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EACX;AACF;","names":[]}
|