@remnic/core 9.54.4 → 9.54.6
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 +24 -23
- package/dist/access-admin-ops-surface.js +55 -50
- package/dist/access-authorization-probe.d.ts +25 -22
- package/dist/access-authorization-probe.js +58 -51
- package/dist/access-boundary.d.ts +26 -23
- package/dist/access-boundary.js +59 -52
- package/dist/access-cli.js +115 -107
- 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 +19 -18
- package/dist/access-extraction-force-flush.js +55 -50
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-lcm-compaction.d.ts +22 -21
- package/dist/access-http-lifecycle-flush.d.ts +22 -21
- package/dist/access-http-offline-stream.d.ts +19 -18
- package/dist/access-http-query.js +55 -50
- package/dist/access-http.d.ts +55 -36
- package/dist/access-http.js +76 -65
- package/dist/access-identity-continuity-surface.d.ts +9 -9
- package/dist/access-identity-continuity-surface.js +55 -50
- package/dist/access-lcm-surface.d.ts +21 -20
- package/dist/access-lcm-surface.js +55 -50
- package/dist/access-mcp.d.ts +22 -21
- package/dist/access-mcp.js +63 -57
- package/dist/access-memory-search-fanout.d.ts +22 -2
- package/dist/access-memory-search-fanout.js +1 -1
- package/dist/{access-namespace-preflight-C7n4tgWC.d.ts → access-namespace-preflight-CCNn_6NE.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +55 -50
- package/dist/access-observe-write-surface.d.ts +22 -21
- package/dist/access-observe-write-surface.js +55 -50
- package/dist/access-offline-manifest.d.ts +10 -9
- package/dist/access-offline-manifest.js +12 -10
- package/dist/access-operations-batch.js +57 -52
- package/dist/access-operations.d.ts +23 -22
- package/dist/access-operations.js +61 -55
- package/dist/access-recall-concurrency.d.ts +21 -20
- package/dist/access-recall-concurrency.js +55 -50
- package/dist/access-recall-response.d.ts +19 -18
- package/dist/access-recall-response.js +55 -50
- package/dist/access-recall-surface.d.ts +19 -18
- package/dist/access-recall-surface.js +55 -50
- package/dist/access-schema.d.ts +2 -2
- package/dist/access-schema.js +5 -4
- package/dist/{access-service-CXvbxLK-.d.ts → access-service-DkSdXK4S.d.ts} +1314 -83
- package/dist/access-service-helpers.d.ts +22 -21
- package/dist/access-service.d.ts +31 -30
- package/dist/access-service.js +55 -50
- package/dist/access-surface-catalog.d.ts +22 -21
- package/dist/access-surface-catalog.js +209 -2
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/access-wearables-meetings-surface.d.ts +3 -3
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +14 -1
- package/dist/active-memory-bridge.js +6 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +4 -2
- package/dist/active-recall.js.map +1 -1
- package/dist/adapters/index.js +7 -7
- package/dist/adapters/registry.js +3 -3
- package/dist/admin-console/public/what-helps-me/app.js +1594 -0
- package/dist/admin-console/public/what-helps-me/index.html +339 -0
- package/dist/admin-console/public/what-helps-me/model.js +685 -0
- package/dist/admin-console/public/what-helps-me/what-helps-me.css +1296 -0
- package/dist/ambient-provenance.d.ts +1 -1
- package/dist/artifact-search.d.ts +1 -1
- package/dist/{auto-sync-TDHLZPVP.js → auto-sync-NR3GPCOA.js} +4 -4
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +9 -9
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +33 -12
- 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 +3 -3
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/calibration.js +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-BOOxl-I2.d.ts → catalog-DGFf8mD9.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 +34 -13
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-ZYLC7NFO.js → chunk-26ZXBTYN.js} +4 -4
- package/dist/chunk-26ZXBTYN.js.map +1 -0
- package/dist/{chunk-LLJMAULE.js → chunk-2FKEIPIT.js} +5 -5
- package/dist/{chunk-3CSX3ETE.js → chunk-2LWQSV6A.js} +23 -11
- package/dist/chunk-2LWQSV6A.js.map +1 -0
- package/dist/{chunk-JBPKEARU.js → chunk-2QSZNTDO.js} +7 -7
- package/dist/chunk-37DZZFFM.js +502 -0
- package/dist/chunk-37DZZFFM.js.map +1 -0
- package/dist/chunk-3HSRWJTC.js +9 -0
- package/dist/chunk-3HSRWJTC.js.map +1 -0
- package/dist/{chunk-XUAMCLY4.js → chunk-3TIZPSJC.js} +17 -13
- package/dist/chunk-3TIZPSJC.js.map +1 -0
- package/dist/{chunk-PDDTI7IC.js → chunk-43SEBY2M.js} +1 -1
- package/dist/{chunk-HWRD23DW.js → chunk-46SZBVDS.js} +3 -3
- package/dist/{chunk-6HPHEFXT.js → chunk-5FD7JMTX.js} +2 -2
- package/dist/{chunk-7BZOBA5J.js → chunk-5NB726GV.js} +70 -16
- package/dist/chunk-5NB726GV.js.map +1 -0
- package/dist/chunk-62OKGIJA.js +39 -0
- package/dist/chunk-62OKGIJA.js.map +1 -0
- package/dist/{chunk-IBKR2TFR.js → chunk-6CYHUWCO.js} +2 -2
- package/dist/{chunk-YN6ASZUG.js → chunk-6QPI2ITJ.js} +5 -5
- package/dist/{chunk-UU3G6GUP.js → chunk-76JJSXK7.js} +1 -1
- package/dist/chunk-76JJSXK7.js.map +1 -0
- package/dist/{chunk-PX2LSHYM.js → chunk-7BD6O7ZV.js} +189 -4
- package/dist/chunk-7BD6O7ZV.js.map +1 -0
- package/dist/chunk-7TBXNYLO.js +346 -0
- package/dist/chunk-7TBXNYLO.js.map +1 -0
- package/dist/{chunk-NH5IR2WM.js → chunk-BRR5EUTF.js} +6 -2
- package/dist/chunk-BRR5EUTF.js.map +1 -0
- package/dist/{chunk-NIS256NW.js → chunk-CIAI24TQ.js} +3 -3
- package/dist/{chunk-T2JHZWH6.js → chunk-D3LBIUWV.js} +11703 -7573
- package/dist/chunk-D3LBIUWV.js.map +1 -0
- package/dist/{chunk-7FGL6CGA.js → chunk-DVYRD4HY.js} +2 -2
- package/dist/{chunk-G7Z6EENO.js → chunk-EKTP7MNS.js} +4 -4
- package/dist/{chunk-S75M5ZRK.js → chunk-F2JRDPON.js} +16 -1
- package/dist/chunk-F2JRDPON.js.map +1 -0
- package/dist/{chunk-RZ4XGKOJ.js → chunk-FFRTNF3L.js} +8 -8
- package/dist/{chunk-VKXWK44Q.js → chunk-FONWBCVY.js} +3 -3
- package/dist/{chunk-DZD576ZU.js → chunk-FPLM2TKD.js} +2 -2
- package/dist/{chunk-ZPJ4QSGP.js → chunk-FTPONF6B.js} +2 -2
- package/dist/{chunk-LJS7KUPV.js → chunk-FWX7W5PM.js} +2 -2
- package/dist/{chunk-UTRO5UJU.js → chunk-G6DFSS6A.js} +5 -1
- package/dist/chunk-G6DFSS6A.js.map +1 -0
- package/dist/{chunk-6LAPLZOS.js → chunk-GJ776BC2.js} +8 -3
- package/dist/chunk-GJ776BC2.js.map +1 -0
- package/dist/{chunk-G4T7L557.js → chunk-GN3WRFJY.js} +6 -6
- package/dist/{chunk-QT2FRYI4.js → chunk-H7NAZ3UB.js} +2 -2
- package/dist/{chunk-APPFNGX6.js → chunk-HBUFQRIM.js} +2 -2
- package/dist/{chunk-UU7TRFNE.js → chunk-HGJ6NJ4E.js} +2 -2
- package/dist/{chunk-WB7JSQCP.js → chunk-HIEWDJ2O.js} +5 -2
- package/dist/{chunk-WB7JSQCP.js.map → chunk-HIEWDJ2O.js.map} +1 -1
- package/dist/{chunk-UES4BHA2.js → chunk-I3MSGFGD.js} +17 -6
- package/dist/chunk-I3MSGFGD.js.map +1 -0
- package/dist/{chunk-C4ZNGVK3.js → chunk-IKP5OYQQ.js} +2 -2
- package/dist/{chunk-KQAFEZQX.js → chunk-IPLYGWQF.js} +5 -5
- package/dist/{chunk-RDJRFDJY.js → chunk-IQ4NVWEF.js} +2 -2
- package/dist/{chunk-CGVHPCP2.js → chunk-JYG4OLWH.js} +2 -2
- package/dist/{chunk-WOPA3SGI.js → chunk-K26NWEOC.js} +8 -8
- package/dist/{chunk-K4QS7LXU.js → chunk-K2UGPTYY.js} +485 -44
- package/dist/chunk-K2UGPTYY.js.map +1 -0
- package/dist/{chunk-L6CQ7WIW.js → chunk-KFBOPMF6.js} +11 -11
- package/dist/chunk-KFBOPMF6.js.map +1 -0
- package/dist/{chunk-G7A5HLUS.js → chunk-KJH6W2I7.js} +2 -2
- package/dist/{chunk-IXEBUYO4.js → chunk-KM55SMLG.js} +11 -2
- package/dist/{chunk-IXEBUYO4.js.map → chunk-KM55SMLG.js.map} +1 -1
- package/dist/{chunk-HOIRB77A.js → chunk-L6Z5OAS4.js} +2 -2
- package/dist/chunk-LC2BDILZ.js +57 -0
- package/dist/chunk-LC2BDILZ.js.map +1 -0
- package/dist/{chunk-227UFSEQ.js → chunk-LEPOP46W.js} +4 -4
- package/dist/{chunk-6R4G7N4S.js → chunk-M4YHAODQ.js} +388 -748
- package/dist/chunk-M4YHAODQ.js.map +1 -0
- package/dist/{chunk-3JLPGJ43.js → chunk-MIH3CJPN.js} +5 -5
- package/dist/{chunk-QHFV6VNU.js → chunk-MK5VEWCR.js} +2 -2
- package/dist/chunk-NISZQW72.js +140 -0
- package/dist/chunk-NISZQW72.js.map +1 -0
- package/dist/{chunk-GUKBW4HL.js → chunk-NQMSEB5D.js} +6 -6
- package/dist/{chunk-G2LUMAUF.js → chunk-OCIP6YMD.js} +12 -5
- package/dist/chunk-OCIP6YMD.js.map +1 -0
- package/dist/{chunk-47P3RYZP.js → chunk-PG4QBPVG.js} +21 -11
- package/dist/chunk-PG4QBPVG.js.map +1 -0
- package/dist/{chunk-WRJVB3C2.js → chunk-PO7NXBG2.js} +2 -2
- package/dist/{chunk-QWGE2PUH.js → chunk-PT54WISF.js} +190 -56
- package/dist/chunk-PT54WISF.js.map +1 -0
- package/dist/{chunk-AZM4CCAC.js → chunk-QZLDRQ4I.js} +8 -8
- package/dist/{chunk-THPPCISA.js → chunk-RBII7637.js} +5 -5
- package/dist/{chunk-JDZPNCMA.js → chunk-RUNORG2P.js} +6 -3
- package/dist/chunk-RUNORG2P.js.map +1 -0
- package/dist/{chunk-JV5YUBIZ.js → chunk-RZQ6W2W4.js} +50 -44
- package/dist/chunk-RZQ6W2W4.js.map +1 -0
- package/dist/{chunk-RUMNY5DT.js → chunk-SMNYAMJD.js} +6 -2
- package/dist/chunk-SMNYAMJD.js.map +1 -0
- package/dist/{chunk-MKQBXQKU.js → chunk-TB6CBZ5V.js} +22 -15
- package/dist/chunk-TB6CBZ5V.js.map +1 -0
- package/dist/{chunk-VMMQDEN5.js → chunk-TO7ZTWJW.js} +2 -2
- package/dist/{chunk-K2TDK7GG.js → chunk-TWTUGCTY.js} +2 -2
- package/dist/{chunk-HLYR2TQL.js → chunk-UCTS3DDH.js} +2 -2
- package/dist/{chunk-JYIJNNN3.js → chunk-UG6VXIOK.js} +2 -2
- package/dist/{chunk-WM65LU67.js → chunk-UKYS3NU5.js} +2 -2
- package/dist/{chunk-WDBX6VER.js → chunk-UPLZNJKN.js} +5 -5
- package/dist/{chunk-C23BYTJV.js → chunk-UW5NPZJ2.js} +40 -12
- package/dist/chunk-UW5NPZJ2.js.map +1 -0
- package/dist/{chunk-2GFK22OZ.js → chunk-V6ERBR7U.js} +2 -2
- package/dist/{chunk-CYF6WVE7.js → chunk-W5ZKPI34.js} +139 -158
- package/dist/chunk-W5ZKPI34.js.map +1 -0
- package/dist/chunk-XX4HPFFM.js +370 -0
- package/dist/chunk-XX4HPFFM.js.map +1 -0
- package/dist/{chunk-ZMYDYNI4.js → chunk-XZHE2DEN.js} +4 -4
- package/dist/chunk-YXQEKCZM.js +223 -0
- package/dist/chunk-YXQEKCZM.js.map +1 -0
- package/dist/{chunk-GYPQL6XN.js → chunk-Z4N7SVVF.js} +35 -47
- package/dist/chunk-Z4N7SVVF.js.map +1 -0
- package/dist/{chunk-MAXVH44E.js → chunk-Z54EM5YZ.js} +7 -7
- package/dist/{chunk-YGLY6QFV.js → chunk-ZRZVLB7O.js} +2 -2
- package/dist/{chunk-4IIFL7GC.js → chunk-ZUKIUXHQ.js} +10 -10
- package/dist/{cli-DVUFIa5E.d.ts → cli-Bze8S0Qs.d.ts} +5 -5
- package/dist/cli.d.ts +22 -21
- package/dist/cli.js +90 -80
- package/dist/coding/pre-action-gate.d.ts +1 -1
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +33 -12
- 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 +4 -2
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +33 -12
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +32 -12
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/contradiction/index.d.ts +2 -2
- package/dist/contradiction/index.js +3 -1
- package/dist/contradiction-scan-JDTBXRGW.js +20 -0
- package/dist/converge-config.d.ts +1 -1
- package/dist/convergence-refresh.d.ts +2 -2
- package/dist/convergence-refresh.js +35 -14
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/backend.js +2 -2
- 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 +25 -24
- package/dist/day-summary.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-XzF76C9n.d.ts → dependency-propagation-delivery-CzEfB31P.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-retrieval.d.ts +2 -2
- package/dist/entity-retrieval.js +33 -12
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +9 -9
- package/dist/explicit-capture.js +7 -4
- package/dist/external-wiki-access.d.ts +22 -21
- package/dist/external-wiki-access.js +56 -51
- 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 +22 -21
- package/dist/extraction-error-classification.d.ts +1 -1
- package/dist/extraction-faithfulness.d.ts +1 -1
- package/dist/extraction-faithfulness.js +2 -2
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction-judge.js +2 -2
- 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.d.ts +1 -1
- package/dist/extraction.js +35 -19
- package/dist/fallback-llm.d.ts +14 -1
- package/dist/fallback-llm.js +1 -1
- package/dist/{first-start-migration-626C5554.js → first-start-migration-CRZCZAJN.js} +11 -3
- package/dist/first-start-migration-CRZCZAJN.js.map +1 -0
- 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-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/in-flight-reads.d.ts +1 -1
- package/dist/index.d.ts +314 -308
- package/dist/index.js +304 -133
- package/dist/inline-script.d.ts +3 -0
- package/dist/inline-script.js +8 -0
- package/dist/inline-script.js.map +1 -0
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/engine.js +3 -3
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/index.js +6 -6
- 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.d.ts +2 -1
- package/dist/local-llm.js +2 -2
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +33 -12
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +33 -12
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +34 -13
- package/dist/{maintenance-BJzNIKBE.d.ts → maintenance-C8aRWfhq.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +22 -21
- package/dist/mcp-read-only-tools.js +1 -1
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-mutations.js +2 -2
- package/dist/memory-projection-store.d.ts +6 -2
- package/dist/memory-projection-store.js +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-snapshot.d.ts +1 -1
- 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 +24 -23
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +8 -8
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +33 -12
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-file-io.d.ts +20 -0
- package/dist/offline-sync-file-io.js +18 -0
- package/dist/offline-sync-file-io.js.map +1 -0
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/offline-sync-impression-drain.js +5 -5
- package/dist/offline-sync.d.ts +8 -6
- package/dist/offline-sync.js +2 -1
- package/dist/operator-doctor-corpus.d.ts +1 -1
- package/dist/operator-doctor-corpus.js +26 -25
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +39 -36
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -4
- package/dist/orchestration/maintenance.js +38 -18
- package/dist/{orchestrator-BiXgBJBQ.d.ts → orchestrator-D85CzDDY.d.ts} +11 -8
- package/dist/orchestrator.d.ts +9 -9
- package/dist/orchestrator.js +117 -109
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-sz_dcuLg.d.ts → pipeline-DPcUifCO.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/proactive-contention.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/public-http-D-SyWNj6.d.ts +25 -0
- package/dist/{purge-533FZLS4.js → purge-GB7PZHT2.js} +2 -2
- package/dist/{qmd-eAWObK4M.d.ts → qmd-dFCHp1r0.d.ts} +1 -1
- 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/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-planner-llm.d.ts +1 -1
- package/dist/recall-planner-llm.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 +2 -1
- package/dist/reconcile/manifest.d.ts +2 -1
- package/dist/reconcile/manifest.js +6 -5
- package/dist/reconcile/plan.d.ts +2 -1
- package/dist/reconcile/plan.js +3 -2
- 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 +5 -3
- 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/schemas.d.ts +38 -38
- package/dist/{scope-profiles-BBQsXuyj.d.ts → scope-profiles-DICK7GtN.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 +6 -6
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +12 -12
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-HD8RkzVA.d.ts → semantic-consolidation-BP7NAX0N.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +34 -13
- package/dist/semantic-rule-promotion.js +33 -12
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +33 -12
- package/dist/{service-DIrtHHEg.d.ts → service-DT-pJyda.d.ts} +660 -660
- 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 +31 -15
- package/dist/{storage-DqCzXi56.d.ts → storage-qOnaJ1Lw.d.ts} +49 -13
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +33 -12
- 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 +518 -0
- package/dist/support-passport/index.js +357 -0
- package/dist/support-passport/index.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 +2 -2
- package/dist/tier-routing.js +7 -1
- package/dist/{tier-stats-2JCZYEF7.js → tier-stats-EXYSKSVN.js} +8 -2
- package/dist/{tier-stats-2JCZYEF7.js.map → tier-stats-EXYSKSVN.js.map} +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/autodetect.js +1 -1
- package/dist/transfer/backup.js +1 -1
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/types.d.ts +36 -36
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-d_j4vC6N.d.ts → types-ed-SvS2t.d.ts} +8 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +2 -2
- package/dist/verified-recall.js +33 -12
- package/dist/write-envelope.d.ts +1 -1
- package/dist/write-rate-limiter.d.ts +11 -5
- package/dist/write-rate-limiter.js +1 -1
- package/package.json +12 -2
- package/scripts/build-with-heap.mjs +11 -1
- package/src/access-authorization-probe.ts +20 -2
- package/src/access-boundary.ts +32 -0
- package/src/access-http.test.ts +1971 -643
- package/src/access-http.ts +81 -74
- package/src/access-mcp.ts +3 -1
- package/src/access-memory-search-fanout.ts +24 -1
- package/src/access-offline-manifest.ts +5 -0
- package/src/access-operations.ts +2 -0
- package/src/access-service-namespace.test.ts +14 -0
- package/src/access-service-observe-scope.test.ts +2 -0
- package/src/access-service-offline-file-content.test.ts +211 -0
- package/src/access-service.ts +628 -884
- package/src/access-surface-catalog.test.ts +40 -10
- package/src/access-surface-catalog.ts +15 -0
- package/src/active-memory-bridge.test.ts +151 -0
- package/src/active-memory-bridge.ts +95 -21
- package/src/briefing.test.ts +38 -0
- package/src/briefing.ts +3 -3
- package/src/cli.ts +6 -1
- package/src/config.ts +18 -9
- package/src/connectors/codex-materialize-runner.ts +2 -0
- package/src/contradiction/contradiction-scan.ts +2 -0
- package/src/contradiction/contradiction.test.ts +42 -0
- package/src/correction/correction-access-wiring.ts +8 -2
- package/src/correction/correction-storage-integration.test.ts +7 -0
- package/src/explicit-capture.test.ts +24 -0
- package/src/explicit-capture.ts +2 -0
- package/src/fallback-llm.test.ts +54 -0
- package/src/fallback-llm.ts +54 -9
- package/src/index.ts +3 -0
- package/src/inline-script.ts +6 -0
- package/src/local-llm-headers-timeout.test.ts +42 -0
- package/src/local-llm.ts +8 -8
- package/src/maintenance/first-start-migration.ts +2 -1
- package/src/maintenance/memory-governance.test.ts +25 -0
- package/src/maintenance/memory-governance.ts +4 -1
- package/src/maintenance/pattern-reinforcement.test.ts +20 -0
- package/src/maintenance/pattern-reinforcement.ts +2 -1
- package/src/maintenance/rebuild-memory-projection.ts +8 -8
- package/src/mcp-read-only-tools.ts +3 -0
- package/src/memory-projection-store.ts +25 -27
- package/src/offline-sync-file-io.ts +68 -0
- package/src/offline-sync.ts +43 -57
- package/src/orchestration/consolidation-run.ts +5 -4
- package/src/orchestration/contradiction-linking-coordinator.ts +7 -0
- package/src/orchestration/contradiction-localization.integration.test.ts +115 -0
- package/src/orchestration/lifecycle-policy-coordinator.ts +2 -1
- package/src/orchestration/qmd-result-resolver.test.ts +215 -14
- package/src/orchestration/qmd-result-resolver.ts +142 -82
- package/src/orchestration/recall-search-pipeline.test.ts +41 -0
- package/src/orchestration/recall-search-pipeline.ts +12 -0
- package/src/orchestration/semantic-consolidation-coordinator.ts +2 -1
- package/src/orchestration/tier-migration-coordinator.ts +23 -56
- package/src/orchestration/workspace-ops.test.ts +37 -0
- package/src/orchestration/workspace-ops.ts +5 -29
- package/src/orchestrator.ts +7 -8
- package/src/relay/mission-access.test.ts +11 -11
- package/src/relay/mission-ui.test.ts +3 -1
- package/src/storage/supersession-audit.ts +7 -1
- package/src/storage/supersession-side-effects.ts +17 -5
- package/src/storage/tombstone-blocked-capture-sync.ts +31 -0
- package/src/storage-contract/failure-semantics.test.ts +102 -0
- package/src/storage.ts +18 -19
- package/src/support-passport/access-http-base.ts +143 -0
- package/src/support-passport/access-http.ts +172 -0
- package/src/support-passport/access-operations.ts +272 -0
- package/src/support-passport/access-service-base.test.ts +193 -0
- package/src/support-passport/access-service-base.ts +176 -0
- package/src/support-passport/access-surface.test.ts +283 -0
- package/src/support-passport/access-surface.ts +286 -0
- package/src/support-passport/card-projection.test.ts +137 -0
- package/src/support-passport/card-projection.ts +348 -0
- package/src/support-passport/card-replacement.ts +126 -0
- package/src/support-passport/card-service-commit-forwarding.test.ts +160 -0
- package/src/support-passport/card-service.test.ts +2964 -0
- package/src/support-passport/card-service.ts +1184 -0
- package/src/support-passport/card-state.ts +158 -0
- package/src/support-passport/config-model-routing.test.ts +37 -0
- package/src/support-passport/config.test.ts +34 -0
- package/src/support-passport/config.ts +41 -0
- package/src/support-passport/contracts.test.ts +156 -0
- package/src/support-passport/contracts.ts +149 -0
- package/src/support-passport/errors.ts +30 -0
- package/src/support-passport/generated-batch.test.ts +185 -0
- package/src/support-passport/generated-batch.ts +338 -0
- package/src/support-passport/grant-contracts.ts +183 -0
- package/src/support-passport/grant-generated-batch-scope.test.ts +201 -0
- package/src/support-passport/grant-service-cache.test.ts +148 -0
- package/src/support-passport/grant-service-cancellation.test.ts +83 -0
- package/src/support-passport/grant-service-marker-cache.test.ts +62 -0
- package/src/support-passport/grant-service.test.ts +3496 -0
- package/src/support-passport/grant-service.ts +513 -0
- package/src/support-passport/grant-store-commit-hook.test.ts +123 -0
- package/src/support-passport/grant-store-order.test.ts +60 -0
- package/src/support-passport/grant-store-path-safety.test.ts +30 -0
- package/src/support-passport/grant-store-recovery.test.ts +448 -0
- package/src/support-passport/grant-store.ts +906 -0
- package/src/support-passport/index.ts +17 -0
- package/src/support-passport/mcp-tools.test.ts +222 -0
- package/src/support-passport/mcp-tools.ts +187 -0
- package/src/support-passport/model-adapter.test.ts +840 -0
- package/src/support-passport/model-adapter.ts +602 -0
- package/src/support-passport/model-audit-pinned-write.test.ts +59 -0
- package/src/support-passport/model-audit.ts +152 -0
- package/src/support-passport/model-bridge.test.ts +456 -0
- package/src/support-passport/model-bridge.ts +439 -0
- package/src/support-passport/model-contracts.ts +124 -0
- package/src/support-passport/model-service.test.ts +1731 -0
- package/src/support-passport/model-service.ts +399 -0
- package/src/support-passport/offline-sync-guard.ts +230 -0
- package/src/support-passport/owner-http.test.ts +671 -0
- package/src/support-passport/owner-lock.ts +70 -0
- package/src/support-passport/private-file.test.ts +99 -0
- package/src/support-passport/private-file.ts +498 -0
- package/src/support-passport/public-http.test.ts +476 -0
- package/src/support-passport/public-http.ts +601 -0
- package/src/support-passport/ui.test.ts +181 -0
- package/src/tier-routing.ts +28 -17
- package/src/training-export/converter.test.ts +24 -0
- package/src/training-export/converter.ts +2 -0
- package/src/types.ts +8 -1
- package/src/wearables/service.test.ts +13 -0
- package/src/wearables/service.ts +2 -0
- package/src/write-rate-limiter.test.ts +30 -6
- package/src/write-rate-limiter.ts +29 -18
- package/dist/chunk-3CSX3ETE.js.map +0 -1
- package/dist/chunk-47P3RYZP.js.map +0 -1
- package/dist/chunk-6LAPLZOS.js.map +0 -1
- package/dist/chunk-6R4G7N4S.js.map +0 -1
- package/dist/chunk-7BZOBA5J.js.map +0 -1
- package/dist/chunk-C23BYTJV.js.map +0 -1
- package/dist/chunk-CYF6WVE7.js.map +0 -1
- package/dist/chunk-G2LUMAUF.js.map +0 -1
- package/dist/chunk-GYPQL6XN.js.map +0 -1
- package/dist/chunk-JDZPNCMA.js.map +0 -1
- package/dist/chunk-JV5YUBIZ.js.map +0 -1
- package/dist/chunk-K4QS7LXU.js.map +0 -1
- package/dist/chunk-L6CQ7WIW.js.map +0 -1
- package/dist/chunk-MKQBXQKU.js.map +0 -1
- package/dist/chunk-NH5IR2WM.js.map +0 -1
- package/dist/chunk-PX2LSHYM.js.map +0 -1
- package/dist/chunk-QWGE2PUH.js.map +0 -1
- package/dist/chunk-RUMNY5DT.js.map +0 -1
- package/dist/chunk-S75M5ZRK.js.map +0 -1
- package/dist/chunk-T2JHZWH6.js.map +0 -1
- package/dist/chunk-UES4BHA2.js.map +0 -1
- package/dist/chunk-UTRO5UJU.js.map +0 -1
- package/dist/chunk-UU3G6GUP.js.map +0 -1
- package/dist/chunk-XUAMCLY4.js.map +0 -1
- package/dist/chunk-ZYLC7NFO.js.map +0 -1
- package/dist/contradiction-scan-HYLIRMJ2.js +0 -14
- package/dist/first-start-migration-626C5554.js.map +0 -1
- /package/dist/{auto-sync-TDHLZPVP.js.map → auto-sync-NR3GPCOA.js.map} +0 -0
- /package/dist/{chunk-LLJMAULE.js.map → chunk-2FKEIPIT.js.map} +0 -0
- /package/dist/{chunk-JBPKEARU.js.map → chunk-2QSZNTDO.js.map} +0 -0
- /package/dist/{chunk-PDDTI7IC.js.map → chunk-43SEBY2M.js.map} +0 -0
- /package/dist/{chunk-HWRD23DW.js.map → chunk-46SZBVDS.js.map} +0 -0
- /package/dist/{chunk-6HPHEFXT.js.map → chunk-5FD7JMTX.js.map} +0 -0
- /package/dist/{chunk-IBKR2TFR.js.map → chunk-6CYHUWCO.js.map} +0 -0
- /package/dist/{chunk-YN6ASZUG.js.map → chunk-6QPI2ITJ.js.map} +0 -0
- /package/dist/{chunk-NIS256NW.js.map → chunk-CIAI24TQ.js.map} +0 -0
- /package/dist/{chunk-7FGL6CGA.js.map → chunk-DVYRD4HY.js.map} +0 -0
- /package/dist/{chunk-G7Z6EENO.js.map → chunk-EKTP7MNS.js.map} +0 -0
- /package/dist/{chunk-RZ4XGKOJ.js.map → chunk-FFRTNF3L.js.map} +0 -0
- /package/dist/{chunk-VKXWK44Q.js.map → chunk-FONWBCVY.js.map} +0 -0
- /package/dist/{chunk-DZD576ZU.js.map → chunk-FPLM2TKD.js.map} +0 -0
- /package/dist/{chunk-ZPJ4QSGP.js.map → chunk-FTPONF6B.js.map} +0 -0
- /package/dist/{chunk-LJS7KUPV.js.map → chunk-FWX7W5PM.js.map} +0 -0
- /package/dist/{chunk-G4T7L557.js.map → chunk-GN3WRFJY.js.map} +0 -0
- /package/dist/{chunk-QT2FRYI4.js.map → chunk-H7NAZ3UB.js.map} +0 -0
- /package/dist/{chunk-APPFNGX6.js.map → chunk-HBUFQRIM.js.map} +0 -0
- /package/dist/{chunk-UU7TRFNE.js.map → chunk-HGJ6NJ4E.js.map} +0 -0
- /package/dist/{chunk-C4ZNGVK3.js.map → chunk-IKP5OYQQ.js.map} +0 -0
- /package/dist/{chunk-KQAFEZQX.js.map → chunk-IPLYGWQF.js.map} +0 -0
- /package/dist/{chunk-RDJRFDJY.js.map → chunk-IQ4NVWEF.js.map} +0 -0
- /package/dist/{chunk-CGVHPCP2.js.map → chunk-JYG4OLWH.js.map} +0 -0
- /package/dist/{chunk-WOPA3SGI.js.map → chunk-K26NWEOC.js.map} +0 -0
- /package/dist/{chunk-G7A5HLUS.js.map → chunk-KJH6W2I7.js.map} +0 -0
- /package/dist/{chunk-HOIRB77A.js.map → chunk-L6Z5OAS4.js.map} +0 -0
- /package/dist/{chunk-227UFSEQ.js.map → chunk-LEPOP46W.js.map} +0 -0
- /package/dist/{chunk-3JLPGJ43.js.map → chunk-MIH3CJPN.js.map} +0 -0
- /package/dist/{chunk-QHFV6VNU.js.map → chunk-MK5VEWCR.js.map} +0 -0
- /package/dist/{chunk-GUKBW4HL.js.map → chunk-NQMSEB5D.js.map} +0 -0
- /package/dist/{chunk-WRJVB3C2.js.map → chunk-PO7NXBG2.js.map} +0 -0
- /package/dist/{chunk-AZM4CCAC.js.map → chunk-QZLDRQ4I.js.map} +0 -0
- /package/dist/{chunk-THPPCISA.js.map → chunk-RBII7637.js.map} +0 -0
- /package/dist/{chunk-VMMQDEN5.js.map → chunk-TO7ZTWJW.js.map} +0 -0
- /package/dist/{chunk-K2TDK7GG.js.map → chunk-TWTUGCTY.js.map} +0 -0
- /package/dist/{chunk-HLYR2TQL.js.map → chunk-UCTS3DDH.js.map} +0 -0
- /package/dist/{chunk-JYIJNNN3.js.map → chunk-UG6VXIOK.js.map} +0 -0
- /package/dist/{chunk-WM65LU67.js.map → chunk-UKYS3NU5.js.map} +0 -0
- /package/dist/{chunk-WDBX6VER.js.map → chunk-UPLZNJKN.js.map} +0 -0
- /package/dist/{chunk-2GFK22OZ.js.map → chunk-V6ERBR7U.js.map} +0 -0
- /package/dist/{chunk-ZMYDYNI4.js.map → chunk-XZHE2DEN.js.map} +0 -0
- /package/dist/{chunk-MAXVH44E.js.map → chunk-Z54EM5YZ.js.map} +0 -0
- /package/dist/{chunk-YGLY6QFV.js.map → chunk-ZRZVLB7O.js.map} +0 -0
- /package/dist/{chunk-4IIFL7GC.js.map → chunk-ZUKIUXHQ.js.map} +0 -0
- /package/dist/{contradiction-scan-HYLIRMJ2.js.map → contradiction-scan-JDTBXRGW.js.map} +0 -0
- /package/dist/{purge-533FZLS4.js.map → purge-GB7PZHT2.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/storage/tombstone-blocked-capture-mutation.ts","../src/storage/tombstone-blocked-capture-sync.ts","../src/storage/tombstone-blocked-capture-index.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { createHash } from \"node:crypto\";\nimport { mkdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { withHeldFileLock } from \"../utils/serialize-mutations.js\";\nimport type { MemoryFile, MemoryFrontmatter } from \"../types.js\";\nconst TOMBSTONE_CAPTURE_WRITE_LOCK_STALE_MS = 60_000;\nconst CAPTURE_WRITE_LOCK_MAX_ATTEMPTS = 3;\nconst CAPTURE_WRITE_LOCK_RETRY_BASE_MS = 25;\nconst CAPTURE_WRITE_LOCK_BUSY_MESSAGE = \"tombstone-blocked capture write lock remained busy\";\n\nexport function isTombstoneBlockedCaptureWriteLockBusy(error: unknown): boolean {\n return error instanceof Error && error.message === CAPTURE_WRITE_LOCK_BUSY_MESSAGE;\n}\n\nexport function normalizeExplicitCaptureContent(value: string): string {\n return value.toLowerCase().replace(/\\s+/g, \" \").trim();\n}\n\nexport function buildExplicitCaptureDedupKey(\n content: string,\n category: string,\n sourceConnector: string | undefined\n): string {\n const encode = (label: string, value: string): string => `${label} ${value.length} ${value}`;\n return [\n encode(\"category\", category),\n encode(\"connector\", sourceConnector?.trim() ?? \"\"),\n encode(\"content\", normalizeExplicitCaptureContent(content)),\n ].join(\" \");\n}\n\nexport function buildCapturePathLockIdentity(pathname: string): string {\n return `path ${pathname.length} ${pathname}`;\n}\n\nexport function captureIdentityHash(memory: MemoryFile): string {\n return createHash(\"sha256\")\n .update(\n buildExplicitCaptureDedupKey(memory.content, memory.frontmatter.category, memory.frontmatter.sourceConnector)\n )\n .digest(\"hex\");\n}\n\nexport type TombstoneBlockedCaptureWriteLockOptions = {\n readonly stateDir: string;\n readonly withHeldFileLock?: typeof withHeldFileLock;\n};\n\nexport class TombstoneBlockedCaptureWriteLock {\n private readonly context = new AsyncLocalStorage<ReadonlySet<string>>();\n\n constructor(private readonly options: TombstoneBlockedCaptureWriteLockOptions) {}\n\n private captureWriteLockPathForHash(identityHash: string): string {\n const filename =\n identityHash.length === 0 ? \"explicit-capture-write.lock\" : `explicit-capture-write-${identityHash}.lock`;\n return path.join(this.options.stateDir, \"tombstone-blocked-capture\", filename);\n }\n\n private async withSingleCaptureWriteLock<T>(lockPath: string, task: () => Promise<T>): Promise<T> {\n await mkdir(path.dirname(lockPath), { recursive: true });\n const retry = Symbol(\"retry\");\n const permanentFailure = Symbol(\"permanent-failure\");\n const runWithHeldFileLock = this.options.withHeldFileLock ?? withHeldFileLock;\n for (let attempt = 0; attempt < CAPTURE_WRITE_LOCK_MAX_ATTEMPTS; attempt += 1) {\n const result = await runWithHeldFileLock<T | typeof retry | typeof permanentFailure>(\n lockPath,\n {\n staleMs: TOMBSTONE_CAPTURE_WRITE_LOCK_STALE_MS,\n maxWaitMs: 1_000,\n pollMs: 25,\n },\n async (acquired, controller) => {\n if (acquired) return await task();\n return controller.failure === \"error\" ? permanentFailure : retry;\n }\n );\n if (result === permanentFailure) {\n throw new Error(\"tombstone-blocked capture write lock acquisition failed\");\n }\n if (result !== retry) return result;\n if (attempt === CAPTURE_WRITE_LOCK_MAX_ATTEMPTS - 1) break;\n const delayState = Promise.withResolvers<void>();\n setTimeout(delayState.resolve, CAPTURE_WRITE_LOCK_RETRY_BASE_MS * 2 ** attempt);\n await delayState.promise;\n }\n throw new Error(CAPTURE_WRITE_LOCK_BUSY_MESSAGE);\n }\n private async withPhysicalCaptureWriteLocks<T>(\n task: () => Promise<T>,\n identityHashes: readonly string[]\n ): Promise<T> {\n const lockPaths = [...new Set(identityHashes.map((hash) => this.captureWriteLockPathForHash(hash)))];\n const acquire = async (index: number): Promise<T> => {\n if (index === lockPaths.length) return await task();\n return await this.withSingleCaptureWriteLock(lockPaths[index], () => acquire(index + 1));\n };\n return await acquire(0);\n }\n\n async withCaptureWriteLock<T>(task: () => Promise<T>, identity?: string | readonly string[]): Promise<T> {\n const identities =\n Array.isArray(identity) && identity.length > 0 ? identity : [typeof identity === \"string\" ? identity : \"\"];\n return await this.withCaptureWriteLocks(task, identities, []);\n }\n\n async withCaptureWriteLockHashes<T>(task: () => Promise<T>, identityHashes: readonly string[]): Promise<T> {\n return await this.withCaptureWriteLocks(task, [], identityHashes);\n }\n\n async withCaptureWriteLockAndHashes<T>(\n task: () => Promise<T>,\n identity: string | readonly string[] | undefined,\n identityHashes: readonly string[]\n ): Promise<T> {\n const identities =\n Array.isArray(identity) && identity.length > 0 ? identity : [typeof identity === \"string\" ? identity : \"\"];\n return await this.withCaptureWriteLocks(task, identities, identityHashes);\n }\n\n private async withCaptureWriteLocks<T>(\n task: () => Promise<T>,\n identities: readonly string[],\n identityHashes: readonly string[]\n ): Promise<T> {\n const current = this.context.getStore();\n const hashToken = (key: string): string =>\n `\\u0000${key.length === 0 ? \"\" : createHash(\"sha256\").update(key).digest(\"hex\")}`;\n const identityHash = (key: string): string =>\n key.length === 0 ? \"\" : createHash(\"sha256\").update(key).digest(\"hex\");\n const identityKeys = [...new Set(identities)];\n const hashes = [...new Set(identityHashes)];\n const requested = [\n ...identityKeys\n .filter((key) => !current || (!current.has(key) && !current.has(hashToken(key))))\n .map((key) => ({ hash: identityHash(key), pathLock: key.startsWith(\"path \") })),\n ...hashes.filter((hash) => !current || !current.has(`\\u0000${hash}`)).map((hash) => ({ hash, pathLock: false })),\n ]\n .filter((entry, index, entries) => entries.findIndex((candidate) => candidate.hash === entry.hash) === index)\n .sort(\n (left, right) =>\n Number(left.pathLock) - Number(right.pathLock) ||\n this.captureWriteLockPathForHash(left.hash).localeCompare(this.captureWriteLockPathForHash(right.hash))\n );\n const physicalMissing = requested.map((entry) => entry.hash);\n if (current && requested.length > 0) {\n const held = [...current]\n .filter((token) => token.startsWith(\"\\u0000\"))\n .map((token) => {\n const hash = token.slice(1);\n const pathLock = [...current].some(\n (key) => !key.startsWith(\"\\u0000\") && key.startsWith(\"path \") && hashToken(key) === token\n );\n return { hash, pathLock };\n });\n const requestedFirst = requested[0];\n const heldLast = held\n .sort(\n (left, right) =>\n Number(left.pathLock) - Number(right.pathLock) ||\n this.captureWriteLockPathForHash(left.hash).localeCompare(this.captureWriteLockPathForHash(right.hash))\n )\n .at(-1);\n if (\n heldLast &&\n requestedFirst &&\n (Number(requestedFirst.pathLock) < Number(heldLast.pathLock) ||\n (requestedFirst.pathLock === heldLast.pathLock &&\n this.captureWriteLockPathForHash(requestedFirst.hash) < this.captureWriteLockPathForHash(heldLast.hash)))\n ) {\n throw new Error(CAPTURE_WRITE_LOCK_BUSY_MESSAGE);\n }\n }\n const next = new Set(current);\n for (const key of identityKeys) {\n next.add(key);\n next.add(hashToken(key));\n }\n for (const hash of hashes) next.add(`\\u0000${hash}`);\n const run = () => this.context.run(next, task);\n if (physicalMissing.length === 0) return await run();\n return await this.withPhysicalCaptureWriteLocks(run, physicalMissing);\n }\n}\n\nexport type TombstoneBlockedMutationHost = {\n readCurrent: () => Promise<MemoryFile | null>;\n isBlocked: (memory: MemoryFile | null) => boolean;\n isQueuedReview: (memory: MemoryFile | null) => boolean;\n memoryIdentity: (memory: MemoryFile) => string;\n prepareWrite: () => Promise<string>;\n commitWrite: (marker: string) => Promise<void>;\n discardWrite: (marker: string) => Promise<void>;\n markUntrusted: () => void;\n writeStorageSecureFile: (pathname: string, fileContent: string) => Promise<void>;\n withCaptureWriteLock: (task: () => Promise<void>, identity: string | readonly string[]) => Promise<void>;\n logWarning: (message: string) => void;\n};\n\nexport type TombstoneBlockedMutation = {\n blocked: boolean;\n pathname: string;\n fileContent: string;\n identity: string | readonly string[];\n updateIndex: (rebuildMarker?: string, current?: MemoryFile | null) => Promise<void>;\n beforeIndexUpdate?: () => Promise<void>;\n coordinate?: boolean;\n};\n\nexport async function runTombstoneBlockedMutation(\n host: TombstoneBlockedMutationHost,\n mutation: TombstoneBlockedMutation\n): Promise<void> {\n let lockIdentity: string | readonly string[] = mutation.identity;\n for (;;) {\n let retryIdentity: string | undefined;\n const mutate = async (): Promise<void> => {\n const current = await host.readCurrent();\n const currentBlocked = host.isBlocked(current);\n const currentQueuedReview = host.isQueuedReview(current);\n if (current !== null && (mutation.coordinate === true || currentBlocked || currentQueuedReview)) {\n const currentIdentity = host.memoryIdentity(current);\n const heldIdentities = Array.isArray(lockIdentity) ? lockIdentity : [lockIdentity];\n if (!heldIdentities.includes(currentIdentity)) {\n retryIdentity = currentIdentity;\n return;\n }\n }\n const rebuildMarker = mutation.blocked || currentBlocked ? await host.prepareWrite() : undefined;\n try {\n await host.writeStorageSecureFile(mutation.pathname, mutation.fileContent);\n } catch (err) {\n if (rebuildMarker) {\n try {\n await host.discardWrite(rebuildMarker);\n } catch (cleanupError) {\n host.markUntrusted();\n host.logWarning(`storage.tombstoneBlocked failed to clear write marker: ${cleanupError}`);\n }\n }\n throw err;\n }\n if (rebuildMarker) {\n try {\n await host.commitWrite(rebuildMarker);\n } catch (err) {\n host.markUntrusted();\n host.logWarning(`storage.tombstoneBlocked committed write marker failed: ${err}`);\n }\n }\n if (rebuildMarker) await mutation.beforeIndexUpdate?.();\n await mutation.updateIndex(rebuildMarker, currentBlocked && current !== null ? current : undefined);\n };\n await host.withCaptureWriteLock(mutate, lockIdentity);\n if (retryIdentity === undefined) return;\n const identities = Array.isArray(lockIdentity) ? [...lockIdentity, retryIdentity] : [lockIdentity, retryIdentity];\n lockIdentity = [...new Set(identities)];\n }\n}\n\nexport function tombstoneBlocked(frontmatter: MemoryFrontmatter): boolean {\n return frontmatter.status === \"pending_review\" && Boolean(frontmatter.blockedBy);\n}\n\nexport function isQueuedReviewFrontmatter(frontmatter: MemoryFrontmatter): boolean {\n return frontmatter.status === \"pending_review\" && (frontmatter.tags ?? []).includes(\"queued-review\");\n}\n\nexport function isQueuedReviewMemory(memory: MemoryFile | null): memory is MemoryFile {\n return memory !== null && isQueuedReviewFrontmatter(memory.frontmatter);\n}\n","import { createHash, randomUUID, type Hash } from \"node:crypto\";\nimport { unlink } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { log } from \"../logger.js\";\nimport type { MemoryFile, MemoryFrontmatter } from \"../types.js\";\nimport { markProjectedMemoryPathInvalid } from \"../memory-projection-store.js\";\nimport { RECALL_FALLBACK_DIRS } from \"../utils/category-dir.js\";\nimport { isErrnoCode } from \"../utils/errno.js\";\nimport { pathMayCarryEntityRefs, requestEntityCanonicalIdReconcile } from \"./entity-canonical-id-references.js\";\nimport { withRawEntityPageMutation } from \"./entity-canonical-id-lock.js\";\nimport { readMaybeEncryptedFileFromChunks, writeMaybeEncryptedFileFromChunks } from \"../secure-store/secure-fs.js\";\nimport * as archiveMutation from \"../archive-mutation-version.js\";\nimport {\n buildExplicitCaptureDedupKey,\n buildCapturePathLockIdentity,\n captureIdentityHash,\n tombstoneBlocked,\n isQueuedReviewMemory,\n runTombstoneBlockedMutation,\n type TombstoneBlockedMutationHost,\n} from \"./tombstone-blocked-capture-mutation.js\";\nimport { TombstoneBlockedCaptureIndex } from \"./tombstone-blocked-capture-index.js\";\nimport type { TombstoneBlockedCaptureIndexOptions } from \"./tombstone-blocked-capture-index.js\";\n\nconst OFFLINE_SYNC_BUFFER_LIMIT_BYTES = 1_048_576;\nconst OFFLINE_SYNC_STAGE_READ_BYTES = 64 * 1024;\nconst OFFLINE_SYNC_FRONTMATTER_DELIMITER = Buffer.from(\"\\n---\\n\", \"utf8\");\n\nconst CASED_CHARACTER = /\\p{Cased}/u;\nconst CASE_IGNORABLE_CHARACTER = /\\p{Case_Ignorable}/u;\n\ntype StreamedOfflineSyncIdentity = {\n identityHash: string;\n};\n\ntype PreparedOfflineSyncChunks = {\n after: MemoryFile | null;\n chunks: AsyncIterable<Buffer>;\n stagePath?: string;\n streamedIdentity?: StreamedOfflineSyncIdentity;\n};\n\ntype ExplicitContentNormalizerSink = {\n write: (chunk: string) => void;\n beginConditionalSigma: () => void;\n resolveConditionalSigma: (useFinalSigma: boolean) => void;\n};\n\nfunction createExplicitContentNormalizer(sink: ExplicitContentNormalizerSink): {\n push: (text: string) => void;\n finish: () => void;\n} {\n let started = false;\n let pendingSpace = false;\n let output = \"\";\n let precededByCased = false;\n let pendingSigma = false;\n const flush = () => {\n if (output.length > 0) {\n sink.write(output);\n output = \"\";\n }\n };\n const emit = (text: string) => {\n for (const character of text) {\n if (/\\s/u.test(character)) {\n if (started) pendingSpace = true;\n continue;\n }\n if (pendingSpace) {\n output += \" \";\n pendingSpace = false;\n }\n output += character;\n started = true;\n if (output.length >= OFFLINE_SYNC_STAGE_READ_BYTES) flush();\n }\n };\n const resolveSigma = (followedByCased: boolean) => {\n flush();\n sink.resolveConditionalSigma(!followedByCased);\n pendingSigma = false;\n };\n const push = (text: string) => {\n for (const character of text) {\n const caseIgnorable = CASE_IGNORABLE_CHARACTER.test(character);\n const cased = CASED_CHARACTER.test(character);\n if (pendingSigma) {\n if (caseIgnorable) {\n emit(character.toLowerCase());\n continue;\n }\n resolveSigma(cased);\n }\n if (character === \"Σ\" && precededByCased) {\n flush();\n sink.beginConditionalSigma();\n pendingSigma = true;\n precededByCased = true;\n continue;\n }\n emit(character.toLowerCase());\n if (!caseIgnorable) precededByCased = cased;\n }\n };\n return {\n push,\n finish() {\n if (pendingSigma) resolveSigma(false);\n flush();\n },\n };\n}\n\nasync function forEachStagedTextChunk(\n stagePath: string,\n offset: number,\n onText: (text: string) => void,\n key: Buffer | null,\n memoryDir: string\n): Promise<void> {\n const decoder = new TextDecoder(\"utf-8\");\n let remaining = offset;\n for await (const chunk of readMaybeEncryptedFileFromChunks(stagePath, key, memoryDir)) {\n const textChunk = remaining >= chunk.length ? null : chunk.subarray(remaining);\n remaining = Math.max(0, remaining - chunk.length);\n if (textChunk === null || textChunk.length === 0) continue;\n const text = decoder.decode(textChunk, { stream: true });\n if (text.length > 0) onText(text);\n }\n const finalText = decoder.decode();\n if (finalText.length > 0) onText(finalText);\n}\n\nasync function findStagedBodyOffset(stagePath: string, key: Buffer | null, memoryDir: string): Promise<number | null> {\n let scannedBytes = 0;\n let tail = Buffer.alloc(0);\n for await (const chunk of readMaybeEncryptedFileFromChunks(stagePath, key, memoryDir)) {\n const window = tail.length > 0 ? Buffer.concat([tail, chunk]) : chunk;\n const windowStart = scannedBytes - tail.length;\n for (let searchFrom = 0; ; ) {\n const delimiterStart = window.indexOf(OFFLINE_SYNC_FRONTMATTER_DELIMITER, searchFrom);\n if (delimiterStart < 0) break;\n if (windowStart + delimiterStart >= 4) {\n return windowStart + delimiterStart + OFFLINE_SYNC_FRONTMATTER_DELIMITER.length;\n }\n searchFrom = delimiterStart + 1;\n }\n scannedBytes += chunk.length;\n const tailLength = Math.min(OFFLINE_SYNC_FRONTMATTER_DELIMITER.length - 1, window.length);\n tail = Buffer.from(window.subarray(window.length - tailLength));\n }\n return null;\n}\n\nasync function readStagedPrefix(\n stagePath: string,\n byteLimit: number,\n key: Buffer | null,\n memoryDir: string\n): Promise<Buffer> {\n const chunks: Buffer[] = [];\n let bytesRead = 0;\n for await (const chunk of readMaybeEncryptedFileFromChunks(stagePath, key, memoryDir)) {\n const length = Math.min(chunk.length, byteLimit - bytesRead);\n if (length > 0) chunks.push(chunk.subarray(0, length));\n bytesRead += length;\n if (bytesRead >= byteLimit) break;\n }\n return Buffer.concat(chunks, bytesRead);\n}\n\nexport type OfflineSyncMemoryParser = (filePath: string, content: Buffer) => MemoryFile | null;\n\nexport function parseTombstoneBlockedOfflineSyncMemory(\n filePath: string,\n content: Buffer,\n memoryDir: string,\n parseFrontmatter: (raw: string) => { frontmatter: MemoryFrontmatter; content: string } | null,\n normalizeFrontmatterForPath: (frontmatter: MemoryFrontmatter, pathRel: string, content?: string) => MemoryFrontmatter,\n toMemoryPathRel: (baseDir: string, filePath: string) => string\n): MemoryFile | null {\n const parsed = parseFrontmatter(content.toString(\"utf8\"));\n if (!parsed) return null;\n return {\n path: filePath,\n frontmatter: normalizeFrontmatterForPath(parsed.frontmatter, toMemoryPathRel(memoryDir, filePath), parsed.content),\n content: parsed.content,\n };\n}\n\nexport abstract class TombstoneBlockedCaptureIndexHost {\n private tombstoneBlockedCaptureIndex: TombstoneBlockedCaptureIndex | null = null;\n protected abstract tombstoneBlockedCaptureIndexOptions(): TombstoneBlockedCaptureIndexOptions;\n protected abstract writeStorageSecureFile(\n filePath: string,\n content: string | Buffer,\n forceEncrypt?: boolean\n ): Promise<void>;\n protected abstract assertManagedStoragePath(filePath: string, method: string): string;\n protected abstract notifyCatalogWriteForPath(filePath: string): void;\n protected abstract readMemoryByPath(filePath: string): Promise<MemoryFile | null>;\n protected abstract invalidateAllMemoriesCache(): void;\n protected abstract invalidateKnowledgeIndexCache(): void;\n protected abstract invalidateColdMemoriesCache(): void;\n protected abstract bumpArtifactWriteVersion(): number;\n protected abstract bumpMemoryStatusVersion(): void;\n protected abstract bumpMemoryCorpusVersion(): void;\n protected abstract markFactHashIndexNotAuthoritative(): void;\n protected abstract deleteManagedStorageFile(filePath: string, deletionMtimeMs?: number | null): Promise<boolean>;\n protected abstract writeManagedStorageFile(filePath: string, write: () => Promise<void>): Promise<void>;\n\n private async writeStorageSecureFileChunks(filePath: string, chunks: AsyncIterable<Buffer>): Promise<void> {\n const options = this.tombstoneBlockedCaptureIndexOptions();\n await writeMaybeEncryptedFileFromChunks(\n filePath,\n chunks,\n options.secureStoreWriteKeyProvider(),\n {},\n options.memoryDir\n );\n this.notifyCatalogWriteForPath(filePath);\n }\n\n protected getTombstoneBlockedCaptureIndex(): TombstoneBlockedCaptureIndex {\n if (this.tombstoneBlockedCaptureIndex === null) {\n this.tombstoneBlockedCaptureIndex = new TombstoneBlockedCaptureIndex(this.tombstoneBlockedCaptureIndexOptions());\n }\n return this.tombstoneBlockedCaptureIndex;\n }\n async withTombstoneBlockedCaptureWriteLock<T>(\n task: () => Promise<T>,\n identity?: string | readonly string[]\n ): Promise<T> {\n return await this.getTombstoneBlockedCaptureIndex().withCaptureWriteLock(task, identity);\n }\n\n protected async withTombstoneBlockedCaptureWriteLockAndHashes<T>(\n task: () => Promise<T>,\n identity: string | readonly string[] | undefined,\n identityHashes: readonly string[]\n ): Promise<T> {\n return await this.getTombstoneBlockedCaptureIndex().withCaptureWriteLockAndHashes(task, identity, identityHashes);\n }\n\n private async writeTombstoneBlockedMutation(\n blocked: boolean,\n pathname: string,\n fileContent: string,\n identity: string | readonly string[],\n updateIndex: (rebuildMarker?: string, current?: MemoryFile | null) => Promise<void>,\n beforeIndexUpdate?: () => Promise<void>,\n coordinate = false\n ): Promise<void> {\n const host: TombstoneBlockedMutationHost = {\n readCurrent: () => this.readMemoryByPath(pathname),\n isBlocked: (memory) => this.isTombstoneBlockedMemory(memory),\n isQueuedReview: (memory) => isQueuedReviewMemory(memory),\n memoryIdentity: (memory) => this.offlineSyncMemoryIdentity(memory),\n prepareWrite: () => this.getTombstoneBlockedCaptureIndex().prepareWrite(),\n commitWrite: (marker) => this.getTombstoneBlockedCaptureIndex().commitWrite(marker),\n discardWrite: (marker) => this.getTombstoneBlockedCaptureIndex().discardWrite(marker),\n markUntrusted: () => this.getTombstoneBlockedCaptureIndex().markUntrusted(),\n writeStorageSecureFile: (target, content) => this.writeStorageSecureFile(target, content),\n withCaptureWriteLock: (task, lockIdentity) => this.withTombstoneBlockedCaptureWriteLock(task, lockIdentity),\n logWarning: (message) => log.warn(message),\n };\n await runTombstoneBlockedMutation(host, {\n blocked,\n pathname,\n fileContent,\n identity,\n updateIndex,\n beforeIndexUpdate,\n coordinate,\n });\n }\n\n protected async writeTombstoneBlockedMemory(\n pathname: string,\n fileContent: string,\n frontmatter: MemoryFrontmatter,\n content: string,\n beforeIndexUpdate?: () => Promise<void>\n ): Promise<void> {\n await this.writeTombstoneBlockedMutation(\n tombstoneBlocked(frontmatter),\n pathname,\n fileContent,\n [\n buildCapturePathLockIdentity(pathname),\n buildExplicitCaptureDedupKey(content, frontmatter.category, frontmatter.sourceConnector),\n ],\n (rebuildMarker) =>\n this.getTombstoneBlockedCaptureIndex().addWrittenMemory(pathname, frontmatter, content, rebuildMarker),\n beforeIndexUpdate\n );\n }\n\n protected async withTombstoneBlockedMemoryPathLock<T>(\n pathname: string,\n task: (current: MemoryFile | null) => Promise<T>,\n additionalPathnames: readonly string[] = []\n ): Promise<T> {\n let lockIdentities = [pathname, ...additionalPathnames].map(buildCapturePathLockIdentity);\n for (;;) {\n let retryIdentity: string | undefined;\n let result: T | undefined;\n await this.withTombstoneBlockedCaptureWriteLock(async () => {\n const current = await this.readMemoryByPath(pathname);\n if (current !== null) {\n const currentIdentity = this.offlineSyncMemoryIdentity(current);\n if (!lockIdentities.includes(currentIdentity)) {\n retryIdentity = currentIdentity;\n return;\n }\n }\n result = await task(current);\n }, lockIdentities);\n if (retryIdentity !== undefined) {\n lockIdentities = [...new Set([...lockIdentities, retryIdentity])];\n continue;\n }\n return result!;\n }\n }\n\n protected async writeTombstoneBlockedChunk(\n pathname: string,\n fileContent: string,\n frontmatter: MemoryFrontmatter,\n content: string,\n findDuplicate: () => Promise<MemoryFile | null>,\n afterWrite: (current: MemoryFile | null) => Promise<void>\n ): Promise<string> {\n const incomingIdentity = buildExplicitCaptureDedupKey(content, frontmatter.category, frontmatter.sourceConnector);\n let lockIdentities = [buildCapturePathLockIdentity(pathname), incomingIdentity];\n for (;;) {\n let retryIdentity: string | undefined;\n let result = frontmatter.id;\n await this.withTombstoneBlockedCaptureWriteLock(async () => {\n const current = await this.readMemoryByPath(pathname);\n if (current !== null) {\n const currentIdentity = this.offlineSyncMemoryIdentity(current);\n if (!lockIdentities.includes(currentIdentity)) {\n retryIdentity = currentIdentity;\n return;\n }\n }\n if (tombstoneBlocked(frontmatter)) {\n const duplicate = await findDuplicate();\n if (duplicate) {\n result = duplicate.frontmatter.id;\n return;\n }\n }\n await this.writeTombstoneBlockedMemory(pathname, fileContent, frontmatter, content);\n await afterWrite(current);\n }, lockIdentities);\n if (retryIdentity === undefined) return result;\n lockIdentities = [...new Set([...lockIdentities, retryIdentity])];\n }\n }\n\n protected async writeTombstoneBlockedUpdate(\n before: MemoryFile,\n fileContent: string,\n frontmatter: MemoryFrontmatter,\n content: string,\n beforeIndexUpdate?: () => Promise<void>\n ): Promise<void> {\n await this.writeTombstoneBlockedMutation(\n tombstoneBlocked(before.frontmatter) || tombstoneBlocked(frontmatter),\n before.path,\n fileContent,\n [\n buildCapturePathLockIdentity(before.path),\n this.offlineSyncMemoryIdentity(before),\n buildExplicitCaptureDedupKey(content, frontmatter.category, frontmatter.sourceConnector),\n ],\n (rebuildMarker, current) =>\n this.getTombstoneBlockedCaptureIndex().syncUpdatedMemory(\n current ?? before,\n frontmatter,\n content,\n rebuildMarker\n ),\n beforeIndexUpdate,\n true\n );\n }\n\n protected async writeTombstoneBlockedFrontmatter(\n before: MemoryFile,\n fileContent: string,\n frontmatter: MemoryFrontmatter,\n beforeIndexUpdate?: () => Promise<void>\n ): Promise<void> {\n await this.writeTombstoneBlockedMutation(\n tombstoneBlocked(before.frontmatter) || tombstoneBlocked(frontmatter),\n before.path,\n fileContent,\n [\n buildCapturePathLockIdentity(before.path),\n this.offlineSyncMemoryIdentity(before),\n buildExplicitCaptureDedupKey(before.content, frontmatter.category, frontmatter.sourceConnector),\n ],\n (rebuildMarker, current) =>\n this.getTombstoneBlockedCaptureIndex().syncUpdatedFrontmatter(current ?? before, frontmatter, rebuildMarker),\n beforeIndexUpdate,\n true\n );\n }\n\n private isTombstoneBlockedMemory(memory: MemoryFile | null): memory is MemoryFile {\n return memory !== null && memory.frontmatter.status === \"pending_review\" && Boolean(memory.frontmatter.blockedBy);\n }\n\n private offlineSyncMemoryIdentity(memory: MemoryFile): string {\n return buildExplicitCaptureDedupKey(\n memory.content,\n memory.frontmatter.category,\n memory.frontmatter.sourceConnector\n );\n }\n private async isDuplicateTombstoneBlockedOfflineSync(\n target: string,\n incoming: MemoryFile,\n streamedIdentity?: StreamedOfflineSyncIdentity\n ): Promise<boolean> {\n const before = await this.readMemoryByPath(target);\n const duplicate = streamedIdentity\n ? true\n : await this.getTombstoneBlockedCaptureIndex().has(\n incoming.content,\n incoming.frontmatter.category,\n incoming.frontmatter.sourceConnector\n );\n if (!duplicate) return false;\n const incomingIdentity = streamedIdentity?.identityHash ?? this.offlineSyncMemoryIdentity(incoming);\n const options = this.tombstoneBlockedCaptureIndexOptions();\n const existing = [...(await options.readAllMemories()), ...(await options.readAllColdMemories())];\n const exactMatch = existing.some(\n (memory) =>\n this.isTombstoneBlockedMemory(memory) &&\n (streamedIdentity ? captureIdentityHash(memory) : this.offlineSyncMemoryIdentity(memory)) === incomingIdentity\n );\n if (!exactMatch) return false;\n if (before === null) return true;\n if (!this.isTombstoneBlockedMemory(before)) return false;\n const beforeIdentity = streamedIdentity ? captureIdentityHash(before) : this.offlineSyncMemoryIdentity(before);\n if (beforeIdentity !== incomingIdentity) return false;\n return streamedIdentity\n ? isDeepStrictEqual(before.frontmatter, incoming.frontmatter)\n : before.content === incoming.content && isDeepStrictEqual(before.frontmatter, incoming.frontmatter);\n }\n\n private async invalidateAfterOfflineSyncMutation(\n filePath: string,\n ownedMarker?: string,\n archiveChanged = true\n ): Promise<void> {\n this.invalidateAllMemoriesCache();\n this.invalidateKnowledgeIndexCache();\n this.markFactHashIndexNotAuthoritative();\n if (filePath.includes(`${path.sep}cold${path.sep}`)) {\n this.invalidateColdMemoriesCache();\n }\n await this.rebuildTombstoneBlockedCaptureAfterInvalidationForPath(filePath, ownedMarker);\n if (filePath.includes(`${path.sep}artifacts${path.sep}`)) {\n this.bumpArtifactWriteVersion();\n }\n const memoryDir = this.tombstoneBlockedCaptureIndexOptions().memoryDir;\n archiveMutation.bumpArchiveMutationForPath(memoryDir, archiveChanged, filePath);\n this.bumpMemoryStatusVersion();\n }\n\n protected async runTombstoneBlockedArchive(\n memory: MemoryFile,\n task: (current: MemoryFile, markDurable: () => void) => Promise<string | null>\n ): Promise<string | null> {\n let archivedPath: string | null = null;\n const archived = await this.runTombstoneBlockedInvalidation(memory, async (current, rebuildMarker, markDurable) => {\n let archiveDurable = false;\n const markArchiveDurable = () => {\n archiveDurable = true;\n markDurable();\n };\n archivedPath = await task(current, markArchiveDurable);\n if (rebuildMarker !== undefined && (archivedPath !== null || archiveDurable)) {\n await this.rebuildTombstoneBlockedCaptureAfterInvalidation(rebuildMarker);\n } else if (rebuildMarker !== undefined) {\n const index = this.getTombstoneBlockedCaptureIndex();\n try {\n await index.discardWrite(rebuildMarker);\n } catch {\n index.markUntrusted();\n }\n }\n return archivedPath !== null;\n });\n return archived ? archivedPath : null;\n }\n\n protected async runTombstoneBlockedInvalidation(\n memory: MemoryFile,\n task: (current: MemoryFile, rebuildMarker: string | undefined, markDurable: () => void) => Promise<boolean>,\n propagateErrors = false,\n coordinate = false\n ): Promise<boolean> {\n let lockIdentities = [\n buildCapturePathLockIdentity(memory.path),\n ...(coordinate || this.isTombstoneBlockedMemory(memory) || isQueuedReviewMemory(memory)\n ? [this.offlineSyncMemoryIdentity(memory)]\n : []),\n ];\n for (;;) {\n const attempt = async () => {\n const options = this.tombstoneBlockedCaptureIndexOptions();\n const current =\n (await this.readMemoryByPath(memory.path)) ??\n (await options.readAllMemories()).find((candidate) => candidate.frontmatter.id === memory.frontmatter.id) ??\n (await options.readAllColdMemories()).find((candidate) => candidate.frontmatter.id === memory.frontmatter.id);\n if (!current) return { result: false };\n const blocked = this.isTombstoneBlockedMemory(current);\n const queuedReview = isQueuedReviewMemory(current);\n if (blocked || queuedReview) {\n const currentIdentity = this.offlineSyncMemoryIdentity(current);\n if (!lockIdentities.includes(currentIdentity)) return { retryIdentity: currentIdentity };\n }\n const blockedIndex = blocked ? this.getTombstoneBlockedCaptureIndex() : null;\n let rebuildMarker: string | undefined;\n let durable = false;\n try {\n if (blockedIndex) rebuildMarker = await blockedIndex.prepareWrite();\n const result = await task(current, rebuildMarker, () => {\n durable = true;\n });\n if (!result && rebuildMarker && !durable && blockedIndex) {\n try {\n await blockedIndex.discardWrite(rebuildMarker);\n } catch {\n blockedIndex.markUntrusted();\n }\n }\n return { result };\n } catch (error) {\n if (rebuildMarker && !durable && blockedIndex) {\n try {\n await blockedIndex.discardWrite(rebuildMarker);\n } catch {\n blockedIndex.markUntrusted();\n }\n }\n if (propagateErrors) throw error;\n return { result: false };\n }\n };\n const result = await this.withTombstoneBlockedCaptureWriteLock(attempt, lockIdentities);\n if (result.retryIdentity !== undefined) {\n lockIdentities = [...new Set([...lockIdentities, result.retryIdentity])];\n continue;\n }\n return result.result;\n }\n }\n async deleteMemoryForMaintenance(\n memory: MemoryFile,\n shouldDelete: (current: MemoryFile) => boolean = () => true\n ): Promise<MemoryFile | null> {\n let deleted: MemoryFile | null = null;\n const removed = await this.runTombstoneBlockedInvalidation(\n memory,\n async (current, rebuildMarker, markDurable) => {\n if (!shouldDelete(current)) return false;\n if (!(await this.deleteManagedStorageFile(current.path))) return false;\n markDurable();\n deleted = current;\n const memoryDir = this.tombstoneBlockedCaptureIndexOptions().memoryDir;\n archiveMutation.bumpArchiveMutationForPath(memoryDir, true, current.path);\n markProjectedMemoryPathInvalid(memoryDir, current.frontmatter.id);\n this.invalidateAllMemoriesCache();\n if (current.path.includes(`${path.sep}cold${path.sep}`)) {\n this.invalidateColdMemoriesCache();\n }\n await this.rebuildTombstoneBlockedCaptureAfterInvalidation(rebuildMarker);\n this.bumpMemoryCorpusVersion();\n this.bumpMemoryStatusVersion();\n return true;\n },\n true,\n true\n );\n return removed ? deleted : null;\n }\n\n protected async runTombstoneBlockedOfflineSyncMutation(\n target: string,\n after: MemoryFile | null,\n write: () => Promise<boolean | void>,\n coordinate = false,\n streamedIdentity?: StreamedOfflineSyncIdentity\n ): Promise<void> {\n for (;;) {\n const before = await this.readMemoryByPath(target);\n const targetIdentity = before === null ? undefined : this.offlineSyncMemoryIdentity(before);\n const afterIdentity = after === null || streamedIdentity ? undefined : this.offlineSyncMemoryIdentity(after);\n const mustCoordinate =\n coordinate || targetIdentity !== undefined || afterIdentity !== undefined || streamedIdentity !== undefined;\n const identities = [\n ...(mustCoordinate ? [buildCapturePathLockIdentity(target)] : []),\n ...(targetIdentity === undefined ? [] : [targetIdentity]),\n ...(afterIdentity === undefined ? [] : [afterIdentity]),\n ];\n let retryIdentity: string | undefined;\n const mutate = async (): Promise<void> => {\n const current = await this.readMemoryByPath(target);\n if (current !== null) {\n const currentIdentity = this.offlineSyncMemoryIdentity(current);\n if (currentIdentity !== targetIdentity) {\n retryIdentity = currentIdentity;\n return;\n }\n }\n if (\n this.isTombstoneBlockedMemory(after) &&\n (await this.isDuplicateTombstoneBlockedOfflineSync(target, after, streamedIdentity))\n ) {\n return;\n }\n const affectsBlockedIndex =\n coordinate || this.isTombstoneBlockedMemory(current) || this.isTombstoneBlockedMemory(after);\n const marker = affectsBlockedIndex ? await this.getTombstoneBlockedCaptureIndex().prepareWrite() : undefined;\n let durable = false;\n try {\n const changed = await write();\n durable = true;\n if (marker) {\n try {\n await this.getTombstoneBlockedCaptureIndex().commitWrite(marker);\n } catch (err) {\n this.getTombstoneBlockedCaptureIndex().markUntrusted();\n log.warn(`storage.offlineSyncFile committed write marker failed: ${err}`);\n }\n }\n await this.invalidateAfterOfflineSyncMutation(target, marker, changed !== false);\n } catch (err) {\n if (marker && !durable) {\n try {\n await this.getTombstoneBlockedCaptureIndex().discardWrite(marker);\n } catch (cleanupError) {\n this.getTombstoneBlockedCaptureIndex().markUntrusted();\n log.warn(`storage.offlineSyncFile failed to clear write marker: ${cleanupError}`);\n }\n }\n throw err;\n }\n };\n if (!mustCoordinate) {\n await mutate();\n } else if (streamedIdentity) {\n await this.withTombstoneBlockedCaptureWriteLockAndHashes(mutate, identities, [streamedIdentity.identityHash]);\n } else {\n await this.withTombstoneBlockedCaptureWriteLock(mutate, identities);\n }\n if (retryIdentity !== undefined) continue;\n return;\n }\n }\n\n protected async writeTombstoneBlockedOfflineSyncFile(target: string, content: Buffer): Promise<void> {\n const options = this.tombstoneBlockedCaptureIndexOptions();\n const after = options.parseMemory?.(target, content) ?? null;\n await withRawEntityPageMutation(path.dirname(options.stateDir), target, async () => {\n await this.runTombstoneBlockedOfflineSyncMutation(\n target,\n after,\n () => this.writeManagedStorageFile(target, () => this.writeStorageSecureFile(target, content))\n );\n });\n }\n\n private async prepareTombstoneBlockedOfflineSyncChunks(\n target: string,\n chunks: AsyncIterable<Buffer>\n ): Promise<PreparedOfflineSyncChunks> {\n const parseMemory = this.tombstoneBlockedCaptureIndexOptions().parseMemory;\n if (!parseMemory) return { after: null, chunks };\n\n const iterator = chunks[Symbol.asyncIterator]();\n const buffered: Buffer[] = [];\n let bufferedBytes = 0;\n let firstOverflow: Buffer | undefined;\n for (;;) {\n const next = await iterator.next();\n if (next.done) break;\n if (bufferedBytes + next.value.length > OFFLINE_SYNC_BUFFER_LIMIT_BYTES) {\n firstOverflow = next.value;\n break;\n }\n bufferedBytes += next.value.length;\n buffered.push(next.value);\n }\n const overflow = firstOverflow;\n if (overflow === undefined) {\n let after: MemoryFile | null = null;\n try {\n after = parseMemory(target, Buffer.concat(buffered));\n } catch {\n after = null;\n }\n const replay = (async function* (): AsyncIterable<Buffer> {\n for (const chunk of buffered) yield chunk;\n })();\n return { after, chunks: replay };\n }\n\n const options = this.tombstoneBlockedCaptureIndexOptions();\n const stagePath = path.join(\n options.stateDir,\n \"tombstone-blocked-capture\",\n \"offline-sync-staging\",\n `${randomUUID()}.stage`\n );\n const stagedChunks = (async function* (): AsyncIterable<Buffer> {\n for (const chunk of buffered) yield chunk;\n yield overflow;\n for (;;) {\n const next = await iterator.next();\n if (next.done) break;\n yield next.value;\n }\n })();\n try {\n await writeMaybeEncryptedFileFromChunks(\n stagePath,\n stagedChunks,\n options.secureStoreWriteKeyProvider(),\n { atomic: false },\n options.memoryDir\n );\n return await this.describeStagedOfflineSyncChunks(target, stagePath, parseMemory);\n } catch (err) {\n await unlink(stagePath).catch(() => {});\n throw err;\n }\n }\n\n private async describeStagedOfflineSyncChunks(\n target: string,\n stagePath: string,\n parseMemory: OfflineSyncMemoryParser\n ): Promise<PreparedOfflineSyncChunks> {\n const options = this.tombstoneBlockedCaptureIndexOptions();\n const key = options.secureStoreKeyProvider();\n const bodyOffset = await findStagedBodyOffset(stagePath, key, options.memoryDir);\n if (bodyOffset === null) {\n return { after: null, chunks: this.readStagedOfflineSyncChunks(stagePath), stagePath };\n }\n const prefix = await readStagedPrefix(stagePath, bodyOffset, key, options.memoryDir);\n let after: MemoryFile | null = null;\n try {\n after = parseMemory(target, prefix.subarray(0, bodyOffset));\n } catch {\n after = null;\n }\n if (after === null) {\n return { after: null, chunks: this.readStagedOfflineSyncChunks(stagePath), stagePath };\n }\n const streamedIdentity = await this.computeStagedOfflineSyncIdentity(stagePath, bodyOffset, after);\n return {\n after,\n chunks: this.readStagedOfflineSyncChunks(stagePath),\n stagePath,\n streamedIdentity,\n };\n }\n\n private async computeStagedOfflineSyncIdentity(\n stagePath: string,\n bodyOffset: number,\n after: MemoryFile\n ): Promise<StreamedOfflineSyncIdentity> {\n let normalizedLength = 0;\n const countNormalizer = createExplicitContentNormalizer({\n write: (chunk) => {\n normalizedLength += chunk.length;\n },\n beginConditionalSigma: () => {\n normalizedLength += 1;\n },\n resolveConditionalSigma: () => {},\n });\n const options = this.tombstoneBlockedCaptureIndexOptions();\n const key = options.secureStoreKeyProvider();\n await forEachStagedTextChunk(stagePath, bodyOffset, (text) => countNormalizer.push(text), key, options.memoryDir);\n countNormalizer.finish();\n\n const category = after.frontmatter.category;\n const connector = after.frontmatter.sourceConnector?.trim() ?? \"\";\n const encode = (label: string, value: string): string => `${label} ${value.length} ${value}`;\n const identityPrefix = [\n encode(\"category\", category),\n encode(\"connector\", connector),\n `content ${normalizedLength} `,\n ].join(\" \");\n let identityHash = createHash(\"sha256\");\n let normalSigmaIdentityHash: Hash | undefined;\n identityHash.update(identityPrefix);\n const identityNormalizer = createExplicitContentNormalizer({\n write: (chunk) => {\n identityHash.update(chunk);\n normalSigmaIdentityHash?.update(chunk);\n },\n beginConditionalSigma: () => {\n normalSigmaIdentityHash = identityHash.copy().update(\"σ\");\n identityHash.update(\"ς\");\n },\n resolveConditionalSigma: (useFinalSigma) => {\n if (!useFinalSigma) {\n if (normalSigmaIdentityHash === undefined) throw new Error(\"conditional sigma hash state is missing\");\n identityHash = normalSigmaIdentityHash;\n }\n normalSigmaIdentityHash = undefined;\n },\n });\n await forEachStagedTextChunk(\n stagePath,\n bodyOffset,\n (text) => identityNormalizer.push(text),\n key,\n options.memoryDir\n );\n identityNormalizer.finish();\n return {\n identityHash: identityHash.digest(\"hex\"),\n };\n }\n\n private async *readStagedOfflineSyncChunks(stagePath: string): AsyncIterable<Buffer> {\n const options = this.tombstoneBlockedCaptureIndexOptions();\n for await (const chunk of readMaybeEncryptedFileFromChunks(\n stagePath,\n options.secureStoreKeyProvider(),\n options.memoryDir\n )) {\n yield chunk;\n }\n }\n\n protected async writeTombstoneBlockedOfflineSyncFileChunks(\n target: string,\n chunks: AsyncIterable<Buffer>\n ): Promise<void> {\n const coordinate = this.getTombstoneBlockedCaptureIndex().shouldRebuildAfterInvalidationForPath(target);\n const prepared: PreparedOfflineSyncChunks = coordinate\n ? await this.prepareTombstoneBlockedOfflineSyncChunks(target, chunks)\n : { after: null, chunks };\n const streamedIdentity = prepared.streamedIdentity;\n const baseDir = path.dirname(this.tombstoneBlockedCaptureIndexOptions().stateDir);\n try {\n await withRawEntityPageMutation(baseDir, target, async () => {\n await this.runTombstoneBlockedOfflineSyncMutation(\n target,\n prepared.after,\n () => this.writeManagedStorageFile(target, () => this.writeStorageSecureFileChunks(target, prepared.chunks)),\n coordinate,\n streamedIdentity\n );\n });\n } finally {\n if (prepared.stagePath !== undefined) await unlink(prepared.stagePath).catch(() => {});\n }\n }\n\n async writeOfflineSyncFile(filePath: string, content: Buffer): Promise<void> {\n const target = this.assertManagedStoragePath(filePath, \"storage.writeOfflineSyncFile\");\n await this.writeTombstoneBlockedOfflineSyncFile(target, content);\n await this.requestSyncReconcileIfMigrationPath(target);\n }\n\n async writeOfflineSyncFileChunks(filePath: string, chunks: AsyncIterable<Buffer>): Promise<void> {\n const target = this.assertManagedStoragePath(filePath, \"storage.writeOfflineSyncFileChunks\");\n await this.writeTombstoneBlockedOfflineSyncFileChunks(target, chunks);\n await this.requestSyncReconcileIfMigrationPath(target);\n }\n\n /**\n * Opaque replicated bytes can restore legacy memory references or entity\n * relationship targets. Other sync traffic must not request reconciliation.\n */\n private async requestSyncReconcileIfMigrationPath(target: string): Promise<void> {\n const stateDir = this.tombstoneBlockedCaptureIndexOptions().stateDir;\n if (!pathMayCarryEntityRefs(path.dirname(stateDir), target)) return;\n await requestEntityCanonicalIdReconcile(stateDir);\n }\n\n async deleteOfflineSyncFile(filePath: string, deletionMtimeMs?: number | null): Promise<void> {\n const target = this.assertManagedStoragePath(filePath, \"storage.deleteOfflineSyncFile\");\n await this.deleteTombstoneBlockedOfflineSyncFile(target, deletionMtimeMs);\n await this.requestSyncReconcileIfMigrationPath(target);\n }\n\n protected async deleteTombstoneBlockedOfflineSyncFile(\n target: string,\n deletionMtimeMs?: number | null\n ): Promise<void> {\n const stateDir = this.tombstoneBlockedCaptureIndexOptions().stateDir;\n await withRawEntityPageMutation(path.dirname(stateDir), target, async () => {\n await this.runTombstoneBlockedOfflineSyncMutation(target, null, async () => {\n return await this.deleteManagedStorageFile(target, deletionMtimeMs);\n });\n });\n }\n\n async checkTombstoneBlockedExplicitCapture(\n ...args: [string, string, string?]\n ): Promise<{ has: boolean; authoritative: boolean }> {\n return this.getTombstoneBlockedCaptureIndex().check(...args);\n }\n\n async hasTombstoneBlockedExplicitCapture(...args: [string, string, string?]): Promise<boolean> {\n return this.getTombstoneBlockedCaptureIndex().has(...args);\n }\n\n async isTombstoneBlockedExplicitCaptureIndexAuthoritative(): Promise<boolean> {\n return this.getTombstoneBlockedCaptureIndex().isAuthoritative();\n }\n\n protected async rebuildTombstoneBlockedCaptureAfterInvalidationForPath(\n filePath: string,\n ownedMarker?: string\n ): Promise<void> {\n const index = this.getTombstoneBlockedCaptureIndex();\n if (index.shouldRebuildAfterInvalidationForPath(filePath)) {\n await this.rebuildTombstoneBlockedCaptureAfterInvalidation(ownedMarker);\n }\n }\n protected async rebuildTombstoneBlockedCaptureAfterInvalidation(ownedMarker?: string): Promise<void> {\n await this.getTombstoneBlockedCaptureIndex().rebuildAfterInvalidation(ownedMarker);\n }\n}\n","import { execFile } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, readdir, rename, stat, unlink, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { log } from \"../logger.js\";\nimport type { MemoryFile, MemoryFrontmatter } from \"../types.js\";\nimport { RECALL_FALLBACK_DIRS } from \"../utils/category-dir.js\";\nimport { isErrnoCode } from \"../utils/errno.js\";\nimport type { withHeldFileLock } from \"../utils/serialize-mutations.js\";\nimport {\n CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS,\n ContentHashIndex,\n type ContentHashIndexLockOptions,\n} from \"./content-hash-index.js\";\nimport {\n TombstoneBlockedCaptureWriteLock,\n buildExplicitCaptureDedupKey,\n} from \"./tombstone-blocked-capture-mutation.js\";\nimport type { OfflineSyncMemoryParser } from \"./tombstone-blocked-capture-sync.js\";\nexport {\n buildExplicitCaptureDedupKey,\n isTombstoneBlockedCaptureWriteLockBusy,\n normalizeExplicitCaptureContent,\n} from \"./tombstone-blocked-capture-mutation.js\";\nexport {\n parseTombstoneBlockedOfflineSyncMemory,\n type OfflineSyncMemoryParser,\n TombstoneBlockedCaptureIndexHost,\n} from \"./tombstone-blocked-capture-sync.js\";\n\nconst REBUILD_MAX_ATTEMPTS = 3;\nconst execFileAsync = promisify(execFile);\nconst REBUILD_RETRY_BASE_MS = 50;\n\nconst ABANDONED_MARKER_MIN_AGE_MS = 60_000;\nconst MARKER_PROCESS_START_TOLERANCE_MS = 2_000;\nconst DOTNET_UNIX_EPOCH_TICKS = 621_355_968_000_000_000;\nconst MARKER_PROCESS_STARTED_AT_MS = Date.now() - process.uptime() * 1000;\n\ntype RebuildMarker = {\n path: string;\n committed: boolean;\n pid?: number;\n ownerId?: string;\n createdAt?: number;\n processStartedAtMs?: number;\n malformed?: boolean;\n};\n\nexport type TombstoneBlockedCaptureIndexOptions = {\n readonly stateDir: string;\n readonly memoryDir: string;\n readonly secureStoreKeyProvider: () => Buffer | null;\n readonly secureStoreWriteKeyProvider: () => Buffer | null;\n readonly lockOptions: () => ContentHashIndexLockOptions;\n readonly readAllMemories: () => Promise<MemoryFile[]>;\n readonly readAllColdMemories: () => Promise<MemoryFile[]>;\n readonly parseMemory?: OfflineSyncMemoryParser;\n readonly withHeldFileLock?: typeof withHeldFileLock;\n};\n\n/**\n * Durable targeted deduplication for tombstone-blocked explicit captures.\n *\n * The active fact-hash index intentionally excludes blocked rows. This small\n * companion index keeps the blocked identity visible without making every\n * authoritative active-index miss scan the full corpus.\n */\nexport class TombstoneBlockedCaptureIndex {\n private readonly options: TombstoneBlockedCaptureIndexOptions;\n private index: ContentHashIndex | null = null;\n private loadPromise: Promise<ContentHashIndex> | null = null;\n private refreshPromise: Promise<ContentHashIndex> | null = null;\n private authoritative = false;\n private readonly captureWriteLock: TombstoneBlockedCaptureWriteLock;\n\n constructor(options: TombstoneBlockedCaptureIndexOptions) {\n this.options = options;\n this.captureWriteLock = new TombstoneBlockedCaptureWriteLock(options);\n }\n\n private createIndex(): ContentHashIndex {\n return new ContentHashIndex(\n path.join(this.options.stateDir, \"tombstone-blocked-capture\"),\n this.options.secureStoreKeyProvider,\n this.options.secureStoreWriteKeyProvider,\n this.options.memoryDir,\n this.options.lockOptions()\n );\n }\n\n private indexPath(): string {\n return path.join(this.options.stateDir, \"tombstone-blocked-capture\", \"fact-hashes.txt\");\n }\n\n private rebuildMarkerPath(): string {\n return path.join(this.options.stateDir, \"tombstone-blocked-capture\", \"rebuild-required\");\n }\n\n private async readRebuildMarkers(): Promise<RebuildMarker[]> {\n try {\n const entries = (await readdir(this.rebuildMarkerPath())).filter((entry) => !entry.startsWith(\".\"));\n return (\n await Promise.all(\n entries.map(async (entry) => {\n const markerPath = path.join(this.rebuildMarkerPath(), entry);\n try {\n const raw = (await readFile(markerPath, \"utf8\")).trim();\n if (raw === \"committed\") return { path: markerPath, committed: true };\n let value: Record<string, unknown>;\n let malformed = false;\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n malformed = true;\n value = {};\n } else {\n value = parsed as Record<string, unknown>;\n }\n } catch {\n malformed = true;\n value = {};\n }\n const state = value.state;\n if (state !== \"pending\" && state !== \"committed\") malformed = true;\n const markerStat = await stat(markerPath).catch(() => null);\n // A torn payload can lose its JSON metadata, but the atomic\n // marker filename and filesystem mtime still identify its owner\n // and age. Preserve both so stale malformed markers can be\n // reaped without treating a fresh pre-commit gap as abandoned.\n const createdAt =\n typeof value.createdAt === \"number\" && Number.isFinite(value.createdAt)\n ? value.createdAt\n : markerStat?.mtimeMs;\n if (typeof value.pid !== \"number\" || !Number.isInteger(value.pid)) malformed = true;\n const ownerId =\n typeof value.ownerId === \"string\" && value.ownerId.length > 0\n ? value.ownerId\n : path.basename(markerPath);\n if (typeof value.ownerId !== \"string\" || value.ownerId.length === 0) malformed = true;\n const processStartedAtMs =\n typeof value.processStartedAtMs === \"number\" &&\n Number.isFinite(value.processStartedAtMs) &&\n value.processStartedAtMs > 0\n ? value.processStartedAtMs\n : undefined;\n return {\n path: markerPath,\n committed: state === \"committed\",\n ...(typeof value.pid === \"number\" && Number.isInteger(value.pid) ? { pid: value.pid } : {}),\n ...(ownerId.length > 0 ? { ownerId } : {}),\n ...(typeof createdAt === \"number\" && Number.isFinite(createdAt) ? { createdAt } : {}),\n ...(processStartedAtMs === undefined ? {} : { processStartedAtMs }),\n ...(malformed ? { malformed: true } : {}),\n };\n } catch (err) {\n if (isErrnoCode(err, \"ENOENT\")) return null;\n throw err;\n }\n })\n )\n ).filter((marker): marker is RebuildMarker => marker !== null);\n } catch (err) {\n if (isErrnoCode(err, \"ENOENT\")) return [];\n throw err;\n }\n }\n private async isAbandonedMarker(marker: RebuildMarker): Promise<boolean> {\n if (marker.committed || marker.createdAt === undefined) return false;\n if (Date.now() - marker.createdAt < ABANDONED_MARKER_MIN_AGE_MS) return false;\n if (marker.malformed) return true;\n return marker.pid !== undefined && !(await this.isProcessAlive(marker.pid, marker.processStartedAtMs));\n }\n\n private async isProcessAlive(pid: number, processStartedAtMs?: number): Promise<boolean> {\n try {\n process.kill(pid, 0);\n } catch (err) {\n return !isErrnoCode(err, \"ESRCH\");\n }\n if (processStartedAtMs === undefined) return true;\n const runningStartedAtMs = await this.readProcessStartedAtMs(pid);\n if (runningStartedAtMs === null) return true;\n return runningStartedAtMs <= processStartedAtMs + MARKER_PROCESS_START_TOLERANCE_MS;\n }\n private async readProcessStartedAtMs(pid: number): Promise<number | null> {\n try {\n if (process.platform === \"win32\") {\n const { stdout } = await execFileAsync(\n \"powershell.exe\",\n [\n \"-NoLogo\",\n \"-NoProfile\",\n \"-NonInteractive\",\n \"-Command\",\n `(Get-Process -Id ${pid} -ErrorAction Stop).StartTime.ToUniversalTime().Ticks`,\n ],\n { encoding: \"utf8\", timeout: 1_000 }\n );\n const ticks = Number(stdout.trim());\n return Number.isFinite(ticks) ? (ticks - DOTNET_UNIX_EPOCH_TICKS) / 10_000 : null;\n }\n const { stdout } = await execFileAsync(\"ps\", [\"-p\", String(pid), \"-o\", \"lstart=\"], {\n encoding: \"utf8\",\n timeout: 1_000,\n });\n const output = stdout.trim();\n if (!output) return null;\n const startedAtMs = Date.parse(output);\n return Number.isFinite(startedAtMs) ? startedAtMs : null;\n } catch {\n return null;\n }\n }\n\n private async getClearableRebuildMarkerPaths(markers: readonly RebuildMarker[]): Promise<string[]> {\n const paths = await Promise.all(\n markers.map(async (marker) => (marker.committed || (await this.isAbandonedMarker(marker)) ? marker.path : null))\n );\n return paths.filter((markerPath): markerPath is string => markerPath !== null);\n }\n\n private async getRebuildMarkers(): Promise<RebuildMarker[]> {\n return await this.readRebuildMarkers();\n }\n\n // A pending marker protects the pre-commit gap; only committed markers are safe for another writer to clear.\n private async writeMarkerAtomically(markerPath: string, payload: string): Promise<void> {\n const temporaryPath = `${markerPath}.tmp-${process.pid}-${randomUUID()}`;\n try {\n await writeFile(temporaryPath, payload, \"utf8\");\n await rename(temporaryPath, markerPath);\n } finally {\n await unlink(temporaryPath).catch((err: unknown) => {\n if (!isErrnoCode(err, \"ENOENT\")) throw err;\n });\n }\n }\n\n private async markRebuildRequired(committed = false): Promise<string> {\n const markerDir = this.rebuildMarkerPath();\n await mkdir(markerDir, { recursive: true });\n const ownerId = randomUUID();\n const markerPath = path.join(markerDir, ownerId);\n await this.writeMarkerAtomically(\n markerPath,\n `${JSON.stringify({\n state: committed ? \"committed\" : \"pending\",\n pid: process.pid,\n ownerId,\n createdAt: Date.now(),\n processStartedAtMs: MARKER_PROCESS_STARTED_AT_MS,\n })}\\n`\n );\n return markerPath;\n }\n\n private async markRebuildCommitted(markerPath: string): Promise<void> {\n await this.writeMarkerAtomically(\n markerPath,\n `${JSON.stringify({\n state: \"committed\",\n pid: process.pid,\n ownerId: path.basename(markerPath),\n createdAt: Date.now(),\n processStartedAtMs: MARKER_PROCESS_STARTED_AT_MS,\n })}\\n`\n );\n }\n\n private async clearRebuildRequired(markerPaths: readonly string[]): Promise<void> {\n await Promise.all(\n markerPaths.map(async (markerPath) => {\n try {\n await unlink(markerPath);\n } catch (err) {\n if (!isErrnoCode(err, \"ENOENT\")) throw err;\n }\n })\n );\n }\n\n private async hasRebuildRequired(excludedMarkers: readonly string[] = []): Promise<boolean> {\n const excluded = new Set(excludedMarkers);\n return (await this.getRebuildMarkers()).some((marker) => !excluded.has(marker.path));\n }\n\n private async setAuthoritative(rebuilt: boolean): Promise<void> {\n this.authoritative = rebuilt && !(await this.getRebuildMarkers()).some((marker) => !marker.committed);\n }\n private isBlocked(memory: MemoryFile): boolean {\n return memory.frontmatter.status === \"pending_review\" && Boolean(memory.frontmatter.blockedBy);\n }\n\n private async rebuild(index: ContentHashIndex): Promise<boolean> {\n return await index.rebuildUnderLock(async () => {\n index.clear();\n const existing = [...(await this.options.readAllMemories()), ...(await this.options.readAllColdMemories())];\n for (const memory of existing) {\n if (!this.isBlocked(memory)) continue;\n index.add(\n buildExplicitCaptureDedupKey(memory.content, memory.frontmatter.category, memory.frontmatter.sourceConnector)\n );\n }\n });\n }\n\n private async hasPersistedIndexOrMarkers(): Promise<boolean> {\n try {\n await stat(this.indexPath());\n return true;\n } catch (err) {\n if (!isErrnoCode(err, \"ENOENT\")) throw err;\n return (await this.readRebuildMarkers()).length > 0;\n }\n }\n\n private async getIndex(): Promise<ContentHashIndex> {\n if (this.index) return this.index;\n if (!this.loadPromise) {\n const index = this.createIndex();\n this.loadPromise = (async () => {\n await index.load();\n const rebuildMarkers = await this.getRebuildMarkers();\n const clearableRebuildMarkers = await this.getClearableRebuildMarkerPaths(rebuildMarkers);\n let persisted = !index.requiresFormatMigration;\n try {\n await stat(this.indexPath());\n if (rebuildMarkers.length > 0) persisted = false;\n } catch (err) {\n if (isErrnoCode(err, \"ENOENT\")) persisted = false;\n else throw err;\n }\n if (!persisted) {\n const lockOptions = this.options.lockOptions();\n const maxAttempts = lockOptions.retryMaxAttempts ?? REBUILD_MAX_ATTEMPTS;\n const baseMs = lockOptions.retryBaseMs ?? REBUILD_RETRY_BASE_MS;\n let rebuilt = false;\n for (let attempt = 0; attempt < maxAttempts; attempt += 1) {\n rebuilt = await this.rebuild(index);\n if (rebuilt || attempt === maxAttempts - 1) break;\n const wait = Math.min(baseMs * 2 ** attempt, CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS);\n const delayState = Promise.withResolvers<void>();\n setTimeout(delayState.resolve, wait);\n await delayState.promise;\n }\n if (!rebuilt) {\n throw new Error(\"tombstone-blocked capture index rebuild lock unavailable\");\n }\n await this.clearRebuildRequired(clearableRebuildMarkers);\n }\n await this.setAuthoritative(true);\n this.index = index;\n return index;\n })().catch((err) => {\n this.loadPromise = null;\n throw err;\n });\n }\n return this.loadPromise;\n }\n\n async withCaptureWriteLock<T>(task: () => Promise<T>, identity?: string | readonly string[]): Promise<T> {\n return await this.captureWriteLock.withCaptureWriteLock(task, identity);\n }\n\n async withCaptureWriteLockHashes<T>(task: () => Promise<T>, identityHashes: readonly string[]): Promise<T> {\n return await this.captureWriteLock.withCaptureWriteLockHashes(task, identityHashes);\n }\n async withCaptureWriteLockAndHashes<T>(\n task: () => Promise<T>,\n identity: string | readonly string[] | undefined,\n identityHashes: readonly string[]\n ): Promise<T> {\n return await this.captureWriteLock.withCaptureWriteLockAndHashes(task, identity, identityHashes);\n }\n\n private async reload(): Promise<ContentHashIndex> {\n if (!this.refreshPromise) {\n this.refreshPromise = (async () => {\n this.index = null;\n this.loadPromise = null;\n return await this.getIndex();\n })().finally(() => {\n this.refreshPromise = null;\n });\n }\n return this.refreshPromise;\n }\n\n /** Return blocked-row membership and authority from one index snapshot. */\n async check(\n content: string,\n category: string,\n sourceConnector?: string\n ): Promise<{ has: boolean; authoritative: boolean }> {\n const key = buildExplicitCaptureDedupKey(content, category, sourceConnector);\n let index = await this.getIndex();\n if ((await this.hasRebuildRequired()) || !(await index.isDiskFingerprintCurrent())) {\n index = await this.reload();\n }\n return { has: index.has(key), authoritative: this.authoritative };\n }\n\n /** Return whether a blocked row matches this explicit-capture identity. */\n async has(content: string, category: string, sourceConnector?: string): Promise<boolean> {\n return (await this.check(content, category, sourceConnector)).has;\n }\n\n /** Return whether the in-memory index is safe to answer an authoritative miss. */\n async isAuthoritative(): Promise<boolean> {\n const index = await this.getIndex();\n if ((await this.hasRebuildRequired()) || !(await index.isDiskFingerprintCurrent())) {\n await this.reload();\n }\n return this.authoritative;\n }\n\n /** Reserve durable rebuild intent before a blocked memory file is committed. */\n async prepareWrite(): Promise<string> {\n return this.markRebuildRequired();\n }\n\n /** Mark a prepared write committed once its memory file is durable. */\n async commitWrite(rebuildMarker: string): Promise<void> {\n await this.markRebuildCommitted(rebuildMarker);\n }\n\n /** Drop a pending marker when the protected memory write never commits. */\n async discardWrite(rebuildMarker: string): Promise<void> {\n await this.clearRebuildRequired([rebuildMarker]);\n }\n\n /** Add a newly persisted blocked row to the durable targeted index. */\n async add(memory: MemoryFile, rebuildMarker?: string): Promise<void> {\n if (!this.isBlocked(memory)) return;\n let index = await this.getIndex();\n if (\n (await this.hasRebuildRequired(rebuildMarker ? [rebuildMarker] : [])) ||\n !(await index.isDiskFingerprintCurrent())\n ) {\n index = await this.reload();\n }\n index.add(\n buildExplicitCaptureDedupKey(memory.content, memory.frontmatter.category, memory.frontmatter.sourceConnector)\n );\n const marker = rebuildMarker ?? (await this.markRebuildRequired(true));\n if (rebuildMarker) await this.markRebuildCommitted(rebuildMarker);\n await index.saveMergingWithDisk();\n await index.flushReconcileRetry();\n if (index.hasPendingChanges) {\n this.markUntrusted();\n return;\n }\n await this.clearRebuildRequired([marker]);\n await this.setAuthoritative(true);\n }\n\n /** Rebuild the index after a blocked row changes or is removed. */\n async rebuildIfLoaded(ownedMarker?: string): Promise<void> {\n const index = this.index ?? ((await this.hasPersistedIndexOrMarkers()) ? this.getIndex() : null);\n if (!index) return;\n const rebuildMarkers = await this.getRebuildMarkers();\n const rebuildMarker = await this.markRebuildRequired(true);\n const clearableRebuildMarkers = await this.getClearableRebuildMarkerPaths(rebuildMarkers);\n const rebuilt = await this.rebuild(await index);\n if (rebuilt) {\n await this.clearRebuildRequired([\n ...clearableRebuildMarkers,\n ...(ownedMarker === undefined || clearableRebuildMarkers.includes(ownedMarker) ? [] : [ownedMarker]),\n rebuildMarker,\n ]);\n }\n await this.setAuthoritative(rebuilt);\n }\n\n /** Rebuild when either side of a write is blocked and its identity changed. */\n async sync(before: MemoryFile, after: MemoryFile, rebuildMarker?: string): Promise<void> {\n const beforeBlocked = this.isBlocked(before);\n const afterBlocked = this.isBlocked(after);\n if (!beforeBlocked && !afterBlocked) return;\n const beforeKey = buildExplicitCaptureDedupKey(\n before.content,\n before.frontmatter.category,\n before.frontmatter.sourceConnector\n );\n const afterKey = buildExplicitCaptureDedupKey(\n after.content,\n after.frontmatter.category,\n after.frontmatter.sourceConnector\n );\n if (beforeBlocked && afterBlocked && beforeKey === afterKey) {\n if (rebuildMarker) {\n await this.markRebuildCommitted(rebuildMarker);\n await this.clearRebuildRequired([rebuildMarker]);\n }\n return;\n }\n const existingMarkers = await this.getRebuildMarkers();\n const marker = rebuildMarker ?? (await this.markRebuildRequired(true));\n const clearableExistingMarkers = await this.getClearableRebuildMarkerPaths(existingMarkers);\n if (rebuildMarker) await this.markRebuildCommitted(rebuildMarker);\n const rebuilt = await this.rebuild(await this.getIndex());\n if (rebuilt) {\n await this.clearRebuildRequired([...clearableExistingMarkers, marker]);\n }\n await this.setAuthoritative(rebuilt);\n }\n markUntrusted(): void {\n this.authoritative = false;\n }\n\n private async failOpen(operation: string, action: () => Promise<void>): Promise<void> {\n try {\n await action();\n } catch (err) {\n this.markUntrusted();\n log.warn(`storage.${operation} completed but failed to update tombstone-blocked capture index: ${err}`);\n }\n }\n\n async addWrittenMemory(\n pathname: string,\n frontmatter: MemoryFrontmatter,\n content: string,\n rebuildMarker?: string\n ): Promise<void> {\n await this.failOpen(\"writeMemory\", () => this.add({ path: pathname, frontmatter, content }, rebuildMarker));\n }\n\n async rebuildAfterInvalidation(ownedMarker?: string): Promise<void> {\n await this.failOpen(\"invalidateMemory\", () => this.rebuildIfLoaded(ownedMarker));\n }\n\n async syncUpdatedMemory(\n before: MemoryFile,\n frontmatter: MemoryFrontmatter,\n content: string,\n rebuildMarker?: string\n ): Promise<void> {\n await this.failOpen(\"updateMemory\", () => this.sync(before, { ...before, frontmatter, content }, rebuildMarker));\n }\n\n async syncUpdatedFrontmatter(\n before: MemoryFile,\n frontmatter: MemoryFrontmatter,\n rebuildMarker?: string\n ): Promise<void> {\n await this.failOpen(\"writeMemoryFrontmatter\", () => this.sync(before, { ...before, frontmatter }, rebuildMarker));\n }\n shouldRebuildAfterInvalidationForPath(filePath: string): boolean {\n const relative = path.relative(path.resolve(this.options.memoryDir), path.resolve(filePath));\n const category = relative.split(path.sep)[0];\n return category === \"cold\" || RECALL_FALLBACK_DIRS.includes(category);\n }\n}\n"],"mappings":"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","names":["createHash","randomUUID","unlink","path","mkdir","path","path","stdout","mkdir","path","result","randomUUID","unlink","createHash"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/local-llm.ts"],"sourcesContent":["import { log } from \"./logger.js\";\nimport type { PluginConfig } from \"./types.js\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport type { ModelRegistry } from \"./model-registry.js\";\nimport { launchProcessSync } from \"./runtime/child-process.js\";\nimport { mergeEnv, readEnvVar } from \"./runtime/env.js\";\nimport { resolveLocalLlmCapabilities } from \"./capabilities.js\";\nimport { resolvePipelineProcessingCapabilities } from \"./capabilities.js\";\nimport { ChatTransport } from \"./local-llm-transport.js\";\nimport {\n extractNonRecoverableBackendReason,\n extractNonRecoverableBackendReasonFromErrorText,\n isAbortError,\n probeFetch,\n resolveUnavailableVerdict,\n type ProbeFetchResult,\n normalizeBackendTripReason,\n waitForRetryBackoff,\n SingleFlightProbe,\n} from \"./local-llm-helpers.js\";\n\n/** Trim trailing slash characters without backtracking regex. */\nfunction trimTrailingSlashes(s: string): string {\n let end = s.length;\n while (end > 0 && s[end - 1] === \"/\") end--;\n return s.substring(0, end);\n}\n\nfunction stripTrailingV1Path(s: string): string {\n return s.endsWith(\"/v1\") ? s.slice(0, -3) : s;\n}\n\nfunction explicitPortFromUrl(s: string): number | null {\n try {\n const parsed = new URL(s);\n if (!parsed.port) return null;\n const port = Number(parsed.port);\n return Number.isInteger(port) ? port : null;\n } catch {\n return null;\n }\n}\n\nfunction isObjectRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction isLlamaCppPropsResponse(value: unknown): boolean {\n return (\n isObjectRecord(value) &&\n isObjectRecord(value.default_generation_settings) &&\n typeof value.total_slots === \"number\" &&\n (\n typeof value.model_path === \"string\" ||\n typeof value.chat_template === \"string\" ||\n typeof value.build_info === \"string\"\n )\n );\n}\n\nfunction isLlamaCppModelsResponse(value: unknown): boolean {\n if (!isObjectRecord(value) || !Array.isArray(value.data)) {\n return false;\n }\n return value.data.some((entry) => {\n if (!isObjectRecord(entry)) return false;\n if (entry.owned_by === \"llamacpp\") return true;\n if (typeof entry.id === \"string\" && entry.id.endsWith(\".gguf\")) return true;\n const meta = entry.meta;\n return (\n isObjectRecord(meta) &&\n (\"n_ctx_train\" in meta || \"n_params\" in meta || \"vocab_type\" in meta)\n );\n });\n}\n\nfunction isLmStudioApiV1ModelsResponse(value: unknown): boolean {\n if (!isObjectRecord(value) || !Array.isArray(value.models)) {\n return false;\n }\n return value.models.some((entry) => {\n if (!isObjectRecord(entry)) return false;\n return (\n typeof entry.key === \"string\" &&\n typeof entry.display_name === \"string\" &&\n (\n typeof entry.format === \"string\" ||\n typeof entry.max_context_length === \"number\" ||\n Array.isArray(entry.loaded_instances)\n )\n );\n });\n}\n\nfunction isLmStudioApiV0ModelsResponse(value: unknown): boolean {\n if (!isObjectRecord(value) || !Array.isArray(value.data)) {\n return false;\n }\n return value.data.some((entry) => {\n if (!isObjectRecord(entry)) return false;\n return (\n typeof entry.id === \"string\" &&\n typeof entry.publisher === \"string\" &&\n (\n typeof entry.compatibility_type === \"string\" ||\n typeof entry.max_context_length === \"number\" ||\n typeof entry.state === \"string\"\n )\n );\n });\n}\n\nfunction isLmStudioNativeModelsResponse(value: unknown): boolean {\n return isLmStudioApiV1ModelsResponse(value) || isLmStudioApiV0ModelsResponse(value);\n}\n\n/**\n * Local LLM client for OpenAI-compatible endpoints (LM Studio, Ollama, MLX, etc.)\n *\n * Based on openclaw-tactician's provider detection patterns for consistency.\n * Provides privacy-preserving, cost-effective LLM operations with\n * graceful fallback to cloud providers when local LLM is unavailable.\n */\nexport type LocalLlmType = \"lmstudio\" | \"ollama\" | \"mlx\" | \"vllm\" | \"llamacpp\" | \"generic\";\n\n/**\n * Backends known to honor `chat_template_kwargs: { enable_thinking: false }`\n * on OpenAI-compatible `/v1/chat/completions`. LM Studio, vLLM, and\n * llama.cpp forward this field to the jinja chat template, where thinking-capable\n * models (Qwen 3.5, Gemma 4, DeepSeek) suppress reasoning tokens.\n *\n * Strict OpenAI-compatible backends (standard OpenAI, Azure OpenAI, some\n * proxies) reject unknown request fields with 400 — which trips the\n * `localLlm400*` cooldown path. `LocalLlmClient` therefore only injects\n * the kwarg when the detected backend is in this set; unknown / `generic`\n * / `ollama` / `mlx` fail open (no injection, no 400 risk). Issue #548.\n */\nconst THINKING_COMPATIBLE_BACKENDS: ReadonlySet<LocalLlmType> = new Set([\n \"lmstudio\",\n \"vllm\",\n \"llamacpp\",\n]);\n\nconst THINKING_SUPPRESSED_OPERATIONS: ReadonlySet<string> = new Set([\n \"extraction\",\n \"extraction-judge\",\n \"extraction-faithfulness\",\n \"contradiction-judge\",\n \"contradiction_verification\",\n \"link_suggestion\",\n \"memory_summarization\",\n \"proactive_extraction\",\n \"lcm-summarize\",\n \"day_summary\",\n \"hourly_summary\",\n \"hourly_summary_extended\",\n]);\n\ninterface LocalServerConfig {\n type: LocalLlmType;\n defaultPort: number;\n healthEndpoint: string;\n modelsEndpoint: string;\n detectFn: (response: unknown) => boolean;\n}\n\nconst LOCAL_SERVERS: LocalServerConfig[] = [\n {\n type: \"ollama\",\n defaultPort: 11434,\n healthEndpoint: \"/\",\n modelsEndpoint: \"/api/tags\",\n detectFn: (resp) => typeof resp === \"string\" && resp.includes(\"Ollama\"),\n },\n {\n type: \"llamacpp\",\n defaultPort: 8080,\n healthEndpoint: \"/health\",\n modelsEndpoint: \"/v1/models\",\n detectFn: (resp) => isObjectRecord(resp) && resp.status === \"ok\",\n },\n {\n type: \"mlx\",\n defaultPort: 8080,\n healthEndpoint: \"/v1/models\",\n modelsEndpoint: \"/v1/models\",\n detectFn: (resp) => isObjectRecord(resp) && Array.isArray(resp.data),\n },\n {\n type: \"lmstudio\",\n defaultPort: 1234,\n healthEndpoint: \"/v1/models\",\n modelsEndpoint: \"/v1/models\",\n detectFn: (resp) => isObjectRecord(resp) && Array.isArray(resp.data),\n },\n {\n type: \"vllm\",\n defaultPort: 8000,\n healthEndpoint: \"/health\",\n modelsEndpoint: \"/v1/models\",\n detectFn: (resp) => resp === \"\" || (isObjectRecord(resp) && !(\"status\" in resp)),\n },\n];\n\nfunction orderedLocalServers(configuredBaseUrl: string): LocalServerConfig[] {\n const configuredPort = explicitPortFromUrl(configuredBaseUrl);\n if (configuredPort === null) return LOCAL_SERVERS;\n const matching = LOCAL_SERVERS.filter(\n (serverConfig) => serverConfig.defaultPort === configuredPort,\n );\n if (matching.length === 0) return LOCAL_SERVERS;\n const matchingTypes = new Set(matching.map((serverConfig) => serverConfig.type));\n return [\n ...matching,\n ...LOCAL_SERVERS.filter((serverConfig) => !matchingTypes.has(serverConfig.type)),\n ];\n}\n\nexport interface LocalModelInfo {\n id: string;\n contextWindow?: number;\n maxTokens?: number;\n}\n\nexport type LocalLlmRequestPriority = \"recall-critical\" | \"background\";\n\ninterface LocalLlmChatCompletionOptions {\n temperature?: number;\n maxTokens?: number;\n responseFormat?: { type: string };\n timeoutMs?: number;\n operation?: string;\n forceDisableThinking?: boolean;\n disableThinking?: boolean;\n priority?: LocalLlmRequestPriority;\n signal?: AbortSignal;\n}\n\ninterface LocalLlmQueuedRequest {\n messages: Array<{ role: string; content: string }>;\n options: LocalLlmChatCompletionOptions;\n priority: LocalLlmRequestPriority;\n enqueuedAtMs: number;\n resolve: (value: LocalLlmChatCompletionResult | null) => void;\n}\n\ninterface LocalLlmChatCompletionResult {\n content: string;\n usage?: { promptTokens: number; completionTokens: number; totalTokens: number };\n}\n\nconst LOCAL_LLM_GLOBAL_BACKEND_STATE = \"__openclawEngramLocalLlmBackendState\";\n\ntype LocalLlmBackendState = {\n untilMs: number;\n reason: string;\n};\nexport class LocalLlmClient {\n private config: PluginConfig;\n private isAvailable: boolean | null = null;\n private lastHealthCheck: number = 0;\n private readonly availabilityProbe = new SingleFlightProbe();\n private detectedType: LocalLlmType | null = null;\n private cachedModelInfo: LocalModelInfo | null = null;\n private cachedLmsContext: number | null = null;\n private lastLmsCheck: number = 0;\n private consecutive400s: number = 0;\n private cooldownUntilMs: number = 0;\n private modelRegistry?: ModelRegistry;\n private _disableThinking: boolean = false;\n private readonly requestQueues: Record<LocalLlmRequestPriority, LocalLlmQueuedRequest[]> = {\n \"recall-critical\": [],\n background: [],\n };\n private readonly queueProcessing = new Set<LocalLlmRequestPriority>();\n private queueDrainScheduled: boolean = false;\n private static readonly HEALTH_CHECK_INTERVAL_MS = 60000; // 1 minute\n private static readonly LMS_CACHE_INTERVAL_MS = 30000; // 30 seconds\n private readonly chatTransport = new ChatTransport();\n\n constructor(config: PluginConfig, modelRegistry?: ModelRegistry) {\n this.config = config;\n this.modelRegistry = modelRegistry;\n }\n\n /**\n * Request thinking/reasoning suppression on the next chat completion.\n *\n * When `true`, the client will inject\n * `chat_template_kwargs: { enable_thinking: false }` into the request\n * body — **but only when the detected backend is known to support it**\n * (LM Studio, vLLM; see `THINKING_COMPATIBLE_BACKENDS`). Strict\n * OpenAI-compat backends reject unknown fields with 400; on those the\n * client fails open (thinking runs normally). This is the safe\n * default for Remnic extraction-style operations: measurable latency\n * win on thinking-capable backends, zero risk on others. Consolidation\n * operations keep thinking enabled unless explicitly added to\n * `THINKING_SUPPRESSED_OPERATIONS`. Issues #548 and #979.\n */\n set disableThinking(value: boolean) {\n this._disableThinking = value;\n }\n\n private resolveHomeDir(): string {\n return this.config.localLlmHomeDir || readEnvVar(\"HOME\") || os.homedir();\n }\n\n private buildRequestHeaders(base: Record<string, string> = {}): Record<string, string> {\n const headers: Record<string, string> = {\n ...base,\n ...(this.config.localLlmHeaders ?? {}),\n };\n if (this.config.localLlmApiKey && this.config.localLlmAuthHeader !== false) {\n headers.Authorization = `Bearer ${this.config.localLlmApiKey}`;\n }\n return headers;\n }\n\n\n /**\n * Set the ModelRegistry for caching detected capabilities\n */\n setModelRegistry(registry: ModelRegistry): void {\n this.modelRegistry = registry;\n }\n\n /**\n * Get the detected server type (null if not detected)\n */\n getDetectedType(): LocalLlmType | null {\n return this.detectedType;\n }\n\n private getBackendKey(): string {\n return trimTrailingSlashes(\n this.config.localLlmUrl.replace(\"localhost\", \"127.0.0.1\"),\n ).replace(/\\/v1$/, \"\");\n }\n\n private getGlobalBackendState(): Map<string, LocalLlmBackendState> {\n const globalAny = globalThis as typeof globalThis & {\n [LOCAL_LLM_GLOBAL_BACKEND_STATE]?: Map<string, LocalLlmBackendState>;\n };\n if (!globalAny[LOCAL_LLM_GLOBAL_BACKEND_STATE]) {\n globalAny[LOCAL_LLM_GLOBAL_BACKEND_STATE] = new Map();\n }\n return globalAny[LOCAL_LLM_GLOBAL_BACKEND_STATE];\n }\n\n private getTrippedBackendState(now: number): LocalLlmBackendState | null {\n const state = this.getGlobalBackendState().get(this.getBackendKey()) ?? null;\n if (!state) return null;\n if (state.untilMs <= now) {\n this.getGlobalBackendState().delete(this.getBackendKey());\n this.lastHealthCheck = 0;\n return null;\n }\n return state;\n }\n\n private markBackendUnavailable(reason: string, durationMs: number): void {\n const normalizedReason = normalizeBackendTripReason(reason);\n if (durationMs > 0) {\n const untilMs = Date.now() + durationMs;\n this.getGlobalBackendState().set(this.getBackendKey(), { untilMs, reason: normalizedReason });\n } else {\n this.getGlobalBackendState().delete(this.getBackendKey());\n }\n this.isAvailable = false;\n this.lastHealthCheck = 0;\n log.warn(\n `local LLM backend unavailable for ${durationMs}ms: model=${this.config.localLlmModel} reason=${normalizedReason}`,\n );\n }\n\n\n /**\n * Fetch with timeout for health checks. Body lives in the sibling helper so\n * this file stays under its size ceiling (issue #1995).\n */\n private async fetchWithTimeout(\n url: string,\n timeoutMs: number = 2000,\n headers?: Record<string, string>,\n signal?: AbortSignal,\n ): Promise<ProbeFetchResult> {\n return await probeFetch(url, {\n timeoutMs,\n headers: this.buildRequestHeaders({ Accept: \"application/json\", ...(headers ?? {}) }),\n signal,\n });\n }\n\n private async probeLmStudioNativeModels(\n probeBaseUrl: string,\n signal?: AbortSignal,\n ): Promise<{ matched: boolean; unauthorized: boolean }> {\n let unauthorized = false;\n for (const endpoint of [\"/api/v1/models\", \"/api/v0/models\"]) {\n if (signal?.aborted) return { matched: false, unauthorized };\n const probe = await this.fetchWithTimeout(`${probeBaseUrl}${endpoint}`, 2000, undefined, signal);\n if (signal?.aborted) return { matched: false, unauthorized };\n if (probe.ok && isLmStudioNativeModelsResponse(probe.data)) {\n return { matched: true, unauthorized };\n }\n if (probe.status === 401 || probe.status === 403) {\n unauthorized = true;\n }\n }\n return { matched: false, unauthorized };\n }\n\n /**\n * Check if local LLM is available\n * Uses 127.0.0.1 instead of localhost to avoid DNS issues (consistent with tactician)\n */\n async checkAvailability(signal?: AbortSignal): Promise<boolean> {\n if (signal?.aborted) return false;\n // Cache health check results for 1 minute\n const now = Date.now();\n const trippedState = this.getTrippedBackendState(now);\n if (trippedState) {\n this.isAvailable = false;\n this.lastHealthCheck = 0;\n log.info(\n `local LLM availability: backend circuit open for ${Math.max(0, trippedState.untilMs - now)}ms (${trippedState.reason})`,\n );\n return false;\n }\n if (this.isAvailable !== null && now - this.lastHealthCheck < LocalLlmClient.HEALTH_CHECK_INTERVAL_MS) {\n return this.isAvailable;\n }\n return await this.availabilityProbe.run((probeSignal) => this.probeAvailability(probeSignal), signal);\n }\n\n private async probeAvailability(signal?: AbortSignal): Promise<boolean> {\n const now = Date.now();\n // Probe server-native endpoints from the server root even when users configure\n // the OpenAI-compatible `/v1` base URL for chat completions.\n const configuredBaseUrl = trimTrailingSlashes(\n this.config.localLlmUrl.replace(\"localhost\", \"127.0.0.1\"),\n );\n const probeBaseUrl = stripTrailingV1Path(configuredBaseUrl);\n let sawUnauthorizedProbe = false;\n // A probe that TIMED OUT says the event loop was busy, not that the backend\n // is down (issue #2210). Tracked separately so a loaded daemon cannot cache\n // itself into a blackout.\n let sawAbortedProbe = false;\n\n // Try to detect which server type is running\n if (signal?.aborted) return false;\n for (const serverConfig of orderedLocalServers(configuredBaseUrl)) {\n const healthUrl = `${probeBaseUrl}${serverConfig.healthEndpoint}`;\n log.debug(`checking ${serverConfig.type} at ${healthUrl}`);\n\n const result = await this.fetchWithTimeout(healthUrl, 2000, undefined, signal);\n if (result.aborted) sawAbortedProbe = true;\n if (signal?.aborted) return false;\n if (result.ok && serverConfig.detectFn(result.data)) {\n if (serverConfig.type === \"mlx\") {\n const lmStudioProbe = await this.probeLmStudioNativeModels(probeBaseUrl, signal);\n if (signal?.aborted) return false;\n if (lmStudioProbe.unauthorized) {\n sawUnauthorizedProbe = true;\n }\n if (lmStudioProbe.matched) {\n this.isAvailable = true;\n this.detectedType = \"lmstudio\";\n this.lastHealthCheck = now;\n log.info(`detected lmstudio at ${configuredBaseUrl}`);\n return true;\n }\n }\n if (serverConfig.type === \"llamacpp\") {\n let sawLlamaCppSignal = false;\n const propsProbe = await this.fetchWithTimeout(`${probeBaseUrl}/props`, 2000, undefined, signal);\n if (propsProbe.aborted) sawAbortedProbe = true;\n if (signal?.aborted) return false;\n if (propsProbe.ok && isLlamaCppPropsResponse(propsProbe.data)) {\n sawLlamaCppSignal = true;\n }\n if (propsProbe.status === 401 || propsProbe.status === 403) {\n sawUnauthorizedProbe = true;\n }\n\n const modelsUrl = `${probeBaseUrl}${serverConfig.modelsEndpoint}`;\n const modelsProbe = await this.fetchWithTimeout(modelsUrl, 2000, undefined, signal);\n if (modelsProbe.aborted) sawAbortedProbe = true;\n if (signal?.aborted) return false;\n if (modelsProbe.ok && isLlamaCppModelsResponse(modelsProbe.data)) {\n sawLlamaCppSignal = true;\n }\n if (modelsProbe.status === 401 || modelsProbe.status === 403) {\n sawUnauthorizedProbe = true;\n continue;\n }\n\n const authConfigured =\n Boolean(this.config.localLlmApiKey) &&\n this.config.localLlmAuthHeader !== false;\n if (!sawLlamaCppSignal || (authConfigured && !modelsProbe.ok)) {\n continue;\n }\n }\n this.isAvailable = true;\n this.detectedType = serverConfig.type;\n this.lastHealthCheck = now;\n log.info(`detected ${serverConfig.type} at ${configuredBaseUrl}`);\n return true;\n }\n if (result.status === 401 || result.status === 403) {\n sawUnauthorizedProbe = true;\n }\n }\n if (signal?.aborted) return false;\n\n // Generic check if specific detection failed\n try {\n const modelsUrl = `${probeBaseUrl}/v1/models`;\n const result = await this.fetchWithTimeout(modelsUrl, 2000, undefined, signal);\n if (result.aborted) sawAbortedProbe = true;\n if (signal?.aborted) return false;\n if (result.ok) {\n this.isAvailable = true;\n this.detectedType = \"generic\";\n this.lastHealthCheck = now;\n log.info(`detected generic OpenAI-compatible server at ${configuredBaseUrl}`);\n return true;\n }\n if (result.status === 401 || result.status === 403) {\n sawUnauthorizedProbe = true;\n }\n } catch {\n // Fall through to unavailable\n }\n\n const wasAvailable = this.isAvailable;\n this.detectedType = null;\n if (sawUnauthorizedProbe) {\n log.warn(\n `local LLM availability probe was unauthorized at ${configuredBaseUrl}; verify localLlmApiKey and localLlmAuthHeader settings`,\n );\n }\n const verdict = resolveUnavailableVerdict({\n baseUrl: configuredBaseUrl,\n sawAbortedProbe,\n wasAvailable,\n });\n this.isAvailable = verdict.cacheVerdict ? false : null;\n this.lastHealthCheck = verdict.cacheVerdict ? now : 0;\n if (verdict.warning) log.warn(verdict.warning);\n log.debug(\"local LLM not available at\", configuredBaseUrl);\n return false;\n }\n\n /**\n * Try to get context window from LM Studio settings.json as fallback.\n * This reads the defaultContextLength setting which is what LM Studio uses\n * when loading models without explicit context configuration.\n */\n private getContextFromLmStudioSettings(): number | null {\n try {\n const homeDir = this.resolveHomeDir();\n const settingsPath = `${homeDir}/.cache/lm-studio/settings.json`;\n\n if (!fs.existsSync(settingsPath)) {\n log.debug(`LM Studio settings: file not found at ${settingsPath}`);\n return null;\n }\n\n const content = fs.readFileSync(settingsPath, \"utf-8\");\n const settings = JSON.parse(content) as {\n defaultContextLength?: {\n type?: string;\n value?: number;\n };\n };\n\n if (settings.defaultContextLength?.value) {\n const contextWindow = settings.defaultContextLength.value;\n log.debug(`LM Studio settings: found default context length: ${contextWindow}`);\n return contextWindow;\n }\n\n return null;\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n log.debug(`LM Studio settings: failed to read - ${errorMsg}`);\n return null;\n }\n }\n\n /**\n * Try to get context window from LMS CLI (LM Studio specific).\n * Uses --json flag for reliable parsing.\n * Returns null if LMS CLI is not available or model not found.\n */\n private getContextFromLmsCli(modelId: string): number | null {\n try {\n // Check if lms CLI exists in common locations.\n // HOME may be absent in launchd environments, so prefer the resolved helper.\n const homeDir = this.resolveHomeDir();\n const lmsPaths = [\n this.config.localLmsCliPath || \"\",\n `${homeDir}/.cache/lm-studio/bin/lms`,\n \"/usr/local/bin/lms\",\n \"/opt/homebrew/bin/lms\",\n ];\n\n const lmsPath = lmsPaths.find((p) => p.length > 0 && fs.existsSync(p));\n if (!lmsPath) {\n log.debug(`LMS CLI: not found in standard locations (checked: ${lmsPaths.join(\", \")})`);\n return null;\n }\n\n // Run lms ps --json to get loaded models with context\n // Use spawnSync with shell and explicit PATH to ensure lms can find its dependencies\n log.debug(`LMS CLI: running: ${lmsPath} ps --json`);\n const existingPath = readEnvVar(\"PATH\") || \"\";\n const result = launchProcessSync(lmsPath, [\"ps\", \"--json\"], {\n encoding: \"utf-8\",\n timeout: 5000,\n shell: false, // Don't use shell for JSON output - more reliable\n env: mergeEnv({\n PATH: `${this.config.localLmsBinDir || `${homeDir}/.cache/lm-studio/bin`}:/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:${existingPath}`,\n HOME: homeDir,\n }),\n });\n\n if (result.error) {\n log.debug(`LMS CLI: spawn error - ${result.error.message}`);\n return null;\n }\n\n if (result.stderr && result.stderr.trim()) {\n log.debug(`LMS CLI: stderr - ${result.stderr.slice(0, 200)}`);\n }\n\n const output = result.stdout || \"\";\n if (!output.trim()) {\n log.debug(\"LMS CLI: empty output - LM Studio may not be running or no models loaded\");\n return null;\n }\n\n // Parse JSON output\n let models: Array<{\n identifier?: string;\n modelKey?: string;\n contextLength?: number;\n maxContextLength?: number;\n }>;\n\n try {\n models = JSON.parse(output) as typeof models;\n } catch (parseErr) {\n log.debug(`LMS CLI: JSON parse error - ${parseErr}`);\n return null;\n }\n\n if (!Array.isArray(models) || models.length === 0) {\n log.debug(\"LMS CLI: no models loaded\");\n return null;\n }\n\n // Find the model matching our configured model ID\n const model = models.find((m) =>\n m.identifier === modelId ||\n m.modelKey === modelId ||\n (m.identifier?.includes(modelId.replace(/@\\d+bit$/, \"\")))\n );\n\n if (!model) {\n log.debug(`LMS CLI: model \"${modelId}\" not found in loaded models: ${models.map(m => m.identifier).join(\", \")}`);\n return null;\n }\n\n // Use contextLength (actual configured) or fall back to maxContextLength (model max)\n const contextWindow = model.contextLength || model.maxContextLength;\n\n if (contextWindow) {\n log.info(`LMS CLI detected context window: ${contextWindow} for ${modelId} (max: ${model.maxContextLength})`);\n return contextWindow;\n }\n\n return null;\n } catch (err) {\n // LMS CLI not available or failed\n const errorMsg = err instanceof Error ? err.message : String(err);\n log.debug(`LMS CLI: failed - ${errorMsg}`);\n return null;\n }\n }\n\n /**\n * Get full model info from LMS CLI including context length and max context length.\n * Returns null if LMS CLI is unavailable or model not found.\n */\n private getLmsModelInfo(modelId: string): { contextLength: number; maxContextLength: number; identifier: string } | null {\n try {\n const result = launchProcessSync(\"lms\", [\"ps\", \"--json\"], {\n encoding: \"utf-8\",\n timeout: 5000,\n shell: false,\n });\n\n if (result.error) {\n return null;\n }\n\n const output = result.stdout || \"\";\n if (!output.trim()) {\n return null;\n }\n\n let models: Array<{\n identifier?: string;\n modelKey?: string;\n contextLength?: number;\n maxContextLength?: number;\n }>;\n\n try {\n models = JSON.parse(output) as typeof models;\n } catch {\n return null;\n }\n\n if (!Array.isArray(models) || models.length === 0) {\n return null;\n }\n\n const model = models.find((m) =>\n m.identifier === modelId ||\n m.modelKey === modelId ||\n (m.identifier?.includes(modelId.replace(/@\\d+bit$/, \"\")))\n );\n\n if (!model || !model.contextLength) {\n return null;\n }\n\n return {\n contextLength: model.contextLength,\n maxContextLength: model.maxContextLength || model.contextLength,\n identifier: model.identifier || modelId,\n };\n } catch {\n return null;\n }\n }\n\n /**\n * Get context window for the configured model, using cache if available.\n * This method caches the result to avoid repeated LMS CLI calls.\n * Order: ModelRegistry (persistent) -> memory cache -> LMS CLI -> settings.json\n */\n getCachedContextWindow(modelId: string): number | null {\n const now = Date.now();\n\n // 1. Check ModelRegistry for persisted context window\n if (this.modelRegistry) {\n const caps = this.modelRegistry.getCapabilities(modelId);\n if (caps.source === \"lmstudio\" && caps.contextWindow) {\n log.debug(`ModelRegistry: using persisted LM Studio context: ${caps.contextWindow}`);\n // Also update memory cache\n this.cachedLmsContext = caps.contextWindow;\n this.lastLmsCheck = now;\n return caps.contextWindow;\n }\n }\n\n // 2. Return in-memory cached value if still valid\n if (this.cachedLmsContext && now - this.lastLmsCheck < LocalLlmClient.LMS_CACHE_INTERVAL_MS) {\n log.debug(`LMS CLI: returning in-memory cached context: ${this.cachedLmsContext}`);\n return this.cachedLmsContext;\n }\n\n // 3. Try LMS CLI (authoritative source)\n const lmsInfo = this.getLmsModelInfo(modelId);\n if (lmsInfo?.contextLength) {\n this.cachedLmsContext = lmsInfo.contextLength;\n this.lastLmsCheck = now;\n // Calculate appropriate output tokens based on context size\n // Use 12.5% of context window, capped at 16K (generous but safe)\n const calculatedOutputTokens = Math.min(Math.floor(lmsInfo.contextLength / 8), 16384);\n const outputTokens = Math.max(calculatedOutputTokens, 4096); // Minimum 4K\n // Persist to ModelRegistry with detected capabilities\n if (this.modelRegistry) {\n this.modelRegistry.setCapabilities(modelId, {\n maxPositionEmbeddings: lmsInfo.maxContextLength || lmsInfo.contextLength,\n contextWindow: lmsInfo.contextLength,\n supportsExtendedContext: (lmsInfo.maxContextLength || lmsInfo.contextLength) > 65536,\n typicalOutputTokens: outputTokens,\n source: \"lmstudio\",\n });\n log.info(`LMS CLI: Stored capabilities for ${modelId}: ${lmsInfo.contextLength} context, ${outputTokens} output tokens`);\n }\n return lmsInfo.contextLength;\n }\n\n // Legacy: Try LMS CLI context only (fallback)\n const legacyContext = this.getContextFromLmsCli(modelId);\n if (legacyContext) {\n this.cachedLmsContext = legacyContext;\n this.lastLmsCheck = now;\n // Persist to ModelRegistry with calculated output tokens\n if (this.modelRegistry) {\n const calculatedOutputTokens = Math.min(Math.floor(legacyContext / 8), 16384);\n const outputTokens = Math.max(calculatedOutputTokens, 4096);\n this.modelRegistry.setCapabilities(modelId, {\n maxPositionEmbeddings: legacyContext,\n contextWindow: legacyContext,\n supportsExtendedContext: false,\n typicalOutputTokens: outputTokens,\n source: \"lmstudio\",\n });\n }\n return legacyContext;\n }\n\n // 4. Fall back to LM Studio settings.json\n const settingsContext = this.getContextFromLmStudioSettings();\n if (settingsContext) {\n log.info(`LM Studio settings: using default context: ${settingsContext}`);\n this.cachedLmsContext = settingsContext;\n this.lastLmsCheck = now;\n return settingsContext;\n }\n\n return null;\n }\n\n /**\n * Clear the LMS context cache. Call this when the model changes.\n */\n clearContextCache(): void {\n this.cachedLmsContext = null;\n this.lastLmsCheck = 0;\n log.debug(\"LMS CLI: context cache cleared\");\n }\n\n private remainingCooldownMs(now: number = Date.now()): number {\n return Math.max(0, this.cooldownUntilMs - now);\n }\n\n private scheduleQueueDrain(): void {\n if (this.queueDrainScheduled) return;\n this.queueDrainScheduled = true;\n\n queueMicrotask(() => {\n this.queueDrainScheduled = false;\n this.startAvailableQueuedRequests();\n });\n }\n\n /** True when a background-priority request occupies or awaits this client's local lane (issue #2011). */\n isBackgroundLaneContended(): boolean {\n return this.queueProcessing.has(\"background\") || this.requestQueues.background.length > 0;\n }\n\n private dequeueQueuedRequest(priority: LocalLlmRequestPriority): LocalLlmQueuedRequest | null {\n const next = this.requestQueues[priority].shift();\n return next ?? null;\n }\n\n private failOpenQueuedRequestsForCooldown(): number {\n let dropped = 0;\n for (const priority of [\"recall-critical\", \"background\"] as const) {\n while (this.requestQueues[priority].length > 0) {\n const queued = this.requestQueues[priority].shift();\n queued?.resolve(null);\n dropped += 1;\n }\n }\n return dropped;\n }\n\n private startAvailableQueuedRequests(): void {\n if (!this.queueProcessing.has(\"recall-critical\")) {\n const nextCritical = this.dequeueQueuedRequest(\"recall-critical\");\n if (nextCritical) {\n this.queueProcessing.add(\"recall-critical\");\n void this.runQueuedRequest(nextCritical);\n }\n }\n\n if (!this.queueProcessing.has(\"background\")) {\n const nextBackground = this.dequeueQueuedRequest(\"background\");\n if (nextBackground) {\n this.queueProcessing.add(\"background\");\n void this.runQueuedRequest(nextBackground);\n }\n }\n }\n\n private async runQueuedRequest(next: LocalLlmQueuedRequest): Promise<void> {\n try {\n if (next.options.signal?.aborted) {\n next.resolve(null);\n return;\n }\n const remainingCooldownMs = this.remainingCooldownMs();\n if (remainingCooldownMs > 0) {\n const additionalDropped = this.failOpenQueuedRequestsForCooldown();\n log.warn(\n `local LLM: cooldown active (${remainingCooldownMs}ms remaining), dropping ${additionalDropped + 1} queued request(s) fail-open`,\n );\n next.resolve(null);\n return;\n }\n\n let result: LocalLlmChatCompletionResult | null = null;\n try {\n result = await this.runChatCompletionRequest(next.messages, next.options, {\n priority: next.priority,\n enqueuedAtMs: next.enqueuedAtMs,\n });\n } catch (err) {\n log.warn(`local LLM queue drain failed open: ${err instanceof Error ? err.message : String(err)}`);\n }\n next.resolve(result);\n } finally {\n this.queueProcessing.delete(next.priority);\n if (this.requestQueues[\"recall-critical\"].length > 0 || this.requestQueues.background.length > 0) {\n this.scheduleQueueDrain();\n }\n }\n }\n\n private async runChatCompletionRequest(\n messages: Array<{ role: string; content: string }>,\n options: LocalLlmChatCompletionOptions,\n queueMeta?: { priority: LocalLlmRequestPriority; enqueuedAtMs: number },\n ): Promise<LocalLlmChatCompletionResult | null> {\n log.debug(\n `local LLM chatCompletion: localLlmEnabled=${resolveLocalLlmCapabilities(this.config).localLlm}, model=${this.config.localLlmModel}`,\n );\n\n const operation = options.operation ?? \"unspecified\";\n const startedAtMs = Date.now();\n if (queueMeta) {\n log.debug(\n `local LLM queue start: priority=${queueMeta.priority} waitMs=${startedAtMs - queueMeta.enqueuedAtMs} op=${operation}`,\n );\n }\n\n try {\n if (options.signal?.aborted) return null;\n const isAvailable = await this.checkAvailability(options.signal);\n if (!isAvailable) {\n log.debug(\n `local LLM: checkAvailability returned false for ${this.config.localLlmUrl}`,\n );\n return null;\n }\n\n const promptChars = messages.reduce((sum, m) => sum + (m.content?.length ?? 0), 0);\n const requestBody: Record<string, unknown> = {\n model: this.config.localLlmModel,\n messages,\n temperature: options.temperature ?? 0.7,\n // Use max_tokens consistent with cloud models\n max_tokens: options.maxTokens ?? 4096,\n stream: false,\n };\n\n // Skip response_format for local LLMs - they don't support json_object type\n // The prompts already instruct the model to output JSON\n // Only send if it's json_schema type which some local LLMs support\n if (options.responseFormat?.type === \"json_schema\") {\n requestBody.response_format = options.responseFormat;\n }\n\n // Suppress thinking/reasoning for operations that benefit from terse,\n // structured output. Thinking-capable models (Qwen 3.5, Gemma 4,\n // DeepSeek) default to thinking-on via their chat template; sending\n // `chat_template_kwargs: { enable_thinking: false }` tells the template\n // to skip reasoning tokens. Consolidation operations on the main client\n // intentionally keep thinking enabled so entity resolution and\n // deduplication can use the model's reasoning path. Fast-tier callers can\n // still force suppression per request to preserve their low-latency\n // contract.\n //\n // Gate the injection on detected backend support (issue #548,\n // Codex P1 on PR #550): `chat_template_kwargs` is an LM Studio /\n // vLLM / llama.cpp extension, not part of standard OpenAI chat\n // completions. Strict OpenAI-compatible backends reject\n // unknown fields with 400, which trips the 400-cooldown path and\n // can effectively disable local extraction. Fail open when the\n // backend hasn't been positively identified as thinking-capable.\n const shouldSuppressThinking =\n options.forceDisableThinking === true ||\n ((options.disableThinking ?? this._disableThinking) &&\n THINKING_SUPPRESSED_OPERATIONS.has(operation));\n if (\n shouldSuppressThinking &&\n this.detectedType !== null &&\n THINKING_COMPATIBLE_BACKENDS.has(this.detectedType)\n ) {\n requestBody.chat_template_kwargs = { enable_thinking: false };\n } else if (shouldSuppressThinking && this.detectedType === \"ollama\") {\n // Ollama's /v1 endpoint silently drops `chat_template_kwargs` AND\n // `enable_thinking` — the ONLY field it maps to think-off is\n // `reasoning_effort` (\"none\" → ThinkValue{false} in openai.go;\n // verified against Ollama 0.32.0 source and live probes,\n // issue #1996). Without this, thinking-capable models (Gemma 4,\n // Qwen 3.5+) burn 1,700-2,000 reasoning tokens per extraction on\n // Ollama, exceed the client timeout, and retry forever. The effort\n // is configurable via `localLlmReasoningEffort` (default \"none\");\n // an empty value disables injection (pre-#1996 behavior).\n const effort = this.config.localLlmReasoningEffort;\n if (effort) {\n requestBody.reasoning_effort = effort;\n }\n }\n\n // Normalize URL (use 127.0.0.1 instead of localhost)\n const baseUrl = trimTrailingSlashes(\n this.config.localLlmUrl.replace(\"localhost\", \"127.0.0.1\"),\n );\n const chatUrl = baseUrl.endsWith(\"/v1\")\n ? `${baseUrl}/chat/completions`\n : `${baseUrl}/v1/chat/completions`;\n\n const requestBodyJson = JSON.stringify(requestBody);\n log.debug(\n `local LLM: sending request to ${chatUrl} with model ${this.config.localLlmModel}`,\n );\n // Avoid logging request bodies by default (can contain sensitive user content).\n log.debug(`local LLM: request body length=${requestBodyJson.length}`);\n\n const effectiveTimeoutMs =\n typeof options.timeoutMs === \"number\"\n ? Math.min(this.config.localLlmTimeoutMs, options.timeoutMs)\n : this.config.localLlmTimeoutMs;\n const maxAttempts = 1 + Math.max(0, this.config.localLlmRetry5xxCount);\n let response: Response | null = null, responseBody = \"\";\n let lastAbortError: Error | null = null;\n for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {\n response = null;\n responseBody = \"\";\n const attemptAbort = new AbortController();\n const onCallerAbort = (): void => {\n attemptAbort.abort(options.signal?.reason);\n };\n if (options.signal) {\n if (options.signal.aborted) {\n onCallerAbort();\n } else {\n options.signal.addEventListener(\"abort\", onCallerAbort, { once: true });\n }\n }\n const attemptTimeout = setTimeout(() => attemptAbort.abort(), effectiveTimeoutMs);\n try {\n response = await this.chatTransport.post({\n url: chatUrl,\n headers: this.buildRequestHeaders({\n \"Content-Type\": \"application/json\",\n }),\n body: requestBodyJson,\n signal: attemptAbort.signal,\n budgetMs: this.config.localLlmTimeoutMs,\n });\n responseBody = await response.text();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n if (options.signal?.aborted) {\n response = null;\n lastAbortError = error;\n break;\n }\n if (response && !response.ok) {\n log.debug(`local LLM failed to read ${response.status} response body: ${error.message}`);\n } else {\n response = null;\n if (!isAbortError(err)) throw err;\n lastAbortError = error;\n if (attempt >= maxAttempts) break;\n const backoffMs = this.config.localLlmRetryBackoffMs * attempt;\n log.warn(\n `local LLM request aborted: op=${operation} attempt=${attempt}/${maxAttempts} timeoutMs=${effectiveTimeoutMs} model=${this.config.localLlmModel}; retrying after ${backoffMs}ms`,\n );\n if (!(await waitForRetryBackoff(backoffMs, options.signal))) return null;\n continue;\n }\n } finally {\n clearTimeout(attemptTimeout);\n options.signal?.removeEventListener(\"abort\", onCallerAbort);\n }\n\n if (response.ok) break;\n if (response.status >= 500 && attempt < maxAttempts) {\n const nonRecoverableReason =\n extractNonRecoverableBackendReasonFromErrorText(responseBody);\n if (nonRecoverableReason) {\n this.markBackendUnavailable(\n nonRecoverableReason,\n this.config.localLlm400CooldownMs,\n );\n this.consecutive400s = 0;\n return null;\n }\n }\n if (response.status < 500 || attempt >= maxAttempts) break;\n\n const backoffMs = this.config.localLlmRetryBackoffMs * attempt;\n log.warn(\n `local LLM request got ${response.status}; retrying (attempt ${attempt + 1}/${maxAttempts}) after ${backoffMs}ms`,\n );\n if (!(await waitForRetryBackoff(backoffMs, options.signal))) return null;\n }\n log.debug(\n `local LLM: received response, status=${response?.status}, ok=${response?.ok}`,\n );\n if (!response) {\n if (lastAbortError) {\n log.warn(\n `local LLM request aborted after ${maxAttempts} attempt(s): op=${operation} timeoutMs=${effectiveTimeoutMs} model=${this.config.localLlmModel} promptChars=${promptChars} durationMs=${Date.now() - startedAtMs}`,\n );\n } else {\n log.warn(\n `local LLM request failed: no response object (op=${operation} model=${this.config.localLlmModel} durationMs=${Date.now() - startedAtMs})`,\n );\n }\n return null;\n }\n\n if (!response.ok) {\n let reason = \"\";\n try {\n const parsed = JSON.parse(responseBody) as { error?: { message?: string } };\n reason = parsed?.error?.message ? ` — ${parsed.error.message}` : \"\";\n } catch {\n log.debug(`local LLM error body: ${responseBody.slice(0, 500)}`);\n }\n log.warn(\n `local LLM request failed: ${response.status} ${response.statusText}${reason} ` +\n `(op=${operation}, model=${this.config.localLlmModel}, url=${chatUrl}, promptChars=${promptChars}, maxTokens=${requestBody.max_tokens as number})`,\n );\n const nonRecoverableReason =\n extractNonRecoverableBackendReason(reason) ??\n extractNonRecoverableBackendReasonFromErrorText(responseBody);\n if (nonRecoverableReason) {\n this.markBackendUnavailable(\n nonRecoverableReason,\n this.config.localLlm400CooldownMs,\n );\n this.consecutive400s = 0;\n return null;\n }\n if (response.status === 400) {\n this.consecutive400s += 1;\n if (this.consecutive400s >= this.config.localLlm400TripThreshold) {\n this.cooldownUntilMs = Date.now() + this.config.localLlm400CooldownMs;\n log.warn(\n `local LLM: entering cooldown for ${this.config.localLlm400CooldownMs}ms ` +\n `after ${this.consecutive400s} consecutive 400 responses`,\n );\n this.consecutive400s = 0;\n }\n } else {\n this.consecutive400s = 0;\n }\n return null;\n }\n this.consecutive400s = 0;\n\n const data = JSON.parse(responseBody) as {\n choices?: Array<{\n message?: { content?: string; reasoning_content?: string };\n }>;\n usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n };\n };\n\n log.debug(\n `local LLM response: choices=${data.choices?.length}, usage=${JSON.stringify(data.usage)}`,\n );\n\n // Thinking models (e.g. Qwen 3.5) may put their response in\n // `reasoning_content` and leave `content` empty. Fall back to\n // reasoning_content so engram still gets a usable result.\n const msg = data.choices?.[0]?.message;\n const content = msg?.content || msg?.reasoning_content || \"\";\n if (!content) {\n log.warn(`local LLM returned empty content. choices=${JSON.stringify(data.choices)?.slice(0, 200)}`);\n return null;\n }\n\n // Estimate tokens if not provided by local LLM\n const usage = data.usage\n ? {\n promptTokens: data.usage.prompt_tokens ?? 0,\n completionTokens: data.usage.completion_tokens ?? 0,\n totalTokens: data.usage.total_tokens ?? 0,\n }\n : this.estimateTokens(messages, content);\n\n const durationMs = Date.now() - startedAtMs;\n if (resolvePipelineProcessingCapabilities(this.config).slowLog && durationMs >= this.config.slowLogThresholdMs) {\n const promptChars = messages.reduce((sum, m) => sum + (m.content?.length ?? 0), 0);\n const op = options.operation ? ` op=${options.operation}` : \"\";\n log.warn(\n `SLOW local LLM:${op} durationMs=${durationMs} model=${this.config.localLlmModel} url=${chatUrl} promptChars=${promptChars} outputTokens=${usage.completionTokens} totalTokens=${usage.totalTokens}`,\n );\n }\n\n log.debug(\"local LLM: request succeeded, tokens:\", usage.totalTokens);\n return { content, usage };\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n const durationMs = Date.now() - startedAtMs;\n if (isAbortError(err)) {\n log.warn(\n `local LLM request aborted: op=${operation} timeoutMs=${options.timeoutMs ?? this.config.localLlmTimeoutMs} model=${this.config.localLlmModel} durationMs=${durationMs} error=${errMsg}`,\n );\n return null;\n }\n log.warn(`local LLM request error: op=${operation} error=${errMsg}`);\n this.isAvailable = false; // Mark as unavailable on non-abort errors\n const nonRecoverableReason = extractNonRecoverableBackendReason(errMsg);\n if (nonRecoverableReason) {\n this.markBackendUnavailable(\n nonRecoverableReason,\n this.config.localLlm400CooldownMs,\n );\n }\n return null;\n } finally {\n if (queueMeta) {\n const finishedAtMs = Date.now();\n const waitMs = startedAtMs - queueMeta.enqueuedAtMs;\n log.debug(\n `local LLM queue finish: priority=${queueMeta.priority} waitMs=${waitMs} runMs=${finishedAtMs - startedAtMs} totalMs=${finishedAtMs - queueMeta.enqueuedAtMs} op=${operation}`,\n );\n }\n }\n }\n\n /**\n * Query the local LLM server for loaded model information.\n * Returns null if unavailable or if the model is not found.\n */\n async getLoadedModelInfo(): Promise<LocalModelInfo | null> {\n const baseUrl = trimTrailingSlashes(\n this.config.localLlmUrl.replace(\"localhost\", \"127.0.0.1\"),\n );\n\n // Handle URL construction - localLlmUrl may already include /v1\n const modelsUrl = baseUrl.endsWith(\"/v1\")\n ? `${baseUrl}/models`\n : `${baseUrl}/v1/models`;\n log.debug(`Fetching model info from ${modelsUrl}`);\n\n try {\n const result = await this.fetchWithTimeout(modelsUrl, 3000);\n if (!result.ok) {\n if (result.status === 401 || result.status === 403) {\n log.warn(\n `Local LLM: unauthorized while fetching models from ${modelsUrl}; verify localLlmApiKey and localLlmAuthHeader settings`,\n );\n }\n log.warn(`Local LLM: Failed to fetch models from ${modelsUrl} - server returned error`);\n return null;\n }\n if (!result.data) {\n log.warn(`Local LLM: No data returned from ${modelsUrl}`);\n return null;\n }\n\n const data = result.data as {\n data?: Array<{\n id?: string;\n object?: string;\n owned_by?: string;\n // LM Studio specific fields\n max_context_length?: number;\n max_tokens?: number;\n // Ollama specific\n name?: string;\n details?: {\n parameter_size?: string;\n family?: string;\n };\n }>;\n };\n\n if (!Array.isArray(data.data) || data.data.length === 0) {\n log.warn(\"Local LLM returned no models\");\n return null;\n }\n\n // Verbose model listings are noisy on every gateway restart. Keep it debug-only.\n const modelIds = data.data.map((m) => m.id).filter(Boolean);\n log.debug(\n `Local LLM: Found ${modelIds.length} model(s). First 10: ${modelIds.slice(0, 10).join(\", \")}`,\n );\n\n // Find the model matching our configured model ID\n const configuredModel = this.config.localLlmModel;\n let model = data.data.find((m) => m.id === configuredModel);\n\n // If not found by exact match, try partial match (handle suffixes like @4bit)\n if (!model) {\n model = data.data.find((m) =>\n configuredModel.includes(m.id || \"\") ||\n (m.id || \"\").includes(configuredModel.replace(/@\\d+bit$/, \"\"))\n );\n }\n\n // If still not found, use the first loaded model and warn\n if (!model) {\n model = data.data[0];\n const availablePreview = data.data\n .map((m) => m.id)\n .filter(Boolean)\n .slice(0, 10)\n .join(\", \");\n log.warn(\n `Configured model \"${configuredModel}\" not found in local LLM. ` +\n `Using \"${model.id}\" instead. Available (first 10): ${availablePreview}`\n );\n }\n\n // Extract context window - try multiple field names\n let contextWindow = model.max_context_length || model.max_tokens;\n\n // If API doesn't report context window, try LMS CLI (LM Studio specific)\n if (!contextWindow) {\n log.info(\"Local LLM: API did not report context window, trying LMS CLI...\");\n const lmsContext = this.getCachedContextWindow(model.id || \"\");\n if (lmsContext) {\n contextWindow = lmsContext;\n }\n }\n\n this.cachedModelInfo = {\n id: model.id || \"unknown\",\n contextWindow: contextWindow,\n maxTokens: model.max_tokens,\n };\n\n log.info(\n `Local LLM model detected: ${this.cachedModelInfo.id}, ` +\n `context window: ${contextWindow?.toLocaleString() || \"unknown (may use default)\"}`\n );\n\n return this.cachedModelInfo;\n } catch (err) {\n log.warn(`Failed to fetch model info: ${err}`);\n return null;\n }\n }\n\n /**\n * Check if the configured model is available and get its actual context window.\n * Warns if there's a mismatch between expected and actual context.\n */\n async validateModelConfig(expectedContextWindow?: number): Promise<{\n available: boolean;\n actualContextWindow?: number;\n warnings: string[];\n }> {\n const warnings: string[] = [];\n\n const modelInfo = await this.getLoadedModelInfo();\n if (!modelInfo) {\n return { available: false, warnings: [\"Could not query local LLM for model info\"] };\n }\n\n // If we have expected context and the server reports one, check for mismatch\n if (expectedContextWindow && modelInfo.contextWindow) {\n if (modelInfo.contextWindow < expectedContextWindow) {\n warnings.push(\n `Context window mismatch: Model ${modelInfo.id} supports ${modelInfo.contextWindow.toLocaleString()} tokens, ` +\n `but engram is configured for ${expectedContextWindow.toLocaleString()}. ` +\n `Set localLlmMaxContext: ${modelInfo.contextWindow} in config to avoid errors.`\n );\n }\n }\n\n // Warn if server doesn't report context window (common with some local LLM setups)\n if (!modelInfo.contextWindow) {\n warnings.push(\n `Local LLM server did not report context window for ${modelInfo.id}. ` +\n `If you get \"context length exceeded\" errors, set localLlmMaxContext in config.`\n );\n }\n\n return {\n available: true,\n actualContextWindow: modelInfo.contextWindow,\n warnings,\n };\n }\n\n /**\n * Make a chat completion request to local LLM\n */\n async chatCompletion(\n messages: Array<{ role: string; content: string }>,\n options: LocalLlmChatCompletionOptions = {},\n ): Promise<LocalLlmChatCompletionResult | null> {\n if (!resolveLocalLlmCapabilities(this.config).localLlm) {\n log.debug(\"local LLM: disabled, returning null\");\n return null;\n }\n\n const remainingMs = this.remainingCooldownMs();\n if (remainingMs > 0) {\n log.debug(`local LLM: cooldown active (${remainingMs}ms remaining), skipping request`);\n return null;\n }\n if (options.priority) {\n const priority = options.priority;\n return await new Promise<LocalLlmChatCompletionResult | null>((resolve) => {\n this.requestQueues[priority].push({\n messages,\n options,\n priority,\n enqueuedAtMs: Date.now(),\n resolve,\n });\n this.scheduleQueueDrain();\n });\n }\n\n return await this.runChatCompletionRequest(messages, options);\n }\n\n /**\n * Estimate tokens when local LLM doesn't return usage stats\n * Rough estimate: 1 token ≈ 4 characters\n */\n private estimateTokens(\n messages: Array<{ role: string; content: string }>,\n response: string\n ): { promptTokens: number; completionTokens: number; totalTokens: number } {\n const promptChars = messages.reduce((sum, m) => sum + m.content.length, 0);\n const promptTokens = Math.ceil(promptChars / 4);\n const completionTokens = Math.ceil(response.length / 4);\n\n return {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n };\n }\n\n /**\n * Try local LLM first, fallback to cloud provider if configured\n */\n async withFallback<T>(\n localOperation: () => Promise<T | null>,\n fallbackOperation: () => Promise<T>,\n operationName: string\n ): Promise<T> {\n // Try local LLM first if enabled\n if (resolveLocalLlmCapabilities(this.config).localLlm) {\n const localResult = await localOperation();\n if (localResult !== null) {\n log.debug(`${operationName}: used local LLM`);\n return localResult;\n }\n\n // Local failed or unavailable\n if (this.config.localLlmFallback) {\n log.info(`${operationName}: local LLM unavailable, falling back to cloud`);\n } else {\n throw new Error(`${operationName}: local LLM unavailable and fallback disabled`);\n }\n }\n\n // Use fallback (cloud provider)\n return fallbackOperation();\n }\n}\n"],"mappings":"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","names":["backoffMs","promptChars"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/write-rate-limiter.ts"],"sourcesContent":["/**\n * Per-principal write rate limiter for the access HTTP surface\n * (issues #1937 / #2029).\n *\n * Extracted from access-http.ts so the limiter is a cohesive unit. Each\n * principal (authenticated caller identity) gets its own rolling-window\n * bucket, so one chatty connector cannot starve writes for the others;\n * requests with no resolved principal share a single global bucket.\n *\n * Deliberately transport-agnostic: it never throws `HttpError` — callers\n * translate a refusal into their own 429. It logs a sampled warning when it\n * refuses a write, so a sustained limit condition is diagnosable server-side\n * (previously the refusal was a bare throw with no trace).\n */\n\nimport { log } from \"./logger.js\";\n\n// Defaults; overridable per instance via the constructor (config/env feed these\n// through the server's `writeRateLimit*` settings).\nconst DEFAULT_WINDOW_MS = 60_000;\nconst DEFAULT_MAX_REQUESTS = 30;\n// At most one rejection warning per this interval, carrying a rejected-count,\n// so a storm is visible without flooding the log.\nconst LOG_INTERVAL_MS = 10_000;\n// Bucket key for requests with no resolved principal.\nconst GLOBAL_BUCKET = \"\";\n\ntype Slot = { readonly recordedAt: number };\n\nfunction sanitizePositiveInteger(value: number | undefined, fallback: number): number {\n return typeof value === \"number\" && Number.isInteger(value) && value >= 1 ? value : fallback;\n}\n\nexport class WriteRateLimiter {\n readonly maxRequests: number;\n readonly windowMs: number;\n // One rolling window per principal. Empty buckets are pruned so the map does\n // not grow without bound.\n private readonly buckets = new Map<string, Slot[]>();\n private lastLogAt = 0;\n private suppressed = 0;\n\n constructor(maxRequests?: number, windowMs?: number) {\n this.maxRequests = sanitizePositiveInteger(maxRequests, DEFAULT_MAX_REQUESTS);\n this.windowMs = sanitizePositiveInteger(windowMs, DEFAULT_WINDOW_MS);\n }\n\n private key(principal?: string): string {\n return principal?.trim() ? principal.trim() : GLOBAL_BUCKET;\n }\n\n private prune(slots: Slot[], now: number): void {\n while (slots.length > 0 && now - (slots[0]?.recordedAt ?? 0) >= this.windowMs) {\n slots.shift();\n }\n }\n\n /** Prune every bucket to the current window; drop buckets that go empty. */\n private sweep(now: number): void {\n for (const [key, slots] of this.buckets) {\n this.prune(slots, now);\n if (slots.length === 0) this.buckets.delete(key);\n }\n }\n\n /** Live reservation/record count for a principal (test/introspection). */\n slotsFor(principal?: string): ReadonlyArray<Slot> {\n return this.buckets.get(this.key(principal)) ?? [];\n }\n\n /** Total live reservations across all principals (test/introspection). */\n totalSlots(): number {\n this.sweep(Date.now());\n let total = 0;\n for (const slots of this.buckets.values()) total += slots.length;\n return total;\n }\n\n /** True if a write may proceed for the principal. Logs (sampled) when not. */\n hasCapacity(principal?: string): boolean {\n this.sweep(Date.now());\n const key = this.key(principal);\n const slots = this.buckets.get(key);\n if (slots && slots.length >= this.maxRequests) {\n this.logRejected(key);\n return false;\n }\n return true;\n }\n\n /** Record a committed write against the principal's rolling window. */\n record(principal?: string): void {\n this.sweep(Date.now());\n const key = this.key(principal);\n const slots = this.buckets.get(key) ?? this.buckets.set(key, []).get(key)!;\n slots.push({ recordedAt: Date.now() });\n }\n\n /**\n * Reserve a slot for an in-flight write. Returns a release function, or\n * `null` when the principal is already at the limit (caller raises its 429).\n */\n reserve(principal?: string): (() => void) | null {\n if (!this.hasCapacity(principal)) return null;\n const key = this.key(principal);\n const slots = this.buckets.get(key) ?? this.buckets.set(key, []).get(key)!;\n const slot: Slot = { recordedAt: Date.now() };\n slots.push(slot);\n let active = true;\n return () => {\n if (!active) return;\n active = false;\n const current = this.buckets.get(key);\n if (!current) return;\n const index = current.indexOf(slot);\n if (index >= 0) current.splice(index, 1);\n if (current.length === 0) this.buckets.delete(key);\n };\n }\n\n private logRejected(principal: string): void {\n const now = Date.now();\n this.suppressed += 1;\n if (now - this.lastLogAt < LOG_INTERVAL_MS) return;\n const rejected = this.suppressed;\n this.suppressed = 0;\n this.lastLogAt = now;\n const who = principal === GLOBAL_BUCKET ? \"(no principal)\" : principal;\n log.warn(\n `write_rate_limited: rejected ${rejected} write(s) for principal ${who}; ` +\n `per-principal limit is ${this.maxRequests} per ${this.windowMs}ms. Raise ` +\n `server.writeRateLimitMaxRequests (or REMNIC_WRITE_RATE_LIMIT_MAX_REQUESTS) if sustained.`,\n );\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/connectors/codex-materialize-runner.ts","../src/namespaces/path.ts"],"sourcesContent":["/**\n * codex-materialize-runner.ts — Thin I/O bridge for the Codex materializer.\n *\n * The pure rendering logic lives in {@link ./codex-materialize.js}. This file\n * is the place callers (consolidation hooks, CLI, session-end hook) go when\n * they want the whole \"load memories from storage → render → write\" flow.\n *\n * Kept deliberately small so #378 never has to reach into orchestrator.ts /\n * importance.ts — the two files Wave 1 agents are editing concurrently.\n */\n\nimport { existsSync } from \"node:fs\";\n\nimport { log } from \"../logger.js\";\nimport { resolveNamespaceChildRoot } from \"../namespaces/path.js\";\nimport { isSafeRouteNamespace } from \"../routing/engine.js\";\nimport { StorageManager } from \"../storage.js\";\nimport type { PluginConfig, MemoryFile } from \"../types.js\";\nimport {\n hasCodexMaterializeSentinel,\n materializeForNamespace,\n type MaterializeResult,\n type RolloutSummaryInput,\n} from \"./codex-materialize.js\";\n\n/** Options accepted by the shared post-consolidation materialize helper. */\nexport interface PostConsolidationMaterializeOptions {\n config: PluginConfig;\n namespace?: string;\n memories?: MemoryFile[];\n memoryDir?: string;\n codexHome?: string;\n rolloutSummaries?: RolloutSummaryInput[];\n now?: Date;\n}\n\n/** Options accepted by the runner. */\nexport interface RunMaterializeOptions {\n /** Remnic config — we only read the `codexMaterialize*` fields. */\n config: PluginConfig;\n /** Namespace to materialize. Overrides the config's `codexMaterializeNamespace`. */\n namespace?: string;\n /** Override the memory directory (defaults to `config.memoryDir`). */\n memoryDir?: string;\n /** Override `<codex_home>` (useful for tests). */\n codexHome?: string;\n /** Optional pre-loaded memories (bypasses disk read — used in tests). */\n memories?: MemoryFile[];\n /** Optional rollout summaries supplied by the caller. */\n rolloutSummaries?: RolloutSummaryInput[];\n /** Current time injection for deterministic runs. */\n now?: Date;\n /** Reason string — logged for observability. */\n reason?: \"consolidation\" | \"session_end\" | \"manual\" | \"cli\";\n}\n\n/**\n * Run the Codex materialization end-to-end. Returns `null` when the feature\n * is disabled in config or when the user hasn't opted in via the sentinel.\n * Never throws for \"expected\" skips; only throws on schema validation or I/O\n * errors that callers actually need to surface.\n */\nexport async function runCodexMaterialize(\n options: RunMaterializeOptions,\n): Promise<MaterializeResult | null> {\n const cfg = options.config;\n if (!cfg.codexMaterializeMemories) {\n log.debug(`[codex-materialize] skipped — codexMaterializeMemories=false`);\n return null;\n }\n\n // Per-trigger gate: session-end runs must honor codexMaterializeOnSessionEnd.\n // The Codex Stop hook (remnic-codex-hook.cjs, session-end event) passes\n // reason=\"session_end\"; when the user has turned off the session-end trigger\n // we short-circuit here without touching disk.\n if (options.reason === \"session_end\" && cfg.codexMaterializeOnSessionEnd === false) {\n log.debug(\n `[codex-materialize] skipped — session-end disabled via codexMaterializeOnSessionEnd=false`,\n );\n return null;\n }\n\n const namespace = resolveNamespace(options.namespace, cfg);\n const memoryDir = options.memoryDir ?? cfg.memoryDir;\n const codexHome = options.codexHome ?? cfg.codex?.codexHome ?? undefined;\n if (!memoryDir) {\n log.warn(`[codex-materialize] skipped — no memoryDir available`);\n return null;\n }\n\n let memories: MemoryFile[];\n if (options.memories) {\n memories = options.memories;\n } else {\n if (!hasCodexMaterializeSentinel(codexHome)) {\n return materializeForNamespace(namespace, {\n memories: [],\n codexHome,\n maxSummaryTokens: cfg.codexMaterializeMaxSummaryTokens,\n rolloutRetentionDays: cfg.codexMaterializeRolloutRetentionDays,\n rolloutSummaries: options.rolloutSummaries,\n now: options.now,\n });\n }\n const nsDir = resolveNamespaceDir(memoryDir, namespace, cfg);\n const storage = new StorageManager(nsDir);\n memories = await storage.readAllMemories();\n }\n\n // Intentionally NOT catching here: per the JSDoc contract above,\n // schema-validation and I/O errors from `materializeForNamespace` must\n // surface to callers so they can exit non-zero (CLI) or log + recover\n // (consolidation post-hook, which has its own narrower try/catch).\n // Catching everything would make a broken MEMORY.md render look like a\n // successful skip, and invisible failures are strictly worse than loud\n // ones for a feature that writes to `~/.codex/memories`.\n const result = materializeForNamespace(namespace, {\n memories,\n codexHome,\n maxSummaryTokens: cfg.codexMaterializeMaxSummaryTokens,\n rolloutRetentionDays: cfg.codexMaterializeRolloutRetentionDays,\n rolloutSummaries: options.rolloutSummaries,\n now: options.now,\n });\n if (options.reason) {\n log.debug(\n `[codex-materialize] ran reason=${options.reason} wrote=${result.wrote} files=${result.filesWritten.length}`,\n );\n }\n return result;\n}\n\n/**\n * Shared helper for post-consolidation materialize hooks.\n *\n * `materializeAfterSemanticConsolidation` and `materializeAfterCausalConsolidation`\n * used to be two nearly-identical copies of this logic; keeping the actual\n * body here means any future guard/logging change happens in one place.\n *\n * The only per-caller knob is `logPrefix`, which is used to tag the\n * non-fatal warning emitted when the materializer throws.\n */\nexport async function runPostConsolidationMaterialize(\n logPrefix: string,\n options: PostConsolidationMaterializeOptions,\n): Promise<MaterializeResult | null> {\n if (!options.config.codexMaterializeMemories) return null;\n if (!options.config.codexMaterializeOnConsolidation) return null;\n try {\n return await runCodexMaterialize({\n config: options.config,\n namespace: options.namespace,\n memories: options.memories,\n memoryDir: options.memoryDir,\n codexHome: options.codexHome,\n rolloutSummaries: options.rolloutSummaries,\n now: options.now,\n reason: \"consolidation\",\n });\n } catch (error) {\n log.warn(\n `${logPrefix} Codex materialize post-hook failed (non-fatal): ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return null;\n }\n}\n\nfunction resolveNamespace(override: string | undefined, cfg: PluginConfig): string {\n const requested = (override ?? cfg.codexMaterializeNamespace ?? \"auto\").trim();\n const defaultNamespace = (cfg.defaultNamespace ?? \"\").trim();\n const namespace =\n requested.length === 0 || requested === \"auto\"\n ? (defaultNamespace.length > 0 ? defaultNamespace : \"default\")\n : requested;\n if (!isSafeRouteNamespace(namespace)) {\n throw new Error(`invalid materialize namespace: ${namespace}`);\n }\n return namespace;\n}\n\n/**\n * Resolve the on-disk storage root for a namespace, matching\n * `NamespaceStorageRouter` in `packages/remnic-core/src/namespaces/storage.ts`.\n *\n * Contract:\n * - When namespaces are disabled, every namespace maps to `memoryDir` itself.\n * - When namespaces are enabled, non-default namespaces always live under\n * `memoryDir/namespaces/<namespace>`.\n * - The default namespace prefers `memoryDir/namespaces/<defaultNamespace>`\n * when that directory already exists (migrated install); otherwise it\n * falls back to the legacy `memoryDir` root so materialization does not\n * silently switch directories out from under an existing install.\n */\nfunction resolveNamespaceDir(\n memoryDir: string,\n namespace: string,\n cfg: PluginConfig,\n): string {\n if (!cfg.namespacesEnabled) return memoryDir;\n\n const configuredDefaultNamespace = (cfg.defaultNamespace ?? \"\").trim();\n const defaultNamespace =\n configuredDefaultNamespace.length > 0 ? configuredDefaultNamespace : \"default\";\n const ns = (namespace || defaultNamespace).trim();\n if (!isSafeRouteNamespace(ns)) {\n throw new Error(`invalid materialize namespace: ${ns}`);\n }\n const namespacedRoot = resolveNamespaceChildRoot(memoryDir, ns, \"materialize namespace path\");\n\n if (ns === defaultNamespace) {\n return existsSync(namespacedRoot) ? namespacedRoot : memoryDir;\n }\n return namespacedRoot;\n}\n","import path from \"node:path\";\n\nexport function assertNamespacePathInsideRoot(\n root: string,\n candidate: string,\n namespace: string,\n label = \"namespace path\",\n): void {\n const relative = path.relative(root, candidate);\n if (relative === \"\" || (!relative.startsWith(\"..\") && !path.isAbsolute(relative))) {\n return;\n }\n throw new Error(`invalid ${label}: ${namespace}`);\n}\n\nexport function resolveNamespaceChildRoot(\n memoryDir: string,\n namespace: string,\n label = \"namespace path\",\n): string {\n const namespaceRoot = path.resolve(memoryDir, \"namespaces\");\n const candidate = path.resolve(namespaceRoot, namespace);\n assertNamespacePathInsideRoot(namespaceRoot, candidate, namespace, label);\n return candidate;\n}\n"],"mappings":"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","names":[]}
|