@remnic/core 9.66.7 → 9.66.9
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 +11 -11
- package/dist/access-admin-ops-surface.js +41 -41
- package/dist/access-authorization-probe.d.ts +11 -11
- package/dist/access-authorization-probe.js +42 -42
- package/dist/access-boundary.d.ts +11 -11
- package/dist/access-boundary.js +41 -41
- package/dist/access-cli.js +101 -99
- 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 +11 -11
- package/dist/access-extraction-force-flush.js +41 -41
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-lcm-compaction.d.ts +11 -11
- package/dist/access-http-lifecycle-flush.d.ts +11 -11
- package/dist/access-http-offline-stream.d.ts +11 -11
- package/dist/access-http-query.d.ts +2 -2
- package/dist/access-http-query.js +43 -43
- package/dist/access-http.d.ts +12 -12
- package/dist/access-http.js +58 -57
- package/dist/access-identity-continuity-surface.d.ts +9 -9
- package/dist/access-identity-continuity-surface.js +41 -41
- package/dist/access-lcm-surface.d.ts +11 -11
- package/dist/access-lcm-surface.js +41 -41
- package/dist/access-mcp-output-schemas.js +1 -1
- package/dist/access-mcp-sse.d.ts +20 -0
- package/dist/access-mcp-sse.js +8 -0
- package/dist/access-mcp-sse.js.map +1 -0
- package/dist/access-mcp.d.ts +11 -11
- package/dist/access-mcp.js +50 -50
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-50VIkPhB.d.ts → access-namespace-preflight-S8fhQcCo.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +41 -41
- package/dist/access-observe-write-surface.d.ts +11 -11
- package/dist/access-observe-write-surface.js +41 -41
- package/dist/access-offline-manifest.d.ts +9 -9
- package/dist/access-offline-manifest.js +5 -5
- package/dist/access-operations-batch.js +42 -42
- package/dist/access-operations.d.ts +11 -11
- package/dist/access-operations.js +47 -47
- package/dist/access-recall-concurrency.d.ts +11 -11
- package/dist/access-recall-concurrency.js +41 -41
- package/dist/access-recall-response.d.ts +11 -11
- package/dist/access-recall-response.js +41 -41
- package/dist/access-recall-surface.d.ts +11 -11
- package/dist/access-recall-surface.js +41 -41
- package/dist/{access-service-Dz6I9AZ-.d.ts → access-service-DcfM0Vf0.d.ts} +7 -7
- package/dist/access-service-helpers.d.ts +11 -11
- package/dist/access-service.d.ts +11 -11
- package/dist/access-service.js +41 -41
- package/dist/access-surface-catalog.d.ts +11 -11
- package/dist/access-surface-catalog.js +43 -43
- 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 +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +3 -3
- package/dist/ambient-provenance.d.ts +1 -1
- package/dist/artifact-search.d.ts +1 -1
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +9 -9
- package/dist/briefing-window.d.ts +2 -2
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +16 -16
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer-turn-helpers.d.ts +1 -1
- package/dist/buffer.d.ts +2 -2
- package/dist/buffer.js +5 -5
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-C2jA-gLm.d.ts → catalog-BmBXTtOs.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 +17 -17
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-52DBRDP5.js → chunk-23F7HGBV.js} +1 -1
- package/dist/{chunk-TNXNECAN.js → chunk-2OXIPF3A.js} +4 -4
- package/dist/{chunk-MSXUEYH6.js → chunk-3HHLOKKU.js} +2 -2
- package/dist/{chunk-OMGQAXUK.js → chunk-47BX23NA.js} +2 -2
- package/dist/{chunk-GO657WPH.js → chunk-52I6AHO6.js} +2 -2
- package/dist/{chunk-QD55QEEY.js → chunk-5CSKIWEB.js} +5 -5
- package/dist/{chunk-NMFPYAJ6.js → chunk-5GZ6UWY5.js} +8 -8
- package/dist/{chunk-KSZKQTZS.js → chunk-5KVMT25I.js} +2 -2
- package/dist/{chunk-D35XLYQ7.js → chunk-5NZIRM4Q.js} +7 -7
- package/dist/{chunk-DB2MKBQ7.js → chunk-7JPKRZD5.js} +2 -2
- package/dist/{chunk-Z3YEGSR4.js → chunk-A7GQBJP5.js} +9 -4
- package/dist/chunk-A7GQBJP5.js.map +1 -0
- package/dist/{chunk-PMNN247H.js → chunk-AAJK3PZ6.js} +2 -2
- package/dist/{chunk-6FUMA6AA.js → chunk-B3TZLAO4.js} +2 -2
- package/dist/{chunk-C24UZSFW.js → chunk-CEH2AFXG.js} +6 -6
- package/dist/{chunk-42GDJAU7.js → chunk-CIWOW2FH.js} +2 -2
- package/dist/{chunk-GNWLTCKC.js → chunk-CNZVEU4X.js} +2 -2
- package/dist/{chunk-CWPTYKCF.js → chunk-DGKVUPBM.js} +3 -3
- package/dist/{chunk-XJE6NHLT.js → chunk-FNYVWAIK.js} +55 -55
- package/dist/{chunk-THZ5UEQZ.js → chunk-FUPIAP6Q.js} +3 -3
- package/dist/{chunk-POSD62F5.js → chunk-G3TUQJFU.js} +2 -2
- package/dist/{chunk-Y3QQZOZ4.js → chunk-G775FE2E.js} +21 -21
- package/dist/{chunk-BEUB5ABO.js → chunk-GB4R6B5M.js} +2 -2
- package/dist/{chunk-XZBXINXZ.js → chunk-GIC6BASP.js} +4 -4
- package/dist/{chunk-6TMIW4HE.js → chunk-HWWGPS2N.js} +2 -2
- package/dist/{chunk-PPAKBRI7.js → chunk-IBHUZEKL.js} +2 -2
- package/dist/{chunk-6LMRUFBH.js → chunk-J67E4JNT.js} +2 -2
- package/dist/{chunk-FKBRP3J2.js → chunk-JHU543UU.js} +2 -2
- package/dist/{chunk-OCYRMVPT.js → chunk-JVGBDNXA.js} +2 -2
- package/dist/{chunk-IGOINKTD.js → chunk-K4E3Y3ZX.js} +2 -2
- package/dist/chunk-K4E3Y3ZX.js.map +1 -0
- package/dist/{chunk-GUG4LW4I.js → chunk-KIJCXLW7.js} +2 -2
- package/dist/{chunk-IN6D6BJG.js → chunk-KVXMN33W.js} +15 -15
- package/dist/chunk-LD72P4YS.js +29 -0
- package/dist/chunk-LD72P4YS.js.map +1 -0
- package/dist/chunk-LTDN5WYF.js +202 -0
- package/dist/chunk-LTDN5WYF.js.map +1 -0
- package/dist/{chunk-2C34EKRJ.js → chunk-MBDUNO7J.js} +2 -2
- package/dist/{chunk-V6LJENGW.js → chunk-MFI4QPW5.js} +2 -2
- package/dist/{chunk-4NKIGSJM.js → chunk-MK7EJLB4.js} +4 -4
- package/dist/{chunk-RACZB5HT.js → chunk-N37X5RU7.js} +4 -4
- package/dist/{chunk-PORKQEAW.js → chunk-NABJLNLU.js} +139 -107
- package/dist/chunk-NABJLNLU.js.map +1 -0
- package/dist/{chunk-AG46P24C.js → chunk-NNBZS3DY.js} +4 -4
- package/dist/{chunk-J2GY4UCR.js → chunk-NSP3HFJZ.js} +2 -2
- package/dist/{chunk-CEK6AHIG.js → chunk-OJSCH7QT.js} +8 -8
- package/dist/{chunk-MAWHEUAA.js → chunk-OT7WQ2I3.js} +2 -2
- package/dist/{chunk-ZW4SVXEY.js → chunk-Q3UMNZRK.js} +6 -6
- package/dist/{chunk-HQI2OQB5.js → chunk-Q5YKVBIV.js} +5 -5
- package/dist/{chunk-FHY6HIAY.js → chunk-QU2V4IO3.js} +2 -2
- package/dist/{chunk-2SW3N5HX.js → chunk-RF3LMSQW.js} +2 -2
- package/dist/{chunk-O7ERGZDA.js → chunk-RJ57OOCD.js} +2 -2
- package/dist/{chunk-KDYU3KZT.js → chunk-RLEJULGN.js} +5 -5
- package/dist/{chunk-7O24F2VS.js → chunk-SJ5L4ZZX.js} +2 -2
- package/dist/{chunk-BWZVFWMX.js → chunk-SOQLXIQX.js} +2 -2
- package/dist/{chunk-ZKE227GO.js → chunk-SRILZVR4.js} +2 -2
- package/dist/chunk-SRT6SHVZ.js +34 -0
- package/dist/chunk-SRT6SHVZ.js.map +1 -0
- package/dist/{chunk-2W4MTU4P.js → chunk-TEJKBBWS.js} +32 -26
- package/dist/chunk-TEJKBBWS.js.map +1 -0
- package/dist/{chunk-YV4ST46A.js → chunk-TKSBOV7N.js} +5 -5
- package/dist/{chunk-67U6UK5Q.js → chunk-TWMGWLHD.js} +2 -2
- package/dist/{chunk-726CXBJO.js → chunk-UAWSKMWQ.js} +2 -2
- package/dist/{chunk-MGVOBLUK.js → chunk-VCYY7XEL.js} +2 -2
- package/dist/{chunk-46XR5FNZ.js → chunk-VSWRJQQW.js} +31 -5
- package/dist/chunk-VSWRJQQW.js.map +1 -0
- package/dist/{chunk-ABPLUXVY.js → chunk-VX5YHEGJ.js} +3 -3
- package/dist/{chunk-SFJPCNBI.js → chunk-XVNDP3RO.js} +2 -2
- package/dist/{chunk-QVEKPVCG.js → chunk-ZC5L6ME2.js} +3 -3
- package/dist/{chunk-VVWOEKOZ.js → chunk-ZJGHOPEO.js} +43 -43
- package/dist/{chunk-4COPICMD.js → chunk-ZVUGRXSY.js} +6 -6
- package/dist/{cli-BOuEb4Ia.d.ts → cli-CFpMhqz5.d.ts} +5 -5
- package/dist/cli.d.ts +13 -13
- package/dist/cli.js +77 -76
- package/dist/coding/export-okf-codegraph.d.ts +1 -1
- package/dist/coding/export-okf-codegraph.js +16 -16
- package/dist/coding/pre-action-gate.d.ts +1 -1
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +16 -16
- 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 +3 -3
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +16 -16
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +17 -17
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/contradiction/index.d.ts +3 -3
- package/dist/converge-config.d.ts +1 -1
- package/dist/convergence-refresh.d.ts +2 -2
- package/dist/convergence-refresh.js +20 -20
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/corpus-watermark.d.ts +1 -1
- package/dist/corpus-watermark.js +19 -19
- package/dist/cross-lingual-recall.d.ts +1 -1
- 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-D4WMbOwA.d.ts → dependency-propagation-delivery-BC3bCEet.d.ts} +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-origin-fields.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +2 -2
- package/dist/entity-retrieval.js +16 -16
- package/dist/entity-schema.d.ts +1 -1
- package/dist/episodic-context.d.ts +1 -1
- package/dist/eval-trajectory-contracts.d.ts +1 -1
- package/dist/eval-trajectory-store.d.ts +1 -1
- package/dist/eval-trajectory.d.ts +1 -1
- package/dist/explicit-capture.d.ts +9 -9
- package/dist/explicit-capture.js +1 -1
- package/dist/external-wiki-access.d.ts +11 -11
- package/dist/external-wiki-access.js +42 -42
- 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 +11 -11
- package/dist/extraction-error-classification.d.ts +1 -1
- package/dist/extraction-faithfulness.d.ts +1 -1
- 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-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 +29 -29
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/{first-start-migration-KHIMCBIC.js → first-start-migration-UG32BSG2.js} +4 -4
- 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-DuJnzwHv.d.ts → index-D4hS9uxK.d.ts} +2 -2
- package/dist/index.d.ts +43 -24
- package/dist/index.js +162 -158
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/engine.js +3 -2
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/index.js +8 -7
- 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 +1 -1
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/location/index.d.ts +3 -3
- package/dist/location/index.js +47 -47
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +17 -17
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +17 -17
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +19 -19
- package/dist/{maintenance-BBeaeYRR.d.ts → maintenance-BRcc3Hx8.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +11 -11
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-controller.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-snapshot.d.ts +1 -1
- package/dist/memory-subject-cli.d.ts +11 -11
- package/dist/memory-subject.d.ts +11 -11
- 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 +17 -17
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +9 -9
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +16 -16
- package/dist/native-binding-hint.d.ts +24 -0
- package/dist/native-binding-hint.js +10 -0
- package/dist/native-binding-hint.js.map +1 -0
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/offline-sync-impression-drain.js +4 -4
- package/dist/operator-doctor-corpus.d.ts +1 -1
- package/dist/operator-doctor-corpus.js +20 -20
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +30 -30
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -4
- package/dist/orchestration/maintenance.js +31 -31
- package/dist/{orchestrator-CaqnwNwM.d.ts → orchestrator-Btq9lzAN.d.ts} +8 -8
- package/dist/orchestrator.d.ts +9 -9
- package/dist/orchestrator.js +124 -122
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-CrxVjlJr.d.ts → pipeline-CZoNdFb5.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-CmQRjQCD.d.ts → public-http-C9-CHc40.d.ts} +1 -1
- package/dist/{qmd-BZgbJm0g.d.ts → qmd-VIjSCKIE.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/qmd.js +4 -4
- 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-memory-map.d.ts +1 -1
- package/dist/recall-navigate-access.d.ts +1 -1
- package/dist/recall-navigate.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +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-renderer.d.ts +1 -1
- package/dist/recall-xray.d.ts +1 -1
- package/dist/reconcile/cursor.d.ts +1 -1
- package/dist/reconcile/manifest.d.ts +1 -1
- package/dist/reconcile/plan.d.ts +1 -1
- package/dist/replay/normalizers/chatgpt.d.ts +1 -1
- package/dist/replay/normalizers/claude.d.ts +1 -1
- package/dist/replay/normalizers/openclaw.d.ts +1 -1
- package/dist/replay/normalizers/shared.d.ts +1 -1
- package/dist/replay/runner.d.ts +1 -1
- package/dist/replay/types.d.ts +1 -1
- package/dist/replica-divergence.d.ts +1 -1
- package/dist/replica-peers-config.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +4 -4
- 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 +24 -24
- package/dist/{scope-profiles-DFaojvoP.d.ts → scope-profiles-DqbxQXIS.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +8 -8
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +12 -12
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/lancedb-backend.js +2 -2
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.js +2 -2
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/orama-backend.js +2 -2
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-BiX68wZA.d.ts → semantic-consolidation-DW3rEUoW.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +17 -17
- package/dist/semantic-rule-promotion.js +16 -16
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +16 -16
- package/dist/{service-D5h2zZ0M.d.ts → service-B2vJU_Bx.d.ts} +2 -2
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/source-agent-qualifier.d.ts +1 -1
- package/dist/source-agent-qualifier.js +27 -27
- package/dist/standup/build.d.ts +9 -3
- package/dist/standup/build.js +99 -7
- package/dist/{storage-CtsaojWD.d.ts → storage-6fn7BPmb.d.ts} +1 -1
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +15 -15
- package/dist/subject-config.d.ts +1 -1
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/support-passport/index.d.ts +14 -14
- package/dist/support-passport/index.js +44 -44
- package/dist/temporal-supersession.d.ts +2 -2
- package/dist/temporal-timeline-recall.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +2 -2
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/export-okf.d.ts +1 -1
- package/dist/transfer/export-okf.js +17 -17
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-CIpfYz1I.d.ts → types-eM7QIcX2.d.ts} +4 -0
- package/dist/types.d.ts +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +16 -16
- package/dist/who-knows-cli.d.ts +11 -11
- package/dist/who-knows-cli.js +17 -17
- package/dist/who-knows.d.ts +11 -11
- package/dist/who-knows.js +16 -16
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +2 -2
- package/scripts/ensure-better-sqlite3.mjs +10 -0
- package/src/access-http.test.ts +41 -17
- package/src/access-http.ts +10 -10
- package/src/access-mcp-output-schemas.ts +1 -1
- package/src/access-mcp-sse.ts +50 -0
- package/src/activity/config.test.ts +1 -1
- package/src/activity/config.ts +20 -4
- package/src/activity/journal.test.ts +40 -0
- package/src/activity/timeline/index.ts +1 -0
- package/src/activity/timeline/recap-window.test.ts +129 -0
- package/src/activity/timeline/recap-window.ts +55 -0
- package/src/activity/types.ts +4 -0
- package/src/config.test.ts +1 -1
- package/src/dedup/index.ts +7 -0
- package/src/dedup/merge-decision.test.ts +157 -0
- package/src/dedup/merge-decision.ts +78 -0
- package/src/lcm/engine.ts +4 -1
- package/src/native-binding-hint.test.ts +38 -0
- package/src/native-binding-hint.ts +51 -0
- package/src/standup/build.test.ts +138 -12
- package/src/standup/build.ts +172 -23
- package/src/standup/http-glue.ts +1 -1
- package/dist/chunk-2W4MTU4P.js.map +0 -1
- package/dist/chunk-46XR5FNZ.js.map +0 -1
- package/dist/chunk-IGOINKTD.js.map +0 -1
- package/dist/chunk-PORKQEAW.js.map +0 -1
- package/dist/chunk-VOSWUO3K.js +0 -100
- package/dist/chunk-VOSWUO3K.js.map +0 -1
- package/dist/chunk-Z3YEGSR4.js.map +0 -1
- /package/dist/{chunk-52DBRDP5.js.map → chunk-23F7HGBV.js.map} +0 -0
- /package/dist/{chunk-TNXNECAN.js.map → chunk-2OXIPF3A.js.map} +0 -0
- /package/dist/{chunk-MSXUEYH6.js.map → chunk-3HHLOKKU.js.map} +0 -0
- /package/dist/{chunk-OMGQAXUK.js.map → chunk-47BX23NA.js.map} +0 -0
- /package/dist/{chunk-GO657WPH.js.map → chunk-52I6AHO6.js.map} +0 -0
- /package/dist/{chunk-QD55QEEY.js.map → chunk-5CSKIWEB.js.map} +0 -0
- /package/dist/{chunk-NMFPYAJ6.js.map → chunk-5GZ6UWY5.js.map} +0 -0
- /package/dist/{chunk-KSZKQTZS.js.map → chunk-5KVMT25I.js.map} +0 -0
- /package/dist/{chunk-D35XLYQ7.js.map → chunk-5NZIRM4Q.js.map} +0 -0
- /package/dist/{chunk-DB2MKBQ7.js.map → chunk-7JPKRZD5.js.map} +0 -0
- /package/dist/{chunk-PMNN247H.js.map → chunk-AAJK3PZ6.js.map} +0 -0
- /package/dist/{chunk-6FUMA6AA.js.map → chunk-B3TZLAO4.js.map} +0 -0
- /package/dist/{chunk-C24UZSFW.js.map → chunk-CEH2AFXG.js.map} +0 -0
- /package/dist/{chunk-42GDJAU7.js.map → chunk-CIWOW2FH.js.map} +0 -0
- /package/dist/{chunk-GNWLTCKC.js.map → chunk-CNZVEU4X.js.map} +0 -0
- /package/dist/{chunk-CWPTYKCF.js.map → chunk-DGKVUPBM.js.map} +0 -0
- /package/dist/{chunk-XJE6NHLT.js.map → chunk-FNYVWAIK.js.map} +0 -0
- /package/dist/{chunk-THZ5UEQZ.js.map → chunk-FUPIAP6Q.js.map} +0 -0
- /package/dist/{chunk-POSD62F5.js.map → chunk-G3TUQJFU.js.map} +0 -0
- /package/dist/{chunk-Y3QQZOZ4.js.map → chunk-G775FE2E.js.map} +0 -0
- /package/dist/{chunk-BEUB5ABO.js.map → chunk-GB4R6B5M.js.map} +0 -0
- /package/dist/{chunk-XZBXINXZ.js.map → chunk-GIC6BASP.js.map} +0 -0
- /package/dist/{chunk-6TMIW4HE.js.map → chunk-HWWGPS2N.js.map} +0 -0
- /package/dist/{chunk-PPAKBRI7.js.map → chunk-IBHUZEKL.js.map} +0 -0
- /package/dist/{chunk-6LMRUFBH.js.map → chunk-J67E4JNT.js.map} +0 -0
- /package/dist/{chunk-FKBRP3J2.js.map → chunk-JHU543UU.js.map} +0 -0
- /package/dist/{chunk-OCYRMVPT.js.map → chunk-JVGBDNXA.js.map} +0 -0
- /package/dist/{chunk-GUG4LW4I.js.map → chunk-KIJCXLW7.js.map} +0 -0
- /package/dist/{chunk-IN6D6BJG.js.map → chunk-KVXMN33W.js.map} +0 -0
- /package/dist/{chunk-2C34EKRJ.js.map → chunk-MBDUNO7J.js.map} +0 -0
- /package/dist/{chunk-V6LJENGW.js.map → chunk-MFI4QPW5.js.map} +0 -0
- /package/dist/{chunk-4NKIGSJM.js.map → chunk-MK7EJLB4.js.map} +0 -0
- /package/dist/{chunk-RACZB5HT.js.map → chunk-N37X5RU7.js.map} +0 -0
- /package/dist/{chunk-AG46P24C.js.map → chunk-NNBZS3DY.js.map} +0 -0
- /package/dist/{chunk-J2GY4UCR.js.map → chunk-NSP3HFJZ.js.map} +0 -0
- /package/dist/{chunk-CEK6AHIG.js.map → chunk-OJSCH7QT.js.map} +0 -0
- /package/dist/{chunk-MAWHEUAA.js.map → chunk-OT7WQ2I3.js.map} +0 -0
- /package/dist/{chunk-ZW4SVXEY.js.map → chunk-Q3UMNZRK.js.map} +0 -0
- /package/dist/{chunk-HQI2OQB5.js.map → chunk-Q5YKVBIV.js.map} +0 -0
- /package/dist/{chunk-FHY6HIAY.js.map → chunk-QU2V4IO3.js.map} +0 -0
- /package/dist/{chunk-2SW3N5HX.js.map → chunk-RF3LMSQW.js.map} +0 -0
- /package/dist/{chunk-O7ERGZDA.js.map → chunk-RJ57OOCD.js.map} +0 -0
- /package/dist/{chunk-KDYU3KZT.js.map → chunk-RLEJULGN.js.map} +0 -0
- /package/dist/{chunk-7O24F2VS.js.map → chunk-SJ5L4ZZX.js.map} +0 -0
- /package/dist/{chunk-BWZVFWMX.js.map → chunk-SOQLXIQX.js.map} +0 -0
- /package/dist/{chunk-ZKE227GO.js.map → chunk-SRILZVR4.js.map} +0 -0
- /package/dist/{chunk-YV4ST46A.js.map → chunk-TKSBOV7N.js.map} +0 -0
- /package/dist/{chunk-67U6UK5Q.js.map → chunk-TWMGWLHD.js.map} +0 -0
- /package/dist/{chunk-726CXBJO.js.map → chunk-UAWSKMWQ.js.map} +0 -0
- /package/dist/{chunk-MGVOBLUK.js.map → chunk-VCYY7XEL.js.map} +0 -0
- /package/dist/{chunk-ABPLUXVY.js.map → chunk-VX5YHEGJ.js.map} +0 -0
- /package/dist/{chunk-SFJPCNBI.js.map → chunk-XVNDP3RO.js.map} +0 -0
- /package/dist/{chunk-QVEKPVCG.js.map → chunk-ZC5L6ME2.js.map} +0 -0
- /package/dist/{chunk-VVWOEKOZ.js.map → chunk-ZJGHOPEO.js.map} +0 -0
- /package/dist/{chunk-4COPICMD.js.map → chunk-ZVUGRXSY.js.map} +0 -0
- /package/dist/{first-start-migration-KHIMCBIC.js.map → first-start-migration-UG32BSG2.js.map} +0 -0
package/src/activity/config.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { coerceBooleanLike, coerceNumber } from "../connectors/coerce.js";
|
|
2
2
|
import { assertValidTimezone } from "./digest.js";
|
|
3
|
+
import { resolveJournalSource } from "./journal-source.js";
|
|
3
4
|
import type { ImportanceLevel } from "../types.js";
|
|
4
5
|
import type {
|
|
5
6
|
ActivityConfig,
|
|
@@ -29,7 +30,7 @@ export function defaultActivityConfig(): ActivityConfig {
|
|
|
29
30
|
minConfidence: 0.7,
|
|
30
31
|
minImportance: "normal",
|
|
31
32
|
maxMemoriesPerDay: 0,
|
|
32
|
-
timeline: { enabled: false, journal: { enabled: false }, qa: { enabled: false, maxRangeDays: 31 } },
|
|
33
|
+
timeline: { enabled: false, journal: { enabled: false, source: "file" }, qa: { enabled: false, maxRangeDays: 31 } },
|
|
33
34
|
};
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -200,7 +201,7 @@ export function parseActivityConfig(raw: unknown): ActivityConfig {
|
|
|
200
201
|
* is malformed and must fail rather than silently disabling the layer.
|
|
201
202
|
*/
|
|
202
203
|
function parseTimelineConfig(raw: unknown): ActivityTimelineConfig {
|
|
203
|
-
if (raw === undefined) return { enabled: false, journal: { enabled: false }, qa: parseTimelineQaConfig(undefined) };
|
|
204
|
+
if (raw === undefined) return { enabled: false, journal: { enabled: false, source: "file" }, qa: parseTimelineQaConfig(undefined) };
|
|
204
205
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
205
206
|
throw new TypeError("activity.timeline must be an object");
|
|
206
207
|
}
|
|
@@ -217,7 +218,7 @@ function parseTimelineConfig(raw: unknown): ActivityTimelineConfig {
|
|
|
217
218
|
}
|
|
218
219
|
|
|
219
220
|
function parseTimelineJournalConfig(raw: unknown): ActivityTimelineJournalConfig {
|
|
220
|
-
if (raw === undefined) return { enabled: false };
|
|
221
|
+
if (raw === undefined) return { enabled: false, source: "file" };
|
|
221
222
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
222
223
|
throw new TypeError("activity.timeline.journal must be an object");
|
|
223
224
|
}
|
|
@@ -226,7 +227,22 @@ function parseTimelineJournalConfig(raw: unknown): ActivityTimelineJournalConfig
|
|
|
226
227
|
if (journal.enabled !== undefined && enabledValue === undefined) {
|
|
227
228
|
throw new TypeError("activity.timeline.journal.enabled must be a boolean");
|
|
228
229
|
}
|
|
229
|
-
|
|
230
|
+
const heading = journal.heading;
|
|
231
|
+
if (heading !== undefined && typeof heading !== "string") {
|
|
232
|
+
throw new TypeError("activity.timeline.journal.heading must be a string");
|
|
233
|
+
}
|
|
234
|
+
const source = journal.source === undefined ? "file" : typeof journal.source === "string" ? journal.source : "";
|
|
235
|
+
const resolved = resolveJournalSource({ source, heading: heading ?? "" });
|
|
236
|
+
if (!resolved.ok) {
|
|
237
|
+
if (resolved.error === "unknown_source") {
|
|
238
|
+
throw new RangeError('activity.timeline.journal.source must be one of "file", "vault"');
|
|
239
|
+
}
|
|
240
|
+
throw new RangeError('activity.timeline.journal.heading must be a non-empty string when source is "vault"');
|
|
241
|
+
}
|
|
242
|
+
if (resolved.mode === "vault") {
|
|
243
|
+
return { enabled: enabledValue ?? false, source: "vault", heading: resolved.heading };
|
|
244
|
+
}
|
|
245
|
+
return { enabled: enabledValue ?? false, source: "file" };
|
|
230
246
|
}
|
|
231
247
|
|
|
232
248
|
function parseTimelineQaConfig(raw: unknown): ActivityTimelineQaConfig {
|
|
@@ -32,6 +32,46 @@ test("activity.timeline.journal.enabled defaults off", () => {
|
|
|
32
32
|
);
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
+
test("activity.timeline.journal source defaults to file with no heading", () => {
|
|
36
|
+
const journal = parseActivityConfig(undefined).timeline.journal;
|
|
37
|
+
assert.equal(journal.source, "file");
|
|
38
|
+
assert.equal(journal.heading, undefined);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("activity.timeline.journal vault mode trims and stores the heading", () => {
|
|
42
|
+
const journal = parseActivityConfig({ timeline: { journal: { enabled: true, source: "vault", heading: " Journal " } } }).timeline.journal;
|
|
43
|
+
assert.deepEqual(journal, { enabled: true, source: "vault", heading: "Journal" });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("activity.timeline.journal vault mode requires a non-empty heading", () => {
|
|
47
|
+
for (const heading of [undefined, "", " "]) {
|
|
48
|
+
assert.throws(
|
|
49
|
+
() => parseActivityConfig({ timeline: { journal: { source: "vault", heading } } }),
|
|
50
|
+
RangeError,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("activity.timeline.journal rejects an unknown source", () => {
|
|
56
|
+
assert.throws(
|
|
57
|
+
() => parseActivityConfig({ timeline: { journal: { source: "memoryDir" } } }),
|
|
58
|
+
/activity\.timeline\.journal\.source must be one of "file", "vault"/,
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("activity.timeline.journal rejects a non-string heading", () => {
|
|
63
|
+
assert.throws(
|
|
64
|
+
() => parseActivityConfig({ timeline: { journal: { heading: 5 } } }),
|
|
65
|
+
/activity\.timeline\.journal\.heading must be a string/,
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("activity.timeline.journal file mode ignores a provided heading", () => {
|
|
70
|
+
const journal = parseActivityConfig({ timeline: { journal: { source: "file", heading: "Ignored" } } }).timeline.journal;
|
|
71
|
+
assert.deepEqual(journal, { enabled: false, source: "file" });
|
|
72
|
+
assert.equal("heading" in journal, false);
|
|
73
|
+
});
|
|
74
|
+
|
|
35
75
|
test("seed is a hard no-op without force", () => {
|
|
36
76
|
withMemoryDir((memoryDir) => {
|
|
37
77
|
const first = seedJournal({
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
clipCardsToRecapWindow,
|
|
6
|
+
type RecapWindowCard,
|
|
7
|
+
} from "./recap-window.js";
|
|
8
|
+
|
|
9
|
+
const MINUTE = 60_000;
|
|
10
|
+
const DAY_START = Date.UTC(2026, 0, 15); // 2026-01-15T00:00:00Z
|
|
11
|
+
const DAY_END = DAY_START + 24 * 60 * 60_000;
|
|
12
|
+
|
|
13
|
+
const iso = (ms: number): string => new Date(ms).toISOString();
|
|
14
|
+
|
|
15
|
+
const card = (id: string, startMs: number, endMs: number): RecapWindowCard => ({
|
|
16
|
+
id,
|
|
17
|
+
startUtc: iso(startMs),
|
|
18
|
+
endUtc: iso(endMs),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("clipCardsToRecapWindow", () => {
|
|
22
|
+
it("returns [] for empty input", () => {
|
|
23
|
+
assert.deepEqual(clipCardsToRecapWindow([], DAY_START, DAY_END), []);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("excludes a card ending exactly at windowStart and one starting exactly at windowEnd", () => {
|
|
27
|
+
const before = card("before", DAY_START - 2 * MINUTE, DAY_START);
|
|
28
|
+
const after = card("after", DAY_END, DAY_END + 2 * MINUTE);
|
|
29
|
+
const inside = card("inside", DAY_START, DAY_END);
|
|
30
|
+
|
|
31
|
+
const result = clipCardsToRecapWindow([before, after, inside], DAY_START, DAY_END);
|
|
32
|
+
|
|
33
|
+
assert.deepEqual(
|
|
34
|
+
result.map((c) => c.id),
|
|
35
|
+
["inside"],
|
|
36
|
+
);
|
|
37
|
+
assert.equal(result[0]?.startMs, DAY_START);
|
|
38
|
+
assert.equal(result[0]?.endMs, DAY_END);
|
|
39
|
+
assert.equal(result[0]?.durationMs, DAY_END - DAY_START);
|
|
40
|
+
|
|
41
|
+
// Each midnight-touching card lands in exactly one adjacent window.
|
|
42
|
+
const windowMs = DAY_END - DAY_START;
|
|
43
|
+
const prevDay = clipCardsToRecapWindow([before], DAY_START - windowMs, DAY_START);
|
|
44
|
+
const nextDay = clipCardsToRecapWindow([after], DAY_END, DAY_END + windowMs);
|
|
45
|
+
assert.equal(prevDay[0]?.id, "before");
|
|
46
|
+
assert.equal(nextDay[0]?.id, "after");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("clips a card straddling the window start", () => {
|
|
50
|
+
const straddle = card("straddle", DAY_START - 30 * MINUTE, DAY_START + 90 * MINUTE);
|
|
51
|
+
|
|
52
|
+
const result = clipCardsToRecapWindow([straddle], DAY_START, DAY_END);
|
|
53
|
+
|
|
54
|
+
assert.equal(result.length, 1);
|
|
55
|
+
assert.equal(result[0]?.startMs, DAY_START);
|
|
56
|
+
assert.equal(result[0]?.endMs, DAY_START + 90 * MINUTE);
|
|
57
|
+
assert.equal(result[0]?.durationMs, 90 * MINUTE);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("clips a card straddling the window end", () => {
|
|
61
|
+
const straddle = card("tail", DAY_END - 5 * MINUTE, DAY_END + 45 * MINUTE);
|
|
62
|
+
|
|
63
|
+
const result = clipCardsToRecapWindow([straddle], DAY_START, DAY_END);
|
|
64
|
+
|
|
65
|
+
assert.equal(result.length, 1);
|
|
66
|
+
assert.equal(result[0]?.endMs, DAY_END);
|
|
67
|
+
assert.equal(result[0]?.durationMs, 5 * MINUTE);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("skips cards with unparseable timestamps without throwing", () => {
|
|
71
|
+
const good = card("good", DAY_START + MINUTE, DAY_START + 10 * MINUTE);
|
|
72
|
+
const bad: RecapWindowCard[] = [
|
|
73
|
+
{ id: "bad-start", startUtc: "not-a-date", endUtc: iso(DAY_START + 5 * MINUTE) },
|
|
74
|
+
{ id: "bad-end", startUtc: iso(DAY_START), endUtc: "" },
|
|
75
|
+
good,
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
const result = clipCardsToRecapWindow(bad, DAY_START, DAY_END);
|
|
79
|
+
|
|
80
|
+
assert.deepEqual(
|
|
81
|
+
result.map((c) => c.id),
|
|
82
|
+
["good"],
|
|
83
|
+
);
|
|
84
|
+
assert.equal(result[0]?.durationMs, 9 * MINUTE);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("throws RangeError on inverted or empty window", () => {
|
|
88
|
+
assert.throws(
|
|
89
|
+
() => clipCardsToRecapWindow([], DAY_END, DAY_START),
|
|
90
|
+
RangeError,
|
|
91
|
+
);
|
|
92
|
+
assert.throws(
|
|
93
|
+
() => clipCardsToRecapWindow([], DAY_START, DAY_START),
|
|
94
|
+
RangeError,
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("throws RangeError on non-finite window bounds", () => {
|
|
99
|
+
// NaN and Infinity pass `end <= start`, so without an explicit finite
|
|
100
|
+
// check they would produce NaN timestamps instead of an error.
|
|
101
|
+
for (const [start, end] of [
|
|
102
|
+
[Number.NaN, DAY_END],
|
|
103
|
+
[DAY_START, Number.NaN],
|
|
104
|
+
[Number.POSITIVE_INFINITY, DAY_END],
|
|
105
|
+
[DAY_START, Number.POSITIVE_INFINITY],
|
|
106
|
+
] as const) {
|
|
107
|
+
assert.throws(() => clipCardsToRecapWindow([], start, end), /finite/);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("sorts by startMs then id, byte-identical across calls", () => {
|
|
112
|
+
const same = DAY_START + 60 * MINUTE;
|
|
113
|
+
const cards: RecapWindowCard[] = [
|
|
114
|
+
card("z-late", DAY_START + 120 * MINUTE, DAY_START + 130 * MINUTE),
|
|
115
|
+
card("b-tie", same, same + 10 * MINUTE),
|
|
116
|
+
card("a-tie", same, same + 10 * MINUTE),
|
|
117
|
+
card("early", DAY_START + 5 * MINUTE, DAY_START + 6 * MINUTE),
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
const first = clipCardsToRecapWindow(cards, DAY_START, DAY_END);
|
|
121
|
+
const second = clipCardsToRecapWindow(cards, DAY_START, DAY_END);
|
|
122
|
+
|
|
123
|
+
assert.deepEqual(
|
|
124
|
+
first.map((c) => c.id),
|
|
125
|
+
["early", "a-tie", "b-tie", "z-late"],
|
|
126
|
+
);
|
|
127
|
+
assert.deepEqual(first, second);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic half-open recap window clip (issue #2051).
|
|
3
|
+
*
|
|
4
|
+
* Clips cards to [windowStartMs, windowEndMs) so recap renderers share one
|
|
5
|
+
* day-boundary derivation instead of re-deriving it per renderer.
|
|
6
|
+
*/
|
|
7
|
+
export interface RecapWindowCard {
|
|
8
|
+
id: string;
|
|
9
|
+
startUtc: string;
|
|
10
|
+
endUtc: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ClippedRecapCard {
|
|
14
|
+
id: string;
|
|
15
|
+
startMs: number;
|
|
16
|
+
endMs: number;
|
|
17
|
+
durationMs: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function clipCardsToRecapWindow(
|
|
21
|
+
cards: readonly RecapWindowCard[],
|
|
22
|
+
windowStartMs: number,
|
|
23
|
+
windowEndMs: number,
|
|
24
|
+
): ClippedRecapCard[] {
|
|
25
|
+
// NaN fails every comparison, so an unchecked non-finite bound slips past the
|
|
26
|
+
// ordering guard and emits NaN timestamps and durations — `durationMs <= 0`
|
|
27
|
+
// is also false for NaN, so the card is not even dropped.
|
|
28
|
+
if (!Number.isFinite(windowStartMs) || !Number.isFinite(windowEndMs)) {
|
|
29
|
+
throw new RangeError(
|
|
30
|
+
`recap window bounds must be finite; got start ${windowStartMs} and end ${windowEndMs}`,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
if (windowEndMs <= windowStartMs) {
|
|
34
|
+
throw new RangeError(
|
|
35
|
+
`recap window end ${windowEndMs} must be greater than start ${windowStartMs}`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
const out: ClippedRecapCard[] = [];
|
|
39
|
+
for (const card of cards) {
|
|
40
|
+
const startMs = Date.parse(card.startUtc);
|
|
41
|
+
const endMs = Date.parse(card.endUtc);
|
|
42
|
+
if (!Number.isFinite(startMs) || !Number.isFinite(endMs)) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const clippedStart = Math.max(startMs, windowStartMs);
|
|
46
|
+
const clippedEnd = Math.min(endMs, windowEndMs);
|
|
47
|
+
const durationMs = clippedEnd - clippedStart;
|
|
48
|
+
if (durationMs <= 0) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
out.push({ id: card.id, startMs: clippedStart, endMs: clippedEnd, durationMs });
|
|
52
|
+
}
|
|
53
|
+
out.sort((a, b) => a.startMs - b.startMs || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
54
|
+
return out;
|
|
55
|
+
}
|
package/src/activity/types.ts
CHANGED
|
@@ -99,6 +99,10 @@ export interface ActivitySourceConfig {
|
|
|
99
99
|
/** Opt-in daily journal settings (issue #1984). Default off. */
|
|
100
100
|
export interface ActivityTimelineJournalConfig {
|
|
101
101
|
enabled: boolean;
|
|
102
|
+
/** Where the journal text is read from (issue #1987). */
|
|
103
|
+
source: "file" | "vault";
|
|
104
|
+
/** Vault section heading, trimmed. Required when `source` is `"vault"`; not stored in `"file"` mode. */
|
|
105
|
+
heading?: string;
|
|
102
106
|
}
|
|
103
107
|
|
|
104
108
|
/** Opt-in timeline Q&A range/search settings (issue #1983 PR1). Default off. */
|
package/src/config.test.ts
CHANGED
|
@@ -2569,7 +2569,7 @@ test("parseConfig forwards activity source settings", () => {
|
|
|
2569
2569
|
minConfidence: 0.7,
|
|
2570
2570
|
minImportance: "normal",
|
|
2571
2571
|
maxMemoriesPerDay: 0,
|
|
2572
|
-
timeline: { enabled: false, journal: { enabled: false }, qa: { enabled: false, maxRangeDays: 31 } },
|
|
2572
|
+
timeline: { enabled: false, journal: { enabled: false, source: "file" }, qa: { enabled: false, maxRangeDays: 31 } },
|
|
2573
2573
|
});
|
|
2574
2574
|
});
|
|
2575
2575
|
|
package/src/dedup/index.ts
CHANGED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
decideMergeOnWrite,
|
|
6
|
+
type MergeCandidate,
|
|
7
|
+
type MergeDecisionOptions,
|
|
8
|
+
} from "./merge-decision.js";
|
|
9
|
+
|
|
10
|
+
const OPTS: MergeDecisionOptions = { updateThreshold: 0.8, duplicateThreshold: 0.92 };
|
|
11
|
+
|
|
12
|
+
function candidate(overrides: Partial<MergeCandidate> = {}): MergeCandidate {
|
|
13
|
+
return {
|
|
14
|
+
id: "fact-1",
|
|
15
|
+
similarity: 0.85,
|
|
16
|
+
updatedAt: "2026-01-02T00:00:00.000Z",
|
|
17
|
+
...overrides,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
test("no candidates creates", () => {
|
|
22
|
+
assert.deepEqual(decideMergeOnWrite([], OPTS), { action: "create" });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("best similarity at or above duplicateThreshold skips as duplicate", () => {
|
|
26
|
+
assert.deepEqual(
|
|
27
|
+
decideMergeOnWrite(
|
|
28
|
+
[candidate({ id: "low", similarity: 0.5 }), candidate({ id: "dup", similarity: 0.92 })],
|
|
29
|
+
OPTS,
|
|
30
|
+
),
|
|
31
|
+
{ action: "skip", reason: "duplicate" },
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("best similarity between thresholds updates the best candidate", () => {
|
|
36
|
+
assert.deepEqual(
|
|
37
|
+
decideMergeOnWrite(
|
|
38
|
+
[candidate({ id: "best", similarity: 0.9 }), candidate({ id: "worst", similarity: 0.81 })],
|
|
39
|
+
OPTS,
|
|
40
|
+
),
|
|
41
|
+
{ action: "update", targetId: "best" },
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("best similarity below updateThreshold creates", () => {
|
|
46
|
+
assert.deepEqual(
|
|
47
|
+
decideMergeOnWrite([candidate({ similarity: 0.79 })], OPTS),
|
|
48
|
+
{ action: "create" },
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("invalid thresholds throw RangeError", () => {
|
|
53
|
+
const bad: MergeDecisionOptions[] = [
|
|
54
|
+
{ updateThreshold: Number.NaN, duplicateThreshold: 0.9 },
|
|
55
|
+
{ updateThreshold: 0.8, duplicateThreshold: Number.POSITIVE_INFINITY },
|
|
56
|
+
{ updateThreshold: -0.1, duplicateThreshold: 0.9 },
|
|
57
|
+
{ updateThreshold: 0.8, duplicateThreshold: 1.1 },
|
|
58
|
+
{ updateThreshold: 0.92, duplicateThreshold: 0.8 }, // duplicate < update
|
|
59
|
+
];
|
|
60
|
+
for (const options of bad) {
|
|
61
|
+
assert.throws(() => decideMergeOnWrite([], options), RangeError);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("updateThreshold 0 is honored, never coerced up", () => {
|
|
66
|
+
const zero = { updateThreshold: 0, duplicateThreshold: 0.92 };
|
|
67
|
+
// similarity 0 is >= 0 -> update, not create and not default-band behavior
|
|
68
|
+
assert.deepEqual(
|
|
69
|
+
decideMergeOnWrite([candidate({ similarity: 0 })], zero),
|
|
70
|
+
{ action: "update", targetId: "fact-1" },
|
|
71
|
+
);
|
|
72
|
+
// no candidates still creates
|
|
73
|
+
assert.deepEqual(decideMergeOnWrite([], zero), { action: "create" });
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("malformed candidates are ignored, never thrown on", () => {
|
|
77
|
+
assert.deepEqual(
|
|
78
|
+
decideMergeOnWrite(
|
|
79
|
+
[
|
|
80
|
+
{ id: "", similarity: 0.99, updatedAt: "2026-01-01T00:00:00.000Z" },
|
|
81
|
+
{ id: " ", similarity: 0.99, updatedAt: "2026-01-01T00:00:00.000Z" },
|
|
82
|
+
{ id: "nan", similarity: Number.NaN, updatedAt: "2026-01-01T00:00:00.000Z" },
|
|
83
|
+
{ id: "inf", similarity: Number.POSITIVE_INFINITY, updatedAt: "2026-01-01T00:00:00.000Z" },
|
|
84
|
+
{ id: "high", similarity: 1.5, updatedAt: "2026-01-01T00:00:00.000Z" },
|
|
85
|
+
{ id: "neg", similarity: -0.2, updatedAt: "2026-01-01T00:00:00.000Z" },
|
|
86
|
+
{ id: "good", similarity: 0.85, updatedAt: "2026-01-02T00:00:00.000Z" },
|
|
87
|
+
],
|
|
88
|
+
OPTS,
|
|
89
|
+
),
|
|
90
|
+
{ action: "update", targetId: "good" },
|
|
91
|
+
);
|
|
92
|
+
// all-malformed behaves like no candidates
|
|
93
|
+
assert.deepEqual(
|
|
94
|
+
decideMergeOnWrite([{ id: "nan", similarity: Number.NaN, updatedAt: "x" }], OPTS),
|
|
95
|
+
{ action: "create" },
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("identical similarity and updatedAt tie-breaks on id ascending, deterministically", () => {
|
|
100
|
+
const candidates = [
|
|
101
|
+
candidate({ id: "b", similarity: 0.9, updatedAt: "2026-03-01T00:00:00.000Z" }),
|
|
102
|
+
candidate({ id: "a", similarity: 0.9, updatedAt: "2026-03-01T00:00:00.000Z" }),
|
|
103
|
+
candidate({ id: "c", similarity: 0.9, updatedAt: "2026-03-01T00:00:00.000Z" }),
|
|
104
|
+
];
|
|
105
|
+
const first = decideMergeOnWrite(candidates, OPTS);
|
|
106
|
+
const second = decideMergeOnWrite([...candidates].reverse(), OPTS);
|
|
107
|
+
assert.deepEqual(first, second);
|
|
108
|
+
assert.deepEqual(first, { action: "update", targetId: "a" });
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("unparseable updatedAt sorts last", () => {
|
|
112
|
+
const candidates = [
|
|
113
|
+
candidate({ id: "bad-date", similarity: 0.9, updatedAt: "not-a-timestamp" }),
|
|
114
|
+
candidate({ id: "good-date", similarity: 0.9, updatedAt: "2026-03-01T00:00:00.000Z" }),
|
|
115
|
+
];
|
|
116
|
+
assert.deepEqual(decideMergeOnWrite(candidates, OPTS), { action: "update", targetId: "good-date" });
|
|
117
|
+
// newer timestamp wins when both parse
|
|
118
|
+
assert.deepEqual(
|
|
119
|
+
decideMergeOnWrite(
|
|
120
|
+
[
|
|
121
|
+
candidate({ id: "older", updatedAt: "2026-01-01T00:00:00.000Z" }),
|
|
122
|
+
candidate({ id: "newer", updatedAt: "2026-06-01T00:00:00.000Z" }),
|
|
123
|
+
],
|
|
124
|
+
OPTS,
|
|
125
|
+
),
|
|
126
|
+
{ action: "update", targetId: "newer" },
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Two unparseable timestamps must not make the comparator asymmetric: testing
|
|
131
|
+
// each side against NaN independently returned 1 for both orderings, so the
|
|
132
|
+
// winner depended on array order.
|
|
133
|
+
test("two unparseable updatedAt values break the tie by id, whatever the order", () => {
|
|
134
|
+
const options = { updateThreshold: 0.5, duplicateThreshold: 0.95 };
|
|
135
|
+
const alpha = { id: "alpha", similarity: 0.7, updatedAt: "not-a-date" };
|
|
136
|
+
const beta = { id: "beta", similarity: 0.7, updatedAt: "also-not-a-date" };
|
|
137
|
+
|
|
138
|
+
for (const candidates of [[alpha, beta], [beta, alpha]]) {
|
|
139
|
+
assert.deepEqual(decideMergeOnWrite(candidates, options), {
|
|
140
|
+
action: "update",
|
|
141
|
+
targetId: "alpha",
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("a parseable updatedAt still outranks an unparseable one in both orders", () => {
|
|
147
|
+
const options = { updateThreshold: 0.5, duplicateThreshold: 0.95 };
|
|
148
|
+
const dated = { id: "zeta", similarity: 0.7, updatedAt: "2026-08-19T00:00:00.000Z" };
|
|
149
|
+
const undated = { id: "alpha", similarity: 0.7, updatedAt: "nope" };
|
|
150
|
+
|
|
151
|
+
for (const candidates of [[dated, undated], [undated, dated]]) {
|
|
152
|
+
assert.deepEqual(decideMergeOnWrite(candidates, options), {
|
|
153
|
+
action: "update",
|
|
154
|
+
targetId: "zeta",
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure create/update/skip decision for merge-on-write (issue #2330 slice).
|
|
3
|
+
*
|
|
4
|
+
* No I/O, no LLM. Invalid thresholds throw RangeError; bad candidates are
|
|
5
|
+
* ignored, never thrown on. `below_threshold` is reserved for a later slice.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type MergeDecision =
|
|
9
|
+
| { action: "create" }
|
|
10
|
+
| { action: "update"; targetId: string }
|
|
11
|
+
| { action: "skip"; reason: "duplicate" | "below_threshold" };
|
|
12
|
+
|
|
13
|
+
export interface MergeCandidate {
|
|
14
|
+
id: string;
|
|
15
|
+
similarity: number; // 0..1
|
|
16
|
+
updatedAt: string; // ISO
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface MergeDecisionOptions {
|
|
20
|
+
updateThreshold: number; // >= this similarity -> update
|
|
21
|
+
duplicateThreshold: number; // >= this similarity -> skip as duplicate
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function assertThreshold(value: number, name: string): void {
|
|
25
|
+
if (!Number.isFinite(value) || value < 0 || value > 1) {
|
|
26
|
+
throw new RangeError(`invalid ${name}: ${value}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Total order: similarity desc, then updatedAt desc, then id asc. NaN times sort last. */
|
|
31
|
+
function compareCandidates(a: MergeCandidate, b: MergeCandidate): number {
|
|
32
|
+
if (a.similarity !== b.similarity) return b.similarity - a.similarity;
|
|
33
|
+
// Date.parse never throws; unparseable timestamps yield NaN and sort last.
|
|
34
|
+
// NaN !== NaN, so the two sides must be classified as a pair: testing each
|
|
35
|
+
// independently returned 1 for BOTH orderings when both were unparseable,
|
|
36
|
+
// which breaks antisymmetry and made the winner depend on array order.
|
|
37
|
+
const aTime = Date.parse(a.updatedAt);
|
|
38
|
+
const bTime = Date.parse(b.updatedAt);
|
|
39
|
+
const aUnparseable = Number.isNaN(aTime);
|
|
40
|
+
const bUnparseable = Number.isNaN(bTime);
|
|
41
|
+
if (aUnparseable !== bUnparseable) return aUnparseable ? 1 : -1;
|
|
42
|
+
if (!aUnparseable && aTime !== bTime) return bTime - aTime;
|
|
43
|
+
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function decideMergeOnWrite(
|
|
47
|
+
candidates: readonly MergeCandidate[],
|
|
48
|
+
options: MergeDecisionOptions,
|
|
49
|
+
): MergeDecision {
|
|
50
|
+
assertThreshold(options.updateThreshold, "updateThreshold");
|
|
51
|
+
assertThreshold(options.duplicateThreshold, "duplicateThreshold");
|
|
52
|
+
if (options.duplicateThreshold < options.updateThreshold) {
|
|
53
|
+
throw new RangeError(
|
|
54
|
+
`duplicateThreshold ${options.duplicateThreshold} < updateThreshold ${options.updateThreshold}`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const valid = candidates.filter(
|
|
59
|
+
(c) =>
|
|
60
|
+
typeof c.id === "string" &&
|
|
61
|
+
c.id.trim() !== "" &&
|
|
62
|
+
typeof c.similarity === "number" &&
|
|
63
|
+
Number.isFinite(c.similarity) &&
|
|
64
|
+
c.similarity >= 0 &&
|
|
65
|
+
c.similarity <= 1,
|
|
66
|
+
);
|
|
67
|
+
if (valid.length === 0) return { action: "create" };
|
|
68
|
+
|
|
69
|
+
const best = valid.reduce((acc, c) => (compareCandidates(c, acc) < 0 ? c : acc));
|
|
70
|
+
|
|
71
|
+
if (best.similarity >= options.duplicateThreshold) {
|
|
72
|
+
return { action: "skip", reason: "duplicate" };
|
|
73
|
+
}
|
|
74
|
+
if (best.similarity >= options.updateThreshold) {
|
|
75
|
+
return { action: "update", targetId: best.id };
|
|
76
|
+
}
|
|
77
|
+
return { action: "create" };
|
|
78
|
+
}
|
package/src/lcm/engine.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { LcmWorkQueue, type LcmObserveMessage } from "./queue.js";
|
|
|
12
12
|
import type { PluginConfig } from "../types.js";
|
|
13
13
|
import { estimateTokenCount } from "../token-estimate.js";
|
|
14
14
|
import { log } from "../logger.js";
|
|
15
|
+
import { nativeBindingRecoveryHint } from "../native-binding-hint.js";
|
|
15
16
|
export interface LcmEngineConfig {
|
|
16
17
|
enabled: boolean;
|
|
17
18
|
leafBatchSize: number;
|
|
@@ -225,7 +226,9 @@ export class LcmEngine {
|
|
|
225
226
|
})
|
|
226
227
|
.catch((err) => {
|
|
227
228
|
if (this.closed) return;
|
|
228
|
-
log.error(
|
|
229
|
+
log.error(
|
|
230
|
+
`LCM observe enqueue initialization error: ${err}${nativeBindingRecoveryHint(err)}`,
|
|
231
|
+
);
|
|
229
232
|
})
|
|
230
233
|
.finally(() => {
|
|
231
234
|
this.releasePendingObserveInit(normalizedSessionId);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import { isNativeBindingError, nativeBindingRecoveryHint } from "./native-binding-hint.js";
|
|
5
|
+
|
|
6
|
+
test("recognizes the bindings-resolution failure operators actually see", () => {
|
|
7
|
+
const error = new Error(
|
|
8
|
+
"Could not locate the bindings file. Tried:\n → .../better-sqlite3/build/Release/better_sqlite3.node",
|
|
9
|
+
);
|
|
10
|
+
assert.equal(isNativeBindingError(error), true);
|
|
11
|
+
const hint = nativeBindingRecoveryHint(error);
|
|
12
|
+
assert.match(hint, /npm rebuild better-sqlite3 --build-from-source/);
|
|
13
|
+
assert.match(hint, new RegExp(`ABI ${process.versions.modules}\\b`));
|
|
14
|
+
assert.match(hint, new RegExp(`Node ${process.versions.node.split(".")[0]}\\b`));
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("recognizes an ABI mismatch reported as NODE_MODULE_VERSION", () => {
|
|
18
|
+
const error = new Error(
|
|
19
|
+
"The module was compiled against a different Node.js version using NODE_MODULE_VERSION 127. " +
|
|
20
|
+
"This version of Node.js requires NODE_MODULE_VERSION 141.",
|
|
21
|
+
);
|
|
22
|
+
assert.equal(isNativeBindingError(error), true);
|
|
23
|
+
assert.match(nativeBindingRecoveryHint(error), /--build-from-source/);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("an unrelated error yields no hint so its message reads unchanged", () => {
|
|
27
|
+
const error = new Error("SQLITE_BUSY: database is locked");
|
|
28
|
+
assert.equal(isNativeBindingError(error), false);
|
|
29
|
+
assert.equal(nativeBindingRecoveryHint(error), "");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("non-Error inputs are handled without throwing", () => {
|
|
33
|
+
assert.equal(isNativeBindingError(undefined), false);
|
|
34
|
+
assert.equal(isNativeBindingError(null), false);
|
|
35
|
+
assert.equal(nativeBindingRecoveryHint({}), "");
|
|
36
|
+
assert.equal(isNativeBindingError("could not locate the bindings file"), true);
|
|
37
|
+
assert.match(nativeBindingRecoveryHint("invalid ELF header"), /--build-from-source/);
|
|
38
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Actionable recovery hint for a missing or ABI-mismatched better-sqlite3
|
|
3
|
+
* native binding (issue #2719).
|
|
4
|
+
*
|
|
5
|
+
* `better-sqlite3` publishes prebuilds for LTS Node only, so a newer runtime
|
|
6
|
+
* installs the package with no loadable binding and every SQLite-backed path
|
|
7
|
+
* fails with a bindings-resolution error. The raw error names neither the
|
|
8
|
+
* running ABI nor the command that fixes it, so operators saw only
|
|
9
|
+
* "Could not locate the bindings file" at first use.
|
|
10
|
+
*
|
|
11
|
+
* The repair itself lives in the `@remnic/core` postinstall script — an
|
|
12
|
+
* OpenClaw plugin package may not ship a child-process-spawning file, so the
|
|
13
|
+
* host-facing packages surface this hint instead of re-implementing a rebuild.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** Substrings that identify a native-binding load failure, lowercased. */
|
|
17
|
+
const BINDING_ERROR_MARKERS = [
|
|
18
|
+
"could not locate the bindings file",
|
|
19
|
+
"was compiled against a different node.js version",
|
|
20
|
+
"node_module_version",
|
|
21
|
+
"invalid elf header",
|
|
22
|
+
"better_sqlite3.node",
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
function messageOf(error: unknown): string {
|
|
26
|
+
if (error instanceof Error) return error.message;
|
|
27
|
+
if (typeof error === "string") return error;
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** True when the error is a better-sqlite3 native-binding load failure. */
|
|
32
|
+
export function isNativeBindingError(error: unknown): boolean {
|
|
33
|
+
const message = messageOf(error).toLowerCase();
|
|
34
|
+
if (message.length === 0) return false;
|
|
35
|
+
return BINDING_ERROR_MARKERS.some((marker) => message.includes(marker));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Suffix naming the running Node ABI and the exact rebuild command, or an
|
|
40
|
+
* empty string when the error is unrelated. Callers append it to their own
|
|
41
|
+
* message so an unrelated failure reads exactly as before.
|
|
42
|
+
*/
|
|
43
|
+
export function nativeBindingRecoveryHint(error: unknown): string {
|
|
44
|
+
if (!isNativeBindingError(error)) return "";
|
|
45
|
+
const major = process.versions.node.split(".")[0] ?? process.versions.node;
|
|
46
|
+
return (
|
|
47
|
+
` — better-sqlite3 has no loadable native binding for Node ${major}` +
|
|
48
|
+
` (ABI ${process.versions.modules}); rebuild it with` +
|
|
49
|
+
" `npm rebuild better-sqlite3 --build-from-source`"
|
|
50
|
+
);
|
|
51
|
+
}
|