@remnic/core 9.66.0 → 9.66.1
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 +22 -22
- package/dist/access-authorization-probe.d.ts +11 -11
- package/dist/access-authorization-probe.js +23 -23
- package/dist/access-boundary.d.ts +11 -11
- package/dist/access-boundary.js +22 -22
- package/dist/access-cli.js +86 -84
- 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 +22 -22
- 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 +24 -24
- package/dist/access-http.d.ts +12 -12
- package/dist/access-http.js +39 -39
- package/dist/access-identity-continuity-surface.d.ts +9 -9
- package/dist/access-identity-continuity-surface.js +22 -22
- package/dist/access-lcm-surface.d.ts +11 -11
- package/dist/access-lcm-surface.js +22 -22
- package/dist/access-mcp.d.ts +11 -11
- package/dist/access-mcp.js +29 -29
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-CLWUoMyW.d.ts → access-namespace-preflight-50VIkPhB.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +22 -22
- package/dist/access-observe-write-surface.d.ts +11 -11
- package/dist/access-observe-write-surface.js +22 -22
- package/dist/access-offline-manifest.d.ts +9 -9
- package/dist/access-operations-batch.js +24 -24
- package/dist/access-operations.d.ts +16 -16
- package/dist/access-operations.js +28 -28
- package/dist/access-recall-concurrency.d.ts +11 -11
- package/dist/access-recall-concurrency.js +22 -22
- package/dist/access-recall-response.d.ts +11 -11
- package/dist/access-recall-response.js +22 -22
- package/dist/access-recall-surface.d.ts +11 -11
- package/dist/access-recall-surface.js +22 -22
- package/dist/access-schema.d.ts +89 -89
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-CEUE1Isj.d.ts → access-service-C2tV2-uL.d.ts} +65 -65
- package/dist/access-service-helpers.d.ts +11 -11
- package/dist/access-service.d.ts +11 -11
- package/dist/access-service.js +22 -22
- package/dist/access-surface-catalog.d.ts +11 -11
- package/dist/access-surface-catalog.js +24 -24
- 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 +2 -2
- package/dist/adapters/index.js +7 -7
- package/dist/adapters/registry.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 +9 -9
- 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/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-D1HgugTA.d.ts → catalog-C2jA-gLm.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 +10 -10
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-J3OND5QT.js → chunk-2FA7U5XY.js} +2 -2
- package/dist/{chunk-GEJKYA7D.js → chunk-2ITZ74A6.js} +9 -9
- package/dist/{chunk-2LBAFHOQ.js → chunk-2X6BGKQL.js} +8 -8
- package/dist/{chunk-KODXCHXJ.js → chunk-46XR5FNZ.js} +39 -3
- package/dist/chunk-46XR5FNZ.js.map +1 -0
- package/dist/{chunk-4STAANV7.js → chunk-4XLTUR6R.js} +3 -3
- package/dist/{chunk-FTTJRVB7.js → chunk-65JOGLIS.js} +2 -2
- package/dist/{chunk-FESUGFIL.js → chunk-7UOXCCCI.js} +2 -2
- package/dist/{chunk-PEBH5DCK.js → chunk-AF5H7TKI.js} +100 -18
- package/dist/chunk-AF5H7TKI.js.map +1 -0
- package/dist/{chunk-6CEGSTBD.js → chunk-B4AOWRYF.js} +4 -4
- package/dist/{chunk-FYWLSFFS.js → chunk-C5IUOBCN.js} +2 -2
- package/dist/{chunk-FKKGCGYL.js → chunk-CHGG74CY.js} +2 -2
- package/dist/{chunk-H7VDIF2V.js → chunk-CX4OLNIH.js} +2 -2
- package/dist/{chunk-Z3YEGSR4.js → chunk-D3UXGE5D.js} +10 -10
- package/dist/{chunk-7XOG75AU.js → chunk-D6Q5EPOV.js} +2 -2
- package/dist/{chunk-MJRGVXPQ.js → chunk-EH65UV2Z.js} +2 -2
- package/dist/{chunk-VQ4AMTGZ.js → chunk-EWKVYCFP.js} +1 -1
- package/dist/{chunk-23CVL7HS.js → chunk-FFUMTH27.js} +2 -2
- package/dist/{chunk-GX6FTSYH.js → chunk-GAJK3DQ5.js} +2 -2
- package/dist/{chunk-O57GM3FR.js → chunk-GBEOUCBB.js} +2 -2
- package/dist/{chunk-DMEHOEC7.js → chunk-H2IRSXZN.js} +3 -3
- package/dist/{chunk-CFVTRUR3.js → chunk-H6RSANGQ.js} +2 -2
- package/dist/chunk-HVAEXYPL.js +98 -0
- package/dist/chunk-HVAEXYPL.js.map +1 -0
- package/dist/{chunk-OAJMQKQR.js → chunk-IGZCAHFL.js} +2 -2
- package/dist/{chunk-CN4VKV3S.js → chunk-INUBU6I5.js} +2 -2
- package/dist/{chunk-CTCGHNWN.js → chunk-J2R275FN.js} +4 -4
- package/dist/{chunk-2QSZNTDO.js → chunk-JBPKEARU.js} +7 -7
- package/dist/{chunk-VQNRICAQ.js → chunk-JKTV3JNX.js} +3 -3
- package/dist/{chunk-33MFRY25.js → chunk-KI64TTBL.js} +2 -2
- package/dist/{chunk-VCKWULAK.js → chunk-KJXCD7VR.js} +5 -5
- package/dist/{chunk-7LCGFUMB.js → chunk-KQAFEZQX.js} +4 -4
- package/dist/{chunk-GSKINLAK.js → chunk-LJABGXC4.js} +3 -3
- package/dist/{chunk-NPIBN2VL.js → chunk-LK4TOYPU.js} +2 -2
- package/dist/{chunk-EZSCW4EM.js → chunk-LYUUA6QB.js} +2 -2
- package/dist/{chunk-I5VB44ZK.js → chunk-M5ERTI4I.js} +2 -2
- package/dist/{chunk-IHGMRDEE.js → chunk-MUEGIKKY.js} +227 -144
- package/dist/chunk-MUEGIKKY.js.map +1 -0
- package/dist/{chunk-QCRJMU5V.js → chunk-NFP4KPTA.js} +2 -2
- package/dist/{chunk-YXFCALVT.js → chunk-NSTNGMSS.js} +8 -8
- package/dist/{chunk-L4X4PJYK.js → chunk-NUF7VSEX.js} +2 -2
- package/dist/{chunk-IVIAHQBR.js → chunk-OJUEUQDJ.js} +2 -2
- package/dist/{chunk-ZW4VXELZ.js → chunk-OMX6TKXH.js} +2 -2
- package/dist/{chunk-EFQCDJZ2.js → chunk-QBNE55VA.js} +2 -2
- package/dist/{chunk-6QDXA26G.js → chunk-RPIS5FQ7.js} +47 -47
- package/dist/{chunk-HJXA6ZM7.js → chunk-S4CDUXQY.js} +2 -2
- package/dist/{chunk-72OIT7KS.js → chunk-S4Z2OBTX.js} +3 -3
- package/dist/{chunk-4FHRZD5T.js → chunk-SZMYGDSM.js} +2 -2
- package/dist/{chunk-MAH64N55.js → chunk-TBCDF556.js} +4 -4
- package/dist/{chunk-56IV4J5W.js → chunk-TCF4MU7L.js} +443 -58
- package/dist/chunk-TCF4MU7L.js.map +1 -0
- package/dist/{chunk-K66OY4EI.js → chunk-TCQRIIIN.js} +2 -2
- package/dist/{chunk-KXIXO76C.js → chunk-U65VJSCJ.js} +14 -14
- package/dist/{chunk-HNPE66LB.js → chunk-U6NGKS32.js} +7 -7
- package/dist/{chunk-WKJFIMVN.js → chunk-UW4RYPWC.js} +2 -2
- package/dist/{chunk-MN5NOOI7.js → chunk-VG4SSPAW.js} +2 -2
- package/dist/chunk-WFCWP4XU.js +17 -0
- package/dist/chunk-WFCWP4XU.js.map +1 -0
- package/dist/{chunk-UNUMFURP.js → chunk-X5PIDJ3L.js} +3 -3
- package/dist/{chunk-RZ3IZIJG.js → chunk-XOHYGIWF.js} +2 -2
- package/dist/{chunk-MINHJ55X.js → chunk-XXW32OL7.js} +1 -1
- package/dist/{chunk-MINHJ55X.js.map → chunk-XXW32OL7.js.map} +1 -1
- package/dist/{chunk-6RUHELWR.js → chunk-YWPLOJ7T.js} +2 -2
- package/dist/{chunk-ZJPM7GZ3.js → chunk-ZPS724LT.js} +2 -2
- package/dist/{chunk-7UTDHQZ5.js → chunk-ZZZWEDF4.js} +2 -2
- package/dist/{cli-BEkm2MEf.d.ts → cli-CsfFMe6n.d.ts} +5 -5
- package/dist/cli.d.ts +13 -13
- package/dist/cli.js +63 -63
- package/dist/coding/export-okf-codegraph.d.ts +1 -1
- package/dist/coding/export-okf-codegraph.js +9 -9
- package/dist/coding/pre-action-gate.d.ts +1 -1
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +9 -9
- 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 +2 -2
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +9 -9
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +9 -9
- 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 +11 -11
- 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 +11 -11
- 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-BCpeo14F.d.ts → dependency-propagation-delivery-D4WMbOwA.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 +9 -9
- 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/external-wiki-access.d.ts +11 -11
- package/dist/external-wiki-access.js +23 -23
- 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 +9 -9
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-path-reconstruction.d.ts +1 -1
- package/dist/graph-path-scoring.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/importers/index.d.ts +1 -1
- package/dist/in-flight-reads.d.ts +1 -1
- package/dist/{index-D_pyMUaP.d.ts → index-DuJnzwHv.d.ts} +2 -2
- package/dist/index.d.ts +807 -698
- package/dist/index.js +130 -102
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/engine.js +4 -4
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/index.js +10 -10
- 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 +11 -11
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +9 -9
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +9 -9
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +10 -10
- package/dist/{maintenance-DDUX9d6D.d.ts → maintenance-BBeaeYRR.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 +10 -10
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +5 -5
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +9 -9
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/operator-doctor-corpus.d.ts +1 -1
- package/dist/operator-doctor-corpus.js +12 -12
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +19 -19
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -4
- package/dist/orchestration/maintenance.js +18 -18
- package/dist/{orchestrator-3F4jihCo.d.ts → orchestrator-CaqnwNwM.d.ts} +8 -8
- package/dist/orchestrator.d.ts +9 -9
- package/dist/orchestrator.js +86 -84
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-CB1dj6Uh.d.ts → pipeline-CrxVjlJr.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-34-JIAGQ.d.ts → public-http-Cwvl4QZr.d.ts} +1 -1
- package/dist/{qmd-edLLZXc5.d.ts → qmd-BZgbJm0g.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +2 -2
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +2 -2
- package/dist/recall-concurrency-config.d.ts +1 -1
- package/dist/recall-deep.d.ts +37 -0
- package/dist/recall-deep.js +81 -0
- package/dist/recall-deep.js.map +1 -0
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-memory-map.d.ts +1 -1
- package/dist/recall-navigate.d.ts +70 -0
- package/dist/recall-navigate.js +51 -0
- package/dist/recall-navigate.js.map +1 -0
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-state-view-wire.d.ts +14 -0
- package/dist/recall-state-view-wire.js +11 -0
- package/dist/recall-state-view-wire.js.map +1 -0
- package/dist/recall-state-view.d.ts +25 -0
- package/dist/recall-state-view.js +22 -0
- package/dist/recall-state-view.js.map +1 -0
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-timings.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +2 -2
- package/dist/reconcile/cursor.d.ts +1 -1
- package/dist/reconcile/manifest.d.ts +1 -1
- package/dist/reconcile/plan.d.ts +1 -1
- package/dist/replay/normalizers/chatgpt.d.ts +1 -1
- package/dist/replay/normalizers/claude.d.ts +1 -1
- package/dist/replay/normalizers/openclaw.d.ts +1 -1
- package/dist/replay/normalizers/shared.d.ts +1 -1
- package/dist/replay/runner.d.ts +1 -1
- package/dist/replay/types.d.ts +1 -1
- package/dist/replica-divergence.d.ts +1 -1
- package/dist/replica-peers-config.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +3 -3
- package/dist/retrieval-agents.d.ts +2 -2
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/salvage-envelope.d.ts +1 -1
- package/dist/schemas.d.ts +152 -152
- package/dist/{scope-profiles-BF2lu81V.d.ts → scope-profiles-DFaojvoP.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 +4 -4
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +10 -10
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-5OuWXBRt.d.ts → semantic-consolidation-BiX68wZA.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +10 -10
- package/dist/semantic-rule-promotion.js +9 -9
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +9 -9
- package/dist/{service-CrqEhLXD.d.ts → service-D5h2zZ0M.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 +7 -7
- package/dist/signal.d.ts +1 -1
- package/dist/source-agent-qualifier.d.ts +1 -1
- package/dist/source-agent-qualifier.js +9 -9
- package/dist/{storage-BQn-xnxm.d.ts → storage-CtsaojWD.d.ts} +1 -1
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +8 -8
- 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 +25 -25
- 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/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +2 -2
- package/dist/transfer/export-okf.d.ts +1 -1
- package/dist/transfer/export-okf.js +10 -10
- package/dist/transfer/types.d.ts +78 -78
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-BRpcNjHZ.d.ts → types-CIpfYz1I.d.ts} +16 -11
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +9 -9
- package/dist/who-knows-cli.d.ts +11 -11
- package/dist/who-knows-cli.js +10 -10
- package/dist/who-knows.d.ts +11 -11
- package/dist/who-knows.js +9 -9
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +2 -2
- package/src/activity/config.test.ts +1 -1
- package/src/activity/config.ts +48 -3
- package/src/activity/index.ts +2 -0
- package/src/activity/journal.test.ts +97 -0
- package/src/activity/journal.ts +113 -0
- package/src/activity/privacy.test.ts +53 -0
- package/src/activity/privacy.ts +80 -0
- package/src/activity/timeline/index.ts +2 -0
- package/src/activity/timeline/journal-recap.test.ts +113 -0
- package/src/activity/timeline/journal-recap.ts +130 -0
- package/src/activity/timeline/query.test.ts +198 -0
- package/src/activity/timeline/query.ts +313 -0
- package/src/activity/timeline/weekly.test.ts +215 -0
- package/src/activity/timeline/weekly.ts +234 -0
- package/src/activity/types.ts +14 -0
- package/src/activity/vault-publish.test.ts +166 -0
- package/src/activity/vault-publish.ts +101 -0
- package/src/config.test.ts +1 -1
- package/src/config.ts +2 -22
- package/src/dedup/merge-on-write.test.ts +91 -0
- package/src/dedup/merge-on-write.ts +75 -0
- package/src/dedup/novelty-gate.test.ts +143 -0
- package/src/dedup/novelty-gate.ts +163 -0
- package/src/location/recall-exclusion.test.ts +1 -0
- package/src/orchestration/extraction-persist.ts +22 -23
- package/src/orchestration/generic-recall-paths.ts +11 -0
- package/src/orchestration/recall-entry.ts +8 -3
- package/src/recall-deep.test.ts +72 -0
- package/src/recall-deep.ts +113 -0
- package/src/recall-navigate.test.ts +84 -0
- package/src/recall-navigate.ts +100 -0
- package/src/recall-state-view-wire.test.ts +35 -0
- package/src/recall-state-view-wire.ts +28 -0
- package/src/recall-state-view.test.ts +131 -0
- package/src/recall-state-view.ts +138 -0
- package/src/shared-context/governance.test.ts +53 -0
- package/src/shared-context/governance.ts +86 -0
- package/src/types.ts +3 -10
- package/dist/chunk-56IV4J5W.js.map +0 -1
- package/dist/chunk-IHGMRDEE.js.map +0 -1
- package/dist/chunk-KODXCHXJ.js.map +0 -1
- package/dist/chunk-PEBH5DCK.js.map +0 -1
- /package/dist/{chunk-J3OND5QT.js.map → chunk-2FA7U5XY.js.map} +0 -0
- /package/dist/{chunk-GEJKYA7D.js.map → chunk-2ITZ74A6.js.map} +0 -0
- /package/dist/{chunk-2LBAFHOQ.js.map → chunk-2X6BGKQL.js.map} +0 -0
- /package/dist/{chunk-4STAANV7.js.map → chunk-4XLTUR6R.js.map} +0 -0
- /package/dist/{chunk-FTTJRVB7.js.map → chunk-65JOGLIS.js.map} +0 -0
- /package/dist/{chunk-FESUGFIL.js.map → chunk-7UOXCCCI.js.map} +0 -0
- /package/dist/{chunk-6CEGSTBD.js.map → chunk-B4AOWRYF.js.map} +0 -0
- /package/dist/{chunk-FYWLSFFS.js.map → chunk-C5IUOBCN.js.map} +0 -0
- /package/dist/{chunk-FKKGCGYL.js.map → chunk-CHGG74CY.js.map} +0 -0
- /package/dist/{chunk-H7VDIF2V.js.map → chunk-CX4OLNIH.js.map} +0 -0
- /package/dist/{chunk-Z3YEGSR4.js.map → chunk-D3UXGE5D.js.map} +0 -0
- /package/dist/{chunk-7XOG75AU.js.map → chunk-D6Q5EPOV.js.map} +0 -0
- /package/dist/{chunk-MJRGVXPQ.js.map → chunk-EH65UV2Z.js.map} +0 -0
- /package/dist/{chunk-VQ4AMTGZ.js.map → chunk-EWKVYCFP.js.map} +0 -0
- /package/dist/{chunk-23CVL7HS.js.map → chunk-FFUMTH27.js.map} +0 -0
- /package/dist/{chunk-GX6FTSYH.js.map → chunk-GAJK3DQ5.js.map} +0 -0
- /package/dist/{chunk-O57GM3FR.js.map → chunk-GBEOUCBB.js.map} +0 -0
- /package/dist/{chunk-DMEHOEC7.js.map → chunk-H2IRSXZN.js.map} +0 -0
- /package/dist/{chunk-CFVTRUR3.js.map → chunk-H6RSANGQ.js.map} +0 -0
- /package/dist/{chunk-OAJMQKQR.js.map → chunk-IGZCAHFL.js.map} +0 -0
- /package/dist/{chunk-CN4VKV3S.js.map → chunk-INUBU6I5.js.map} +0 -0
- /package/dist/{chunk-CTCGHNWN.js.map → chunk-J2R275FN.js.map} +0 -0
- /package/dist/{chunk-2QSZNTDO.js.map → chunk-JBPKEARU.js.map} +0 -0
- /package/dist/{chunk-VQNRICAQ.js.map → chunk-JKTV3JNX.js.map} +0 -0
- /package/dist/{chunk-33MFRY25.js.map → chunk-KI64TTBL.js.map} +0 -0
- /package/dist/{chunk-VCKWULAK.js.map → chunk-KJXCD7VR.js.map} +0 -0
- /package/dist/{chunk-7LCGFUMB.js.map → chunk-KQAFEZQX.js.map} +0 -0
- /package/dist/{chunk-GSKINLAK.js.map → chunk-LJABGXC4.js.map} +0 -0
- /package/dist/{chunk-NPIBN2VL.js.map → chunk-LK4TOYPU.js.map} +0 -0
- /package/dist/{chunk-EZSCW4EM.js.map → chunk-LYUUA6QB.js.map} +0 -0
- /package/dist/{chunk-I5VB44ZK.js.map → chunk-M5ERTI4I.js.map} +0 -0
- /package/dist/{chunk-QCRJMU5V.js.map → chunk-NFP4KPTA.js.map} +0 -0
- /package/dist/{chunk-YXFCALVT.js.map → chunk-NSTNGMSS.js.map} +0 -0
- /package/dist/{chunk-L4X4PJYK.js.map → chunk-NUF7VSEX.js.map} +0 -0
- /package/dist/{chunk-IVIAHQBR.js.map → chunk-OJUEUQDJ.js.map} +0 -0
- /package/dist/{chunk-ZW4VXELZ.js.map → chunk-OMX6TKXH.js.map} +0 -0
- /package/dist/{chunk-EFQCDJZ2.js.map → chunk-QBNE55VA.js.map} +0 -0
- /package/dist/{chunk-6QDXA26G.js.map → chunk-RPIS5FQ7.js.map} +0 -0
- /package/dist/{chunk-HJXA6ZM7.js.map → chunk-S4CDUXQY.js.map} +0 -0
- /package/dist/{chunk-72OIT7KS.js.map → chunk-S4Z2OBTX.js.map} +0 -0
- /package/dist/{chunk-4FHRZD5T.js.map → chunk-SZMYGDSM.js.map} +0 -0
- /package/dist/{chunk-MAH64N55.js.map → chunk-TBCDF556.js.map} +0 -0
- /package/dist/{chunk-K66OY4EI.js.map → chunk-TCQRIIIN.js.map} +0 -0
- /package/dist/{chunk-KXIXO76C.js.map → chunk-U65VJSCJ.js.map} +0 -0
- /package/dist/{chunk-HNPE66LB.js.map → chunk-U6NGKS32.js.map} +0 -0
- /package/dist/{chunk-WKJFIMVN.js.map → chunk-UW4RYPWC.js.map} +0 -0
- /package/dist/{chunk-MN5NOOI7.js.map → chunk-VG4SSPAW.js.map} +0 -0
- /package/dist/{chunk-UNUMFURP.js.map → chunk-X5PIDJ3L.js.map} +0 -0
- /package/dist/{chunk-RZ3IZIJG.js.map → chunk-XOHYGIWF.js.map} +0 -0
- /package/dist/{chunk-6RUHELWR.js.map → chunk-YWPLOJ7T.js.map} +0 -0
- /package/dist/{chunk-ZJPM7GZ3.js.map → chunk-ZPS724LT.js.map} +0 -0
- /package/dist/{chunk-7UTDHQZ5.js.map → chunk-ZZZWEDF4.js.map} +0 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User-owned daily journal (issue #1984 PR1).
|
|
3
|
+
*
|
|
4
|
+
* Files live at `<memoryDir>/journal/<YYYY-MM-DD>.md` — inside the memory
|
|
5
|
+
* directory but outside scan roots, same placement as activity digests.
|
|
6
|
+
* Once written, Remnic never rewrites the file except `seed` with force.
|
|
7
|
+
* All writes go through writeJournalFile, which stats first.
|
|
8
|
+
*/
|
|
9
|
+
import { mkdirSync, statSync, writeFileSync } from "node:fs";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
|
|
12
|
+
import { isValidActivityDate } from "./digest.js";
|
|
13
|
+
|
|
14
|
+
export const JOURNAL_DIR_NAME = "journal";
|
|
15
|
+
|
|
16
|
+
/** Minimal card shape the seed renderer needs. TimelineCard is assignable. */
|
|
17
|
+
export interface JournalSeedCard {
|
|
18
|
+
title: string;
|
|
19
|
+
startUtc: string;
|
|
20
|
+
endUtc: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface SeedJournalInput {
|
|
24
|
+
memoryDir: string;
|
|
25
|
+
date: string;
|
|
26
|
+
cards?: readonly JournalSeedCard[];
|
|
27
|
+
force?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface SeedJournalResult {
|
|
31
|
+
path: string;
|
|
32
|
+
wrote: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const JOURNAL_DAY_TOPLEVEL = /^journal[\\/]\d{4}-\d{2}-\d{2}\.md$/i;
|
|
36
|
+
const JOURNAL_DAY_ANYWHERE = /(?:^|[\\/])journal[\\/]\d{4}-\d{2}-\d{2}\.md$/i;
|
|
37
|
+
|
|
38
|
+
export function journalPath(memoryDir: string, date: string): string {
|
|
39
|
+
if (!isValidActivityDate(date)) {
|
|
40
|
+
throw new RangeError(`Invalid journal date "${date}"; expected YYYY-MM-DD.`);
|
|
41
|
+
}
|
|
42
|
+
return path.join(memoryDir, JOURNAL_DIR_NAME, `${date}.md`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Root-aware journal day-file check. With a memory root, only the top-level
|
|
47
|
+
* `journal/<date>.md` matches so a nested `facts/journal/<date>.md` stays
|
|
48
|
+
* recallable. Without a root, fall back to the day-file shape anywhere.
|
|
49
|
+
*/
|
|
50
|
+
export function isJournalDayPath(filePath: string, memoryRoot?: string): boolean {
|
|
51
|
+
if (memoryRoot !== undefined && memoryRoot.length > 0) {
|
|
52
|
+
const relative = path.relative(memoryRoot, path.resolve(memoryRoot, filePath));
|
|
53
|
+
return JOURNAL_DAY_TOPLEVEL.test(relative);
|
|
54
|
+
}
|
|
55
|
+
return JOURNAL_DAY_ANYWHERE.test(filePath);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function todayJournalDate(now: Date = new Date()): string {
|
|
59
|
+
const year = now.getFullYear();
|
|
60
|
+
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
61
|
+
const day = String(now.getDate()).padStart(2, "0");
|
|
62
|
+
return `${year}-${month}-${day}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function formatCardDuration(card: JournalSeedCard): string {
|
|
66
|
+
const start = Date.parse(card.startUtc);
|
|
67
|
+
const end = Date.parse(card.endUtc);
|
|
68
|
+
const minutes =
|
|
69
|
+
Number.isFinite(start) && Number.isFinite(end) && end > start
|
|
70
|
+
? Math.round((end - start) / 60_000)
|
|
71
|
+
: 0;
|
|
72
|
+
return `${minutes}m`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function renderJournalSeed(date: string, cards: readonly JournalSeedCard[]): string {
|
|
76
|
+
const ordered = [...cards].sort((left, right) => {
|
|
77
|
+
const byStart = left.startUtc.localeCompare(right.startUtc);
|
|
78
|
+
return byStart !== 0 ? byStart : left.title.localeCompare(right.title);
|
|
79
|
+
});
|
|
80
|
+
const glance =
|
|
81
|
+
ordered.length === 0
|
|
82
|
+
? ["_No cards._"]
|
|
83
|
+
: ordered.map((card) => `- ${formatCardDuration(card)} ${card.title}`);
|
|
84
|
+
return [`# Journal — ${date}`, "", "## Day at a glance", "", ...glance, "", "## Notes", "", ""].join(
|
|
85
|
+
"\n",
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The only journal write. Stats first; existing files are a no-op unless force. */
|
|
90
|
+
function writeJournalFile(filePath: string, content: string, force: boolean): boolean {
|
|
91
|
+
let exists = false;
|
|
92
|
+
try {
|
|
93
|
+
statSync(filePath);
|
|
94
|
+
exists = true;
|
|
95
|
+
} catch (err) {
|
|
96
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
97
|
+
if (code !== "ENOENT") throw err;
|
|
98
|
+
}
|
|
99
|
+
if (exists && !force) return false;
|
|
100
|
+
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
101
|
+
writeFileSync(filePath, content);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function seedJournal(input: SeedJournalInput): SeedJournalResult {
|
|
106
|
+
const filePath = journalPath(input.memoryDir, input.date);
|
|
107
|
+
const wrote = writeJournalFile(
|
|
108
|
+
filePath,
|
|
109
|
+
renderJournalSeed(input.date, input.cards ?? []),
|
|
110
|
+
input.force === true,
|
|
111
|
+
);
|
|
112
|
+
return { path: filePath, wrote };
|
|
113
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
MS_PER_DAY,
|
|
6
|
+
observationsForExport,
|
|
7
|
+
parseActivityPrivacy,
|
|
8
|
+
shouldRetain,
|
|
9
|
+
} from "./privacy.js";
|
|
10
|
+
|
|
11
|
+
test("retentionDays 0 keeps forever", () => {
|
|
12
|
+
const policy = parseActivityPrivacy({ enabled: true, retentionDays: 0 });
|
|
13
|
+
assert.equal(policy.retentionDays, 0);
|
|
14
|
+
const now = 1_700_000_000_000;
|
|
15
|
+
assert.equal(shouldRetain(now - 400 * MS_PER_DAY, now, policy.retentionDays), true);
|
|
16
|
+
assert.equal(shouldRetain(now, now, 0), true);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("negative retentionDays throws", () => {
|
|
20
|
+
assert.throws(() => parseActivityPrivacy({ retentionDays: -1 }), /non-negative integer/);
|
|
21
|
+
assert.throws(() => parseActivityPrivacy({ retentionDays: -30 }), /non-negative integer/);
|
|
22
|
+
assert.throws(() => shouldRetain(0, 0, -1), /non-negative integer/);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("exact expiry boundary is half-open", () => {
|
|
26
|
+
const now = 1_700_000_000_000;
|
|
27
|
+
const days = 30;
|
|
28
|
+
const captured = now - days * MS_PER_DAY;
|
|
29
|
+
assert.equal(shouldRetain(captured, now, days), false);
|
|
30
|
+
assert.equal(shouldRetain(captured + 1, now, days), true);
|
|
31
|
+
assert.equal(shouldRetain(captured - 1, now, days), false);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("disabled master denies retain and empties export", () => {
|
|
35
|
+
const policy = parseActivityPrivacy({
|
|
36
|
+
enabled: false,
|
|
37
|
+
retentionDays: 0,
|
|
38
|
+
exportIncludeObservations: true,
|
|
39
|
+
});
|
|
40
|
+
assert.equal(policy.enabled, false);
|
|
41
|
+
assert.equal(shouldRetain(1_700_000_000_000, 1_700_000_000_000, policy.retentionDays, policy.enabled), false);
|
|
42
|
+
assert.deepEqual(observationsForExport(["obs"], policy), []);
|
|
43
|
+
assert.equal(parseActivityPrivacy(undefined).enabled, false);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("exportIncludeObservations defaults false", () => {
|
|
47
|
+
assert.equal(parseActivityPrivacy(undefined).exportIncludeObservations, false);
|
|
48
|
+
assert.equal(parseActivityPrivacy({ enabled: true }).exportIncludeObservations, false);
|
|
49
|
+
const policy = parseActivityPrivacy({ enabled: true });
|
|
50
|
+
assert.deepEqual(observationsForExport(["obs"], policy), []);
|
|
51
|
+
const included = parseActivityPrivacy({ enabled: true, exportIncludeObservations: true });
|
|
52
|
+
assert.deepEqual(observationsForExport(["obs"], included), ["obs"]);
|
|
53
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activity retention and export policy (issue #2053 first slice).
|
|
3
|
+
*
|
|
4
|
+
* Pure helpers. Surfaces and parseConfig wiring wait for a later PR.
|
|
5
|
+
* Master `activity.enabled` false denies retain and empties export.
|
|
6
|
+
*/
|
|
7
|
+
import { coerceBooleanLike, coerceNumber } from "../connectors/coerce.js";
|
|
8
|
+
|
|
9
|
+
export const MS_PER_DAY = 86_400_000;
|
|
10
|
+
|
|
11
|
+
export interface ActivityPrivacyPolicy {
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
/** `0` keeps forever. */
|
|
14
|
+
retentionDays: number;
|
|
15
|
+
exportIncludeObservations: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function asRecord(value: unknown, name: string): Record<string, unknown> {
|
|
19
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
20
|
+
throw new TypeError(`${name} must be an object`);
|
|
21
|
+
}
|
|
22
|
+
return value as Record<string, unknown>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function parseGate(value: unknown, key: string, fallback: boolean): boolean {
|
|
26
|
+
if (value === undefined) return fallback;
|
|
27
|
+
const parsed = coerceBooleanLike(value, key);
|
|
28
|
+
if (parsed === undefined) throw new TypeError(`${key} must be a boolean`);
|
|
29
|
+
return parsed;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Parse retention and export knobs. Independent of timeline/journal/weekly blocks. */
|
|
33
|
+
export function parseActivityPrivacy(raw?: unknown): ActivityPrivacyPolicy {
|
|
34
|
+
if (raw === undefined || raw === null) {
|
|
35
|
+
return { enabled: false, retentionDays: 0, exportIncludeObservations: false };
|
|
36
|
+
}
|
|
37
|
+
const config = asRecord(raw, "activity");
|
|
38
|
+
const enabled = parseGate(config.enabled, "activity.enabled", false);
|
|
39
|
+
let retentionDays = 0;
|
|
40
|
+
if (config.retentionDays !== undefined) {
|
|
41
|
+
const parsed = coerceNumber(config.retentionDays, "activity.retentionDays");
|
|
42
|
+
if (parsed === undefined || !Number.isInteger(parsed) || parsed < 0) {
|
|
43
|
+
throw new RangeError("activity.retentionDays must be a non-negative integer");
|
|
44
|
+
}
|
|
45
|
+
retentionDays = parsed;
|
|
46
|
+
}
|
|
47
|
+
const exportIncludeObservations = parseGate(
|
|
48
|
+
config.exportIncludeObservations,
|
|
49
|
+
"activity.exportIncludeObservations",
|
|
50
|
+
false,
|
|
51
|
+
);
|
|
52
|
+
return { enabled, retentionDays, exportIncludeObservations };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Half-open retain window: keep when age is in `[0, retentionDays)`.
|
|
57
|
+
* `retentionDays === 0` keeps forever. Master off denies.
|
|
58
|
+
*/
|
|
59
|
+
export function shouldRetain(
|
|
60
|
+
capturedAtMs: number,
|
|
61
|
+
nowMs: number,
|
|
62
|
+
retentionDays: number,
|
|
63
|
+
enabled = true,
|
|
64
|
+
): boolean {
|
|
65
|
+
if (!enabled) return false;
|
|
66
|
+
if (retentionDays < 0 || !Number.isInteger(retentionDays)) {
|
|
67
|
+
throw new RangeError("activity.retentionDays must be a non-negative integer");
|
|
68
|
+
}
|
|
69
|
+
if (retentionDays === 0) return true;
|
|
70
|
+
return nowMs - capturedAtMs < retentionDays * MS_PER_DAY;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Export payload. Master off or the export gate off returns empty. */
|
|
74
|
+
export function observationsForExport<T>(
|
|
75
|
+
observations: readonly T[],
|
|
76
|
+
policy: Pick<ActivityPrivacyPolicy, "enabled" | "exportIncludeObservations">,
|
|
77
|
+
): readonly T[] {
|
|
78
|
+
if (!policy.enabled || !policy.exportIncludeObservations) return [];
|
|
79
|
+
return observations;
|
|
80
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import type { TimelineCard } from "./types.js";
|
|
5
|
+
import { renderDeterministicJournal } from "./journal-recap.js";
|
|
6
|
+
|
|
7
|
+
const DATE = "2026-08-17";
|
|
8
|
+
const TZ = "UTC";
|
|
9
|
+
|
|
10
|
+
function card(
|
|
11
|
+
overrides: Partial<TimelineCard> & Pick<TimelineCard, "id" | "startUtc" | "endUtc">,
|
|
12
|
+
): TimelineCard {
|
|
13
|
+
return {
|
|
14
|
+
kind: "activity",
|
|
15
|
+
title: "Untitled",
|
|
16
|
+
summary: "none",
|
|
17
|
+
categoryId: "development",
|
|
18
|
+
confidence: 1,
|
|
19
|
+
dayKey: DATE,
|
|
20
|
+
timezone: TZ,
|
|
21
|
+
machine: "ws-a",
|
|
22
|
+
evidenceIds: [],
|
|
23
|
+
evidenceRange: null,
|
|
24
|
+
...overrides,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const EMPTY_DAY = [
|
|
29
|
+
`# Journal — ${DATE} (${TZ})`,
|
|
30
|
+
"",
|
|
31
|
+
"## Categories",
|
|
32
|
+
"",
|
|
33
|
+
"_No categories._",
|
|
34
|
+
"",
|
|
35
|
+
"## Cards",
|
|
36
|
+
"",
|
|
37
|
+
"_No cards._",
|
|
38
|
+
"",
|
|
39
|
+
"## Gaps / idle / pause",
|
|
40
|
+
"",
|
|
41
|
+
"- Gaps: 1",
|
|
42
|
+
"- Idle: 0",
|
|
43
|
+
"- Pause: 0",
|
|
44
|
+
"",
|
|
45
|
+
].join("\n");
|
|
46
|
+
|
|
47
|
+
const ONE_ACTIVITY_PLUS_IDLE = [
|
|
48
|
+
`# Journal — ${DATE} (${TZ})`,
|
|
49
|
+
"",
|
|
50
|
+
"## Categories",
|
|
51
|
+
"",
|
|
52
|
+
"- 90m development",
|
|
53
|
+
"- 15m system.idle",
|
|
54
|
+
"",
|
|
55
|
+
"## Cards",
|
|
56
|
+
"",
|
|
57
|
+
"- 90m Terminal",
|
|
58
|
+
"- 15m Idle",
|
|
59
|
+
"",
|
|
60
|
+
"## Gaps / idle / pause",
|
|
61
|
+
"",
|
|
62
|
+
"- Gaps: 2",
|
|
63
|
+
"- Idle: 1",
|
|
64
|
+
"- Pause: 0",
|
|
65
|
+
"",
|
|
66
|
+
].join("\n");
|
|
67
|
+
|
|
68
|
+
function activityPlusIdle(): TimelineCard[] {
|
|
69
|
+
return [
|
|
70
|
+
card({
|
|
71
|
+
id: "idle-1",
|
|
72
|
+
kind: "idle",
|
|
73
|
+
title: "Idle",
|
|
74
|
+
categoryId: "system.idle",
|
|
75
|
+
startUtc: "2026-08-17T15:30:00.000Z",
|
|
76
|
+
endUtc: "2026-08-17T15:45:00.000Z",
|
|
77
|
+
}),
|
|
78
|
+
card({
|
|
79
|
+
id: "act-1",
|
|
80
|
+
title: "Terminal",
|
|
81
|
+
startUtc: "2026-08-17T14:00:00.000Z",
|
|
82
|
+
endUtc: "2026-08-17T15:30:00.000Z",
|
|
83
|
+
}),
|
|
84
|
+
];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
test("empty day still produces a valid journal body", () => {
|
|
88
|
+
const body = renderDeterministicJournal([], { date: DATE, timezone: TZ });
|
|
89
|
+
assert.equal(body, EMPTY_DAY);
|
|
90
|
+
assert.match(body, /^# Journal — /);
|
|
91
|
+
assert.equal(body.endsWith("\n"), true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("one activity plus idle lists durations, titles, and gap counts", () => {
|
|
95
|
+
const body = renderDeterministicJournal(activityPlusIdle(), { date: DATE, timezone: TZ });
|
|
96
|
+
assert.equal(body, ONE_ACTIVITY_PLUS_IDLE);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("same cards render byte-stable on rerun", () => {
|
|
100
|
+
const cards = activityPlusIdle();
|
|
101
|
+
const first = renderDeterministicJournal(cards, { date: DATE, timezone: TZ });
|
|
102
|
+
const second = renderDeterministicJournal(cards, { date: DATE, timezone: TZ });
|
|
103
|
+
assert.equal(first, second);
|
|
104
|
+
assert.equal(Buffer.byteLength(first), Buffer.byteLength(second));
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("recap does not invent people or mood claims", () => {
|
|
108
|
+
const body = renderDeterministicJournal(activityPlusIdle(), { date: DATE, timezone: TZ });
|
|
109
|
+
assert.equal(/Alice|Bob|Charlie|meeting with/i.test(body), false);
|
|
110
|
+
assert.equal(/productivity|mood|intent|score/i.test(body), false);
|
|
111
|
+
assert.match(body, /Terminal/);
|
|
112
|
+
assert.match(body, /Idle/);
|
|
113
|
+
});
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic daily journal recap from timeline cards (issue #2051).
|
|
3
|
+
*
|
|
4
|
+
* Pure: cards + date + timezone in, Markdown out. No LLM, no I/O.
|
|
5
|
+
* Same cards always produce the same bytes. Empty days still yield a
|
|
6
|
+
* valid file body. Does not invent people, mood, or productivity claims.
|
|
7
|
+
*/
|
|
8
|
+
import { activityDayWindow } from "../digest.js";
|
|
9
|
+
import type { TimelineCard } from "./types.js";
|
|
10
|
+
|
|
11
|
+
export interface DeterministicJournalOptions {
|
|
12
|
+
date: string;
|
|
13
|
+
timezone: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ClippedCard {
|
|
17
|
+
card: TimelineCard;
|
|
18
|
+
startMs: number;
|
|
19
|
+
endMs: number;
|
|
20
|
+
durationMs: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function formatMinutes(ms: number): string {
|
|
24
|
+
return `${Math.round(ms / 60_000)}m`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function clipToWindow(
|
|
28
|
+
cards: readonly TimelineCard[],
|
|
29
|
+
winStart: number,
|
|
30
|
+
winEnd: number,
|
|
31
|
+
): ClippedCard[] {
|
|
32
|
+
const clipped: ClippedCard[] = [];
|
|
33
|
+
for (const card of cards) {
|
|
34
|
+
const start = Date.parse(card.startUtc);
|
|
35
|
+
const end = Date.parse(card.endUtc);
|
|
36
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) continue;
|
|
37
|
+
const startMs = Math.max(start, winStart);
|
|
38
|
+
const endMs = Math.min(end, winEnd);
|
|
39
|
+
if (endMs <= startMs) continue;
|
|
40
|
+
clipped.push({ card, startMs, endMs, durationMs: endMs - startMs });
|
|
41
|
+
}
|
|
42
|
+
clipped.sort((left, right) => {
|
|
43
|
+
const byStart = left.startMs - right.startMs;
|
|
44
|
+
if (byStart !== 0) return byStart;
|
|
45
|
+
const byTitle = left.card.title.localeCompare(right.card.title);
|
|
46
|
+
if (byTitle !== 0) return byTitle;
|
|
47
|
+
return left.card.id.localeCompare(right.card.id);
|
|
48
|
+
});
|
|
49
|
+
return clipped;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function categoryLines(clipped: readonly ClippedCard[]): string[] {
|
|
53
|
+
const totals = new Map<string, number>();
|
|
54
|
+
for (const row of clipped) {
|
|
55
|
+
totals.set(row.card.categoryId, (totals.get(row.card.categoryId) ?? 0) + row.durationMs);
|
|
56
|
+
}
|
|
57
|
+
const rows = [...totals.entries()].sort((left, right) => {
|
|
58
|
+
const byDuration = right[1] - left[1];
|
|
59
|
+
return byDuration !== 0 ? byDuration : left[0].localeCompare(right[0]);
|
|
60
|
+
});
|
|
61
|
+
if (rows.length === 0) return ["_No categories._"];
|
|
62
|
+
return rows.map(([categoryId, durationMs]) => `- ${formatMinutes(durationMs)} ${categoryId}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function mergeIntervals(intervals: readonly (readonly [number, number])[]): Array<[number, number]> {
|
|
66
|
+
const ordered = [...intervals].sort((left, right) => left[0] - right[0] || left[1] - right[1]);
|
|
67
|
+
const merged: Array<[number, number]> = [];
|
|
68
|
+
for (const [start, end] of ordered) {
|
|
69
|
+
const last = merged[merged.length - 1];
|
|
70
|
+
if (!last || start > last[1]) merged.push([start, end]);
|
|
71
|
+
else last[1] = Math.max(last[1], end);
|
|
72
|
+
}
|
|
73
|
+
return merged;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function countGaps(
|
|
77
|
+
winStart: number,
|
|
78
|
+
winEnd: number,
|
|
79
|
+
intervals: readonly (readonly [number, number])[],
|
|
80
|
+
): number {
|
|
81
|
+
const merged = mergeIntervals(intervals);
|
|
82
|
+
let gaps = 0;
|
|
83
|
+
let cursor = winStart;
|
|
84
|
+
for (const [start, end] of merged) {
|
|
85
|
+
if (start > cursor) gaps += 1;
|
|
86
|
+
cursor = Math.max(cursor, end);
|
|
87
|
+
}
|
|
88
|
+
if (cursor < winEnd) gaps += 1;
|
|
89
|
+
return gaps;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Render a byte-stable Markdown journal for one local day. */
|
|
93
|
+
export function renderDeterministicJournal(
|
|
94
|
+
cards: readonly TimelineCard[],
|
|
95
|
+
options: DeterministicJournalOptions,
|
|
96
|
+
): string {
|
|
97
|
+
const window = activityDayWindow(options.date, options.timezone);
|
|
98
|
+
const winStart = Date.parse(window.startUtc);
|
|
99
|
+
const winEnd = Date.parse(window.endUtc);
|
|
100
|
+
const clipped = clipToWindow(cards, winStart, winEnd);
|
|
101
|
+
const idle = clipped.filter((row) => row.card.kind === "idle").length;
|
|
102
|
+
const pause = clipped.filter((row) => row.card.kind === "pause").length;
|
|
103
|
+
const gaps = countGaps(
|
|
104
|
+
winStart,
|
|
105
|
+
winEnd,
|
|
106
|
+
clipped.map((row) => [row.startMs, row.endMs]),
|
|
107
|
+
);
|
|
108
|
+
const cardLines =
|
|
109
|
+
clipped.length === 0
|
|
110
|
+
? ["_No cards._"]
|
|
111
|
+
: clipped.map((row) => `- ${formatMinutes(row.durationMs)} ${row.card.title}`);
|
|
112
|
+
return [
|
|
113
|
+
`# Journal — ${options.date} (${options.timezone})`,
|
|
114
|
+
"",
|
|
115
|
+
"## Categories",
|
|
116
|
+
"",
|
|
117
|
+
...categoryLines(clipped),
|
|
118
|
+
"",
|
|
119
|
+
"## Cards",
|
|
120
|
+
"",
|
|
121
|
+
...cardLines,
|
|
122
|
+
"",
|
|
123
|
+
"## Gaps / idle / pause",
|
|
124
|
+
"",
|
|
125
|
+
`- Gaps: ${gaps}`,
|
|
126
|
+
`- Idle: ${idle}`,
|
|
127
|
+
`- Pause: ${pause}`,
|
|
128
|
+
"",
|
|
129
|
+
].join("\n");
|
|
130
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import { parseActivityConfig } from "../config.js";
|
|
5
|
+
import {
|
|
6
|
+
queryTimelineRange,
|
|
7
|
+
queryTimelineSearch,
|
|
8
|
+
runTimelineCliCommand,
|
|
9
|
+
type TimelineQueryCard,
|
|
10
|
+
} from "./query.js";
|
|
11
|
+
import type { TimelineCard } from "./types.js";
|
|
12
|
+
|
|
13
|
+
function card(overrides: Partial<TimelineQueryCard> & Pick<TimelineCard, "id" | "startUtc" | "endUtc">): TimelineQueryCard {
|
|
14
|
+
return {
|
|
15
|
+
kind: "activity",
|
|
16
|
+
title: "Untitled",
|
|
17
|
+
summary: "none",
|
|
18
|
+
categoryId: "development",
|
|
19
|
+
confidence: 1,
|
|
20
|
+
dayKey: overrides.startUtc.slice(0, 10),
|
|
21
|
+
timezone: "UTC",
|
|
22
|
+
machine: "ws-a",
|
|
23
|
+
evidenceIds: [],
|
|
24
|
+
evidenceRange: null,
|
|
25
|
+
...overrides,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const BOUNDARY_A = card({
|
|
30
|
+
id: "a",
|
|
31
|
+
title: "Morning editor",
|
|
32
|
+
summary: "typed in vscode",
|
|
33
|
+
startUtc: "2026-07-10T10:00:00.000Z",
|
|
34
|
+
endUtc: "2026-07-10T11:00:00.000Z",
|
|
35
|
+
});
|
|
36
|
+
const BOUNDARY_B = card({
|
|
37
|
+
id: "b",
|
|
38
|
+
title: "Standup call",
|
|
39
|
+
summary: "zoom standup",
|
|
40
|
+
categoryId: "communication",
|
|
41
|
+
startUtc: "2026-07-10T11:00:00.000Z",
|
|
42
|
+
endUtc: "2026-07-10T12:00:00.000Z",
|
|
43
|
+
});
|
|
44
|
+
const LATER = card({
|
|
45
|
+
id: "c",
|
|
46
|
+
title: "Docs writeup",
|
|
47
|
+
summary: "edited the standup notes",
|
|
48
|
+
detailedSummary: "longer writeup about the morning editor session",
|
|
49
|
+
startUtc: "2026-07-11T09:00:00.000Z",
|
|
50
|
+
endUtc: "2026-07-11T10:00:00.000Z",
|
|
51
|
+
});
|
|
52
|
+
const DISTRACTION = card({
|
|
53
|
+
id: "d",
|
|
54
|
+
title: "Video break",
|
|
55
|
+
summary: "watched a clip",
|
|
56
|
+
categoryId: "entertainment",
|
|
57
|
+
distraction: true,
|
|
58
|
+
startUtc: "2026-07-10T12:00:00.000Z",
|
|
59
|
+
endUtc: "2026-07-10T12:30:00.000Z",
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const CARDS = [BOUNDARY_A, BOUNDARY_B, LATER, DISTRACTION];
|
|
63
|
+
|
|
64
|
+
test("range at a boundary instant returns only the containing card", () => {
|
|
65
|
+
const instant = "2026-07-10T11:00:00.000Z";
|
|
66
|
+
const result = queryTimelineRange(CARDS, { from: instant, to: instant, format: "cards" });
|
|
67
|
+
assert.ok(Array.isArray(result));
|
|
68
|
+
assert.deepEqual(
|
|
69
|
+
result.map((item) => item.id),
|
|
70
|
+
["b"],
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("range rejects a reversed window and names the limit", () => {
|
|
75
|
+
assert.throws(
|
|
76
|
+
() =>
|
|
77
|
+
queryTimelineRange(CARDS, {
|
|
78
|
+
from: "2026-07-11T00:00:00.000Z",
|
|
79
|
+
to: "2026-07-10T00:00:00.000Z",
|
|
80
|
+
format: "cards",
|
|
81
|
+
}),
|
|
82
|
+
/reversed range/,
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("range rejects a 400-day window and names maxRangeDays", () => {
|
|
87
|
+
assert.throws(
|
|
88
|
+
() =>
|
|
89
|
+
queryTimelineRange(CARDS, {
|
|
90
|
+
from: "2026-01-01T00:00:00.000Z",
|
|
91
|
+
to: "2027-02-05T00:00:00.000Z",
|
|
92
|
+
format: "cards",
|
|
93
|
+
}),
|
|
94
|
+
/maxRangeDays \(31\)/,
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("range format cards and compact both round-trip the fixture", () => {
|
|
99
|
+
const from = "2026-07-10T10:00:00.000Z";
|
|
100
|
+
const to = "2026-07-10T12:00:00.000Z";
|
|
101
|
+
const full = queryTimelineRange(CARDS, { from, to, format: "cards", includeDistractions: true });
|
|
102
|
+
assert.ok(Array.isArray(full));
|
|
103
|
+
assert.deepEqual(
|
|
104
|
+
full.map((item) => item.id),
|
|
105
|
+
["a", "b"],
|
|
106
|
+
);
|
|
107
|
+
const compact = queryTimelineRange(CARDS, { from, to, format: "compact", includeDistractions: true });
|
|
108
|
+
assert.ok(!Array.isArray(compact));
|
|
109
|
+
assert.equal(compact.day, "2026-07-10");
|
|
110
|
+
assert.deepEqual(compact.cards, [
|
|
111
|
+
{ start: BOUNDARY_A.startUtc, end: BOUNDARY_A.endUtc, category: "development", title: "Morning editor", summary: "typed in vscode" },
|
|
112
|
+
{ start: BOUNDARY_B.startUtc, end: BOUNDARY_B.endUtc, category: "communication", title: "Standup call", summary: "zoom standup" },
|
|
113
|
+
]);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("range rejects an unknown format against the allow-list", () => {
|
|
117
|
+
assert.throws(
|
|
118
|
+
() =>
|
|
119
|
+
queryTimelineRange(CARDS, {
|
|
120
|
+
from: "2026-07-10T10:00:00.000Z",
|
|
121
|
+
to: "2026-07-10T11:00:00.000Z",
|
|
122
|
+
format: "markdown" as "cards",
|
|
123
|
+
}),
|
|
124
|
+
/format must be one of: cards, compact/,
|
|
125
|
+
);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("search empty results stay ok and unreadable store is distinct", () => {
|
|
129
|
+
assert.deepEqual(queryTimelineSearch(CARDS, { query: "no-such-token" }), { ok: true, results: [] });
|
|
130
|
+
assert.deepEqual(queryTimelineSearch(null, { query: "editor" }), { ok: false, error: "store_unreadable" });
|
|
131
|
+
assert.deepEqual(queryTimelineSearch(undefined, { query: "editor" }), { ok: false, error: "store_unreadable" });
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("search rank is match count then recency then id", () => {
|
|
135
|
+
const shuffled = [LATER, DISTRACTION, BOUNDARY_B, BOUNDARY_A];
|
|
136
|
+
const found = queryTimelineSearch(shuffled, { query: "standup editor" });
|
|
137
|
+
assert.equal(found.ok, true);
|
|
138
|
+
if (!found.ok) return;
|
|
139
|
+
assert.deepEqual(
|
|
140
|
+
found.results.map((item) => item.id),
|
|
141
|
+
["c", "b", "a"],
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("search honors detailedSummary and a 1..50 limit", () => {
|
|
146
|
+
const found = queryTimelineSearch(CARDS, { query: "writeup", limit: 1 });
|
|
147
|
+
assert.equal(found.ok, true);
|
|
148
|
+
if (!found.ok) return;
|
|
149
|
+
assert.deepEqual(
|
|
150
|
+
found.results.map((item) => item.id),
|
|
151
|
+
["c"],
|
|
152
|
+
);
|
|
153
|
+
assert.throws(() => queryTimelineSearch(CARDS, { query: "editor", limit: 0 }), /limit must be an integer from 1 to 50/);
|
|
154
|
+
assert.throws(() => queryTimelineSearch(CARDS, { query: "editor", limit: 51 }), /limit must be an integer from 1 to 50/);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("timeline.qa defaults off and honors 0-style disable plus maxRangeDays bounds", () => {
|
|
158
|
+
const defaults = parseActivityConfig(undefined).timeline.qa;
|
|
159
|
+
assert.deepEqual(defaults, { enabled: false, maxRangeDays: 31 });
|
|
160
|
+
assert.equal(parseActivityConfig({ timeline: { qa: { enabled: 0 } } }).timeline.qa.enabled, false);
|
|
161
|
+
assert.equal(parseActivityConfig({ timeline: { qa: { enabled: "off" } } }).timeline.qa.enabled, false);
|
|
162
|
+
assert.equal(parseActivityConfig({ timeline: { qa: { enabled: true, maxRangeDays: 1 } } }).timeline.qa.maxRangeDays, 1);
|
|
163
|
+
assert.throws(
|
|
164
|
+
() => parseActivityConfig({ timeline: { qa: { maxRangeDays: 0 } } }),
|
|
165
|
+
/maxRangeDays must be an integer from 1 to 366/,
|
|
166
|
+
);
|
|
167
|
+
assert.throws(
|
|
168
|
+
() => parseActivityConfig({ timeline: { qa: { maxRangeDays: 367 } } }),
|
|
169
|
+
/maxRangeDays must be an integer from 1 to 366/,
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("CLI runner accepts in-memory cards and does not need a store", async () => {
|
|
174
|
+
const qa = { enabled: true, maxRangeDays: 31 };
|
|
175
|
+
let out = "";
|
|
176
|
+
let err = "";
|
|
177
|
+
const io = {
|
|
178
|
+
stdout: { write: (chunk: string) => (out += chunk) },
|
|
179
|
+
stderr: { write: (chunk: string) => (err += chunk) },
|
|
180
|
+
};
|
|
181
|
+
const rangeCode = await runTimelineCliCommand(
|
|
182
|
+
{ cards: CARDS, qa, timelineEnabled: true },
|
|
183
|
+
["range", "--from", "2026-07-10T11:00:00.000Z", "--to", "2026-07-10T11:00:00.000Z", "--format", "cards"],
|
|
184
|
+
io,
|
|
185
|
+
);
|
|
186
|
+
assert.equal(rangeCode, 0);
|
|
187
|
+
assert.deepEqual(JSON.parse(out).map((item: TimelineCard) => item.id), ["b"]);
|
|
188
|
+
|
|
189
|
+
out = "";
|
|
190
|
+
const unread = await runTimelineCliCommand(
|
|
191
|
+
{ cards: null, qa, timelineEnabled: true },
|
|
192
|
+
["search", "--query", "editor"],
|
|
193
|
+
io,
|
|
194
|
+
);
|
|
195
|
+
assert.equal(unread, 1);
|
|
196
|
+
assert.deepEqual(JSON.parse(out), { ok: false, error: "store_unreadable" });
|
|
197
|
+
assert.equal(err, "");
|
|
198
|
+
});
|