@remnic/core 9.23.3 → 9.24.0
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 +13 -10
- package/dist/access-admin-ops-surface.js +50 -46
- package/dist/access-boundary.d.ts +13 -10
- package/dist/access-boundary.js +50 -46
- package/dist/access-cli.js +125 -122
- package/dist/access-cli.js.map +1 -1
- package/dist/access-health-types.d.ts +54 -0
- package/dist/access-health-types.js +1 -0
- package/dist/access-health-types.js.map +1 -0
- package/dist/access-http.d.ts +14 -11
- package/dist/access-http.js +59 -55
- package/dist/access-identity-continuity-surface.d.ts +9 -8
- package/dist/access-identity-continuity-surface.js +50 -46
- package/dist/access-lcm-surface.d.ts +13 -10
- package/dist/access-lcm-surface.js +50 -46
- package/dist/access-mcp.d.ts +13 -10
- package/dist/access-mcp.js +55 -51
- package/dist/access-memory-search-fanout.d.ts +3 -2
- package/dist/{access-namespace-preflight-CmErR03h.d.ts → access-namespace-preflight-wRSZEO5V.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +4 -3
- package/dist/access-namespace-preflight.js +50 -46
- package/dist/access-observe-write-surface.d.ts +13 -10
- package/dist/access-observe-write-surface.js +50 -46
- package/dist/access-operations-batch.js +52 -48
- package/dist/access-operations.d.ts +17 -14
- package/dist/access-operations.js +54 -50
- package/dist/access-recall-concurrency.d.ts +13 -10
- package/dist/access-recall-concurrency.js +50 -46
- package/dist/access-recall-response.d.ts +13 -10
- package/dist/access-recall-response.js +50 -46
- package/dist/access-recall-surface.d.ts +13 -10
- package/dist/access-recall-surface.js +50 -46
- package/dist/access-schema.d.ts +86 -86
- package/dist/access-schema.js +8 -8
- package/dist/{access-service-CKEROeFP.d.ts → access-service-BlNKWBC9.d.ts} +11 -38
- package/dist/access-service.d.ts +15 -12
- package/dist/access-service.js +50 -46
- package/dist/access-surface-catalog.d.ts +13 -10
- package/dist/access-wearables-meetings-surface.d.ts +4 -3
- package/dist/action-confidence.d.ts +2 -1
- package/dist/active-memory-bridge.d.ts +2 -1
- package/dist/active-recall.d.ts +2 -1
- package/dist/active-recall.js +3 -2
- package/dist/active-recall.js.map +1 -1
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/behavior-learner.d.ts +2 -1
- package/dist/behavior-signals.d.ts +2 -1
- package/dist/bootstrap.d.ts +9 -8
- package/dist/briefing.d.ts +3 -2
- package/dist/briefing.js +14 -14
- package/dist/buffer-surprise-report.d.ts +2 -1
- package/dist/buffer.d.ts +6 -2
- package/dist/buffer.js +1 -1
- package/dist/bulk-import/index.d.ts +4 -3
- package/dist/calibration.d.ts +2 -1
- package/dist/capabilities.d.ts +2 -1
- package/dist/{catalog-BFTVo2Ez.d.ts → catalog-BLLxZ5L5.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +2 -1
- package/dist/causal-consolidation.d.ts +2 -1
- package/dist/causal-consolidation.js +15 -15
- package/dist/causal-trajectory-graph.d.ts +2 -1
- package/dist/{chunk-P2BLHUL2.js → chunk-2NAFR36N.js} +5 -5
- package/dist/{chunk-WMLHJ5UW.js → chunk-2WSX7U2N.js} +2 -2
- package/dist/{chunk-E3ZMFFQZ.js → chunk-3FLPLDIL.js} +2 -2
- package/dist/chunk-443XCR34.js +213 -0
- package/dist/chunk-443XCR34.js.map +1 -0
- package/dist/{chunk-JNIJBXVK.js → chunk-4KAIGQVU.js} +2 -2
- package/dist/{chunk-354T54RR.js → chunk-4TFIRMTE.js} +28 -1
- package/dist/chunk-4TFIRMTE.js.map +1 -0
- package/dist/{chunk-XWPFZX6S.js → chunk-7CP7SO32.js} +108 -37
- package/dist/chunk-7CP7SO32.js.map +1 -0
- package/dist/{chunk-XGEFGNL5.js → chunk-7IECFM4E.js} +18 -10
- package/dist/chunk-7IECFM4E.js.map +1 -0
- package/dist/{chunk-ZN4UZWMT.js → chunk-A5N6ISRK.js} +2 -2
- package/dist/{chunk-XKGPIWGI.js → chunk-AVPXXV6J.js} +4 -4
- package/dist/{chunk-4PQ4KLLK.js → chunk-BHKDCWGH.js} +9 -9
- package/dist/{chunk-5CPI2QWG.js → chunk-CSXHRAJJ.js} +3 -3
- package/dist/{chunk-QCKQEBIY.js → chunk-DFTCJVXI.js} +6 -6
- package/dist/{chunk-QE63OHLB.js → chunk-ECG64R7B.js} +5 -5
- package/dist/{chunk-ATEAL5EI.js → chunk-EFYACT2X.js} +2 -2
- package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
- package/dist/{chunk-UGH6VPPO.js → chunk-FHH6R3QI.js} +2 -2
- package/dist/{chunk-WZVIB5BK.js → chunk-GCKIDUHE.js} +69 -53
- package/dist/chunk-GCKIDUHE.js.map +1 -0
- package/dist/{chunk-MAXVH44E.js → chunk-GF22HWS2.js} +10 -10
- package/dist/{chunk-ZYU7ZFLT.js → chunk-GJJBPI6I.js} +5 -1
- package/dist/chunk-GJJBPI6I.js.map +1 -0
- package/dist/{chunk-AU7Q3LSC.js → chunk-JBPKEARU.js} +4 -4
- package/dist/{chunk-IPLYGWQF.js → chunk-KQAFEZQX.js} +5 -5
- package/dist/{chunk-CUUY5CC4.js → chunk-L7DTBRTI.js} +2 -2
- package/dist/{chunk-AYAROCWD.js → chunk-LLISELSY.js} +2 -2
- package/dist/{chunk-MMTG3SCJ.js → chunk-M5EPMNS4.js} +2 -2
- package/dist/{chunk-HSSIWAZP.js → chunk-M6ZJR6HU.js} +140 -124
- package/dist/{chunk-HSSIWAZP.js.map → chunk-M6ZJR6HU.js.map} +1 -1
- package/dist/{chunk-WWZ35GWW.js → chunk-MX4TUDMB.js} +2 -2
- package/dist/{chunk-SVZGA2MK.js → chunk-NHPROHJD.js} +2 -2
- package/dist/{chunk-6TWXQ4E2.js → chunk-NM7SKDZM.js} +2 -2
- package/dist/chunk-NQ7E27HN.js +261 -0
- package/dist/chunk-NQ7E27HN.js.map +1 -0
- package/dist/{chunk-QJAQNXWI.js → chunk-ONHRRJZM.js} +6 -6
- package/dist/{chunk-V2XVMCP6.js → chunk-P25IPRXP.js} +7 -7
- package/dist/{chunk-KXGE7L62.js → chunk-RENY3AIW.js} +67 -67
- package/dist/chunk-RENY3AIW.js.map +1 -0
- package/dist/{chunk-3L5O7SMT.js → chunk-SGMORUEU.js} +3 -3
- package/dist/{chunk-3ZW6ZQO6.js → chunk-TBJV4BZE.js} +13 -13
- package/dist/{chunk-7I7AYIUQ.js → chunk-TGHUJC64.js} +5 -5
- package/dist/{chunk-SCOT5DTA.js → chunk-V25OWXAC.js} +1 -1
- package/dist/chunk-V25OWXAC.js.map +1 -0
- package/dist/{chunk-DLKRXYRL.js → chunk-V4MAO2Y4.js} +15 -15
- package/dist/{chunk-5FZQ74K5.js → chunk-WMR5WPZU.js} +2 -2
- package/dist/{chunk-M6LJXJ3J.js → chunk-WNSQOXWS.js} +6 -6
- package/dist/{chunk-KNB7IBBU.js → chunk-WO7KBATL.js} +4 -4
- package/dist/{chunk-3NAHZQU3.js → chunk-X3SQYAPP.js} +2 -2
- package/dist/{chunk-3UOSVTFR.js → chunk-XBRPEGXF.js} +5 -5
- package/dist/{chunk-GD3QOBMB.js → chunk-XHYCPA5C.js} +2 -2
- package/dist/{chunk-ES5IHQ6M.js → chunk-Z3B4V4YI.js} +5 -5
- package/dist/chunk-ZLNOX7B2.js +60 -0
- package/dist/chunk-ZLNOX7B2.js.map +1 -0
- package/dist/{cli-BrwT9YMD.d.ts → cli-Zc6UclD1.d.ts} +5 -5
- package/dist/cli.d.ts +15 -12
- package/dist/cli.js +85 -82
- package/dist/compounding/engine.d.ts +3 -2
- package/dist/compounding/engine.js +14 -14
- package/dist/compounding/preference-consolidator.d.ts +2 -1
- package/dist/compression-optimizer.d.ts +2 -1
- package/dist/config.d.ts +2 -1
- package/dist/config.js +3 -2
- package/dist/connectors/codex-materialize-runner.d.ts +2 -1
- package/dist/connectors/codex-materialize-runner.js +14 -14
- package/dist/connectors/codex-materialize.d.ts +2 -1
- package/dist/connectors/index.d.ts +2 -1
- package/dist/connectors/index.js +17 -17
- package/dist/consolidation-provenance-check.d.ts +3 -2
- package/dist/consolidation-undo.d.ts +3 -2
- package/dist/contradiction/index.d.ts +3 -2
- package/dist/conversation-index/backend.d.ts +2 -1
- package/dist/conversation-index/chunker.d.ts +2 -1
- package/dist/conversation-index/faiss-adapter.d.ts +2 -1
- package/dist/conversation-index/indexer.d.ts +2 -1
- package/dist/conversation-index/search.d.ts +2 -1
- package/dist/corpus-watermark.d.ts +228 -0
- package/dist/corpus-watermark.js +106 -0
- package/dist/corpus-watermark.js.map +1 -0
- package/dist/dashboard-runtime.js +2 -2
- package/dist/day-summary.d.ts +2 -1
- package/dist/delinearize.d.ts +2 -1
- package/dist/direct-answer-wiring.d.ts +2 -1
- package/dist/direct-answer.d.ts +2 -1
- package/dist/embedding-fallback.d.ts +2 -1
- package/dist/enrichment/index.d.ts +2 -1
- package/dist/entity-retrieval.d.ts +3 -2
- package/dist/entity-retrieval.js +14 -14
- package/dist/entity-schema.d.ts +2 -1
- package/dist/explicit-capture.d.ts +9 -8
- package/dist/extraction-error-classification.d.ts +2 -1
- package/dist/extraction-faithfulness.d.ts +2 -1
- package/dist/extraction-judge-telemetry.d.ts +2 -1
- package/dist/extraction-judge-training.d.ts +2 -1
- package/dist/extraction-judge.d.ts +2 -1
- package/dist/extraction-liveness.d.ts +5 -0
- package/dist/extraction-liveness.js +26 -0
- package/dist/extraction-liveness.js.map +1 -0
- package/dist/extraction.d.ts +2 -1
- package/dist/extraction.js +1 -1
- package/dist/fallback-llm.d.ts +2 -1
- package/dist/graph-dashboard-diff.d.ts +2 -1
- package/dist/graph-dashboard-key.d.ts +2 -1
- package/dist/graph-dashboard-parser.d.ts +2 -1
- package/dist/graph-edge-reinforcement.d.ts +2 -1
- package/dist/graph-snapshot.d.ts +2 -1
- package/dist/graph.d.ts +2 -1
- package/dist/importance.d.ts +2 -1
- package/dist/importers/index.d.ts +2 -1
- package/dist/in-flight-reads.d.ts +2 -1
- package/dist/index.d.ts +427 -424
- package/dist/index.js +212 -209
- package/dist/intent.d.ts +2 -1
- package/dist/lcm/engine.d.ts +2 -1
- package/dist/lcm/engine.js +4 -4
- package/dist/lcm/index.d.ts +2 -1
- package/dist/lcm/index.js +12 -12
- package/dist/lcm/tools.d.ts +2 -1
- package/dist/lifecycle.d.ts +2 -1
- package/dist/live-connectors-runner.d.ts +2 -1
- package/dist/local-llm.d.ts +2 -1
- package/dist/local-model-endpoint.d.ts +2 -1
- package/dist/maintenance/memory-governance.d.ts +2 -1
- package/dist/maintenance/memory-governance.js +14 -14
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +3 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +14 -14
- package/dist/maintenance/rebuild-memory-projection.d.ts +3 -2
- package/dist/maintenance/rebuild-memory-projection.js +15 -15
- package/dist/{maintenance-C98njYb8.d.ts → maintenance-DD22oIx9.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +13 -10
- package/dist/memory-action-policy.d.ts +2 -1
- package/dist/memory-cache.d.ts +2 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +2 -1
- package/dist/memory-projection-store.d.ts +2 -1
- package/dist/memory-provenance.d.ts +2 -1
- package/dist/memory-worth-outcomes.d.ts +3 -2
- package/dist/models-json.d.ts +2 -1
- package/dist/namespaces/migrate.d.ts +4 -3
- package/dist/namespaces/migrate.js +21 -21
- package/dist/namespaces/principal.d.ts +2 -1
- package/dist/namespaces/search.d.ts +2 -1
- package/dist/namespaces/search.js +6 -6
- package/dist/namespaces/storage.d.ts +4 -3
- package/dist/namespaces/storage.js +14 -14
- package/dist/native-knowledge.d.ts +2 -1
- package/dist/offline-sync-impression-drain.d.ts +2 -1
- package/dist/offline-sync-impression-drain.js +4 -4
- package/dist/operator-doctor-corpus.d.ts +31 -0
- package/dist/operator-doctor-corpus.js +91 -0
- package/dist/operator-doctor-corpus.js.map +1 -0
- package/dist/operator-doctor-types.d.ts +22 -0
- package/dist/operator-doctor-types.js +1 -0
- package/dist/operator-doctor-types.js.map +1 -0
- package/dist/operator-toolkit.d.ts +10 -13
- package/dist/operator-toolkit.js +35 -29
- package/dist/orchestration/compression-guideline-coordinator.d.ts +3 -2
- package/dist/orchestration/maintenance.d.ts +5 -4
- package/dist/orchestration/maintenance.js +20 -20
- package/dist/{orchestrator-C9ZVppNh.d.ts → orchestrator-BTGgoKli.d.ts} +7 -7
- package/dist/orchestrator.d.ts +9 -8
- package/dist/orchestrator.js +123 -120
- package/dist/patterns-cli.d.ts +2 -1
- package/dist/{pipeline-id_-sqYm.d.ts → pipeline-Bq5zU6Bp.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +2 -1
- package/dist/proactive-contention.d.ts +2 -1
- package/dist/provenance.d.ts +2 -1
- package/dist/{qmd-R6XaQOZK.d.ts → qmd-3ILJkC70.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +3 -2
- package/dist/qmd-recall-cache.d.ts +2 -1
- package/dist/qmd.d.ts +3 -2
- package/dist/qmd.js +4 -4
- package/dist/recall-concurrency-config.d.ts +2 -1
- package/dist/recall-disclosure-escalation.d.ts +2 -1
- package/dist/recall-explain-renderer.d.ts +2 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-memory-map.d.ts +2 -1
- package/dist/recall-planner-llm.d.ts +2 -1
- package/dist/recall-state.d.ts +2 -1
- package/dist/recall-tag-filter.d.ts +2 -1
- package/dist/recall-timings.d.ts +2 -1
- package/dist/recall-xray-cli.d.ts +2 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +2 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +2 -1
- package/dist/recall-xray.js +2 -2
- package/dist/replay/normalizers/chatgpt.d.ts +2 -1
- package/dist/replay/normalizers/claude.d.ts +2 -1
- package/dist/replay/normalizers/openclaw.d.ts +2 -1
- package/dist/replay/normalizers/shared.d.ts +2 -1
- package/dist/replay/runner.d.ts +2 -1
- package/dist/replay/types.d.ts +2 -1
- package/dist/resolve-auth-token.d.ts +2 -1
- package/dist/resume-bundles.js +5 -4
- package/dist/retrieval-agents.d.ts +3 -2
- package/dist/retrieval-tiers.d.ts +2 -1
- package/dist/routing/engine.d.ts +2 -1
- package/dist/routing/store.d.ts +2 -1
- package/dist/salvage-envelope.d.ts +2 -1
- package/dist/schemas.d.ts +76 -76
- package/dist/{scope-profiles-EhyuAKkd.d.ts → scope-profiles-D0-ff4xT.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +2 -1
- package/dist/search/factory.d.ts +2 -1
- package/dist/search/factory.js +5 -5
- package/dist/search/index.d.ts +2 -1
- package/dist/search/index.js +9 -9
- package/dist/search/lancedb-backend.d.ts +2 -1
- package/dist/search/meilisearch-backend.d.ts +2 -1
- package/dist/search/noop-backend.d.ts +2 -1
- package/dist/search/orama-backend.d.ts +2 -1
- package/dist/search/port.d.ts +2 -1
- package/dist/search/remote-backend.d.ts +2 -1
- package/dist/{semantic-consolidation-B2xo26UQ.d.ts → semantic-consolidation-4Tfh7c1g.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +3 -2
- package/dist/semantic-consolidation.js +15 -15
- package/dist/semantic-rule-promotion.js +14 -14
- package/dist/semantic-rule-verifier.d.ts +2 -1
- package/dist/semantic-rule-verifier.js +14 -14
- package/dist/{service-DgFWlZ-r.d.ts → service-Z8uzoAST.d.ts} +2 -2
- package/dist/session-observer-bands.d.ts +2 -1
- package/dist/session-observer-state.d.ts +2 -1
- package/dist/shared-context/manager.d.ts +10 -9
- package/dist/signal.d.ts +2 -1
- package/dist/{storage-XGawwqXE.d.ts → storage-BZVMn8yJ.d.ts} +25 -2
- package/dist/storage.d.ts +3 -2
- package/dist/storage.js +13 -13
- package/dist/summarizer.d.ts +2 -1
- package/dist/summary-snapshot.d.ts +2 -1
- package/dist/temporal-supersession.d.ts +3 -2
- package/dist/temporal-timeline-recall.d.ts +2 -1
- package/dist/temporal-validity.d.ts +2 -1
- package/dist/threading.d.ts +2 -1
- package/dist/tier-migration.d.ts +3 -2
- package/dist/tier-routing.d.ts +2 -1
- package/dist/tokens.js +2 -2
- package/dist/topics.d.ts +2 -1
- package/dist/transcript.d.ts +2 -1
- package/dist/transfer/autodetect.js +1 -1
- package/dist/transfer/backup.js +3 -3
- package/dist/transfer/capsule-export.js +4 -4
- package/dist/transfer/capsule-import.js +1 -1
- package/dist/transfer/types.d.ts +66 -66
- package/dist/trust-score-stage.d.ts +2 -1
- package/dist/trust-score.d.ts +2 -1
- package/dist/{types-Bbs0qz6b.d.ts → types-DHBxIF0s.d.ts} +181 -5
- package/dist/types.d.ts +2 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +2 -1
- package/dist/verified-recall.js +14 -14
- package/dist/write-envelope.d.ts +2 -1
- package/package.json +12 -2
- package/src/access-health-types.ts +51 -0
- package/src/access-http.test.ts +22 -2
- package/src/access-service-health.test.ts +212 -0
- package/src/access-service.ts +14 -37
- package/src/buffer.ts +30 -0
- package/src/cli.ts +2 -4
- package/src/config.ts +4 -4
- package/src/corpus-watermark.test.ts +840 -0
- package/src/corpus-watermark.ts +531 -0
- package/src/extraction-liveness.test.ts +461 -0
- package/src/extraction-liveness.ts +462 -0
- package/src/operator-doctor-corpus.test.ts +171 -0
- package/src/operator-doctor-corpus.ts +95 -0
- package/src/operator-doctor-types.ts +20 -0
- package/src/operator-toolkit.ts +12 -13
- package/src/recall/archive-scoring.ts +18 -0
- package/src/storage/memory-read-store.ts +91 -22
- package/src/storage.ts +27 -2
- package/src/types.ts +4 -4
- package/dist/chunk-354T54RR.js.map +0 -1
- package/dist/chunk-KXGE7L62.js.map +0 -1
- package/dist/chunk-SCOT5DTA.js.map +0 -1
- package/dist/chunk-WZVIB5BK.js.map +0 -1
- package/dist/chunk-XGEFGNL5.js.map +0 -1
- package/dist/chunk-XWPFZX6S.js.map +0 -1
- package/dist/chunk-ZYU7ZFLT.js.map +0 -1
- /package/dist/{chunk-P2BLHUL2.js.map → chunk-2NAFR36N.js.map} +0 -0
- /package/dist/{chunk-WMLHJ5UW.js.map → chunk-2WSX7U2N.js.map} +0 -0
- /package/dist/{chunk-E3ZMFFQZ.js.map → chunk-3FLPLDIL.js.map} +0 -0
- /package/dist/{chunk-JNIJBXVK.js.map → chunk-4KAIGQVU.js.map} +0 -0
- /package/dist/{chunk-ZN4UZWMT.js.map → chunk-A5N6ISRK.js.map} +0 -0
- /package/dist/{chunk-XKGPIWGI.js.map → chunk-AVPXXV6J.js.map} +0 -0
- /package/dist/{chunk-4PQ4KLLK.js.map → chunk-BHKDCWGH.js.map} +0 -0
- /package/dist/{chunk-5CPI2QWG.js.map → chunk-CSXHRAJJ.js.map} +0 -0
- /package/dist/{chunk-QCKQEBIY.js.map → chunk-DFTCJVXI.js.map} +0 -0
- /package/dist/{chunk-QE63OHLB.js.map → chunk-ECG64R7B.js.map} +0 -0
- /package/dist/{chunk-ATEAL5EI.js.map → chunk-EFYACT2X.js.map} +0 -0
- /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
- /package/dist/{chunk-UGH6VPPO.js.map → chunk-FHH6R3QI.js.map} +0 -0
- /package/dist/{chunk-MAXVH44E.js.map → chunk-GF22HWS2.js.map} +0 -0
- /package/dist/{chunk-AU7Q3LSC.js.map → chunk-JBPKEARU.js.map} +0 -0
- /package/dist/{chunk-IPLYGWQF.js.map → chunk-KQAFEZQX.js.map} +0 -0
- /package/dist/{chunk-CUUY5CC4.js.map → chunk-L7DTBRTI.js.map} +0 -0
- /package/dist/{chunk-AYAROCWD.js.map → chunk-LLISELSY.js.map} +0 -0
- /package/dist/{chunk-MMTG3SCJ.js.map → chunk-M5EPMNS4.js.map} +0 -0
- /package/dist/{chunk-WWZ35GWW.js.map → chunk-MX4TUDMB.js.map} +0 -0
- /package/dist/{chunk-SVZGA2MK.js.map → chunk-NHPROHJD.js.map} +0 -0
- /package/dist/{chunk-6TWXQ4E2.js.map → chunk-NM7SKDZM.js.map} +0 -0
- /package/dist/{chunk-QJAQNXWI.js.map → chunk-ONHRRJZM.js.map} +0 -0
- /package/dist/{chunk-V2XVMCP6.js.map → chunk-P25IPRXP.js.map} +0 -0
- /package/dist/{chunk-3L5O7SMT.js.map → chunk-SGMORUEU.js.map} +0 -0
- /package/dist/{chunk-3ZW6ZQO6.js.map → chunk-TBJV4BZE.js.map} +0 -0
- /package/dist/{chunk-7I7AYIUQ.js.map → chunk-TGHUJC64.js.map} +0 -0
- /package/dist/{chunk-DLKRXYRL.js.map → chunk-V4MAO2Y4.js.map} +0 -0
- /package/dist/{chunk-5FZQ74K5.js.map → chunk-WMR5WPZU.js.map} +0 -0
- /package/dist/{chunk-M6LJXJ3J.js.map → chunk-WNSQOXWS.js.map} +0 -0
- /package/dist/{chunk-KNB7IBBU.js.map → chunk-WO7KBATL.js.map} +0 -0
- /package/dist/{chunk-3NAHZQU3.js.map → chunk-X3SQYAPP.js.map} +0 -0
- /package/dist/{chunk-3UOSVTFR.js.map → chunk-XBRPEGXF.js.map} +0 -0
- /package/dist/{chunk-GD3QOBMB.js.map → chunk-XHYCPA5C.js.map} +0 -0
- /package/dist/{chunk-ES5IHQ6M.js.map → chunk-Z3B4V4YI.js.map} +0 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Corpus-watermark doctor check (issue #2149).
|
|
3
|
+
*
|
|
4
|
+
* Lives beside operator-toolkit.ts (which is at its structural-ratchet ceiling)
|
|
5
|
+
* so the god file only gains the wiring. Reports each namespace's memory-file
|
|
6
|
+
* count, newest write, and a short census-digest prefix, and WARNS when a
|
|
7
|
+
* namespace could not be scanned (unreadable/churning) or enumeration failed —
|
|
8
|
+
* a scan failure omits a namespace, so an unconditional "ok" would certify a
|
|
9
|
+
* partial fleet or conflate an unreadable corpus with an empty deployment
|
|
10
|
+
* (issue #2156 round-7). Peer comparison (count/age divergence thresholds) is a
|
|
11
|
+
* follow-up.
|
|
12
|
+
*
|
|
13
|
+
* Namespaces are resolved through the shared resolveCorpusNamespaceRoots helper
|
|
14
|
+
* (issue #2156 finding C) so the doctor and the /health surface enumerate the
|
|
15
|
+
* SAME tenant set and cannot drift. The caller supplies `storageFactory` (a
|
|
16
|
+
* `dir -> CorpusStorage`) so this module never imports the storage god-module
|
|
17
|
+
* directly (issue #1533 ratchet).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
type CorpusStorage,
|
|
22
|
+
type CorpusWatermark,
|
|
23
|
+
computeCorpusWatermarks,
|
|
24
|
+
resolveCorpusNamespaceRoots,
|
|
25
|
+
} from "./corpus-watermark.js";
|
|
26
|
+
import type { OperatorDoctorCheck } from "./operator-toolkit.js";
|
|
27
|
+
import type { PluginConfig } from "./types.js";
|
|
28
|
+
|
|
29
|
+
const DIGEST_PREFIX_LENGTH = 12;
|
|
30
|
+
|
|
31
|
+
export async function summarizeCorpusWatermark(
|
|
32
|
+
config: PluginConfig,
|
|
33
|
+
storageFactory: (dir: string) => CorpusStorage,
|
|
34
|
+
now?: Date
|
|
35
|
+
): Promise<OperatorDoctorCheck> {
|
|
36
|
+
let resolved: string[] = [];
|
|
37
|
+
let watermarks: CorpusWatermark[] = [];
|
|
38
|
+
let enumerationFailed = false;
|
|
39
|
+
try {
|
|
40
|
+
// The doctor orchestrator has no live namespace catalog, so config-driven
|
|
41
|
+
// enumeration (deduped by root inside the shared helper) is the whole set.
|
|
42
|
+
const rootByNamespace = new Map<string, string>();
|
|
43
|
+
for (const root of await resolveCorpusNamespaceRoots({ config })) {
|
|
44
|
+
rootByNamespace.set(root.namespace, root.rootDir);
|
|
45
|
+
}
|
|
46
|
+
resolved = [...rootByNamespace.keys()];
|
|
47
|
+
watermarks = await computeCorpusWatermarks(
|
|
48
|
+
resolved,
|
|
49
|
+
(namespace) => storageFactory(rootByNamespace.get(namespace) ?? config.memoryDir),
|
|
50
|
+
now
|
|
51
|
+
);
|
|
52
|
+
} catch {
|
|
53
|
+
// Namespace ENUMERATION itself failed (distinct from a per-namespace scan).
|
|
54
|
+
enumerationFailed = true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const computed = new Set(watermarks.map((w) => w.namespace));
|
|
58
|
+
const failed = resolved.filter((namespace) => !computed.has(namespace));
|
|
59
|
+
const lines = watermarks.map(
|
|
60
|
+
(w) =>
|
|
61
|
+
`${w.namespace}: ${w.memoryFileCount} files, newest=${w.newestWriteAt ?? "never"}, ` +
|
|
62
|
+
`digest=${w.digest.slice(0, DIGEST_PREFIX_LENGTH)}`
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
if (enumerationFailed) {
|
|
66
|
+
return {
|
|
67
|
+
key: "corpus_watermark",
|
|
68
|
+
status: "warn",
|
|
69
|
+
summary: "Could not resolve corpus namespaces (enumeration failed).",
|
|
70
|
+
remediation: "Ensure the memory directory and namespace catalog are readable, then rerun `remnic doctor`.",
|
|
71
|
+
details: { corpus: watermarks, resolved: resolved.length, failed: failed.length },
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (failed.length > 0) {
|
|
75
|
+
return {
|
|
76
|
+
key: "corpus_watermark",
|
|
77
|
+
status: "warn",
|
|
78
|
+
summary:
|
|
79
|
+
`Corpus watermark unavailable for ${failed.length} of ${resolved.length} namespace(s) ` +
|
|
80
|
+
`(unreadable or churning): ${failed.join(", ")}.` +
|
|
81
|
+
(lines.length > 0 ? ` Reporting ${lines.length}: ${lines.join("; ")}` : ""),
|
|
82
|
+
remediation: "Inspect the omitted namespace roots for permission/backend errors, then rerun `remnic doctor`.",
|
|
83
|
+
details: { corpus: watermarks, resolved: resolved.length, failed },
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
key: "corpus_watermark",
|
|
88
|
+
status: "ok",
|
|
89
|
+
summary:
|
|
90
|
+
resolved.length === 0
|
|
91
|
+
? "No corpus namespaces resolved yet."
|
|
92
|
+
: `Corpus watermark across ${watermarks.length} namespace(s): ${lines.join("; ")}`,
|
|
93
|
+
details: { corpus: watermarks },
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared operator-doctor result type.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `operator-toolkit.ts` (issue #2151 review, PR 2155) so that a
|
|
5
|
+
* lightweight doctor summarizer such as `extraction-liveness.ts` can type its
|
|
6
|
+
* return value WITHOUT importing the whole `operator-toolkit` module. That
|
|
7
|
+
* back-edge (`extraction-liveness` -> `operator-toolkit` -> `extraction-liveness`)
|
|
8
|
+
* was a circular type reference that made the standalone DTS entry for
|
|
9
|
+
* `extraction-liveness.ts` resolve the entire operator-toolkit + config + types
|
|
10
|
+
* graph, pushing the tsup DTS worker past its heap limit
|
|
11
|
+
* (ERR_WORKER_OUT_OF_MEMORY). This interface has no dependencies, so importing it
|
|
12
|
+
* costs nothing.
|
|
13
|
+
*/
|
|
14
|
+
export interface OperatorDoctorCheck {
|
|
15
|
+
key: string;
|
|
16
|
+
status: "ok" | "warn" | "error";
|
|
17
|
+
summary: string;
|
|
18
|
+
remediation?: string;
|
|
19
|
+
details?: unknown;
|
|
20
|
+
}
|
package/src/operator-toolkit.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { constants as fsConstants } from "node:fs";
|
|
|
3
3
|
import { access, mkdir, readFile, readdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
4
4
|
import { lintWorkspaceFiles } from "./hygiene.js";
|
|
5
5
|
import { parseConfig } from "./config.js";
|
|
6
|
+
import { summarizeExtractionLiveness, type ExtractionBufferSource } from "./extraction-liveness.js";
|
|
7
|
+
import type { OperatorDoctorCheck } from "./operator-doctor-types.js";
|
|
6
8
|
import { readEnvVar, resolveHomeDir } from "./runtime/env.js";
|
|
7
9
|
import { resolvePluginEntry } from "./plugin-entry-resolver.js";
|
|
8
10
|
import {
|
|
@@ -28,7 +30,7 @@ import { analyzeGraphHealth, type GraphHealthReport } from "./graph.js";
|
|
|
28
30
|
import { resolveAccessSetupCapabilities,
|
|
29
31
|
resolveCapabilities,
|
|
30
32
|
resolveGraphConstructionCapabilities,
|
|
31
|
-
resolveQmdCapabilities,
|
|
33
|
+
resolveQmdCapabilities, resolveIndexingCapabilities,
|
|
32
34
|
resolveEvalCapabilities, resolvePresentationCapabilities, resolveConsolidationCapabilities } from "./capabilities.js";
|
|
33
35
|
import {
|
|
34
36
|
analyzeSessionIntegrity,
|
|
@@ -55,8 +57,7 @@ import type {
|
|
|
55
57
|
} from "./types.js";
|
|
56
58
|
import { reportBufferSurpriseDistribution } from "./buffer-surprise-report.js";
|
|
57
59
|
import { readJudgeVerdictStats } from "./extraction-judge-telemetry.js";
|
|
58
|
-
import {
|
|
59
|
-
|
|
60
|
+
import { summarizeCorpusWatermark } from "./operator-doctor-corpus.js";
|
|
60
61
|
|
|
61
62
|
const OPENCLAW_REMNIC_PLUGIN_IDS = ["openclaw-remnic", "openclaw-engram"] as const;
|
|
62
63
|
|
|
@@ -158,6 +159,7 @@ export interface OperatorToolkitOrchestrator extends ConversationIndexLike {
|
|
|
158
159
|
config: PluginConfig;
|
|
159
160
|
storage: StorageManager;
|
|
160
161
|
qmd: QmdRuntimeLike;
|
|
162
|
+
buffer?: ExtractionBufferSource;
|
|
161
163
|
}
|
|
162
164
|
|
|
163
165
|
export interface OperatorConfigLoadResult {
|
|
@@ -226,13 +228,11 @@ export interface OperatorSetupReport {
|
|
|
226
228
|
verificationCommands: string[];
|
|
227
229
|
}
|
|
228
230
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
details?: unknown;
|
|
235
|
-
}
|
|
231
|
+
// `OperatorDoctorCheck` moved to `./operator-doctor-types.js` so summarizers avoid
|
|
232
|
+
// pulling the operator-toolkit DTS graph (PR 2155); re-exported to keep the surface stable.
|
|
233
|
+
export type { OperatorDoctorCheck };
|
|
234
|
+
export { summarizeExtractionLiveness };
|
|
235
|
+
export type { ExtractionBufferSource };
|
|
236
236
|
|
|
237
237
|
export interface OperatorConfigReviewFinding {
|
|
238
238
|
key: string;
|
|
@@ -1497,10 +1497,9 @@ export async function runOperatorDoctor(options: OperatorDoctorOptions): Promise
|
|
|
1497
1497
|
// Informational only — never errors, never blocks doctor from returning ok.
|
|
1498
1498
|
checks.push(await summarizeTierDistribution(options.orchestrator.storage));
|
|
1499
1499
|
|
|
1500
|
-
// Dreams phases thresholds and last-run timestamps (issue #678 PR 2/4).
|
|
1501
|
-
// Surfaces per-phase: enabled status, cadence, threshold values, and the
|
|
1502
|
-
// best-available last-run timestamp for each of the three pipeline phases.
|
|
1503
1500
|
checks.push(await summarizeDreamsPhases(config, storage));
|
|
1501
|
+
checks.push(await summarizeExtractionLiveness(config, storage, options.orchestrator.buffer));
|
|
1502
|
+
checks.push(await summarizeCorpusWatermark(config, (dir) => new StorageManager(dir))); // corpus watermark #2149
|
|
1504
1503
|
|
|
1505
1504
|
// Security mitigation status (issue #565).
|
|
1506
1505
|
// Reports whether the cross-namespace budget and anomaly detection
|
|
@@ -250,6 +250,17 @@ class ArchiveScoringWorkerPool {
|
|
|
250
250
|
private idle: Worker[] = [];
|
|
251
251
|
private waiters: Array<{ resolve: (worker: Worker) => void; reject: (err: Error) => void }> = [];
|
|
252
252
|
private terminated = false;
|
|
253
|
+
private busy = 0;
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Workers currently checked out and scoring — NOT callers waiting in line.
|
|
257
|
+
* A caller parked in `acquire()` is queued, not running, so this is the only
|
|
258
|
+
* honest measure of real task overlap: with a size-1 pool it never exceeds 1
|
|
259
|
+
* no matter how many callers are queued behind it.
|
|
260
|
+
*/
|
|
261
|
+
get busyWorkers(): number {
|
|
262
|
+
return this.busy;
|
|
263
|
+
}
|
|
253
264
|
|
|
254
265
|
constructor(size: number = defaultPoolSize()) {
|
|
255
266
|
this.targetSize = Math.max(1, size);
|
|
@@ -265,11 +276,13 @@ class ArchiveScoringWorkerPool {
|
|
|
265
276
|
return [];
|
|
266
277
|
}
|
|
267
278
|
let abandoned = false;
|
|
279
|
+
this.busy += 1;
|
|
268
280
|
try {
|
|
269
281
|
return await this.dispatch(worker, task, abortSignal, () => {
|
|
270
282
|
abandoned = true;
|
|
271
283
|
});
|
|
272
284
|
} finally {
|
|
285
|
+
this.busy -= 1;
|
|
273
286
|
if (abandoned) this.retireWorker(worker);
|
|
274
287
|
else this.release(worker);
|
|
275
288
|
}
|
|
@@ -424,6 +437,11 @@ export class OffThreadArchiveScoring implements ArchiveScoringStrategy {
|
|
|
424
437
|
}
|
|
425
438
|
}
|
|
426
439
|
|
|
440
|
+
/** Workers currently scoring; 0 when no pool has been created yet. */
|
|
441
|
+
get busyWorkers(): number {
|
|
442
|
+
return this.pool?.busyWorkers ?? 0;
|
|
443
|
+
}
|
|
444
|
+
|
|
427
445
|
async score(
|
|
428
446
|
items: ReadonlyArray<ArchiveScoreItem>,
|
|
429
447
|
tokens: ReadonlyArray<string>,
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* listing.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import type { Dirent } from "node:fs";
|
|
12
|
+
import type { Dirent, Stats } from "node:fs";
|
|
13
13
|
import { lstat, readdir, realpath, stat } from "node:fs/promises";
|
|
14
14
|
import path from "node:path";
|
|
15
15
|
import { toMemoryPathRel } from "../memory-lifecycle-ledger-utils.js";
|
|
@@ -79,6 +79,20 @@ export interface MemoryReadStoreDeps {
|
|
|
79
79
|
readonly wearablesDir: string;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* A backend READ failure worth propagating from a strict (census) scan: an
|
|
84
|
+
* errno-coded fs error that is NOT `ENOENT`. `ENOENT` (an absent dir) and
|
|
85
|
+
* code-less rejections (symlink-escape containment failures from
|
|
86
|
+
* assertPathInsideRoot) are expected and skipped; `EACCES`/`EIO`/… mean the
|
|
87
|
+
* corpus is unreadable and a divergence census must fail loud rather than
|
|
88
|
+
* silently publish a partial count (issue #2156 round-5). Non-strict (recall)
|
|
89
|
+
* callers ignore this and degrade to a best-effort partial scan.
|
|
90
|
+
*/
|
|
91
|
+
function isPropagatableReadError(err: unknown): boolean {
|
|
92
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
93
|
+
return typeof code === "string" && code !== "ENOENT";
|
|
94
|
+
}
|
|
95
|
+
|
|
82
96
|
export class MemoryReadStore {
|
|
83
97
|
constructor(
|
|
84
98
|
private readonly deps: MemoryReadStoreDeps,
|
|
@@ -184,33 +198,66 @@ export class MemoryReadStore {
|
|
|
184
198
|
return latestArtifacts;
|
|
185
199
|
}
|
|
186
200
|
|
|
187
|
-
|
|
201
|
+
/**
|
|
202
|
+
* Recursively collect `*.md` paths under `startDirs`, hardened against symlink
|
|
203
|
+
* escape: a category dir symlinked outside memoryDir (e.g. decisions/ -> an
|
|
204
|
+
* external dir) must NOT pull out-of-store files into a scan (info leak). Same
|
|
205
|
+
* walker-hardening pattern as document-scanner.ts / cli.ts /
|
|
206
|
+
* consolidation-provenance-check.ts; reuses the shared containment helper.
|
|
207
|
+
* Paths only — no frontmatter parse.
|
|
208
|
+
*/
|
|
209
|
+
private async collectContainedMarkdownPaths(
|
|
210
|
+
startDirs: readonly string[],
|
|
211
|
+
propagateReadErrors = false,
|
|
212
|
+
): Promise<string[]> {
|
|
188
213
|
const filePaths: string[] = [];
|
|
189
214
|
|
|
190
|
-
// Resolve the memory root once for containment checks below.
|
|
191
|
-
// symlinked outside memoryDir (e.g. decisions/ -> an external dir) must NOT
|
|
192
|
-
// pull out-of-store files into the QMD-unavailable recall fallback (info
|
|
193
|
-
// leak). Same walker-hardening pattern as document-scanner.ts / cli.ts /
|
|
194
|
-
// consolidation-provenance-check.ts; reuses the shared containment helper.
|
|
215
|
+
// Resolve the memory root once for the containment checks below.
|
|
195
216
|
let memoryRootReal: string;
|
|
196
217
|
try {
|
|
197
218
|
memoryRootReal = await realpath(this.deps.baseDir);
|
|
198
|
-
} catch {
|
|
219
|
+
} catch (err) {
|
|
220
|
+
// ENOENT: a not-yet-created root ⇒ empty. Any other errno is a backend
|
|
221
|
+
// read failure — propagate for strict census callers (never publish an
|
|
222
|
+
// unreadable corpus as empty); non-strict (recall) callers degrade.
|
|
223
|
+
if (propagateReadErrors && isPropagatableReadError(err)) throw err;
|
|
199
224
|
return filePaths;
|
|
200
225
|
}
|
|
201
226
|
|
|
202
|
-
const collectPaths = async (dir: string) => {
|
|
227
|
+
const collectPaths = async (dir: string): Promise<void> => {
|
|
203
228
|
// Directory-level guard, isolated from per-entry handling: skip symlinked
|
|
204
229
|
// or non-directory category dirs and assert the resolved dir stays inside
|
|
205
230
|
// the memory root before reading. A failure here means the whole subtree
|
|
206
231
|
// does not exist or escaped the store — fail closed by skipping it.
|
|
232
|
+
let dirStat: Stats;
|
|
233
|
+
try {
|
|
234
|
+
dirStat = await lstat(dir);
|
|
235
|
+
} catch (err) {
|
|
236
|
+
// ENOENT (absent category dir) is expected; a backend read error
|
|
237
|
+
// (EACCES/EIO/…) propagates in strict census mode.
|
|
238
|
+
if (propagateReadErrors && isPropagatableReadError(err)) throw err;
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if (dirStat.isSymbolicLink()) return; // never follow symlinks out of the store (both modes)
|
|
242
|
+
if (!dirStat.isDirectory()) {
|
|
243
|
+
// A category root that EXISTS but is not a directory (e.g. `facts/`
|
|
244
|
+
// replaced by a regular file) is a layout/backend failure: a strict
|
|
245
|
+
// census must not silently publish the remaining categories as healthy.
|
|
246
|
+
// Non-strict (recall) stays best-effort and skips it (issue #2156 round-10).
|
|
247
|
+
if (propagateReadErrors) {
|
|
248
|
+
throw new Error(`corpus scan: expected a directory but found a non-directory at ${dir}`);
|
|
249
|
+
}
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
207
252
|
let entries: Dirent[];
|
|
208
253
|
try {
|
|
209
|
-
const dirStat = await lstat(dir);
|
|
210
|
-
if (dirStat.isSymbolicLink() || !dirStat.isDirectory()) return;
|
|
211
254
|
assertPathInsideRoot(memoryRootReal, await realpath(dir), dir);
|
|
212
255
|
entries = await readdir(dir, { withFileTypes: true });
|
|
213
|
-
} catch {
|
|
256
|
+
} catch (err) {
|
|
257
|
+
// A containment failure carries no errno (skip); a backend read error
|
|
258
|
+
// (EACCES …) propagates in strict census mode so a partial subtree is
|
|
259
|
+
// not silently counted.
|
|
260
|
+
if (propagateReadErrors && isPropagatableReadError(err)) throw err;
|
|
214
261
|
return;
|
|
215
262
|
}
|
|
216
263
|
|
|
@@ -231,8 +278,11 @@ export class MemoryReadStore {
|
|
|
231
278
|
try {
|
|
232
279
|
assertPathInsideRoot(memoryRootReal, await realpath(fullPath), fullPath);
|
|
233
280
|
filePaths.push(fullPath);
|
|
234
|
-
} catch {
|
|
235
|
-
//
|
|
281
|
+
} catch (err) {
|
|
282
|
+
// ENOENT (vanished mid-scan) or a code-less containment/symlink
|
|
283
|
+
// rejection ⇒ skip just this file. A backend read error propagates
|
|
284
|
+
// in strict census mode.
|
|
285
|
+
if (propagateReadErrors && isPropagatableReadError(err)) throw err;
|
|
236
286
|
}
|
|
237
287
|
}
|
|
238
288
|
}
|
|
@@ -243,6 +293,13 @@ export class MemoryReadStore {
|
|
|
243
293
|
}
|
|
244
294
|
};
|
|
245
295
|
|
|
296
|
+
for (const dir of startDirs) {
|
|
297
|
+
await collectPaths(dir);
|
|
298
|
+
}
|
|
299
|
+
return filePaths;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async collectActiveMemoryPaths(options?: { propagateReadErrors?: boolean }): Promise<string[]> {
|
|
246
303
|
// Scan EVERY supported memory category directory, not just the legacy four
|
|
247
304
|
// (facts/procedures/reasoning-traces/corrections). Issue #1497: the QMD
|
|
248
305
|
// filesystem-fallback recall path (orchestrator `recent_scan` ->
|
|
@@ -269,14 +326,26 @@ export class MemoryReadStore {
|
|
|
269
326
|
// those files (they have a `---` frontmatter block) and leak queue items
|
|
270
327
|
// into recall. None of these excluded dirs are in RECALL_FALLBACK_DIRS; the
|
|
271
328
|
// exclusion is asserted by tests in storage-fallback-category-dirs.test.ts.
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
329
|
+
return this.collectContainedMarkdownPaths(
|
|
330
|
+
RECALL_FALLBACK_DIRS.map((dir) => path.join(this.deps.baseDir, dir)),
|
|
331
|
+
options?.propagateReadErrors ?? false,
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Cold-tier memory paths for the corpus census (issue #2156 finding D).
|
|
337
|
+
* Demoted memories move to `<baseDir>/cold/...` but stay active and reachable
|
|
338
|
+
* via cold recall, so the divergence census MUST count them — otherwise two
|
|
339
|
+
* replicas whose cold tiers differ show a false "converged" digest. Paths
|
|
340
|
+
* only, same symlink/containment hardening as the hot scan. This is NOT part
|
|
341
|
+
* of the recall fallback corpus (collectActiveMemoryPaths), whose scan is
|
|
342
|
+
* intentionally unchanged.
|
|
343
|
+
*/
|
|
344
|
+
async collectColdMemoryPaths(options?: { propagateReadErrors?: boolean }): Promise<string[]> {
|
|
345
|
+
return this.collectContainedMarkdownPaths(
|
|
346
|
+
[this.deps.resolveTierRootDir("cold")],
|
|
347
|
+
options?.propagateReadErrors ?? false,
|
|
348
|
+
);
|
|
280
349
|
}
|
|
281
350
|
|
|
282
351
|
async readMemoriesWindow(options: {
|
package/src/storage.ts
CHANGED
|
@@ -4643,8 +4643,33 @@ export class StorageManager {
|
|
|
4643
4643
|
return Math.max(1, Math.floor(batchSize));
|
|
4644
4644
|
}
|
|
4645
4645
|
|
|
4646
|
-
|
|
4647
|
-
|
|
4646
|
+
/**
|
|
4647
|
+
* Public cheap active-memory path scan (issue #2149 corpus watermark). Lists
|
|
4648
|
+
* active memory file paths without parsing frontmatter — safe on a 100k+
|
|
4649
|
+
* corpus, unlike readAllMemories().
|
|
4650
|
+
*/
|
|
4651
|
+
async collectActiveMemoryPaths(options?: { propagateReadErrors?: boolean }): Promise<string[]> {
|
|
4652
|
+
return this.memoryReadStore.collectActiveMemoryPaths(options);
|
|
4653
|
+
}
|
|
4654
|
+
|
|
4655
|
+
/**
|
|
4656
|
+
* Public cheap cold-tier path scan (issue #2156 finding D corpus census).
|
|
4657
|
+
* Lists demoted-but-reachable memory paths under `cold/` without parsing
|
|
4658
|
+
* frontmatter, so the divergence watermark counts the cold tier too.
|
|
4659
|
+
*/
|
|
4660
|
+
async collectColdMemoryPaths(options?: { propagateReadErrors?: boolean }): Promise<string[]> {
|
|
4661
|
+
return this.memoryReadStore.collectColdMemoryPaths(options);
|
|
4662
|
+
}
|
|
4663
|
+
|
|
4664
|
+
/**
|
|
4665
|
+
* Combined hot+cold corpus-mutation sentinel (issue #2156). Changes whenever
|
|
4666
|
+
* EITHER tier is written, so a divergence census can bracket its hot/cold scan
|
|
4667
|
+
* and detect a tier migration (write-cold-then-unlink-hot) racing the walkers,
|
|
4668
|
+
* then retry for a consistent snapshot instead of caching a transient
|
|
4669
|
+
* double-count or miss.
|
|
4670
|
+
*/
|
|
4671
|
+
getCorpusScanVersion(): string {
|
|
4672
|
+
return `${this.getMemoryCorpusVersion()}:${this.readColdWriteVersion()}`;
|
|
4648
4673
|
}
|
|
4649
4674
|
|
|
4650
4675
|
private async readParsedMemoriesFromPaths(filePaths: string[], batchSize?: number): Promise<MemoryFile[]> {
|
package/src/types.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { BoundedJsonlStateConfig } from "./bounded-jsonl-state.js";
|
|
|
2
2
|
import type { MeetingsConfig } from "./meetings/types.js";
|
|
3
3
|
import type { ActivityConfig } from "./activity/types.js";
|
|
4
4
|
import type { WearablesConfig } from "./wearables/types.js";
|
|
5
|
+
import type { ExtractionLivenessConfig } from "./extraction-liveness.js";
|
|
5
6
|
|
|
6
7
|
export type ReasoningEffort = "none" | "low" | "medium" | "high";
|
|
7
8
|
export type TriggerMode = "smart" | "every_n" | "time_based";
|
|
@@ -1206,10 +1207,9 @@ export interface PluginConfig extends BoundedJsonlStateConfig {
|
|
|
1206
1207
|
*/
|
|
1207
1208
|
dreamsPhases: DreamsPhasesConfig;
|
|
1208
1209
|
procedural: ProceduralConfig;
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
*
|
|
1212
|
-
* defaults. PR 1 parses + persists only; extraction wiring lands in PR 2.
|
|
1210
|
+
extractionLiveness: ExtractionLivenessConfig;
|
|
1211
|
+
/** Claim-level provenance spans (#1575); see `ProvenanceConfig` for defaults.
|
|
1212
|
+
* PR 1 parses + persists only; extraction wiring lands in PR 2.
|
|
1213
1213
|
*/
|
|
1214
1214
|
provenance: ProvenanceConfig;
|
|
1215
1215
|
/**
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/buffer.ts"],"sourcesContent":["import { log } from \"./logger.js\";\nimport { scanSignals } from \"./signal.js\";\nimport type { StorageManager } from \"./storage.js\";\nimport type {\n BufferEntryState,\n BufferState,\n BufferSurpriseEvent,\n BufferTurn,\n PluginConfig,\n SignalLevel,\n} from \"./types.js\";\nimport { resolvePresentationCapabilities } from \"./capabilities.js\";\n\nexport type TriggerDecision = \"extract_now\" | \"extract_batch\" | \"keep_buffering\";\n\nexport interface AddTurnOutcome {\n decision: TriggerDecision;\n extractionTurns?: BufferTurn[];\n}\n\n/**\n * Optional surprise probe injected into `SmartBuffer`.\n *\n * Computes a D-MEM-style novelty score in `[0, 1]` for an incoming turn.\n * The buffer treats the probe as purely additive: if it is not provided, if\n * the feature flag is off, or if the probe throws/times out, the buffer\n * falls back to the existing signal/turn-count/time triggers unchanged.\n *\n * Callers are responsible for sampling recent memories and passing them\n * through the embedding pipeline — the buffer does not want to know about\n * storage, embeddings, or QMD.\n *\n * @param bufferKey Identifier for the active buffer (session/thread).\n * @param turn The incoming turn whose novelty is being scored.\n * @param recentTurns Turns already buffered for this key (most recent first\n * is NOT guaranteed — treat as unordered corpus).\n * @returns A surprise score in `[0, 1]`, or `null` if no score could be\n * produced (e.g. empty corpus, probe declined to embed).\n */\nexport interface BufferSurpriseProbe {\n scoreTurn(\n bufferKey: string,\n turn: BufferTurn,\n recentTurns: readonly BufferTurn[],\n ): Promise<number | null>;\n}\n\nconst MAX_BUFFER_ENTRY_COUNT = 200;\n\n/**\n * Upper bound on how long a debounced buffer save may be deferred under\n * sustained activity (issue #1909). Each new turn re-arms the trailing-edge\n * timer, so a steady stream of turns could otherwise push the save out\n * indefinitely and widen the crash-loss window without bound. Once a pending\n * save has been deferred this many debounce windows, the next scheduled save\n * forces an inline flush instead of re-arming.\n */\nconst BUFFER_SAVE_MAX_DEFER_MULTIPLIER = 5;\n\n/**\n * Node's maximum 32-bit `setTimeout` delay (2^31-1 ms, ~24.8 days). A larger\n * value is overflow-clamped by Node to 1ms with a TimeoutOverflowWarning, so we\n * clamp defensively at every timer arm (issue #1909 review round 5) even though\n * parseConfig already caps `bufferSaveDebounceMs` — a directly-constructed config\n * could still exceed it.\n */\nconst MAX_SET_TIMEOUT_MS = 2_147_483_647;\n\n/**\n * Minimal data carried on the serialized telemetry write chain\n * (issue #563 PR 3).\n *\n * We intentionally do NOT capture the full `BufferTurn` here: under\n * slow filesystem latency the chain can back up, and retaining\n * `turn.content` for every pending append causes memory pressure on\n * large conversations. Only the fields the ledger row actually needs\n * cross the chain boundary.\n */\ninterface SurpriseTelemetryQueueEntry {\n bufferKey: string;\n turnRole: \"user\" | \"assistant\";\n sessionKey: string | null;\n surpriseScore: number;\n triggered: boolean;\n turnCountInWindow: number;\n /**\n * ISO timestamp captured at the moment the turn was scored, NOT when\n * the ledger append eventually runs. Backpressure on the serialized\n * write chain could otherwise shift event timestamps away from the\n * real decision moment and distort the distribution report (p90\n * inflated, current-threshold row misidentified).\n */\n timestamp: string;\n /**\n * Threshold value in force when `triggered` was computed. Must be\n * snapshot here rather than read from `config` at emit time — a\n * concurrent config change between queue and write would otherwise\n * record `triggered=true` against a newer threshold the operator\n * never set, distorting precision/recall interpretation.\n */\n threshold: number;\n}\n\ninterface AddTurnMutationResult {\n decision: TriggerDecision;\n signalLevel: SignalLevel;\n priorTurns: BufferTurn[];\n activeTurnsSnapshot: BufferTurn[];\n retainedTurnsSnapshot: BufferTurn[];\n turnCountInWindow: number;\n}\n\nexport class SmartBuffer {\n private state: BufferState;\n private loaded = false;\n private loadPromise: Promise<void> | null = null;\n private readonly surpriseProbe: BufferSurpriseProbe | null;\n private mutationChain: Promise<unknown> = Promise.resolve();\n /**\n * Serialized write chain for `BUFFER_SURPRISE` telemetry events.\n *\n * The telemetry path is fire-and-forget (`addTurn` does not await the\n * ledger append), but multiple concurrent appends would still settle\n * out of order under variable filesystem latency. The report path\n * assumes chronological ordering — it slices the tail of the ledger\n * and treats the most recent entry as the current threshold in force.\n * Chaining ensures each append only runs after the previous settles,\n * preserving wall-clock order.\n *\n * We include a `.catch` on every link so a rejected append does not\n * poison the chain (CLAUDE.md rule #40).\n */\n private surpriseTelemetryWriteChain: Promise<unknown> = Promise.resolve();\n\n /**\n * Debounced buffer-save state (issue #1909). Steady-state buffering used to\n * serialize + rewrite the whole multi-session buffer on EVERY turn. We now\n * coalesce those writes onto a trailing-edge timer; correctness boundaries\n * (extraction trigger, extraction clear, shutdown) still force an immediate\n * flush. `bufferSaveDebounceMs: 0` restores save-every-turn exactly.\n */\n private saveTimer: NodeJS.Timeout | null = null;\n private pendingSave = false;\n /** Wall-clock ms when the currently-pending save was first scheduled (issue\n * #1909). Null when nothing is pending. Used to bound deferral under sustained\n * activity (see BUFFER_SAVE_MAX_DEFER_MULTIPLIER). */\n private firstPendingAtMs: number | null = null;\n\n constructor(\n private readonly config: PluginConfig,\n private readonly storage: StorageManager,\n surpriseProbe: BufferSurpriseProbe | null = null,\n ) {\n this.state = { turns: [], lastExtractionAt: null, extractionCount: 0 };\n this.surpriseProbe = surpriseProbe;\n }\n\n private enqueueMutation<T>(op: () => Promise<T>): Promise<T> {\n const run = this.mutationChain.catch(() => {}).then(op);\n this.mutationChain = run.catch(() => {});\n return run;\n }\n\n private entryFor(key: string): BufferEntryState {\n if (!this.state.entries) {\n this.state.entries = Object.create(null) as NonNullable<BufferState[\"entries\"]>;\n }\n const entries = this.state.entries as NonNullable<BufferState[\"entries\"]>;\n if (Object.hasOwn(entries, key)) {\n const stored = entries[key];\n // Guard against corrupted state/buffer.json — if the stored entry\n // is not a valid object shape, discard it and recreate.\n if (stored && typeof stored === \"object\" && Array.isArray(stored.turns)) {\n return stored;\n }\n // Corrupted — fall through to recreate.\n }\n const created: BufferEntryState = {\n turns: [],\n lastExtractionAt: null,\n extractionCount: 0,\n };\n entries[key] = created;\n return created;\n }\n\n private peekEntry(key: string): BufferEntryState | null {\n const existing = this.state.entries?.[key];\n if (existing) return existing;\n if (key !== \"default\") return null;\n return {\n turns: Array.isArray(this.state.turns) ? this.state.turns : [],\n lastExtractionAt: this.state.lastExtractionAt ?? null,\n extractionCount:\n typeof this.state.extractionCount === \"number\" ? this.state.extractionCount : 0,\n };\n }\n\n private normalizeState(state: BufferState): BufferState {\n const entries = Object.assign(\n Object.create(null),\n state.entries ?? {},\n ) as NonNullable<BufferState[\"entries\"]>;\n if (!entries.default) {\n entries.default = {\n turns: Array.isArray(state.turns) ? [...state.turns] : [],\n lastExtractionAt: state.lastExtractionAt ?? null,\n extractionCount:\n typeof state.extractionCount === \"number\" ? state.extractionCount : 0,\n };\n }\n return {\n turns: entries.default.turns,\n lastExtractionAt: entries.default.lastExtractionAt,\n extractionCount: entries.default.extractionCount,\n entries,\n };\n }\n\n private entryActivityAt(entry: BufferEntryState): number {\n const lastTurnAt = entry.turns.reduce((latest, turn) => {\n const parsed = Date.parse(turn.timestamp);\n return Number.isFinite(parsed) ? Math.max(latest, parsed) : latest;\n }, -1);\n const lastExtractionAt =\n typeof entry.lastExtractionAt === \"string\"\n ? Date.parse(entry.lastExtractionAt)\n : Number.NaN;\n return Math.max(\n lastTurnAt,\n Number.isFinite(lastExtractionAt) ? lastExtractionAt : -1,\n );\n }\n\n private pruneEntries(retainKeys: string[]): void {\n const entries = this.state.entries;\n if (!entries) return;\n const keys = Object.keys(entries);\n if (keys.length <= MAX_BUFFER_ENTRY_COUNT) return;\n\n const insertionOrder = new Map(keys.map((key, index) => [key, index]));\n const byOldestActivity = (left: string, right: string): number => {\n const leftAt = this.entryActivityAt(entries[left] ?? {\n turns: [],\n lastExtractionAt: null,\n extractionCount: 0,\n });\n const rightAt = this.entryActivityAt(entries[right] ?? {\n turns: [],\n lastExtractionAt: null,\n extractionCount: 0,\n });\n if (leftAt !== rightAt) return leftAt - rightAt;\n return (insertionOrder.get(left) ?? 0) - (insertionOrder.get(right) ?? 0);\n };\n const candidates = keys.filter((key) => key !== \"default\" && !retainKeys.includes(key));\n const emptyRemovable = candidates\n .filter((key) => (entries[key]?.turns.length ?? 0) === 0)\n .sort(byOldestActivity);\n\n const removableCount = Math.max(0, keys.length - MAX_BUFFER_ENTRY_COUNT);\n let evicted = emptyRemovable.slice(0, removableCount);\n if (evicted.length < removableCount) {\n // Retained failed extractions (#1908 retry retention) keep their turns in\n // the buffer, so empty entries alone may not bring the map back under the\n // cap during a provider outage. The cap is the documented bound — evict\n // the oldest NON-empty sessions too rather than growing unboundedly\n // (codex review).\n const nonEmptyRemovable = candidates\n .filter((key) => (entries[key]?.turns.length ?? 0) > 0)\n .sort(byOldestActivity);\n evicted = evicted.concat(nonEmptyRemovable.slice(0, removableCount - evicted.length));\n }\n for (const key of evicted) {\n delete entries[key];\n }\n if (evicted.length > 0) {\n // Loud degradation (extraction hot-loop hardening, Step 7): during a\n // prolonged provider outage the breaker holds extraction off and buffered\n // sessions accumulate. Eviction of the oldest empty session entries past\n // MAX_BUFFER_ENTRY_COUNT is bounded and expected, but must be observable —\n // not silent — so operators can see buffer pressure.\n log.warn(\n `buffer: pruned ${evicted.length} oldest session entr${evicted.length === 1 ? \"y\" : \"ies\"} ` +\n `past MAX_BUFFER_ENTRY_COUNT=${MAX_BUFFER_ENTRY_COUNT}; buffered turn data for those sessions was dropped`,\n );\n }\n }\n\n private async loadUnlocked(): Promise<void> {\n if (this.loaded) return;\n if (!this.loadPromise) {\n this.loadPromise = this.storage.loadBuffer()\n .then((state) => {\n this.state = this.normalizeState(state);\n this.loaded = true;\n })\n .finally(() => {\n this.loadPromise = null;\n });\n }\n await this.loadPromise;\n }\n\n async load(): Promise<void> {\n await this.enqueueMutation(async () => this.loadUnlocked());\n }\n\n /**\n * Reset the buffer to an empty, usable state.\n * Called when the persisted buffer file is corrupt and load() fails,\n * so the buffer can still accept new turns for the rest of the session.\n */\n resetToEmpty(): void {\n this.state = { turns: [], lastExtractionAt: null, extractionCount: 0 };\n this.loaded = true;\n }\n\n private async saveUnlocked(): Promise<void> {\n await this.storage.saveBuffer(this.state);\n }\n\n async save(): Promise<void> {\n await this.enqueueMutation(async () => this.saveUnlocked());\n }\n\n /**\n * Schedule a coalesced, TRUE trailing-edge buffer save (issue #1909). Only\n * used for the debounced (`bufferSaveDebounceMs > 0`) steady-state buffering\n * path; the debounce-off and correctness-boundary paths save inline within the\n * mutation (see `recordTurnUnlocked`). Each call re-arms the timer from now so\n * the write lands one full window after the LAST turn, not the first. To keep\n * sustained activity from deferring the save without bound (which would widen\n * the crash-loss window), once a pending save has been deferred\n * BUFFER_SAVE_MAX_DEFER_MULTIPLIER windows it is flushed inline instead of\n * re-armed. Runs inside the record mutation, so the inline save is awaited.\n */\n private async scheduleSave(): Promise<void> {\n const ms = Math.min(this.config.bufferSaveDebounceMs, MAX_SET_TIMEOUT_MS);\n const now = Date.now();\n this.pendingSave = true;\n if (this.firstPendingAtMs === null) this.firstPendingAtMs = now;\n if (now - this.firstPendingAtMs >= ms * BUFFER_SAVE_MAX_DEFER_MULTIPLIER) {\n // Staleness cap hit: persist now rather than deferring further. Drop the\n // armed timer, attempt the write, and only mark clean on success — a\n // failure keeps the save pending + re-arms so shutdown/the next timer\n // retries (issue #1909 review round 2), never diverging memory from disk.\n clearTimeout(this.saveTimer ?? undefined);\n this.saveTimer = null;\n try {\n await this.saveUnlocked();\n this.pendingSave = false;\n this.firstPendingAtMs = null;\n } catch (err) {\n log.warn(`buffer.scheduleSave: staleness-cap save failed, keeping it pending: ${describeError(err)}`);\n this.saveTimer = setTimeout(() => {\n this.saveTimer = null;\n void this.flushPendingSave();\n }, ms);\n this.saveTimer.unref?.();\n }\n return;\n }\n // True trailing edge: drop any armed timer and re-arm from now.\n clearTimeout(this.saveTimer ?? undefined);\n this.saveTimer = setTimeout(() => {\n this.saveTimer = null;\n void this.flushPendingSave();\n }, ms);\n this.saveTimer.unref?.();\n }\n\n /** Cancel any armed debounced save without persisting. */\n private cancelScheduledSave(): void {\n if (this.saveTimer) {\n clearTimeout(this.saveTimer);\n this.saveTimer = null;\n }\n this.pendingSave = false;\n this.firstPendingAtMs = null;\n }\n\n /**\n * Persist the current in-memory buffer state inline, keeping the global\n * pending-save state armed until the write SUCCEEDS (issue #1909). On failure\n * the save stays pending and — when debounced — a background retry timer is\n * (re-)armed before the error propagates, so a graceful shutdown flush and the\n * next timer tick both retry. A failed post-mutation save therefore can never\n * silently drop buffered turns, including turns from OTHER sessions that were\n * only in the debounced pending state. The pending flag is cleared (and any\n * armed timer dropped) only after a durable write.\n */\n private async saveNowRetainingPendingOnFailure(context: string): Promise<void> {\n this.pendingSave = true;\n if (this.firstPendingAtMs === null) this.firstPendingAtMs = Date.now();\n try {\n await this.saveUnlocked();\n } catch (err) {\n log.warn(\n `buffer.${context}: inline save failed, keeping it pending for retry: ${describeError(err)}`,\n );\n const ms = Math.min(this.config.bufferSaveDebounceMs, MAX_SET_TIMEOUT_MS);\n if (ms > 0 && !this.saveTimer) {\n this.saveTimer = setTimeout(() => {\n this.saveTimer = null;\n void this.flushPendingSave();\n }, ms);\n this.saveTimer.unref?.();\n }\n throw err;\n }\n this.cancelScheduledSave();\n }\n\n /**\n * Force any pending debounced save to land now (issue #1909). Idempotent and\n * safe to call from outside a mutation (timer tick, shutdown/dispose, or the\n * surprise-promotion path in `addTurnWithOutcome`). Do NOT `await` this from\n * inside a mutation — it enqueues its own mutation and would deadlock the\n * serializer.\n *\n * `throwOnFailure` (review round 7 finding 5): when set, a failed write is\n * rethrown AFTER the keep-pending + re-arm bookkeeping, so the surprise\n * extraction boundary gets the same durability guarantee as the built-in\n * extract triggers (which propagate saveUnlocked failures) instead of\n * proceeding to extract_now on a non-durable triggering turn. Default false\n * keeps the fail-open behavior for the timer/shutdown callers.\n */\n async flushPendingSave(opts?: { throwOnFailure?: boolean }): Promise<void> {\n if (this.saveTimer) {\n clearTimeout(this.saveTimer);\n this.saveTimer = null;\n }\n if (!this.pendingSave) return;\n try {\n await this.enqueueMutation(async () => {\n // Re-check under the serializer: a concurrent flush may have already\n // persisted this pending save. Skip the redundant write (no phantom\n // writes).\n if (!this.pendingSave) return;\n await this.saveUnlocked();\n // Clear the pending flag INSIDE the same serialized mutation as the\n // write (issue #1909 review). Clearing it after the enqueue await\n // resolved raced an addTurn interleaving between the write and the\n // clear: that turn's scheduleSave re-armed pendingSave + a timer, then\n // this clear wiped it and the debounced flush was lost. Atomic\n // write+clear means any addTurn runs wholly before (its turn is in the\n // write) or wholly after (its pendingSave survives and its timer\n // persists it).\n this.pendingSave = false;\n this.firstPendingAtMs = null;\n });\n } catch (err) {\n // The write failed: memory and disk diverge (issue #1909 review round 2).\n // Keep the save PENDING (do NOT clear pendingSave/firstPendingAtMs) so a\n // graceful shutdown flush and the re-armed timer both retry it — clearing\n // the flag here would drop the buffered turns permanently. Re-arm a\n // background retry when debounced.\n log.warn(`buffer.flushPendingSave: save failed, keeping it pending for retry: ${describeError(err)}`);\n const ms = Math.min(this.config.bufferSaveDebounceMs, MAX_SET_TIMEOUT_MS);\n if (ms > 0 && !this.saveTimer) {\n this.saveTimer = setTimeout(() => {\n this.saveTimer = null;\n void this.flushPendingSave();\n }, ms);\n this.saveTimer.unref?.();\n }\n // Surprise-extraction boundary needs the durability guarantee — surface\n // the failure so the caller does not proceed with a non-durable flush.\n if (opts?.throwOnFailure) throw err;\n return; // fail-open for timer/shutdown callers\n }\n }\n\n async addTurn(bufferKey: string, turn: BufferTurn): Promise<TriggerDecision> {\n return (await this.addTurnWithOutcome(bufferKey, turn)).decision;\n }\n\n async addTurnWithOutcome(\n bufferKey: string,\n turn: BufferTurn,\n ): Promise<AddTurnOutcome> {\n const mutation = await this.enqueueMutation(() => this.recordTurnUnlocked(bufferKey, turn));\n let decision = mutation.decision;\n let extractionTurns: BufferTurn[] | undefined;\n\n // Surprise-gated flush (issue #563). Additive only: if the probe is\n // disabled, unavailable, or the score is below threshold, the decision\n // from the existing trigger logic stands. The probe only ever *promotes*\n // `keep_buffering` → `extract_now`; it never suppresses an existing\n // flush. This preserves the invariant that enabling surprise cannot\n // *reduce* extraction frequency.\n if (\n decision === \"keep_buffering\" &&\n resolvePresentationCapabilities(this.config).bufferSurpriseTrigger &&\n this.surpriseProbe !== null &&\n // Matching the existing \"smart\" branch: surprise is a lower-tier\n // novelty signal that should not second-guess a high-signal hit\n // (which already flushes) or fight `every_n` / `time_based` modes.\n this.config.triggerMode === \"smart\" &&\n mutation.signalLevel !== \"high\"\n ) {\n const surprise = await this.computeSurpriseSafe(bufferKey, turn, mutation.priorTurns);\n if (surprise !== null) {\n const shouldPromote = surprise > this.config.bufferSurpriseThreshold;\n let triggered = false;\n if (shouldPromote) {\n const currentTurns = await this.getExtractionTurnsIfBufferSnapshotStillCurrent(\n bufferKey,\n mutation.activeTurnsSnapshot,\n mutation.retainedTurnsSnapshot,\n );\n if (currentTurns) {\n log.debug(\n `buffer[${bufferKey}]: surprise=${surprise.toFixed(3)} > threshold=${this.config.bufferSurpriseThreshold} → extract_now`,\n );\n decision = \"extract_now\";\n triggered = true;\n extractionTurns = currentTurns;\n // Issue #1909 (review): the record mutation for a `keep_buffering`\n // turn only SCHEDULED a debounced save. Surprise now promotes it to\n // extract_now AFTER that mutation, so force the buffer durable before\n // the caller runs extraction — otherwise state/buffer.json lags the\n // extracted turns by up to the debounce window on a crash. Safe to\n // await here: we are outside the record mutation (deadlock-free).\n // throwOnFailure (round 7 finding 5): match the built-in extract\n // triggers — a failed durability write must NOT silently proceed to\n // extract_now on a non-durable turn.\n await this.flushPendingSave({ throwOnFailure: true });\n } else {\n log.debug(\n `buffer[${bufferKey}]: surprise=${surprise.toFixed(3)} ignored because buffer changed before probe resolved`,\n );\n }\n }\n // Emit telemetry on every scored turn — both triggering and\n // non-triggering — so operators can fit the threshold to real\n // traffic distributions. Fire-and-forget: `addTurn` does NOT\n // await the ledger append, so slow/contended filesystems cannot\n // add JSONL-append latency to every `processTurn`. But we DO\n // serialize writes through a promise chain so concurrent\n // appends settle in wall-clock order — the report path assumes\n // chronological tail rows and reads the most recent as the\n // \"current\" threshold.\n //\n // Project only the fields we need into the queue entry rather\n // than capturing the full `BufferTurn` — under slow filesystem\n // latency the chain can back up, and we must not retain the\n // (potentially large) `turn.content` string for every pending\n // append.\n this.queueSurpriseTelemetryWrite({\n bufferKey,\n turnRole: turn.role,\n sessionKey:\n typeof turn.sessionKey === \"string\" ? turn.sessionKey : null,\n surpriseScore: surprise,\n triggered,\n turnCountInWindow: mutation.turnCountInWindow,\n // Stamp at decision time so backpressure on the write chain\n // does not shift the event's apparent moment away from when\n // the turn was actually scored.\n timestamp: new Date().toISOString(),\n // Snapshot the threshold used to compute `triggered` so a\n // concurrent config mutation cannot retroactively change\n // what the ledger row claims the decision was against.\n threshold: this.config.bufferSurpriseThreshold,\n });\n }\n }\n\n log.debug(\n `buffer[${bufferKey}]: ${mutation.turnCountInWindow} turns, signal=${mutation.signalLevel}, decision=${decision}`,\n );\n return extractionTurns ? { decision, extractionTurns } : { decision };\n }\n\n private async recordTurnUnlocked(bufferKey: string, turn: BufferTurn): Promise<AddTurnMutationResult> {\n await this.loadUnlocked();\n const entry = this.entryFor(bufferKey);\n const priorTurns = entry.turns.slice();\n entry.turns.push(turn);\n const activeTurnsSnapshot = entry.turns.map(copyBufferTurn);\n const retainedTurnsSnapshot = (entry.retainedTurns ?? []).map(copyBufferTurn);\n if (bufferKey === \"default\") {\n this.state.turns = entry.turns;\n }\n\n const signal = scanSignals(turn.content, this.config.highSignalPatterns);\n const decision = this.evaluate(entry, signal.level);\n const turnCountInWindow = entry.turns.length;\n\n this.pruneEntries([bufferKey]);\n if (decision === \"keep_buffering\" && this.config.bufferSaveDebounceMs > 0) {\n // Steady-state buffering: coalesce the whole-state serialize onto a\n // trailing-edge timer (issue #1909) instead of rewriting per turn.\n await this.scheduleSave();\n } else {\n // Persist immediately within this mutation (awaited) when either:\n // - the turn triggered extraction (the buffer must be durable), or\n // - debounce is disabled (bufferSaveDebounceMs <= 0), which reproduces\n // the legacy save-every-turn behavior byte-for-byte.\n // Round 8 thread 4: the write is attempted BEFORE the pending state is\n // cleared, and a failed write leaves it pending (+ a re-armed timer) for\n // shutdown/timer retry instead of dropping the in-memory turns.\n await this.saveNowRetainingPendingOnFailure(\"recordTurn\");\n }\n return {\n decision,\n signalLevel: signal.level,\n priorTurns,\n activeTurnsSnapshot,\n retainedTurnsSnapshot,\n turnCountInWindow,\n };\n }\n\n private async getExtractionTurnsIfBufferSnapshotStillCurrent(\n bufferKey: string,\n activeTurnsSnapshot: readonly BufferTurn[],\n retainedTurnsSnapshot: readonly BufferTurn[],\n ): Promise<BufferTurn[] | null> {\n return this.enqueueMutation(async () => {\n await this.loadUnlocked();\n const entry = this.peekEntry(bufferKey);\n if (!entry) return null;\n const retained = entry.retainedTurns ?? [];\n const stillCurrent =\n bufferTurnArrayIsSuffixOfSnapshot(entry.turns, activeTurnsSnapshot) &&\n bufferTurnArraysEqual(retained, retainedTurnsSnapshot);\n if (!stillCurrent) return null;\n return [...retained, ...entry.turns];\n });\n }\n\n /**\n * Enqueue a telemetry append on the serialized write chain.\n *\n * The chain is a classic `writeChain = writeChain.then(fn).catch(...)`\n * — each link waits for the previous to settle before its append\n * starts, so out-of-order chronology cannot happen even under\n * variable filesystem latency. We always attach `.catch` so one\n * rejection does not poison the chain for the rest of the session\n * (CLAUDE.md rule #40). The error is logged through\n * `emitSurpriseEventSafe` itself, which swallows its own rejections.\n *\n * Public surface is deliberately narrow — only `addTurn` should call\n * this, so the surprise telemetry path stays centralized.\n */\n private queueSurpriseTelemetryWrite(params: SurpriseTelemetryQueueEntry): void {\n this.surpriseTelemetryWriteChain = this.surpriseTelemetryWriteChain\n .then(() => this.emitSurpriseEventSafe(params))\n .catch(() => {\n // `emitSurpriseEventSafe` already handles the logging. We\n // swallow here only so one failure does not break the chain\n // for future writes.\n });\n }\n\n /**\n * Append a single `BUFFER_SURPRISE` telemetry row (issue #563 PR 3).\n *\n * Deliberately swallows write errors: the buffer must never fail to\n * record a turn because the observation ledger is read-only, out of\n * disk, or otherwise unhappy. The log line at debug lets operators\n * confirm the path fired without polluting the error channel.\n */\n private async emitSurpriseEventSafe(\n params: SurpriseTelemetryQueueEntry,\n ): Promise<void> {\n const storage = this.storage as StorageManager & {\n appendBufferSurpriseEvents?: (\n events: BufferSurpriseEvent[],\n ) => Promise<number>;\n };\n if (typeof storage.appendBufferSurpriseEvents !== \"function\") {\n // Older StorageManager / test double without the telemetry sink.\n // Silently skip — core path is still covered by the log line above.\n return;\n }\n const event: BufferSurpriseEvent = {\n event: \"BUFFER_SURPRISE\",\n // Use the decision-time stamp captured when the event was\n // queued, NOT `Date.now()` here — backpressure on the write\n // chain could otherwise shift timestamps into the future relative\n // to when the turn was scored.\n timestamp: params.timestamp,\n bufferKey: params.bufferKey,\n sessionKey: params.sessionKey,\n turnRole: params.turnRole,\n surpriseScore: params.surpriseScore,\n // Use the snapshotted threshold from the queue entry, not the\n // live config — see `SurpriseTelemetryQueueEntry.threshold`\n // doc for the rationale.\n threshold: params.threshold,\n triggeredFlush: params.triggered,\n turnCountInWindow: params.turnCountInWindow,\n };\n try {\n await storage.appendBufferSurpriseEvents([event]);\n } catch (err) {\n // Same guard as `computeSurpriseSafe`: non-Error rejections must\n // not crash the telemetry helper, which would defeat the whole\n // point of isolating the ledger write from the hot path.\n log.debug(\n `buffer[${params.bufferKey}]: surprise telemetry write failed, continuing: ${describeError(err)}`,\n );\n }\n }\n\n /**\n * Invoke the injected surprise probe defensively. Any error (probe throws,\n * embedder unavailable, timeout) is swallowed and logged at debug: the\n * surprise path must never crash the happy-path trigger evaluation. A\n * `null` return indicates \"no score available, fall through to existing\n * triggers\".\n */\n private async computeSurpriseSafe(\n bufferKey: string,\n turn: BufferTurn,\n priorTurns: readonly BufferTurn[],\n ): Promise<number | null> {\n if (!this.surpriseProbe) return null;\n try {\n // Hard timeout around the probe so a hung embedder cannot stall\n // `addTurn()` before `save()`. A slow probe would otherwise\n // prevent the just-appended turn from ever being persisted. The\n // timeout is a soft bound — we race it against the probe, take\n // whichever settles first, and treat the timeout as\n // \"probe unavailable, fall through\" rather than an error that\n // surfaces to the caller.\n const score = await probeWithTimeout(\n this.surpriseProbe.scoreTurn(bufferKey, turn, priorTurns),\n this.config.bufferSurpriseProbeTimeoutMs,\n );\n if (score === null) return null;\n if (typeof score !== \"number\" || !Number.isFinite(score)) {\n log.debug(\n `buffer[${bufferKey}]: surprise probe returned non-finite score (${String(score)}), ignoring`,\n );\n return null;\n }\n // Defensive clamp: formula lives in buffer-surprise.ts, but we never\n // want a misbehaving probe to inject an out-of-range value into the\n // threshold comparison.\n if (score < 0) return 0;\n if (score > 1) return 1;\n return score;\n } catch (err) {\n // `err` may be any thrown value — `throw null` and\n // `Promise.reject(\"x\")` are both legal. Accessing `.message` on a\n // non-Error would itself throw and defeat the failure-isolation\n // contract, so describe the value safely.\n log.debug(\n `buffer[${bufferKey}]: surprise probe failed, falling back to existing triggers: ${describeError(err)}`,\n );\n return null;\n }\n }\n\n private evaluate(entry: BufferEntryState, signalLevel: SignalLevel): TriggerDecision {\n if (this.config.triggerMode === \"smart\") {\n if (signalLevel === \"high\") return \"extract_now\";\n\n if (entry.turns.length >= this.config.bufferMaxTurns) {\n return \"extract_batch\";\n }\n\n if (entry.lastExtractionAt) {\n const elapsed =\n Date.now() - new Date(entry.lastExtractionAt).getTime();\n if (elapsed >= this.config.bufferMaxMinutes * 60_000) {\n return \"extract_batch\";\n }\n }\n\n return \"keep_buffering\";\n }\n\n if (this.config.triggerMode === \"every_n\") {\n return entry.turns.length >= this.config.bufferMaxTurns\n ? \"extract_batch\"\n : \"keep_buffering\";\n }\n\n if (this.config.triggerMode === \"time_based\") {\n if (!entry.lastExtractionAt) {\n return entry.turns.length >= this.config.bufferMaxTurns\n ? \"extract_batch\"\n : \"keep_buffering\";\n }\n const elapsed =\n Date.now() - new Date(entry.lastExtractionAt).getTime();\n return elapsed >= this.config.bufferMaxMinutes * 60_000\n ? \"extract_batch\"\n : \"keep_buffering\";\n }\n\n return \"keep_buffering\";\n }\n\n getTurns(bufferKey = \"default\"): BufferTurn[] {\n const entry = this.peekEntry(bufferKey);\n if (!entry) return [];\n const retained = entry.retainedTurns ?? [];\n // Retained turns (from a previous defer verdict, issue #562 PR 2) are\n // prepended so the chronological order — oldest context first — is\n // preserved for the next extraction pass.\n return [...retained, ...entry.turns];\n }\n\n /**\n * Retain a subset of the current turns across `clearAfterExtraction` so a\n * future extraction pass sees the context behind a deferred candidate\n * (issue #562, PR 2). Callers pass the turns that were seen during the\n * current extraction; the buffer keeps the tail (latest `max` turns) as\n * the retention window. Passing an empty array or `max <= 0` clears the\n * retention slot instead.\n */\n async retainDeferredTurns(\n bufferKey: string,\n turns: BufferTurn[],\n max = 10,\n ): Promise<void> {\n await this.enqueueMutation(async () => {\n await this.loadUnlocked();\n const entry = this.entryFor(bufferKey);\n if (!Array.isArray(turns) || turns.length === 0 || max <= 0) {\n delete entry.retainedTurns;\n } else {\n // Guard `slice(-max)` against `max === 0` (CLAUDE.md gotcha 27):\n // `slice(-0)` equals `slice(0)` and would return ALL entries. We\n // already early-return above when max <= 0.\n const tail = turns.slice(-max);\n // Copy explicit fields only — never spread an external object into a\n // plain object because spread preserves any own `__proto__` /\n // `constructor` keys that may have arrived via JSON deserialization\n // of untrusted input (CodeQL js/prototype-polluting-assignment).\n entry.retainedTurns = tail.map<BufferTurn>((t) => {\n const copy: BufferTurn = {\n role: t.role,\n content: typeof t.content === \"string\" ? t.content : \"\",\n timestamp:\n typeof t.timestamp === \"string\"\n ? t.timestamp\n : new Date().toISOString(),\n };\n if (typeof t.sessionKey === \"string\") copy.sessionKey = t.sessionKey;\n if (typeof t.logicalSessionKey === \"string\") {\n copy.logicalSessionKey = t.logicalSessionKey;\n }\n if (\n t.providerThreadId === null ||\n typeof t.providerThreadId === \"string\"\n ) {\n copy.providerThreadId = t.providerThreadId;\n }\n if (typeof t.turnFingerprint === \"string\") {\n copy.turnFingerprint = t.turnFingerprint;\n }\n if (typeof t.persistProcessedFingerprint === \"boolean\") {\n copy.persistProcessedFingerprint = t.persistProcessedFingerprint;\n }\n if (typeof t.sourceConnector === \"string\") copy.sourceConnector = t.sourceConnector;\n return copy;\n });\n }\n await this.saveUnlocked();\n });\n }\n\n /**\n * Return the current retention window (issue #562, PR 2). Primarily for\n * tests and diagnostics.\n */\n getRetainedDeferredTurns(bufferKey = \"default\"): BufferTurn[] {\n const entry = this.peekEntry(bufferKey);\n return entry?.retainedTurns ? [...entry.retainedTurns] : [];\n }\n\n async findBufferKeyForSession(sessionKey: string): Promise<string | null> {\n const bufferKeys = await this.findBufferKeysForSession(sessionKey);\n return bufferKeys[0] ?? null;\n }\n\n async findBufferKeysForSession(sessionKey: string): Promise<string[]> {\n if (typeof sessionKey !== \"string\" || sessionKey.length === 0) return [];\n await this.mutationChain.catch(() => {});\n await this.load();\n\n const matches: string[] = [];\n const directEntry = this.peekEntry(sessionKey);\n if ((directEntry?.turns.length ?? 0) > 0) {\n matches.push(sessionKey);\n }\n\n const entries = this.state.entries ?? {};\n for (const [bufferKey, entry] of Object.entries(entries)) {\n if (\n !matches.includes(bufferKey) &&\n entry.turns.some(\n (turn) =>\n typeof turn.sessionKey === \"string\" && turn.sessionKey === sessionKey,\n )\n ) {\n matches.push(bufferKey);\n }\n }\n\n return matches;\n }\n\n async clearAfterExtraction(\n bufferKey = \"default\",\n extractedTurns?: readonly BufferTurn[],\n ): Promise<void> {\n await this.enqueueMutation(async () => {\n // Drop any armed debounce TIMER so it cannot fire mid-mutation and race\n // our post-clear write, but KEEP the pending-save state (issue #1909\n // review round 13): the post-clear save below can fail, and clearing the\n // pending flag here would strand buffered turns — including turns from\n // OTHER sessions that only ever entered the debounced pending state — with\n // nothing left to retry them. saveNowRetainingPendingOnFailure clears the\n // pending state only after a durable write, or re-arms it on failure.\n if (this.saveTimer) {\n clearTimeout(this.saveTimer);\n this.saveTimer = null;\n }\n await this.loadUnlocked();\n const entry = this.entryFor(bufferKey);\n if (Array.isArray(extractedTurns)) {\n const liveExtractedTurns = liveTurnsFromExtractionSnapshot(\n entry,\n extractedTurns,\n );\n let clearedLiveTurns = false;\n if (liveExtractedTurns.length > 0) {\n const matchedCount = matchingQueuedExtractionPrefixLength(\n entry.turns,\n liveExtractedTurns,\n );\n if (matchedCount > 0) {\n entry.turns = entry.turns.slice(matchedCount);\n clearedLiveTurns = true;\n } else {\n log.debug(\n `buffer[${bufferKey}]: extraction clear skipped because live turns changed before clear`,\n );\n }\n }\n if (!clearedLiveTurns) {\n await this.saveNowRetainingPendingOnFailure(\"clearAfterExtraction\");\n return;\n }\n } else {\n entry.turns = [];\n }\n entry.lastExtractionAt = new Date().toISOString();\n entry.extractionCount += 1;\n if (bufferKey === \"default\") {\n this.state.turns = entry.turns;\n this.state.lastExtractionAt = entry.lastExtractionAt;\n this.state.extractionCount = entry.extractionCount;\n }\n this.pruneEntries([bufferKey]);\n await this.saveNowRetainingPendingOnFailure(\"clearAfterExtraction\");\n });\n }\n\n getExtractionCount(bufferKey = \"default\"): number {\n return this.peekEntry(bufferKey)?.extractionCount ?? 0;\n }\n\n /**\n * Await any pending `BUFFER_SURPRISE` telemetry writes.\n *\n * The telemetry path is fire-and-forget from the hot path's point of\n * view, but tests and before-exit hooks sometimes need to make sure\n * the ledger has been flushed before they assert on its contents or\n * close the process. This method resolves once the current chain\n * head has settled; new writes scheduled after this call return a\n * separate, later settlement.\n *\n * Never throws — the chain already catches its own rejections.\n */\n async flushSurpriseTelemetry(): Promise<void> {\n await this.surpriseTelemetryWriteChain;\n }\n}\n\n/**\n * Render an arbitrary thrown value as a short string for debug logging.\n *\n * JavaScript permits throwing *any* value (`throw null`,\n * `Promise.reject(\"x\")`, `throw { reason: \"timeout\" }`) — not just\n * `Error` instances. The defensive catch blocks in `SmartBuffer` must\n * never themselves throw while trying to log the failure, or they\n * would defeat the whole point of isolating the surprise path from the\n * core extraction decision.\n */\nfunction describeError(err: unknown): string {\n if (err instanceof Error) return err.message;\n if (err === null) return \"null\";\n if (err === undefined) return \"undefined\";\n if (typeof err === \"string\") return err;\n try {\n return JSON.stringify(err);\n } catch {\n return String(err);\n }\n}\n\nfunction copyBufferTurn(turn: BufferTurn): BufferTurn {\n const copy: BufferTurn = {\n role: turn.role,\n content: turn.content,\n timestamp: turn.timestamp,\n };\n if (typeof turn.sessionKey === \"string\") copy.sessionKey = turn.sessionKey;\n if (typeof turn.logicalSessionKey === \"string\") {\n copy.logicalSessionKey = turn.logicalSessionKey;\n }\n if (\n turn.providerThreadId === null ||\n typeof turn.providerThreadId === \"string\"\n ) {\n copy.providerThreadId = turn.providerThreadId;\n }\n if (typeof turn.turnFingerprint === \"string\") {\n copy.turnFingerprint = turn.turnFingerprint;\n }\n if (typeof turn.persistProcessedFingerprint === \"boolean\") {\n copy.persistProcessedFingerprint = turn.persistProcessedFingerprint;\n }\n if (typeof turn.sourceConnector === \"string\") {\n copy.sourceConnector = turn.sourceConnector;\n }\n return copy;\n}\n\nfunction bufferTurnsEqual(left: BufferTurn | undefined, right: BufferTurn): boolean {\n if (!left) return false;\n return (\n left.role === right.role &&\n left.content === right.content &&\n left.timestamp === right.timestamp &&\n left.sessionKey === right.sessionKey &&\n left.logicalSessionKey === right.logicalSessionKey &&\n left.providerThreadId === right.providerThreadId &&\n left.turnFingerprint === right.turnFingerprint &&\n left.persistProcessedFingerprint === right.persistProcessedFingerprint &&\n left.sourceConnector === right.sourceConnector\n );\n}\n\nfunction bufferTurnArraysEqual(\n left: readonly BufferTurn[],\n right: readonly BufferTurn[],\n): boolean {\n return (\n left.length === right.length &&\n left.every((turn, index) => bufferTurnsEqual(turn, right[index]))\n );\n}\n\nfunction bufferTurnArrayIsSuffixOfSnapshot(\n liveTurns: readonly BufferTurn[],\n snapshot: readonly BufferTurn[],\n): boolean {\n if (liveTurns.length === 0 || liveTurns.length > snapshot.length) {\n return false;\n }\n const offset = snapshot.length - liveTurns.length;\n return liveTurns.every((turn, index) =>\n bufferTurnsEqual(turn, snapshot[offset + index]),\n );\n}\n\nfunction liveTurnsFromExtractionSnapshot(\n entry: BufferEntryState,\n extractedTurns: readonly BufferTurn[],\n): readonly BufferTurn[] {\n const retainedTurns = entry.retainedTurns ?? [];\n if (\n retainedTurns.length > 0 &&\n extractedTurns.length >= retainedTurns.length &&\n retainedTurns.every((turn, index) =>\n bufferTurnsEqual(extractedTurns[index], turn),\n )\n ) {\n const withoutRetainedPrefix = extractedTurns.slice(retainedTurns.length);\n if (\n withoutRetainedPrefix.length > 0 &&\n matchingPrefixLength(entry.turns, withoutRetainedPrefix) > 0\n ) {\n return withoutRetainedPrefix;\n }\n }\n return extractedTurns;\n}\n\nfunction matchingPrefixLength(\n liveTurns: readonly BufferTurn[],\n extractedTurns: readonly BufferTurn[],\n): number {\n let index = 0;\n while (\n index < liveTurns.length &&\n index < extractedTurns.length &&\n bufferTurnsEqual(liveTurns[index], extractedTurns[index])\n ) {\n index += 1;\n }\n return index;\n}\n\nfunction matchingQueuedExtractionPrefixLength(\n liveTurns: readonly BufferTurn[],\n extractedTurns: readonly BufferTurn[],\n): number {\n let bestMatchedCount = 0;\n for (let start = 0; start < extractedTurns.length; start += 1) {\n const matchedCount = matchingPrefixLength(\n liveTurns,\n extractedTurns.slice(start),\n );\n if (matchedCount > bestMatchedCount) {\n bestMatchedCount = matchedCount;\n if (bestMatchedCount === liveTurns.length) break;\n }\n }\n return bestMatchedCount;\n}\n\n/**\n * Sentinel error class for the probe timeout path. Catching it via\n * `instanceof` lets the buffer's surprise helper distinguish a timeout\n * from a probe rejection (which could carry operational context the\n * operator wants to see).\n */\nclass ProbeTimeoutError extends Error {\n constructor(timeoutMs: number) {\n super(`probe exceeded ${timeoutMs}ms`);\n this.name = \"ProbeTimeoutError\";\n }\n}\n\n/**\n * Race `inflight` against a timeout clock. Resolves with `inflight`'s\n * value if it settles first, otherwise rejects with `ProbeTimeoutError`.\n * The timer is cleared in both branches so a fast-resolving probe does\n * not leak a handle that would keep the Node event loop alive.\n */\nfunction probeWithTimeout<T>(\n inflight: Promise<T>,\n timeoutMs: number,\n): Promise<T> {\n let timer: NodeJS.Timeout | null = null;\n const timeout = new Promise<T>((_, reject) => {\n timer = setTimeout(() => reject(new ProbeTimeoutError(timeoutMs)), timeoutMs);\n // `.unref()` so the timer does not hold the event loop open if the\n // caller decides the probe result is no longer interesting.\n if (typeof (timer as NodeJS.Timeout).unref === \"function\") {\n (timer as NodeJS.Timeout).unref();\n }\n });\n return Promise.race([inflight, timeout]).finally(() => {\n if (timer) clearTimeout(timer);\n });\n}\n"],"mappings":"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","names":[]}
|